2023-10-16 07:40:34 +00:00
|
|
|
import { Show, createMemo, createSignal, onMount, For, createEffect } from 'solid-js'
|
2023-02-17 09:21:02 +00:00
|
|
|
import { Comment } from './Comment'
|
2023-03-08 16:35:13 +00:00
|
|
|
import styles from './Article.module.scss'
|
2022-11-26 21:27:54 +00:00
|
|
|
import { clsx } from 'clsx'
|
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-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-07-24 08:58:07 +00:00
|
|
|
import SimplifiedEditor from '../Editor/SimplifiedEditor'
|
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-03-04 17:26:28 +00:00
|
|
|
const sortCommentsByRating = (a: Reaction, b: Reaction): -1 | 0 | 1 => {
|
|
|
|
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-01-26 06:59:43 +00:00
|
|
|
type Props = {
|
2023-10-12 14:18:01 +00:00
|
|
|
articleAuthors: Author[]
|
2023-01-26 06:59:43 +00:00
|
|
|
shoutSlug: string
|
|
|
|
shoutId: number
|
|
|
|
}
|
|
|
|
|
|
|
|
export const CommentsTree = (props: Props) => {
|
2023-04-17 10:31:20 +00:00
|
|
|
const { session } = useSession()
|
|
|
|
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[]>([])
|
2023-10-16 07:40:34 +00:00
|
|
|
const [clearEditor, setClearEditor] = createSignal(false)
|
|
|
|
const [clickedReplyId, setClickedReplyId] = createSignal<number>()
|
|
|
|
|
2023-02-17 09:21:02 +00:00
|
|
|
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 comments = createMemo(() =>
|
|
|
|
Object.values(reactionEntities).filter((reaction) => reaction.kind === 'COMMENT')
|
|
|
|
)
|
|
|
|
|
|
|
|
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-03-06 14:06:48 +00:00
|
|
|
const dateFromLocalStorage = new Date(localStorage.getItem(`${props.shoutSlug}`))
|
|
|
|
const currentDate = new Date()
|
|
|
|
const setCookie = () => localStorage.setItem(`${props.shoutSlug}`, `${currentDate}`)
|
|
|
|
|
|
|
|
onMount(() => {
|
|
|
|
if (!dateFromLocalStorage) {
|
2023-02-21 12:32:51 +00:00
|
|
|
setCookie()
|
2023-03-06 14:06:48 +00:00
|
|
|
} else if (currentDate > dateFromLocalStorage) {
|
2023-02-21 12:32:51 +00:00
|
|
|
const newComments = comments().filter((c) => {
|
2023-04-17 10:31:20 +00:00
|
|
|
if (c.replyTo || c.createdBy.slug === session()?.user.slug) {
|
2023-02-28 17:13:14 +00:00
|
|
|
return
|
|
|
|
}
|
2023-03-06 14:06:48 +00:00
|
|
|
const created = new Date(c.createdAt)
|
|
|
|
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
|
|
|
})
|
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-10-16 07:40:34 +00:00
|
|
|
setClearEditor(true)
|
2023-01-20 04:40:55 +00:00
|
|
|
} catch (error) {
|
|
|
|
console.error('[handleCreate reaction]:', error)
|
|
|
|
}
|
2023-10-16 07:40:34 +00:00
|
|
|
setClearEditor(false)
|
2023-01-20 04:40:55 +00:00
|
|
|
}
|
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}>
|
2023-04-03 12:55:00 +00:00
|
|
|
<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}> +{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')
|
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-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}>
|
|
|
|
<For each={sortedComments().filter((r) => !r.replyTo)}>
|
|
|
|
{(reaction) => (
|
|
|
|
<Comment
|
|
|
|
sortedComments={sortedComments()}
|
2023-10-12 14:18:01 +00:00
|
|
|
isArticleAuthor={Boolean(
|
|
|
|
props.articleAuthors.some((a) => a.slug === reaction.createdBy.slug)
|
|
|
|
)}
|
2023-02-28 17:13:14 +00:00
|
|
|
comment={reaction}
|
2023-10-16 07:40:34 +00:00
|
|
|
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={
|
2023-04-03 12:55:00 +00:00
|
|
|
<div class={styles.signInMessage}>
|
2023-03-10 17:42:48 +00:00
|
|
|
{t('To write a comment, you must')}{' '}
|
2023-02-28 17:13:14 +00:00
|
|
|
<a href="?modal=auth&mode=register" class={styles.link}>
|
|
|
|
{t('sign up')}
|
2023-03-10 17:42:48 +00:00
|
|
|
</a>{' '}
|
|
|
|
{t('or')}
|
2023-02-28 17:13:14 +00:00
|
|
|
<a href="?modal=auth&mode=login" class={styles.link}>
|
|
|
|
{t('sign in')}
|
|
|
|
</a>
|
|
|
|
</div>
|
|
|
|
}
|
|
|
|
>
|
2023-07-24 08:58:07 +00:00
|
|
|
<SimplifiedEditor
|
|
|
|
quoteEnabled={true}
|
|
|
|
imageEnabled={true}
|
2023-10-16 07:40:34 +00:00
|
|
|
autoFocus={true}
|
|
|
|
submitByCtrlEnter={true}
|
2023-02-28 17:13:14 +00:00
|
|
|
placeholder={t('Write a comment...')}
|
|
|
|
onSubmit={(value) => handleSubmitComment(value)}
|
2023-10-16 07:40:34 +00:00
|
|
|
setClear={clearEditor()}
|
2023-02-28 17:13:14 +00:00
|
|
|
/>
|
|
|
|
</ShowIfAuthenticated>
|
|
|
|
</>
|
2022-11-26 16:51:08 +00:00
|
|
|
)
|
|
|
|
}
|