likes/dislikes refactoring, who voted popup
This commit is contained in:
parent
9294052226
commit
9b2cd4be2d
|
@ -2,16 +2,14 @@ import { capitalize, formatDate } from '../../utils'
|
|||
import './Full.scss'
|
||||
import { Icon } from '../_shared/Icon'
|
||||
import { AuthorCard } from '../Author/Card'
|
||||
import { createEffect, createMemo, createSignal, For, Match, onMount, Show, Switch } from 'solid-js'
|
||||
import { createMemo, createSignal, For, Match, onMount, Show, Switch } from 'solid-js'
|
||||
import type { Author, Shout } from '../../graphql/types.gen'
|
||||
import { ReactionKind } from '../../graphql/types.gen'
|
||||
|
||||
import MD from './MD'
|
||||
import { SharePopup } from './SharePopup'
|
||||
import { getDescription } from '../../utils/meta'
|
||||
import stylesHeader from '../Nav/Header.module.scss'
|
||||
import styles from '../../styles/Article.module.scss'
|
||||
import { RatingControl } from './RatingControl'
|
||||
import { ShoutRatingControl } from './ShoutRatingControl'
|
||||
import { clsx } from 'clsx'
|
||||
import { CommentsTree } from './CommentsTree'
|
||||
import { useSession } from '../../context/session'
|
||||
|
@ -20,10 +18,8 @@ import Slider from '../_shared/Slider'
|
|||
import { getPagePath } from '@nanostores/router'
|
||||
import { router } from '../../stores/router'
|
||||
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
|
||||
|
@ -60,7 +56,7 @@ const MediaView = (props: { media: MediaItem; kind: Shout['layout'] }) => {
|
|||
|
||||
export const FullArticle = (props: ArticleProps) => {
|
||||
const { t } = useLocalize()
|
||||
const { userSlug, session } = useSession()
|
||||
const { userSlug, isAuthenticated } = useSession()
|
||||
const [isReactionsLoaded, setIsReactionsLoaded] = createSignal(false)
|
||||
const formattedDate = createMemo(() => formatDate(new Date(props.article.createdAt)))
|
||||
|
||||
|
@ -91,7 +87,7 @@ export const FullArticle = (props: ArticleProps) => {
|
|||
setIsReactionsLoaded(true)
|
||||
})
|
||||
|
||||
const canEdit = () => props.article.authors?.some((a) => a.slug === session()?.user?.slug)
|
||||
const canEdit = () => props.article.authors?.some((a) => a.slug === userSlug())
|
||||
|
||||
const bookmark = (ev) => {
|
||||
// TODO: implement bookmark clicked
|
||||
|
@ -106,68 +102,9 @@ export const FullArticle = (props: ArticleProps) => {
|
|||
})
|
||||
|
||||
const {
|
||||
reactionEntities,
|
||||
actions: { loadReactionsBy, createReaction, deleteReaction }
|
||||
actions: { loadReactionsBy }
|
||||
} = useReactions()
|
||||
|
||||
const updateReactions = () => {
|
||||
loadReactionsBy({
|
||||
by: { shout: props.article.slug }
|
||||
})
|
||||
}
|
||||
|
||||
const isUpvoted = createMemo(() =>
|
||||
checkReaction(Object.values(reactionEntities), ReactionKind.Like, userSlug(), props.article.id)
|
||||
)
|
||||
|
||||
const isDownvoted = createMemo(() =>
|
||||
checkReaction(Object.values(reactionEntities), ReactionKind.Dislike, userSlug(), props.article.id)
|
||||
)
|
||||
|
||||
const deleteShoutReaction = async (reactionKind: ReactionKind) => {
|
||||
const reactionToDelete = Object.values(reactionEntities).find(
|
||||
(r) =>
|
||||
r.kind === reactionKind &&
|
||||
r.createdBy.slug === userSlug() &&
|
||||
r.shout.id === props.article.id &&
|
||||
!r.replyTo
|
||||
)
|
||||
return deleteReaction(reactionToDelete.id)
|
||||
}
|
||||
|
||||
const handleRatingChange = async (isUpvote: boolean) => {
|
||||
if (isUpvote) {
|
||||
if (isUpvoted()) {
|
||||
await deleteShoutReaction(ReactionKind.Like)
|
||||
} else if (isDownvoted()) {
|
||||
await deleteShoutReaction(ReactionKind.Dislike)
|
||||
} else {
|
||||
await createReaction({
|
||||
kind: ReactionKind.Like,
|
||||
shout: props.article.id
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if (isDownvoted()) {
|
||||
await deleteShoutReaction(ReactionKind.Dislike)
|
||||
} else if (isUpvoted()) {
|
||||
await deleteShoutReaction(ReactionKind.Like)
|
||||
} else {
|
||||
await createReaction({
|
||||
kind: ReactionKind.Dislike,
|
||||
shout: props.article.id
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
loadShout(props.article.slug)
|
||||
updateReactions()
|
||||
}
|
||||
|
||||
createEffect(() => {
|
||||
console.log('reactions', reactionEntities)
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title>{props.article.title}</Title>
|
||||
|
@ -248,14 +185,7 @@ export const FullArticle = (props: ArticleProps) => {
|
|||
<div class="col-md-8 shift-content">
|
||||
<div class={styles.shoutStats}>
|
||||
<div class={styles.shoutStatsItem}>
|
||||
<RatingControl
|
||||
rating={props.article.stat?.rating}
|
||||
class={styles.ratingControl}
|
||||
onUpvote={() => handleRatingChange(true)}
|
||||
onDownvote={() => handleRatingChange(false)}
|
||||
isUpvoted={isUpvoted()}
|
||||
isDownvoted={isDownvoted()}
|
||||
/>
|
||||
<ShoutRatingControl shout={props.article} class={styles.ratingControl} />
|
||||
</div>
|
||||
|
||||
<Show when={props.article.stat?.viewed}>
|
||||
|
@ -299,7 +229,7 @@ export const FullArticle = (props: ArticleProps) => {
|
|||
</div>
|
||||
</div>
|
||||
<div class={styles.help}>
|
||||
<Show when={session()?.token}>
|
||||
<Show when={isAuthenticated()}>
|
||||
<button class="button">{t('Cooperate')}</button>
|
||||
</Show>
|
||||
<Show when={canEdit()}>
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
import styles from './RatingControl.module.scss'
|
||||
import { clsx } from 'clsx'
|
||||
|
||||
interface RatingControlProps {
|
||||
rating?: number
|
||||
class?: string
|
||||
onUpvote: () => Promise<void> | void
|
||||
onDownvote: () => Promise<void> | void
|
||||
isUpvoted: boolean
|
||||
isDownvoted: boolean
|
||||
}
|
||||
|
||||
export const RatingControl = (props: RatingControlProps) => {
|
||||
return (
|
||||
<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={clsx(styles.ratingControl, styles.upvoteButton)} onClick={props.onUpvote}>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
42
src/components/Article/ShoutRatingControl.module.scss
Normal file
42
src/components/Article/ShoutRatingControl.module.scss
Normal file
|
@ -0,0 +1,42 @@
|
|||
.rating {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
|
||||
&.isDownvoted .downvoteButton,
|
||||
&.isUpvoted .upvoteButton {
|
||||
background: #000;
|
||||
border-color: #000;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.ratingValue {
|
||||
font-weight: bold;
|
||||
margin: 0 4px;
|
||||
padding: 0 4px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: #000;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.ratingControl {
|
||||
align-items: center;
|
||||
border: 2px solid;
|
||||
border-radius: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
height: 0.9em;
|
||||
line-height: 0;
|
||||
font-size: 1.6em;
|
||||
padding: 0;
|
||||
width: 0.9em;
|
||||
|
||||
&:hover {
|
||||
background: #000;
|
||||
border-color: #000;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
108
src/components/Article/ShoutRatingControl.tsx
Normal file
108
src/components/Article/ShoutRatingControl.tsx
Normal file
|
@ -0,0 +1,108 @@
|
|||
import styles from './ShoutRatingControl.module.scss'
|
||||
import { clsx } from 'clsx'
|
||||
import { createMemo, For, Match, Switch } from 'solid-js'
|
||||
import { Author, ReactionKind, Shout } from '../../graphql/types.gen'
|
||||
import { loadShout } from '../../stores/zine/articles'
|
||||
import { useSession } from '../../context/session'
|
||||
import { useReactions } from '../../context/reactions'
|
||||
import { Button } from '../_shared/Button'
|
||||
import Userpic from '../Author/Userpic'
|
||||
import { AuthorCard } from '../Author/Card'
|
||||
import { Popup } from '../_shared/Popup'
|
||||
|
||||
interface ShoutRatingControlProps {
|
||||
shout: Shout
|
||||
class?: string
|
||||
}
|
||||
|
||||
export const ShoutRatingControl = (props: ShoutRatingControlProps) => {
|
||||
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.shout.id &&
|
||||
!r.replyTo
|
||||
)
|
||||
|
||||
const isUpvoted = createMemo(() => checkReaction(ReactionKind.Like))
|
||||
|
||||
const isDownvoted = createMemo(() => checkReaction(ReactionKind.Dislike))
|
||||
|
||||
const shoutRatingReactions = createMemo(() =>
|
||||
Object.values(reactionEntities).filter(
|
||||
(r) => [ReactionKind.Like, ReactionKind.Dislike].includes(r.kind) && r.shout.id === props.shout.id
|
||||
)
|
||||
)
|
||||
|
||||
const deleteShoutReaction = async (reactionKind: ReactionKind) => {
|
||||
const reactionToDelete = Object.values(reactionEntities).find(
|
||||
(r) =>
|
||||
r.kind === reactionKind &&
|
||||
r.createdBy.slug === userSlug() &&
|
||||
r.shout.id === props.shout.id &&
|
||||
!r.replyTo
|
||||
)
|
||||
return deleteReaction(reactionToDelete.id)
|
||||
}
|
||||
|
||||
const handleRatingChange = async (isUpvote: boolean) => {
|
||||
if (isUpvoted()) {
|
||||
await deleteShoutReaction(ReactionKind.Like)
|
||||
} else if (isDownvoted()) {
|
||||
await deleteShoutReaction(ReactionKind.Dislike)
|
||||
} else {
|
||||
await createReaction({
|
||||
kind: isUpvote ? ReactionKind.Like : ReactionKind.Dislike,
|
||||
shout: props.shout.id
|
||||
})
|
||||
}
|
||||
|
||||
loadShout(props.shout.slug)
|
||||
loadReactionsBy({
|
||||
by: { shout: props.shout.slug }
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
class={clsx(styles.rating, props.class, {
|
||||
[styles.isUpvoted]: isUpvoted(),
|
||||
[styles.isDownvoted]: isDownvoted()
|
||||
})}
|
||||
>
|
||||
<button
|
||||
class={clsx(styles.ratingControl, styles.downvoteButton)}
|
||||
onClick={() => handleRatingChange(false)}
|
||||
>
|
||||
−
|
||||
</button>
|
||||
|
||||
<Popup trigger={<span class={styles.ratingValue}>{props.shout.stat.rating}</span>} variant="tiny">
|
||||
<ul class={clsx('nodash')}>
|
||||
<For each={shoutRatingReactions()}>
|
||||
{(reaction) => (
|
||||
<li>
|
||||
{reaction.kind === ReactionKind.Like ? <>+1</> : <>−1</>} {reaction.createdBy.name}
|
||||
</li>
|
||||
)}
|
||||
</For>
|
||||
</ul>
|
||||
</Popup>
|
||||
|
||||
<button
|
||||
class={clsx(styles.ratingControl, styles.upvoteButton)}
|
||||
onClick={() => handleRatingChange(true)}
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
41
src/components/Author/AuthorRatingControl.tsx
Normal file
41
src/components/Author/AuthorRatingControl.tsx
Normal file
|
@ -0,0 +1,41 @@
|
|||
import styles from './AuthorRatingControl.module.scss'
|
||||
import { clsx } from 'clsx'
|
||||
import type { Author } from '../../graphql/types.gen'
|
||||
|
||||
interface AuthorRatingControlProps {
|
||||
author: Author
|
||||
class?: string
|
||||
}
|
||||
|
||||
export const AuthorRatingControl = (props: AuthorRatingControlProps) => {
|
||||
const isUpvoted = false
|
||||
const isDownvoted = false
|
||||
|
||||
const handleRatingChange = (isUpvote: boolean) => {
|
||||
console.log('handleRatingChange', { isUpvote })
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
class={clsx(styles.rating, props.class, {
|
||||
[styles.isUpvoted]: isUpvoted,
|
||||
[styles.isDownvoted]: isDownvoted
|
||||
})}
|
||||
>
|
||||
<button
|
||||
class={clsx(styles.ratingControl, styles.downvoteButton)}
|
||||
onClick={() => handleRatingChange(false)}
|
||||
>
|
||||
−
|
||||
</button>
|
||||
{/*TODO*/}
|
||||
<span class={styles.ratingValue}>{123}</span>
|
||||
<button
|
||||
class={clsx(styles.ratingControl, styles.upvoteButton)}
|
||||
onClick={() => handleRatingChange(true)}
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -6,17 +6,13 @@ import { Icon } from '../_shared/Icon'
|
|||
import styles from './Card.module.scss'
|
||||
import { clsx } from 'clsx'
|
||||
import { CardTopic } from './CardTopic'
|
||||
import { RatingControl } from '../Article/RatingControl'
|
||||
import { ShoutRatingControl } from '../Article/ShoutRatingControl'
|
||||
import { getShareUrl, SharePopup } from '../Article/SharePopup'
|
||||
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?: {
|
||||
|
@ -66,13 +62,6 @@ 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]
|
||||
|
@ -85,57 +74,7 @@ export const ArticleCard = (props: ArticleCardProps) => {
|
|||
|
||||
const { title, subtitle } = getTitleAndSubtitle(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 deleteShoutReaction = async (reactionKind: ReactionKind) => {
|
||||
const reactionToDelete = Object.values(reactionEntities).find(
|
||||
(r) => r.kind === reactionKind && r.createdBy.slug === userSlug() && r.shout.id === id && !r.replyTo
|
||||
)
|
||||
return deleteReaction(reactionToDelete.id)
|
||||
}
|
||||
|
||||
const handleRatingChange = async (isUpvote: boolean) => {
|
||||
if (isUpvote) {
|
||||
if (isUpvoted()) {
|
||||
await deleteShoutReaction(ReactionKind.Like)
|
||||
} else if (isDownvoted()) {
|
||||
await deleteShoutReaction(ReactionKind.Dislike)
|
||||
} else {
|
||||
await createReaction({
|
||||
kind: ReactionKind.Like,
|
||||
shout: id
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if (isDownvoted()) {
|
||||
await deleteShoutReaction(ReactionKind.Dislike)
|
||||
} else if (isUpvoted()) {
|
||||
await deleteShoutReaction(ReactionKind.Like)
|
||||
} else {
|
||||
await createReaction({
|
||||
kind: ReactionKind.Dislike,
|
||||
shout: id
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
loadShout(slug)
|
||||
updateReactions()
|
||||
}
|
||||
const { cover, layout, slug, authors, stat, body } = props.article
|
||||
|
||||
return (
|
||||
<section
|
||||
|
@ -227,14 +166,7 @@ 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}
|
||||
onUpvote={() => handleRatingChange(true)}
|
||||
onDownvote={() => handleRatingChange(false)}
|
||||
isUpvoted={isUpvoted()}
|
||||
isDownvoted={isDownvoted()}
|
||||
/>
|
||||
<ShoutRatingControl shout={props.article} class={styles.shoutCardDetailsItem} />
|
||||
|
||||
<div class={clsx(styles.shoutCardDetailsItem, styles.shoutCardDetailsViewed)}>
|
||||
<Icon name="eye" class={clsx(styles.icon, styles.feedControlIcon)} />
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import style from './CardTopic.module.scss'
|
||||
import { clsx } from 'clsx'
|
||||
import { getPagePath } from '@nanostores/router'
|
||||
import { router } from '../../stores/router'
|
||||
import styles from './CardTopic.module.scss'
|
||||
|
||||
interface CardTopicProps {
|
||||
type CardTopicProps = {
|
||||
title: string
|
||||
slug: string
|
||||
isFloorImportant?: boolean
|
||||
|
@ -9,12 +12,11 @@ interface CardTopicProps {
|
|||
export const CardTopic = (props: CardTopicProps) => {
|
||||
return (
|
||||
<div
|
||||
class={style.shoutTopic}
|
||||
classList={{
|
||||
[style.shoutTopicFloorImportant]: props.isFloorImportant
|
||||
}}
|
||||
class={clsx(styles.shoutTopic, {
|
||||
[styles.shoutTopicFloorImportant]: props.isFloorImportant
|
||||
})}
|
||||
>
|
||||
<a href={`/topic/${props.slug}`}>{props.title}</a>
|
||||
<a href={getPagePath(router, 'topic', { slug: props.slug })}>{props.title}</a>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -10,12 +10,6 @@ type FeedArticlePopupProps = {
|
|||
description: string
|
||||
} & Omit<PopupProps, 'children'>
|
||||
|
||||
export const getShareUrl = (params: { pathname?: string } = {}) => {
|
||||
if (typeof location === 'undefined') return ''
|
||||
const pathname = params.pathname ?? location.pathname
|
||||
return location.origin + pathname
|
||||
}
|
||||
|
||||
export const FeedArticlePopup = (props: FeedArticlePopupProps) => {
|
||||
const { t } = useLocalize()
|
||||
return (
|
||||
|
|
|
@ -14,15 +14,13 @@ export const ProfilePopup = (props: ProfilePopupProps) => {
|
|||
actions: { signOut }
|
||||
} = useSession()
|
||||
|
||||
const { t, lang } = useLocalize()
|
||||
const { t } = useLocalize()
|
||||
|
||||
return (
|
||||
<Popup {...props} horizontalAnchor="right" variant="bordered">
|
||||
<ul class="nodash">
|
||||
<li>
|
||||
<a href={getPagePath(router, 'author', { slug: userSlug(), lang: lang() } as never)}>
|
||||
{t('Profile')}
|
||||
</a>
|
||||
<a href={getPagePath(router, 'author', { slug: userSlug() })}>{t('Profile')}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">{t('Drafts')}</a>
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
import { FullArticle } from '../Article/FullArticle'
|
||||
import type { Shout } from '../../graphql/types.gen'
|
||||
|
||||
interface ArticlePageProps {
|
||||
article: Shout
|
||||
}
|
||||
|
||||
export const ArticleView = (props: ArticlePageProps) => {
|
||||
return <FullArticle article={props.article} />
|
||||
}
|
|
@ -9,7 +9,6 @@ import { loadShouts, useArticlesStore } from '../../stores/zine/articles'
|
|||
import { useRouter } from '../../stores/router'
|
||||
import { restoreScrollPosition, saveScrollPosition } from '../../utils/scroll'
|
||||
import { splitToPages } from '../../utils/splitToPages'
|
||||
import { RatingControl } from '../Article/RatingControl'
|
||||
import styles from './Author.module.scss'
|
||||
import stylesArticle from '../../styles/Article.module.scss'
|
||||
import { clsx } from 'clsx'
|
||||
|
@ -19,6 +18,7 @@ import { AuthorCard } from '../Author/Card'
|
|||
import { apiClient } from '../../utils/apiClient'
|
||||
import { Comment } from '../Article/Comment'
|
||||
import { useLocalize } from '../../context/localize'
|
||||
import { AuthorRatingControl } from '../Author/AuthorRatingControl'
|
||||
|
||||
type AuthorProps = {
|
||||
shouts: Shout[]
|
||||
|
@ -138,7 +138,6 @@ export const AuthorView = (props: AuthorProps) => {
|
|||
</div>
|
||||
<div class={clsx('col-md-4', styles.additionalControls)}>
|
||||
<Popup
|
||||
{...props}
|
||||
trigger={
|
||||
<div class={styles.subscribers}>
|
||||
<Switch>
|
||||
|
@ -179,7 +178,7 @@ export const AuthorView = (props: AuthorProps) => {
|
|||
|
||||
<div class={styles.ratingContainer}>
|
||||
{t('Karma')}
|
||||
<RatingControl rating={19} class={styles.ratingControl} />
|
||||
<AuthorRatingControl author={props.author} class={styles.ratingControl} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { createMemo, onMount, Show } from 'solid-js'
|
||||
import type { Shout } from '../graphql/types.gen'
|
||||
import { PageLayout } from '../components/_shared/PageLayout'
|
||||
import { ArticleView } from '../components/Views/Article'
|
||||
import type { PageProps } from './types'
|
||||
import { loadShout, useArticlesStore } from '../stores/zine/articles'
|
||||
import { useRouter } from '../stores/router'
|
||||
import { Loading } from '../components/_shared/Loading'
|
||||
import { ReactionsProvider } from '../context/reactions'
|
||||
import { FullArticle } from '../components/Article/FullArticle'
|
||||
|
||||
export const ArticlePage = (props: PageProps) => {
|
||||
const shouts = props.article ? [props.article] : []
|
||||
|
@ -50,7 +50,7 @@ export const ArticlePage = (props: PageProps) => {
|
|||
<PageLayout headerTitle={article()?.title || ''} articleBody={article()?.body} cover={article()?.cover}>
|
||||
<ReactionsProvider>
|
||||
<Show when={Boolean(article())} fallback={<Loading />}>
|
||||
<ArticleView article={article()} />
|
||||
<FullArticle article={article()} />
|
||||
</Show>
|
||||
</ReactionsProvider>
|
||||
</PageLayout>
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
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