2023-02-07 12:48:45 +00:00
|
|
|
import { Show, createMemo, createSignal, onMount, For } from 'solid-js'
|
2023-02-17 09:21:02 +00:00
|
|
|
import { Comment } from './Comment'
|
2022-11-26 16:51:08 +00:00
|
|
|
import styles from '../../styles/Article.module.scss'
|
2022-11-26 21:27:54 +00:00
|
|
|
import { clsx } from 'clsx'
|
2023-02-17 09:21:02 +00:00
|
|
|
import { Loading } from '../_shared/Loading'
|
2023-02-21 12:32:51 +00:00
|
|
|
import { Author, Reaction, ReactionKind } from '../../graphql/types.gen'
|
2023-01-20 04:40:55 +00:00
|
|
|
import { useSession } from '../../context/session'
|
2023-01-26 06:59:43 +00:00
|
|
|
import CommentEditor from '../_shared/CommentEditor'
|
2023-02-17 09:21:02 +00:00
|
|
|
import { Button } from '../_shared/Button'
|
|
|
|
import { useReactions } from '../../context/reactions'
|
|
|
|
import { byCreated } from '../../utils/sortby'
|
|
|
|
import { ShowIfAuthenticated } from '../_shared/ShowIfAuthenticated'
|
|
|
|
import { useLocalize } from '../../context/localize'
|
2023-02-21 12:32:51 +00:00
|
|
|
import Cookie from 'js-cookie'
|
2022-11-26 16:51:08 +00:00
|
|
|
|
2023-02-21 12:32:51 +00:00
|
|
|
type CommentsOrder = 'createdAt' | 'rating' | 'newOnly'
|
2022-11-26 16:51:08 +00:00
|
|
|
|
2023-01-26 06:59:43 +00:00
|
|
|
type Props = {
|
|
|
|
commentAuthors: Author[]
|
|
|
|
shoutSlug: string
|
|
|
|
shoutId: number
|
|
|
|
}
|
|
|
|
|
|
|
|
export const CommentsTree = (props: Props) => {
|
2023-02-17 09:21:02 +00:00
|
|
|
const [commentsOrder, setCommentsOrder] = createSignal<CommentsOrder>('createdAt')
|
|
|
|
const {
|
|
|
|
reactionEntities,
|
2023-02-28 17:13:14 +00:00
|
|
|
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 { t } = useLocalize()
|
|
|
|
|
|
|
|
const [newReactionsCount, setNewReactionsCount] = createSignal<number>(0)
|
2023-02-21 12:32:51 +00:00
|
|
|
const [newReactions, setNewReactions] = createSignal<Reaction[]>([])
|
2023-02-17 09:21:02 +00:00
|
|
|
|
|
|
|
const comments = createMemo(() =>
|
|
|
|
Object.values(reactionEntities).filter((reaction) => reaction.kind === 'COMMENT')
|
|
|
|
)
|
|
|
|
|
|
|
|
const sortedComments = createMemo(() => {
|
|
|
|
let newSortedComments = [...comments()]
|
|
|
|
newSortedComments = newSortedComments.sort(byCreated)
|
|
|
|
|
|
|
|
if (commentsOrder() === 'rating') {
|
|
|
|
newSortedComments = newSortedComments.sort((a, b) => {
|
|
|
|
if (a.replyTo && b.replyTo) {
|
|
|
|
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
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-02-21 12:32:51 +00:00
|
|
|
if (commentsOrder() === 'newOnly') {
|
|
|
|
newSortedComments = newReactions()
|
|
|
|
}
|
|
|
|
|
2023-02-17 09:21:02 +00:00
|
|
|
newSortedComments.reverse()
|
|
|
|
|
|
|
|
return newSortedComments
|
|
|
|
})
|
|
|
|
|
|
|
|
const updateNewReactionsCount = () => {
|
2023-02-21 12:32:51 +00:00
|
|
|
const dateFromCookie = new Date(Cookie.get(`${props.shoutSlug}`)).valueOf()
|
|
|
|
const setCookie = () => Cookie.set(`${props.shoutSlug}`, `${Date.now()}`)
|
|
|
|
if (!dateFromCookie) {
|
|
|
|
setCookie()
|
|
|
|
} else if (Date.now() > dateFromCookie) {
|
|
|
|
const newComments = comments().filter((c) => {
|
2023-02-28 17:13:14 +00:00
|
|
|
if (c.replyTo) {
|
|
|
|
return
|
|
|
|
}
|
2023-02-21 12:32:51 +00:00
|
|
|
const commentDate = new Date(c.createdAt).valueOf()
|
|
|
|
return commentDate > dateFromCookie
|
|
|
|
})
|
|
|
|
setNewReactions(newComments)
|
|
|
|
setNewReactionsCount(newComments.length)
|
|
|
|
setCookie()
|
2023-02-11 10:49:31 +00:00
|
|
|
}
|
|
|
|
}
|
2023-02-07 12:48:45 +00:00
|
|
|
|
2023-01-20 04:40:55 +00:00
|
|
|
const { session } = useSession()
|
2023-02-17 09:21:02 +00:00
|
|
|
|
|
|
|
onMount(async () => {
|
2023-02-28 17:13:14 +00:00
|
|
|
updateNewReactionsCount()
|
2023-02-17 09:21:02 +00:00
|
|
|
})
|
2023-01-20 04:40:55 +00:00
|
|
|
|
2023-01-26 06:59:43 +00:00
|
|
|
const [submitted, setSubmitted] = createSignal<boolean>(false)
|
2023-01-20 04:40:55 +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-01-26 06:59:43 +00:00
|
|
|
setSubmitted(true)
|
2023-01-20 04:40:55 +00:00
|
|
|
} catch (error) {
|
|
|
|
console.error('[handleCreate reaction]:', error)
|
|
|
|
}
|
|
|
|
}
|
2023-01-26 06:59:43 +00:00
|
|
|
|
2022-11-26 16:51:08 +00:00
|
|
|
return (
|
2023-02-28 17:13:14 +00:00
|
|
|
<>
|
|
|
|
<div class={styles.commentsHeaderWrapper}>
|
|
|
|
<h2 id="comments" class={styles.commentsHeader}>
|
|
|
|
{t('Comments')} {comments().length.toString() || ''}
|
|
|
|
<Show when={newReactionsCount() > 0}>
|
|
|
|
<span class={styles.newReactions}> +{newReactionsCount()}</span>
|
|
|
|
</Show>
|
|
|
|
</h2>
|
|
|
|
|
|
|
|
<ul class={clsx(styles.commentsViewSwitcher, 'view-switcher')}>
|
|
|
|
<Show when={newReactionsCount() > 0}>
|
|
|
|
<li classList={{ selected: commentsOrder() === 'newOnly' }}>
|
2023-02-07 12:48:45 +00:00
|
|
|
<Button
|
|
|
|
variant="inline"
|
2023-02-28 17:13:14 +00:00
|
|
|
value={t('New only')}
|
2023-02-07 12:48:45 +00:00
|
|
|
onClick={() => {
|
2023-02-28 17:13:14 +00:00
|
|
|
setCommentsOrder('newOnly')
|
2022-11-27 08:15:03 +00:00
|
|
|
}}
|
2023-02-07 12:48:45 +00:00
|
|
|
/>
|
2022-11-26 21:27:54 +00:00
|
|
|
</li>
|
2023-02-28 17:13:14 +00:00
|
|
|
</Show>
|
|
|
|
<li classList={{ selected: commentsOrder() === 'createdAt' }}>
|
|
|
|
<Button
|
|
|
|
variant="inline"
|
|
|
|
value={t('By time')}
|
|
|
|
onClick={() => {
|
|
|
|
setCommentsOrder('createdAt')
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
</li>
|
|
|
|
<li classList={{ selected: commentsOrder() === 'rating' }}>
|
|
|
|
<Button
|
|
|
|
variant="inline"
|
|
|
|
value={t('By rating')}
|
|
|
|
onClick={() => {
|
|
|
|
setCommentsOrder('rating')
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
</li>
|
2023-01-20 04:40:55 +00:00
|
|
|
</ul>
|
2023-02-28 17:13:14 +00:00
|
|
|
</div>
|
|
|
|
<ul class={styles.comments}>
|
|
|
|
<For each={sortedComments().filter((r) => !r.replyTo)}>
|
|
|
|
{(reaction) => (
|
|
|
|
<Comment
|
|
|
|
sortedComments={sortedComments()}
|
|
|
|
isArticleAuthor={Boolean(props.commentAuthors.some((a) => a.slug === session()?.user.slug))}
|
|
|
|
comment={reaction}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
</For>
|
|
|
|
</ul>
|
|
|
|
<ShowIfAuthenticated
|
|
|
|
fallback={
|
|
|
|
<div class={styles.signInMessage} id="comments">
|
|
|
|
{t('To write a comment, you must')}
|
|
|
|
<a href="?modal=auth&mode=register" class={styles.link}>
|
|
|
|
{t('sign up')}
|
|
|
|
</a>
|
|
|
|
{t('or')}
|
|
|
|
<a href="?modal=auth&mode=login" class={styles.link}>
|
|
|
|
{t('sign in')}
|
|
|
|
</a>
|
|
|
|
</div>
|
|
|
|
}
|
|
|
|
>
|
|
|
|
<CommentEditor
|
|
|
|
placeholder={t('Write a comment...')}
|
|
|
|
clear={submitted()}
|
|
|
|
onSubmit={(value) => handleSubmitComment(value)}
|
|
|
|
/>
|
|
|
|
</ShowIfAuthenticated>
|
|
|
|
</>
|
2022-11-26 16:51:08 +00:00
|
|
|
)
|
|
|
|
}
|