webapp/src/components/Article/CommentsTree.tsx

139 lines
4.5 KiB
TypeScript
Raw Normal View History

2023-02-07 12:48:45 +00:00
import { Show, createMemo, createSignal, onMount, For } from 'solid-js'
2022-11-26 16:51:08 +00:00
import Comment from './Comment'
import { t } from '../../utils/intl'
import styles from '../../styles/Article.module.scss'
import { createReaction, useReactionsStore } from '../../stores/zine/reactions'
2022-11-26 16:51:08 +00:00
import type { Reaction } from '../../graphql/types.gen'
2022-11-26 21:27:54 +00:00
import { clsx } from 'clsx'
import { byCreated, byStat } from '../../utils/sortby'
import { Loading } from '../Loading'
import { Author, ReactionKind } from '../../graphql/types.gen'
import { useSession } from '../../context/session'
import CommentEditor from '../_shared/CommentEditor'
import { ShowOnlyOnClient } from '../_shared/ShowOnlyOnClient'
2023-02-07 12:48:45 +00:00
import Button from '../_shared/Button'
2022-11-26 16:51:08 +00:00
const ARTICLE_COMMENTS_PAGE_SIZE = 50
const MAX_COMMENT_LEVEL = 6
type Props = {
commentAuthors: Author[]
shoutSlug: string
shoutId: number
}
export const CommentsTree = (props: Props) => {
2022-11-26 16:51:08 +00:00
const [getCommentsPage, setCommentsPage] = createSignal(0)
const [commentsOrder, setCommentsOrder] = createSignal<'rating' | 'createdAt'>('createdAt')
2022-11-26 16:51:08 +00:00
const [isCommentsLoading, setIsCommentsLoading] = createSignal(false)
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
2022-12-06 16:03:55 +00:00
const { sortedReactions, loadReactionsBy } = useReactionsStore()
2023-02-07 12:48:45 +00:00
const reactions = createMemo<Reaction[]>(() =>
sortedReactions().sort(commentsOrder() === 'rating' ? byStat('rating') : byCreated)
)
2023-02-07 12:48:45 +00:00
const { session } = useSession()
2022-11-26 16:51:08 +00:00
const loadMore = async () => {
try {
const page = getCommentsPage()
setIsCommentsLoading(true)
const { hasMore } = await loadReactionsBy({
2022-12-06 16:03:55 +00:00
by: { shout: props.shoutSlug, comment: true },
2022-11-26 16:51:08 +00:00
limit: ARTICLE_COMMENTS_PAGE_SIZE,
offset: page * ARTICLE_COMMENTS_PAGE_SIZE
})
setIsLoadMoreButtonVisible(hasMore)
} finally {
setIsCommentsLoading(false)
}
}
2022-11-29 11:01:23 +00:00
const getCommentById = (cid: number) => reactions().find((r: Reaction) => r.id === cid)
2022-11-26 16:51:08 +00:00
const getCommentLevel = (c: Reaction, level = 0) => {
if (c && c.replyTo && level < MAX_COMMENT_LEVEL) {
return getCommentLevel(getCommentById(c.replyTo), level + 1)
}
return level
}
onMount(async () => await loadMore())
const [submitted, setSubmitted] = createSignal<boolean>(false)
const handleSubmitComment = async (value) => {
try {
await createReaction(
{
kind: ReactionKind.Comment,
body: value,
shout: props.shoutId
},
{
name: session().user.name,
userpic: session().user.userpic,
slug: session().user.slug
}
)
setSubmitted(true)
} catch (error) {
console.error('[handleCreate reaction]:', error)
}
}
2022-11-26 16:51:08 +00:00
return (
<div>
<Show when={!isCommentsLoading()} fallback={<Loading />}>
2022-11-26 21:27:54 +00:00
<div class={styles.commentsHeaderWrapper}>
<h2 id="comments" class={styles.commentsHeader}>
{t('Comments')} {reactions().length.toString() || ''}
</h2>
<ul class={clsx(styles.commentsViewSwitcher, 'view-switcher')}>
<li classList={{ selected: commentsOrder() === 'createdAt' || !commentsOrder() }}>
2023-02-07 12:48:45 +00:00
<Button
variant="inline"
value={t('By time')}
onClick={() => {
setCommentsOrder('createdAt')
}}
2023-02-07 12:48:45 +00:00
/>
2022-11-26 21:27:54 +00:00
</li>
<li classList={{ selected: commentsOrder() === 'rating' }}>
2023-02-07 12:48:45 +00:00
<Button
variant="inline"
value={t('By rating')}
onClick={() => {
setCommentsOrder('rating')
}}
2023-02-07 12:48:45 +00:00
/>
2022-11-26 21:27:54 +00:00
</li>
</ul>
</div>
<ul class={styles.comments}>
<For
each={reactions()
.reverse()
.filter((r) => !r.replyTo)}
>
{(reaction) => (
<Comment
isArticleAuthor={Boolean(props.commentAuthors.some((a) => a.slug === session()?.user.slug))}
reactions={reactions()}
comment={reaction}
/>
)}
</For>
</ul>
2022-11-26 16:51:08 +00:00
<Show when={isLoadMoreButtonVisible()}>
2022-12-03 07:15:13 +00:00
<button onClick={loadMore}>{t('Load more')}</button>
2022-11-26 16:51:08 +00:00
</Show>
<ShowOnlyOnClient>
<CommentEditor
2023-02-07 12:48:45 +00:00
placeholder={t('Write a comment...')}
clear={submitted()}
onSubmit={(value) => handleSubmitComment(value)}
/>
</ShowOnlyOnClient>
2022-11-26 16:51:08 +00:00
</Show>
</div>
2022-11-26 16:51:08 +00:00
)
}