Comments rating

This commit is contained in:
ilya-bkv 2023-03-09 15:34:08 +03:00
parent 58a2091e0d
commit 4389124912
5 changed files with 165 additions and 45 deletions

View File

@ -201,39 +201,6 @@
margin-bottom: 1.2rem; margin-bottom: 1.2rem;
} }
.commentRating {
align-items: center;
display: flex;
font-weight: bold;
}
.commentRatingValue {
padding: 0 0.3em;
}
.commentRatingPositive {
color: #2bb452;
}
.commentRatingNegative {
color: #d00820;
}
.commentRatingControl {
border-left: 6px solid transparent;
border-right: 6px solid transparent;
height: 0;
width: 0;
}
.commentRatingControlUp {
border-bottom: 8px solid rgb(0 0 0 / 40%);
}
.commentRatingControlDown {
border-top: 8px solid rgb(0 0 0 / 40%);
}
.compactUserpic { .compactUserpic {
height: 28px; height: 28px;
width: 28px; width: 28px;

View File

@ -13,7 +13,7 @@ import { useReactions } from '../../context/reactions'
import { useSnackbar } from '../../context/snackbar' import { useSnackbar } from '../../context/snackbar'
import { ShowIfAuthenticated } from '../_shared/ShowIfAuthenticated' import { ShowIfAuthenticated } from '../_shared/ShowIfAuthenticated'
import { useLocalize } from '../../context/localize' import { useLocalize } from '../../context/localize'
import Cookie from 'js-cookie' import { CommentRatingControl } from './CommentRatingControl'
const CommentEditor = lazy(() => import('../_shared/CommentEditor')) const CommentEditor = lazy(() => import('../_shared/CommentEditor'))
@ -97,6 +97,7 @@ export const Comment = (props: Props) => {
return ( return (
<li class={clsx(styles.comment, { [styles.isNew]: !isCommentAuthor() && createdAt > props.lastSeen })}> <li class={clsx(styles.comment, { [styles.isNew]: !isCommentAuthor() && createdAt > props.lastSeen })}>
<Show when={!!body()}> <Show when={!!body()}>
<div style={{ color: 'red' }}>{comment().id}</div>
<div class={styles.commentContent}> <div class={styles.commentContent}>
<Show <Show
when={!props.compact} when={!props.compact}
@ -140,17 +141,7 @@ export const Comment = (props: Props) => {
</div> </div>
</Show> </Show>
</div> </div>
<div <CommentRatingControl comment={comment()} />
class={styles.commentRating}
classList={{
[styles.commentRatingPositive]: comment().stat.rating > 0,
[styles.commentRatingNegative]: comment().stat.rating < 0
}}
>
<button class={clsx(styles.commentRatingControl, styles.commentRatingControlUp)} />
<div class={styles.commentRatingValue}>{comment().stat.rating || 0}</div>
<button class={clsx(styles.commentRatingControl, styles.commentRatingControlDown)} />
</div>
</div> </div>
</Show> </Show>
<div class={styles.commentBody} id={'comment-' + (comment().id || '')}> <div class={styles.commentBody} id={'comment-' + (comment().id || '')}>

View File

@ -0,0 +1,45 @@
.commentRating {
align-items: center;
display: flex;
font-weight: bold;
.commentRatingValue {
padding: 0 0.3em;
margin: 0 0.6rem;
font-size: 1.2rem;
cursor: pointer;
transition: opacity 0.3s ease-in-out;
&:hover {
opacity: 0.5;
}
}
.commentRatingPositive {
color: #2bb452;
}
.commentRatingNegative {
color: #d00820;
}
.commentRatingControl {
border-left: 6px solid transparent;
border-right: 6px solid transparent;
height: 0;
width: 0;
}
.commentRatingControlUp {
border-bottom: 8px solid rgb(0 0 0 / 40%);
&.voted {
border-bottom-color: #2bb452;
}
}
.commentRatingControlDown {
border-top: 8px solid rgb(0 0 0 / 40%);
&.voted {
border-top-color: #d00820;
}
}
}

View File

@ -0,0 +1,112 @@
import { clsx } from 'clsx'
import styles from './CommentRatingControl.module.scss'
import type { Reaction } from '../../graphql/types.gen'
import { ReactionKind } from '../../graphql/types.gen'
import { useSession } from '../../context/session'
import { useReactions } from '../../context/reactions'
import { createMemo, For } from 'solid-js'
import { loadShout } from '../../stores/zine/articles'
import { Popup } from '../_shared/Popup'
type Props = {
comment: Reaction
}
export const CommentRatingControl = (props: Props) => {
const { userSlug } = useSession()
const {
reactionEntities,
actions: { createReaction, deleteReaction, loadReactionsBy }
} = useReactions()
const checkReaction = (reactionKind: ReactionKind) =>
Object.values(reactionEntities).some(
(r) =>
r.kind === reactionKind &&
r.createdBy.slug === userSlug() &&
r.shout.id === props.comment.shout.id &&
r.replyTo === props.comment.id
)
const isUpvoted = createMemo(() => checkReaction(ReactionKind.Like))
const isDownvoted = createMemo(() => checkReaction(ReactionKind.Dislike))
const canVote = userSlug() !== props.comment.createdBy.slug
const shoutRatingReactions = createMemo(() =>
Object.values(reactionEntities).filter(
(r) =>
[ReactionKind.Like, ReactionKind.Dislike].includes(r.kind) &&
r.shout.id === props.comment.shout.id &&
r.replyTo === props.comment.id
)
)
const deleteCommentReaction = async (reactionKind: ReactionKind) => {
const reactionToDelete = Object.values(reactionEntities).find(
(r) =>
r.kind === reactionKind &&
r.createdBy.slug === userSlug() &&
r.shout.id === props.comment.shout.id &&
r.replyTo === props.comment.id
)
return deleteReaction(reactionToDelete.id)
}
const handleRatingChange = async (isUpvote: boolean) => {
if (isUpvoted()) {
await deleteCommentReaction(ReactionKind.Like)
} else if (isDownvoted()) {
await deleteCommentReaction(ReactionKind.Dislike)
} else {
console.log('!!! createReaction:')
await createReaction({
kind: isUpvote ? ReactionKind.Like : ReactionKind.Dislike,
shout: props.comment.shout.id,
replyTo: props.comment.id
})
}
await loadShout(props.comment.shout.slug)
await loadReactionsBy({
by: { shout: props.comment.shout.slug }
})
}
return (
<div class={styles.commentRating}>
<button
onClick={() => canVote && handleRatingChange(true)}
class={clsx(styles.commentRatingControl, styles.commentRatingControlUp, {
[styles.voted]: isUpvoted()
})}
/>
<Popup
trigger={
<div
class={clsx(styles.commentRatingValue, {
[styles.commentRatingPositive]: props.comment.stat.rating > 0,
[styles.commentRatingNegative]: props.comment.stat.rating < 0
})}
>
{props.comment.stat.rating || 0}
</div>
}
variant="tiny"
>
<ul class={clsx('nodash')}>
<For each={shoutRatingReactions()}>
{(reaction) => (
<li>
{reaction.kind === ReactionKind.Like ? <>+1</> : <>&minus;1</>} {reaction.createdBy.name}
</li>
)}
</For>
</ul>
</Popup>
<button
onClick={() => canVote && handleRatingChange(false)}
class={clsx(styles.commentRatingControl, styles.commentRatingControlDown, {
[styles.voted]: isDownvoted()
})}
/>
</div>
)
}

5
src/utils/jsonParse.ts Normal file
View File

@ -0,0 +1,5 @@
export const jsonParse = <T>(obj: T) => JSON.parse(JSON.stringify(obj))
export const jsonParseLog = <T>(msg: string, obj: T) => {
console.log(`${msg}: `, JSON.parse(JSON.stringify(obj)))
}