webapp/src/components/Article/CommentsTree.tsx

198 lines
5.9 KiB
TypeScript
Raw Normal View History

2022-11-26 21:27:54 +00:00
import { clsx } from 'clsx'
import { Show, createMemo, createSignal, onMount, For, lazy } from 'solid-js'
import { useLocalize } from '../../context/localize'
2023-02-17 09:21:02 +00:00
import { useReactions } from '../../context/reactions'
import { useSession } from '../../context/session'
2023-11-28 13:18:25 +00:00
import { Author, Reaction, ReactionKind } from '../../graphql/schema/core.gen'
2023-02-17 09:21:02 +00:00
import { byCreated } from '../../utils/sortby'
import { Button } from '../_shared/Button'
2023-02-17 09:21:02 +00:00
import { ShowIfAuthenticated } from '../_shared/ShowIfAuthenticated'
2022-11-26 16:51:08 +00:00
import { Comment } from './Comment'
import styles from './Article.module.scss'
const SimplifiedEditor = lazy(() => import('../Editor/SimplifiedEditor'))
2023-02-21 12:32:51 +00:00
type CommentsOrder = 'createdAt' | 'rating' | 'newOnly'
2022-11-26 16:51:08 +00:00
2023-03-04 17:26:28 +00:00
const sortCommentsByRating = (a: Reaction, b: Reaction): -1 | 0 | 1 => {
2023-11-28 13:18:25 +00:00
if (a.reply_to && b.reply_to) {
2023-03-04 17:26:28 +00:00
return 0
}
const x = (a?.stat && a.stat.rating) || 0
const y = (b?.stat && b.stat.rating) || 0
if (x > y) {
return 1
}
if (x < y) {
return -1
}
return 0
}
type Props = {
2023-10-12 14:18:01 +00:00
articleAuthors: Author[]
shoutSlug: string
shoutId: number
}
export const CommentsTree = (props: Props) => {
2023-11-30 08:07:31 +00:00
const { author } = useSession()
2023-04-17 10:31:20 +00:00
const { t } = useLocalize()
2023-02-17 09:21:02 +00:00
const [commentsOrder, setCommentsOrder] = createSignal<CommentsOrder>('createdAt')
2023-04-17 10:31:20 +00:00
const [newReactions, setNewReactions] = createSignal<Reaction[]>([])
const [clearEditor, setClearEditor] = createSignal(false)
const [clickedReplyId, setClickedReplyId] = createSignal<number>()
2023-02-17 09:21:02 +00:00
const {
reactionEntities,
actions: { createReaction },
2023-02-17 09:21:02 +00:00
} = useReactions()
2023-02-11 10:49:31 +00:00
2023-02-17 09:21:02 +00:00
const comments = createMemo(() =>
Object.values(reactionEntities).filter((reaction) => reaction.kind === 'COMMENT'),
2023-02-17 09:21:02 +00:00
)
const sortedComments = createMemo(() => {
let newSortedComments = [...comments()]
newSortedComments = newSortedComments.sort(byCreated)
2023-03-04 17:26:28 +00:00
if (commentsOrder() === 'newOnly') {
return newReactions().reverse()
2023-02-17 09:21:02 +00:00
}
2023-03-04 17:26:28 +00:00
if (commentsOrder() === 'rating') {
newSortedComments = newSortedComments.sort(sortCommentsByRating)
2023-02-21 12:32:51 +00:00
}
2023-02-17 09:21:02 +00:00
newSortedComments.reverse()
return newSortedComments
})
2023-11-28 13:18:25 +00:00
const dateFromLocalStorage = Number.parseInt(localStorage.getItem(`${props.shoutSlug}`))
2023-03-06 14:06:48 +00:00
const currentDate = new Date()
const setCookie = () => localStorage.setItem(`${props.shoutSlug}`, `${currentDate}`)
onMount(() => {
if (!dateFromLocalStorage) {
2023-02-21 12:32:51 +00:00
setCookie()
2023-11-28 13:18:25 +00:00
} else if (currentDate.getTime() > dateFromLocalStorage) {
2023-02-21 12:32:51 +00:00
const newComments = comments().filter((c) => {
2023-11-30 08:07:31 +00:00
if (c.reply_to || c.created_by.slug === author()?.slug) {
2023-02-28 17:13:14 +00:00
return
}
2023-11-28 13:18:25 +00:00
const created = c.created_at
2023-03-06 14:06:48 +00:00
return created > dateFromLocalStorage
2023-02-21 12:32:51 +00:00
})
setNewReactions(newComments)
setCookie()
2023-02-11 10:49:31 +00:00
}
2023-02-17 09:21:02 +00:00
})
const handleSubmitComment = async (value) => {
try {
2023-02-17 09:21:02 +00:00
await createReaction({
kind: ReactionKind.Comment,
body: value,
shout: props.shoutId,
2023-02-17 09:21:02 +00:00
})
setClearEditor(true)
} catch (error) {
console.error('[handleCreate reaction]:', error)
}
setClearEditor(false)
}
2022-11-26 16:51:08 +00:00
return (
2023-02-28 17:13:14 +00:00
<>
<div class={styles.commentsHeaderWrapper}>
<h2 class={styles.commentsHeader}>
2023-02-28 17:13:14 +00:00
{t('Comments')} {comments().length.toString() || ''}
2023-03-06 14:06:48 +00:00
<Show when={newReactions().length > 0}>
<span class={styles.newReactions}>&nbsp;+{newReactions().length}</span>
2023-02-28 17:13:14 +00:00
</Show>
</h2>
2023-04-17 10:31:20 +00:00
<Show when={comments().length > 0}>
<ul class={clsx(styles.commentsViewSwitcher, 'view-switcher')}>
<Show when={newReactions().length > 0}>
2023-05-22 22:01:04 +00:00
<li classList={{ 'view-switcher__item--selected': commentsOrder() === 'newOnly' }}>
2023-04-17 10:31:20 +00:00
<Button
2023-05-22 22:01:04 +00:00
variant="light"
2023-04-17 10:31:20 +00:00
value={t('New only')}
onClick={() => {
setCommentsOrder('newOnly')
}}
/>
</li>
</Show>
2023-05-22 22:01:04 +00:00
<li classList={{ 'view-switcher__item--selected': commentsOrder() === 'createdAt' }}>
2023-02-07 12:48:45 +00:00
<Button
2023-05-22 22:01:04 +00:00
variant="light"
2023-04-17 10:31:20 +00:00
value={t('By time')}
2023-02-07 12:48:45 +00:00
onClick={() => {
2023-04-17 10:31:20 +00:00
setCommentsOrder('createdAt')
}}
2023-02-07 12:48:45 +00:00
/>
2022-11-26 21:27:54 +00:00
</li>
2023-05-22 22:01:04 +00:00
<li classList={{ 'view-switcher__item--selected': commentsOrder() === 'rating' }}>
2023-04-17 10:31:20 +00:00
<Button
2023-05-22 22:01:04 +00:00
variant="light"
2023-04-17 10:31:20 +00:00
value={t('By rating')}
onClick={() => {
setCommentsOrder('rating')
}}
/>
</li>
</ul>
</Show>
2023-02-28 17:13:14 +00:00
</div>
<ul class={styles.comments}>
2024-01-11 17:40:06 +00:00
<For each={sortedComments().filter((r) => !r.reply_to)}>
2023-02-28 17:13:14 +00:00
{(reaction) => (
<Comment
sortedComments={sortedComments()}
2023-10-12 14:18:01 +00:00
isArticleAuthor={Boolean(
props.articleAuthors.some((a) => a?.slug === reaction.created_by.slug),
2023-10-12 14:18:01 +00:00
)}
2023-02-28 17:13:14 +00:00
comment={reaction}
clickedReply={(id) => setClickedReplyId(id)}
clickedReplyId={clickedReplyId()}
2023-03-06 14:06:48 +00:00
lastSeen={dateFromLocalStorage}
2023-02-28 17:13:14 +00:00
/>
)}
</For>
</ul>
<ShowIfAuthenticated
fallback={
<div class={styles.signInMessage}>
2023-03-10 17:42:48 +00:00
{t('To write a comment, you must')}{' '}
2024-01-27 06:21:48 +00:00
<a href="?m=auth&mode=register" class={styles.link}>
2023-02-28 17:13:14 +00:00
{t('sign up')}
2023-03-10 17:42:48 +00:00
</a>{' '}
{t('or')}&nbsp;
2024-01-27 06:21:48 +00:00
<a href="?m=auth&mode=login" class={styles.link}>
2023-02-28 17:13:14 +00:00
{t('sign in')}
</a>
</div>
}
>
<SimplifiedEditor
quoteEnabled={true}
imageEnabled={true}
autoFocus={false}
submitByCtrlEnter={true}
2023-02-28 17:13:14 +00:00
placeholder={t('Write a comment...')}
onSubmit={(value) => handleSubmitComment(value)}
setClear={clearEditor()}
2023-02-28 17:13:14 +00:00
/>
</ShowIfAuthenticated>
</>
2022-11-26 16:51:08 +00:00
)
}