diff --git a/src/components/Article/FullArticle.tsx b/src/components/Article/FullArticle.tsx
index 7d25a6bf..2a39073f 100644
--- a/src/components/Article/FullArticle.tsx
+++ b/src/components/Article/FullArticle.tsx
@@ -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 (
<>
{props.article.title}
@@ -248,14 +185,7 @@ export const FullArticle = (props: ArticleProps) => {
- handleRatingChange(true)}
- onDownvote={() => handleRatingChange(false)}
- isUpvoted={isUpvoted()}
- isDownvoted={isDownvoted()}
- />
+
@@ -299,7 +229,7 @@ export const FullArticle = (props: ArticleProps) => {
-
+
diff --git a/src/components/Article/RatingControl.tsx b/src/components/Article/RatingControl.tsx
deleted file mode 100644
index bfed81bb..00000000
--- a/src/components/Article/RatingControl.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import styles from './RatingControl.module.scss'
-import { clsx } from 'clsx'
-
-interface RatingControlProps {
- rating?: number
- class?: string
- onUpvote: () => Promise | void
- onDownvote: () => Promise | void
- isUpvoted: boolean
- isDownvoted: boolean
-}
-
-export const RatingControl = (props: RatingControlProps) => {
- return (
-
-
- {props?.rating || ''}
-
-
- )
-}
diff --git a/src/components/Article/ShoutRatingControl.module.scss b/src/components/Article/ShoutRatingControl.module.scss
new file mode 100644
index 00000000..bd8a218a
--- /dev/null
+++ b/src/components/Article/ShoutRatingControl.module.scss
@@ -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;
+ }
+}
diff --git a/src/components/Article/ShoutRatingControl.tsx b/src/components/Article/ShoutRatingControl.tsx
new file mode 100644
index 00000000..20a76b1c
--- /dev/null
+++ b/src/components/Article/ShoutRatingControl.tsx
@@ -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 (
+
+
+
+
{props.shout.stat.rating}} variant="tiny">
+
+
+ {(reaction) => (
+ -
+ {reaction.kind === ReactionKind.Like ? <>+1> : <>−1>} {reaction.createdBy.name}
+
+ )}
+
+
+
+
+
+
+ )
+}
diff --git a/src/components/Article/RatingControl.module.scss b/src/components/Author/AuthorRatingControl.module.scss
similarity index 100%
rename from src/components/Article/RatingControl.module.scss
rename to src/components/Author/AuthorRatingControl.module.scss
diff --git a/src/components/Author/AuthorRatingControl.tsx b/src/components/Author/AuthorRatingControl.tsx
new file mode 100644
index 00000000..d1461d83
--- /dev/null
+++ b/src/components/Author/AuthorRatingControl.tsx
@@ -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 (
+
+
+ {/*TODO*/}
+ {123}
+
+
+ )
+}
diff --git a/src/components/Feed/Card.tsx b/src/components/Feed/Card.tsx
index d6522466..6ccc1fbb 100644
--- a/src/components/Feed/Card.tsx
+++ b/src/components/Feed/Card.tsx
@@ -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 (
{
-
handleRatingChange(true)}
- onDownvote={() => handleRatingChange(false)}
- isUpvoted={isUpvoted()}
- isDownvoted={isDownvoted()}
- />
+
diff --git a/src/components/Feed/CardTopic.tsx b/src/components/Feed/CardTopic.tsx
index 617d10d9..0b51c3f0 100644
--- a/src/components/Feed/CardTopic.tsx
+++ b/src/components/Feed/CardTopic.tsx
@@ -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 (
)
}
diff --git a/src/components/Feed/FeedArticlePopup.tsx b/src/components/Feed/FeedArticlePopup.tsx
index 3d4872ed..22851e26 100644
--- a/src/components/Feed/FeedArticlePopup.tsx
+++ b/src/components/Feed/FeedArticlePopup.tsx
@@ -10,12 +10,6 @@ type FeedArticlePopupProps = {
description: string
} & Omit
-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 (
diff --git a/src/components/Nav/ProfilePopup.tsx b/src/components/Nav/ProfilePopup.tsx
index 8aced53b..9dd0e135 100644
--- a/src/components/Nav/ProfilePopup.tsx
+++ b/src/components/Nav/ProfilePopup.tsx
@@ -14,15 +14,13 @@ export const ProfilePopup = (props: ProfilePopupProps) => {
actions: { signOut }
} = useSession()
- const { t, lang } = useLocalize()
+ const { t } = useLocalize()
return (
-
-
- {t('Profile')}
-
+ {t('Profile')}
-
{t('Drafts')}
diff --git a/src/components/Views/Article.tsx b/src/components/Views/Article.tsx
deleted file mode 100644
index 996fc513..00000000
--- a/src/components/Views/Article.tsx
+++ /dev/null
@@ -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
-}
diff --git a/src/components/Views/Author.tsx b/src/components/Views/Author.tsx
index b9f14f3a..2d46b85e 100644
--- a/src/components/Views/Author.tsx
+++ b/src/components/Views/Author.tsx
@@ -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) => {
@@ -179,7 +178,7 @@ export const AuthorView = (props: AuthorProps) => {
diff --git a/src/pages/article.page.tsx b/src/pages/article.page.tsx
index c5ffd429..8378bf37 100644
--- a/src/pages/article.page.tsx
+++ b/src/pages/article.page.tsx
@@ -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) => {
}>
-
+
diff --git a/src/utils/checkReaction.ts b/src/utils/checkReaction.ts
deleted file mode 100644
index 19f157ea..00000000
--- a/src/utils/checkReaction.ts
+++ /dev/null
@@ -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
- )