likes dislikes
This commit is contained in:
parent
dc85e222b6
commit
b3b1c48d92
|
@ -22,16 +22,14 @@ type Props = {
|
|||
}
|
||||
|
||||
export const CommentsTree = (props: Props) => {
|
||||
const [isCommentsLoading, setIsCommentsLoading] = createSignal(false)
|
||||
const [commentsOrder, setCommentsOrder] = createSignal<CommentsOrder>('createdAt')
|
||||
const {
|
||||
reactionEntities,
|
||||
actions: { loadReactionsBy, createReaction }
|
||||
actions: { createReaction }
|
||||
} = useReactions()
|
||||
|
||||
const { t } = useLocalize()
|
||||
|
||||
// TODO: server side?
|
||||
const [newReactionsCount, setNewReactionsCount] = createSignal<number>(0)
|
||||
const [newReactions, setNewReactions] = createSignal<Reaction[]>([])
|
||||
|
||||
|
@ -79,7 +77,9 @@ export const CommentsTree = (props: Props) => {
|
|||
setCookie()
|
||||
} else if (Date.now() > dateFromCookie) {
|
||||
const newComments = comments().filter((c) => {
|
||||
if (c.replyTo) return
|
||||
if (c.replyTo) {
|
||||
return
|
||||
}
|
||||
const commentDate = new Date(c.createdAt).valueOf()
|
||||
return commentDate > dateFromCookie
|
||||
})
|
||||
|
@ -92,15 +92,7 @@ export const CommentsTree = (props: Props) => {
|
|||
const { session } = useSession()
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
setIsCommentsLoading(true)
|
||||
await loadReactionsBy({
|
||||
by: { shout: props.shoutSlug }
|
||||
})
|
||||
updateNewReactionsCount()
|
||||
} finally {
|
||||
setIsCommentsLoading(false)
|
||||
}
|
||||
updateNewReactionsCount()
|
||||
})
|
||||
|
||||
const [submitted, setSubmitted] = createSignal<boolean>(false)
|
||||
|
@ -118,80 +110,78 @@ export const CommentsTree = (props: Props) => {
|
|||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Show when={!isCommentsLoading()} fallback={<Loading />}>
|
||||
<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>
|
||||
<>
|
||||
<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' }}>
|
||||
<Button
|
||||
variant="inline"
|
||||
value={t('New only')}
|
||||
onClick={() => {
|
||||
setCommentsOrder('newOnly')
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
</Show>
|
||||
<li classList={{ selected: commentsOrder() === 'createdAt' }}>
|
||||
<ul class={clsx(styles.commentsViewSwitcher, 'view-switcher')}>
|
||||
<Show when={newReactionsCount() > 0}>
|
||||
<li classList={{ selected: commentsOrder() === 'newOnly' }}>
|
||||
<Button
|
||||
variant="inline"
|
||||
value={t('By time')}
|
||||
value={t('New only')}
|
||||
onClick={() => {
|
||||
setCommentsOrder('createdAt')
|
||||
setCommentsOrder('newOnly')
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
<li classList={{ selected: commentsOrder() === 'rating' }}>
|
||||
<Button
|
||||
variant="inline"
|
||||
value={t('By rating')}
|
||||
onClick={() => {
|
||||
setCommentsOrder('rating')
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</Show>
|
||||
</div>
|
||||
</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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@ import { capitalize, formatDate } from '../../utils'
|
|||
import './Full.scss'
|
||||
import { Icon } from '../_shared/Icon'
|
||||
import { AuthorCard } from '../Author/Card'
|
||||
import { createMemo, For, Match, onMount, Show, Switch } from 'solid-js'
|
||||
import type { Author, Shout } from '../../graphql/types.gen'
|
||||
import { createEffect, createMemo, createSignal, For, Match, onMount, Show, Switch } from 'solid-js'
|
||||
import type { Author, Reaction, Shout } from '../../graphql/types.gen'
|
||||
import { ReactionKind } from '../../graphql/types.gen'
|
||||
|
||||
import MD from './MD'
|
||||
|
@ -23,6 +23,7 @@ import { useReactions } from '../../context/reactions'
|
|||
import { loadShout } from '../../stores/zine/articles'
|
||||
import { Title } from '@solidjs/meta'
|
||||
import { useLocalize } from '../../context/localize'
|
||||
import { checkReaction } from '../../utils/checkReaction'
|
||||
|
||||
interface ArticleProps {
|
||||
article: Shout
|
||||
|
@ -59,7 +60,8 @@ const MediaView = (props: { media: MediaItem; kind: Shout['layout'] }) => {
|
|||
|
||||
export const FullArticle = (props: ArticleProps) => {
|
||||
const { t } = useLocalize()
|
||||
const { session } = useSession()
|
||||
const { userSlug, session } = useSession()
|
||||
const [isReactionsLoaded, setIsReactionsLoaded] = createSignal(false)
|
||||
const formattedDate = createMemo(() => formatDate(new Date(props.article.createdAt)))
|
||||
|
||||
const mainTopic = createMemo(
|
||||
|
@ -81,6 +83,14 @@ export const FullArticle = (props: ArticleProps) => {
|
|||
}
|
||||
})
|
||||
|
||||
onMount(async () => {
|
||||
await loadReactionsBy({
|
||||
by: { shout: props.article.slug }
|
||||
})
|
||||
|
||||
setIsReactionsLoaded(true)
|
||||
})
|
||||
|
||||
const canEdit = () => props.article.authors?.some((a) => a.slug === session()?.user?.slug)
|
||||
|
||||
const bookmark = (ev) => {
|
||||
|
@ -96,27 +106,52 @@ export const FullArticle = (props: ArticleProps) => {
|
|||
})
|
||||
|
||||
const {
|
||||
actions: { createReaction }
|
||||
reactionEntities,
|
||||
actions: { loadReactionsBy, createReaction, deleteReaction }
|
||||
} = useReactions()
|
||||
|
||||
const handleUpvote = async () => {
|
||||
await createReaction({
|
||||
kind: ReactionKind.Like,
|
||||
shout: props.article.id
|
||||
const updateReactions = () => {
|
||||
loadReactionsBy({
|
||||
by: { shout: props.article.slug }
|
||||
})
|
||||
|
||||
await loadShout(props.article.slug)
|
||||
}
|
||||
|
||||
const handleDownvote = async () => {
|
||||
await createReaction({
|
||||
kind: ReactionKind.Dislike,
|
||||
shout: props.article.id
|
||||
})
|
||||
const isUpvoted = createMemo(() =>
|
||||
checkReaction(Object.values(reactionEntities), ReactionKind.Like, userSlug(), props.article.id)
|
||||
)
|
||||
|
||||
await loadShout(props.article.slug)
|
||||
const isDownvoted = createMemo(() =>
|
||||
checkReaction(Object.values(reactionEntities), ReactionKind.Dislike, userSlug(), props.article.id)
|
||||
)
|
||||
|
||||
const handleRatingChange = async (isUpvote: boolean) => {
|
||||
const reactionKind = isUpvote ? ReactionKind.Like : ReactionKind.Dislike
|
||||
const isReacted = (isUpvote && isUpvoted()) || (!isUpvote && isDownvoted())
|
||||
|
||||
if (isReacted) {
|
||||
const reactionToDelete = Object.values(reactionEntities).find(
|
||||
(r) =>
|
||||
r.kind === reactionKind &&
|
||||
r.createdBy.slug === userSlug() &&
|
||||
r.shout.id === props.article.id &&
|
||||
!r.replyTo
|
||||
)
|
||||
await deleteReaction(reactionToDelete.id)
|
||||
} else {
|
||||
await createReaction({
|
||||
kind: reactionKind,
|
||||
shout: props.article.id
|
||||
})
|
||||
}
|
||||
|
||||
loadShout(props.article.slug)
|
||||
updateReactions()
|
||||
}
|
||||
|
||||
createEffect(() => {
|
||||
console.log('reactions', reactionEntities)
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title>{props.article.title}</Title>
|
||||
|
@ -200,8 +235,10 @@ export const FullArticle = (props: ArticleProps) => {
|
|||
<RatingControl
|
||||
rating={props.article.stat?.rating}
|
||||
class={styles.ratingControl}
|
||||
onUpvote={handleUpvote}
|
||||
onDownvote={handleDownvote}
|
||||
onUpvote={() => handleRatingChange(true)}
|
||||
onDownvote={() => handleRatingChange(false)}
|
||||
isUpvoted={isUpvoted()}
|
||||
isDownvoted={isDownvoted()}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
@ -265,22 +302,24 @@ export const FullArticle = (props: ArticleProps) => {
|
|||
</div>
|
||||
|
||||
<div class={styles.shoutAuthorsList}>
|
||||
<Show when={props.article?.authors?.length > 1}>
|
||||
<Show when={props.article.authors.length > 1}>
|
||||
<h4>{t('Authors')}</h4>
|
||||
</Show>
|
||||
<For each={props.article?.authors}>
|
||||
{(a: Author) => (
|
||||
<For each={props.article.authors}>
|
||||
{(a) => (
|
||||
<div class="col-xl-6">
|
||||
<AuthorCard author={a} compact={false} hasLink={true} liteButtons={true} />
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
<CommentsTree
|
||||
shoutId={props.article?.id}
|
||||
shoutSlug={props.article?.slug}
|
||||
commentAuthors={props.article?.authors}
|
||||
/>
|
||||
<Show when={isReactionsLoaded()}>
|
||||
<CommentsTree
|
||||
shoutId={props.article.id}
|
||||
shoutSlug={props.article.slug}
|
||||
commentAuthors={props.article.authors}
|
||||
/>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
.rating {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
|
||||
&.isDownvoted .downvoteButton,
|
||||
&.isUpvoted .upvoteButton {
|
||||
background: #000;
|
||||
border-color: #000;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.ratingValue {
|
||||
|
|
|
@ -12,12 +12,17 @@ interface RatingControlProps {
|
|||
|
||||
export const RatingControl = (props: RatingControlProps) => {
|
||||
return (
|
||||
<div class={clsx(props.class, styles.rating)}>
|
||||
<button class={styles.ratingControl} onClick={props.onDownvote}>
|
||||
<div
|
||||
class={clsx(styles.rating, props.class, {
|
||||
[styles.isUpvoted]: props.isUpvoted,
|
||||
[styles.isDownvoted]: props.isDownvoted
|
||||
})}
|
||||
>
|
||||
<button class={clsx(styles.ratingControl, styles.downvoteButton)} onClick={props.onDownvote}>
|
||||
−
|
||||
</button>
|
||||
<span class={styles.ratingValue}>{props?.rating || ''}</span>
|
||||
<button class={styles.ratingControl} onClick={props.onUpvote}>
|
||||
<button class={clsx(styles.ratingControl, styles.upvoteButton)} onClick={props.onUpvote}>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -12,6 +12,11 @@ import stylesHeader from '../Nav/Header.module.scss'
|
|||
import { getDescription } from '../../utils/meta'
|
||||
import { FeedArticlePopup } from './FeedArticlePopup'
|
||||
import { useLocalize } from '../../context/localize'
|
||||
import { ReactionKind } from '../../graphql/types.gen'
|
||||
import { loadShout } from '../../stores/zine/articles'
|
||||
import { useReactions } from '../../context/reactions'
|
||||
import { checkReaction } from '../../utils/checkReaction'
|
||||
import { useSession } from '../../context/session'
|
||||
|
||||
interface ArticleCardProps {
|
||||
settings?: {
|
||||
|
@ -61,6 +66,13 @@ const getTitleAndSubtitle = (article: Shout): { title: string; subtitle: string
|
|||
export const ArticleCard = (props: ArticleCardProps) => {
|
||||
const { t, lang } = useLocalize()
|
||||
|
||||
const { userSlug } = useSession()
|
||||
|
||||
const {
|
||||
reactionEntities,
|
||||
actions: { createReaction, deleteReaction, loadReactionsBy }
|
||||
} = useReactions()
|
||||
|
||||
const mainTopic =
|
||||
props.article.topics.find((articleTopic) => articleTopic.slug === props.article.mainTopic) ||
|
||||
props.article.topics[0]
|
||||
|
@ -73,7 +85,41 @@ export const ArticleCard = (props: ArticleCardProps) => {
|
|||
|
||||
const { title, subtitle } = getTitleAndSubtitle(props.article)
|
||||
|
||||
const { cover, layout, slug, authors, stat, body } = props.article
|
||||
const { cover, layout, slug, authors, stat, body, id } = props.article
|
||||
|
||||
const updateReactions = () => {
|
||||
loadReactionsBy({
|
||||
by: { shout: slug }
|
||||
})
|
||||
}
|
||||
|
||||
const isUpvoted = createMemo(() =>
|
||||
checkReaction(Object.values(reactionEntities), ReactionKind.Like, userSlug(), id)
|
||||
)
|
||||
|
||||
const isDownvoted = createMemo(() =>
|
||||
checkReaction(Object.values(reactionEntities), ReactionKind.Dislike, userSlug(), id)
|
||||
)
|
||||
|
||||
const handleRatingChange = async (isUpvote: boolean) => {
|
||||
const reactionKind = isUpvote ? ReactionKind.Like : ReactionKind.Dislike
|
||||
const isReacted = (isUpvote && isUpvoted()) || (!isUpvote && isDownvoted())
|
||||
|
||||
if (isReacted) {
|
||||
const reactionToDelete = Object.values(reactionEntities).find(
|
||||
(r) => r.kind === reactionKind && r.createdBy.slug === userSlug() && r.shout.id === id && !r.replyTo
|
||||
)
|
||||
await deleteReaction(reactionToDelete.id)
|
||||
} else {
|
||||
await createReaction({
|
||||
kind: reactionKind,
|
||||
shout: id
|
||||
})
|
||||
}
|
||||
|
||||
loadShout(slug)
|
||||
updateReactions()
|
||||
}
|
||||
|
||||
return (
|
||||
<section
|
||||
|
@ -165,7 +211,14 @@ export const ArticleCard = (props: ArticleCardProps) => {
|
|||
<Show when={props.settings?.isFeedMode}>
|
||||
<section class={styles.shoutCardDetails}>
|
||||
<div class={styles.shoutCardDetailsContent}>
|
||||
<RatingControl rating={stat?.rating} class={styles.shoutCardDetailsItem} />
|
||||
<RatingControl
|
||||
rating={stat.rating}
|
||||
class={styles.shoutCardDetailsItem}
|
||||
onUpvote={() => handleRatingChange(true)}
|
||||
onDownvote={() => handleRatingChange(false)}
|
||||
isUpvoted={isUpvoted()}
|
||||
isDownvoted={isDownvoted()}
|
||||
/>
|
||||
|
||||
<div class={clsx(styles.shoutCardDetailsItem, styles.shoutCardDetailsViewed)}>
|
||||
<Icon name="eye" class={clsx(styles.icon, styles.feedControlIcon)} />
|
||||
|
|
|
@ -59,11 +59,18 @@ export const FeedView = () => {
|
|||
})
|
||||
|
||||
const loadMore = async () => {
|
||||
const { hasMore } = await loadShouts({
|
||||
const { hasMore, newShouts } = await loadShouts({
|
||||
filters: { visibility: 'community' },
|
||||
limit: FEED_PAGE_SIZE,
|
||||
offset: sortedArticles().length
|
||||
})
|
||||
|
||||
loadReactionsBy({
|
||||
by: {
|
||||
shouts: newShouts.map((s) => s.slug)
|
||||
}
|
||||
})
|
||||
|
||||
setIsLoadMoreButtonVisible(hasMore)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import type { JSX } from 'solid-js'
|
||||
import { createContext, onCleanup, useContext } from 'solid-js'
|
||||
import type { Reaction, ReactionBy, ReactionInput } from '../graphql/types.gen'
|
||||
import { ReactionKind } from '../graphql/types.gen'
|
||||
import { apiClient } from '../utils/apiClient'
|
||||
import { createStore } from 'solid-js/store'
|
||||
|
||||
|
@ -41,12 +42,33 @@ export const ReactionsProvider = (props: { children: JSX.Element }) => {
|
|||
|
||||
const createReaction = async (input: ReactionInput): Promise<void> => {
|
||||
const reaction = await apiClient.createReaction(input)
|
||||
setReactionEntities(reaction.id, reaction)
|
||||
|
||||
const changes = {
|
||||
[reaction.id]: reaction
|
||||
}
|
||||
|
||||
if ([ReactionKind.Like, ReactionKind.Dislike].includes(reaction.kind)) {
|
||||
const oppositeReactionKind =
|
||||
reaction.kind === ReactionKind.Like ? ReactionKind.Dislike : ReactionKind.Like
|
||||
|
||||
const oppositeReaction = Object.values(reactionEntities).find(
|
||||
(r) =>
|
||||
r.kind === oppositeReactionKind &&
|
||||
r.createdBy.slug === reaction.createdBy.slug &&
|
||||
r.shout.id === reaction.shout.id &&
|
||||
r.replyTo === reaction.replyTo
|
||||
)
|
||||
|
||||
if (oppositeReaction) {
|
||||
changes[oppositeReaction.id] = undefined
|
||||
}
|
||||
}
|
||||
|
||||
setReactionEntities(changes)
|
||||
}
|
||||
|
||||
const deleteReaction = async (id: number): Promise<void> => {
|
||||
const reaction = await apiClient.destroyReaction(id)
|
||||
console.debug('[deleteReaction]:', reaction.id)
|
||||
setReactionEntities((oldState) => ({
|
||||
...oldState,
|
||||
[reaction.id]: undefined
|
||||
|
|
|
@ -2,16 +2,19 @@ import { createSignal, Show } from 'solid-js'
|
|||
import { PageLayout } from '../../components/_shared/PageLayout'
|
||||
import { Donate } from '../../components/Discours/Donate'
|
||||
import { Icon } from '../../components/_shared/Icon'
|
||||
|
||||
// const title = t('Support us')
|
||||
import { Meta, Title } from '@solidjs/meta'
|
||||
import { useLocalize } from '../../context/localize'
|
||||
|
||||
export const HelpPage = () => {
|
||||
const [indexExpanded, setIndexExpanded] = createSignal(true)
|
||||
|
||||
const { t } = useLocalize()
|
||||
|
||||
const toggleIndexExpanded = () => setIndexExpanded((oldExpanded) => !oldExpanded)
|
||||
|
||||
return (
|
||||
<PageLayout>
|
||||
<Title>{t('Support us')}</Title>
|
||||
<Meta name="description">Здесь можно поддержать Дискурс материально.</Meta>
|
||||
<Meta name="keywords">Discours.io, помощь, благотворительность</Meta>
|
||||
|
||||
|
|
|
@ -126,24 +126,32 @@ const addSortedArticles = (articles: Shout[]) => {
|
|||
export const loadShout = async (slug: string): Promise<void> => {
|
||||
const newArticle = await apiClient.getShout(slug)
|
||||
addArticles([newArticle])
|
||||
const newArticleIndex = sortedArticles().findIndex((s) => s.id === newArticle.id)
|
||||
if (newArticleIndex >= 0) {
|
||||
const newSortedArticles = [...sortedArticles()]
|
||||
newSortedArticles[newArticleIndex] = newArticle
|
||||
setSortedArticles(newSortedArticles)
|
||||
}
|
||||
}
|
||||
|
||||
export const loadShouts = async (options: LoadShoutsOptions): Promise<{ hasMore: boolean }> => {
|
||||
const newArticles = await apiClient.getShouts({
|
||||
export const loadShouts = async (
|
||||
options: LoadShoutsOptions
|
||||
): Promise<{ hasMore: boolean; newShouts: Shout[] }> => {
|
||||
const newShouts = await apiClient.getShouts({
|
||||
...options,
|
||||
limit: options.limit + 1
|
||||
})
|
||||
|
||||
const hasMore = newArticles.length === options.limit + 1
|
||||
const hasMore = newShouts.length === options.limit + 1
|
||||
|
||||
if (hasMore) {
|
||||
newArticles.splice(-1)
|
||||
newShouts.splice(-1)
|
||||
}
|
||||
|
||||
addArticles(newArticles)
|
||||
addSortedArticles(newArticles)
|
||||
addArticles(newShouts)
|
||||
addSortedArticles(newShouts)
|
||||
|
||||
return { hasMore }
|
||||
return { hasMore, newShouts }
|
||||
}
|
||||
|
||||
export const resetSortedArticles = () => {
|
||||
|
|
|
@ -278,7 +278,7 @@ export const apiClient = {
|
|||
const resp = await publicGraphQLClient
|
||||
.query(reactionsLoadBy, { by, limit: limit ?? 1000, offset: 0 })
|
||||
.toPromise()
|
||||
console.debug(resp)
|
||||
// console.debug(resp)
|
||||
return resp.data.loadReactionsBy
|
||||
},
|
||||
|
||||
|
|
11
src/utils/checkReaction.ts
Normal file
11
src/utils/checkReaction.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import type { Reaction, ReactionKind } from '../graphql/types.gen'
|
||||
|
||||
export const checkReaction = (
|
||||
reactions: Reaction[],
|
||||
reactionKind: ReactionKind,
|
||||
userSlug: string,
|
||||
shoutId: number
|
||||
) =>
|
||||
reactions.some(
|
||||
(r) => r.kind === reactionKind && r.createdBy.slug === userSlug && r.shout.id === shoutId && !r.replyTo
|
||||
)
|
Loading…
Reference in New Issue
Block a user