article page. & comments fix

This commit is contained in:
Igor Lobanov 2022-12-06 17:03:55 +01:00
parent 2a70e3ebc5
commit e332a8a674
9 changed files with 40 additions and 38 deletions

View File

@ -1,11 +1,10 @@
import { For, Show } from 'solid-js' import { For, Show, createMemo, createSignal, onMount } from 'solid-js'
import { useSession } from '../../context/session' import { useSession } from '../../context/session'
import Comment from './Comment' import Comment from './Comment'
import { t } from '../../utils/intl' import { t } from '../../utils/intl'
import { showModal } from '../../stores/ui' import { showModal } from '../../stores/ui'
import styles from '../../styles/Article.module.scss' import styles from '../../styles/Article.module.scss'
import { useReactionsStore } from '../../stores/zine/reactions' import { useReactionsStore } from '../../stores/zine/reactions'
import { createMemo, createSignal, onMount } from 'solid-js'
import type { Reaction } from '../../graphql/types.gen' import type { Reaction } from '../../graphql/types.gen'
import { clsx } from 'clsx' import { clsx } from 'clsx'
import { byCreated, byStat } from '../../utils/sortby' import { byCreated, byStat } from '../../utils/sortby'
@ -14,17 +13,17 @@ import { Loading } from '../Loading'
const ARTICLE_COMMENTS_PAGE_SIZE = 50 const ARTICLE_COMMENTS_PAGE_SIZE = 50
const MAX_COMMENT_LEVEL = 6 const MAX_COMMENT_LEVEL = 6
export const CommentsTree = (props: { shout: string; reactions?: Reaction[] }) => { export const CommentsTree = (props: { shoutSlug: string }) => {
const [getCommentsPage, setCommentsPage] = createSignal(0) const [getCommentsPage, setCommentsPage] = createSignal(0)
const [commentsOrder, setCommentsOrder] = createSignal<'rating' | 'createdAt'>('createdAt') const [commentsOrder, setCommentsOrder] = createSignal<'rating' | 'createdAt'>('createdAt')
const [isCommentsLoading, setIsCommentsLoading] = createSignal(false) const [isCommentsLoading, setIsCommentsLoading] = createSignal(false)
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false) const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
const { session } = useSession() const { session } = useSession()
const { sortedReactions, loadReactionsBy } = useReactionsStore({ reactions: props.reactions }) const { sortedReactions, loadReactionsBy } = useReactionsStore()
const reactions = createMemo<Reaction[]>(() => const reactions = createMemo<Reaction[]>(() =>
sortedReactions() sortedReactions()
.sort(commentsOrder() === 'rating' ? byStat('rating') : byCreated) .sort(commentsOrder() === 'rating' ? byStat('rating') : byCreated)
.filter((r) => r.shout.slug === props.shout) .filter((r) => r.shout.slug === props.shoutSlug)
) )
const loadMore = async () => { const loadMore = async () => {
@ -33,7 +32,7 @@ export const CommentsTree = (props: { shout: string; reactions?: Reaction[] }) =
setIsCommentsLoading(true) setIsCommentsLoading(true)
const { hasMore } = await loadReactionsBy({ const { hasMore } = await loadReactionsBy({
by: { shout: props.shout, comment: true }, by: { shout: props.shoutSlug, comment: true },
limit: ARTICLE_COMMENTS_PAGE_SIZE, limit: ARTICLE_COMMENTS_PAGE_SIZE,
offset: page * ARTICLE_COMMENTS_PAGE_SIZE offset: page * ARTICLE_COMMENTS_PAGE_SIZE
}) })

View File

@ -222,7 +222,7 @@ export const FullArticle = (props: ArticleProps) => {
)} )}
</For> </For>
</div> </div>
<CommentsTree shout={props.article?.slug} /> <CommentsTree shoutSlug={props.article?.slug} />
</div> </div>
</div> </div>
) )

View File

@ -32,6 +32,7 @@ interface AuthorCardProps {
export const AuthorCard = (props: AuthorCardProps) => { export const AuthorCard = (props: AuthorCardProps) => {
const { const {
session, session,
isSessionLoaded,
actions: { loadSession } actions: { loadSession }
} = useSession() } = useSession()
@ -100,7 +101,7 @@ export const AuthorCard = (props: AuthorCardProps) => {
</Show> </Show>
</div> </div>
<ShowOnlyOnClient> <ShowOnlyOnClient>
<Show when={session.state !== 'pending'}> <Show when={isSessionLoaded()}>
<Show when={canFollow()}> <Show when={canFollow()}>
<div class={styles.authorSubscribe}> <div class={styles.authorSubscribe}>
<Show <Show

View File

@ -21,7 +21,7 @@ export const HeaderAuth = (props: HeaderAuthProps) => {
const [visibleWarnings, setVisibleWarnings] = createSignal(false) const [visibleWarnings, setVisibleWarnings] = createSignal(false)
const { warnings } = useWarningsStore() const { warnings } = useWarningsStore()
const { session, isAuthenticated } = useSession() const { session, isSessionLoaded, isAuthenticated } = useSession()
const toggleWarnings = () => setVisibleWarnings(!visibleWarnings()) const toggleWarnings = () => setVisibleWarnings(!visibleWarnings())
@ -38,7 +38,7 @@ export const HeaderAuth = (props: HeaderAuthProps) => {
return ( return (
<ShowOnlyOnClient> <ShowOnlyOnClient>
<Show when={session.state !== 'pending'}> <Show when={isSessionLoaded()}>
<div class={styles.usernav}> <div class={styles.usernav}>
<div class={clsx(styles.userControl, styles.userControl, 'col')}> <div class={clsx(styles.userControl, styles.userControl, 'col')}>
<div class={clsx(styles.userControlItem, styles.userControlItemVerbose)}> <div class={clsx(styles.userControlItem, styles.userControlItemVerbose)}>

View File

@ -28,6 +28,7 @@ interface TopicProps {
export const TopicCard = (props: TopicProps) => { export const TopicCard = (props: TopicProps) => {
const { const {
session, session,
isSessionLoaded,
actions: { loadSession } actions: { loadSession }
} = useSession() } = useSession()
@ -89,7 +90,7 @@ export const TopicCard = (props: TopicProps) => {
classList={{ 'col-md-3': !props.compact && !props.subscribeButtonBottom }} classList={{ 'col-md-3': !props.compact && !props.subscribeButtonBottom }}
> >
<ShowOnlyOnClient> <ShowOnlyOnClient>
<Show when={session.state !== 'pending'}> <Show when={isSessionLoaded()}>
<button <button
onClick={() => subscribe(!subscribed())} onClick={() => subscribe(!subscribed())}
class="button--light button--subscribe-topic" class="button--light button--subscribe-topic"

View File

@ -27,7 +27,7 @@ export const FEED_PAGE_SIZE = 20
export const FeedView = () => { export const FeedView = () => {
// state // state
const { sortedArticles } = useArticlesStore() const { sortedArticles } = useArticlesStore()
const { sortedReactions: topComments, loadReactionsBy } = useReactionsStore({}) const { sortedReactions: topComments, loadReactionsBy } = useReactionsStore()
const { sortedAuthors } = useAuthorsStore() const { sortedAuthors } = useAuthorsStore()
const { topTopics } = useTopicsStore() const { topTopics } = useTopicsStore()
const { topAuthors } = useTopAuthorsStore() const { topAuthors } = useTopAuthorsStore()

View File

@ -1,11 +1,12 @@
import type { Accessor, JSX, Resource } from 'solid-js' import type { Accessor, JSX, Resource } from 'solid-js'
import { createContext, createMemo, createResource, onMount, useContext } from 'solid-js' import { createContext, createMemo, createResource, createSignal, onMount, useContext } from 'solid-js'
import type { AuthResult } from '../graphql/types.gen' import type { AuthResult } from '../graphql/types.gen'
import { apiClient } from '../utils/apiClient' import { apiClient } from '../utils/apiClient'
import { resetToken, setToken } from '../graphql/privateGraphQLClient' import { resetToken, setToken } from '../graphql/privateGraphQLClient'
type SessionContextType = { type SessionContextType = {
session: Resource<AuthResult> session: Resource<AuthResult>
isSessionLoaded: Accessor<boolean>
userSlug: Accessor<string> userSlug: Accessor<string>
isAuthenticated: Accessor<boolean> isAuthenticated: Accessor<boolean>
actions: { actions: {
@ -18,27 +19,33 @@ type SessionContextType = {
const SessionContext = createContext<SessionContextType>() const SessionContext = createContext<SessionContextType>()
const getSession = async (): Promise<AuthResult> => {
try {
const authResult = await apiClient.getSession()
if (!authResult) {
return null
}
setToken(authResult.token)
return authResult
} catch (error) {
console.error('getSession error:', error)
resetToken()
return null
}
}
export function useSession() { export function useSession() {
return useContext(SessionContext) return useContext(SessionContext)
} }
export const SessionProvider = (props: { children: JSX.Element }) => { export const SessionProvider = (props: { children: JSX.Element }) => {
const [session, { refetch: loadSession, mutate }] = createResource<AuthResult>(getSession) const [isSessionLoaded, setIsSessionLoaded] = createSignal(false)
const getSession = async (): Promise<AuthResult> => {
try {
const authResult = await apiClient.getSession()
if (!authResult) {
return null
}
setToken(authResult.token)
setIsSessionLoaded(true)
return authResult
} catch (error) {
console.error('getSession error:', error)
resetToken()
return null
}
}
const [session, { refetch: loadSession, mutate }] = createResource<AuthResult>(getSession, {
ssrLoadFrom: 'initial',
initialValue: null
})
const userSlug = createMemo(() => session()?.user?.slug) const userSlug = createMemo(() => session()?.user?.slug)
@ -71,7 +78,7 @@ export const SessionProvider = (props: { children: JSX.Element }) => {
confirmEmail confirmEmail
} }
const value: SessionContextType = { session, userSlug, isAuthenticated, actions } const value: SessionContextType = { session, isSessionLoaded, userSlug, isAuthenticated, actions }
onMount(() => { onMount(() => {
loadSession() loadSession()

View File

@ -22,8 +22,8 @@ export type AuthResult = {
} }
export type Author = { export type Author = {
bio?: Maybe<Scalars['String']>
about?: Maybe<Scalars['String']> about?: Maybe<Scalars['String']>
bio?: Maybe<Scalars['String']>
caption?: Maybe<Scalars['String']> caption?: Maybe<Scalars['String']>
id: Scalars['Int'] id: Scalars['Int']
lastSeen?: Maybe<Scalars['DateTime']> lastSeen?: Maybe<Scalars['DateTime']>
@ -334,8 +334,6 @@ export type Permission = {
export type ProfileInput = { export type ProfileInput = {
about?: InputMaybe<Scalars['String']> about?: InputMaybe<Scalars['String']>
bio?: InputMaybe<Scalars['String']> bio?: InputMaybe<Scalars['String']>
slug?: InputMaybe<Scalars['String']>
about?: InputMaybe<Scalars['String']>
links?: InputMaybe<Array<InputMaybe<Scalars['String']>>> links?: InputMaybe<Array<InputMaybe<Scalars['String']>>>
name?: InputMaybe<Scalars['String']> name?: InputMaybe<Scalars['String']>
slug?: InputMaybe<Scalars['String']> slug?: InputMaybe<Scalars['String']>

View File

@ -37,11 +37,7 @@ export const deleteReaction = async (reactionId: number) => {
console.debug(resp) console.debug(resp)
return resp return resp
} }
export const useReactionsStore = (initialState: { reactions?: Reaction[] }) => { export const useReactionsStore = () => {
if (initialState.reactions) {
setSortedReactions([...initialState.reactions])
}
return { return {
reactionsByShout, reactionsByShout,
sortedReactions, sortedReactions,