commentsByAuthor

This commit is contained in:
Untone 2024-09-03 13:21:59 +03:00
parent e176544e36
commit 7714977391
3 changed files with 69 additions and 22 deletions

View File

@ -8,8 +8,9 @@ import { useAuthors } from '~/context/authors'
import { SHOUTS_PER_PAGE, useFeed } from '~/context/feed'
import { useFollowing } from '~/context/following'
import { useLocalize } from '~/context/localize'
import { useReactions } from '~/context/reactions'
import { useSession } from '~/context/session'
import { loadShouts } from '~/graphql/api/public'
import { loadReactions, loadShouts } from '~/graphql/api/public'
import { graphqlClientCreate } from '~/graphql/client'
import getAuthorFollowersQuery from '~/graphql/query/core/author-followers'
import getAuthorFollowsQuery from '~/graphql/query/core/author-follows'
@ -33,6 +34,7 @@ type AuthorViewProps = {
}
export const PRERENDERED_ARTICLES_COUNT = 12
const COMMENTS_PER_PAGE = 12
// const LOAD_MORE_PAGE_SIZE = 9
export const AuthorView = (props: AuthorViewProps) => {
@ -55,15 +57,12 @@ export const AuthorView = (props: AuthorViewProps) => {
const [following, changeFollowing] = createSignal<Array<Author | Topic>>([] as Array<Author | Topic>) // flat AuthorFollowsResult
const [showExpandBioControl, setShowExpandBioControl] = createSignal(false)
const [commented, setCommented] = createSignal<Reaction[]>([])
const [followersLoaded, setFollowersLoaded] = createSignal(false)
const [followingsLoaded, setFollowingsLoaded] = createSignal(false)
// derivatives
const me = createMemo<Author>(() => session()?.user?.app_data?.profile as Author)
// Переход по табам
createEffect(() => {
setCurrentTab(params.tab)
})
// Объединенный эффект для загрузки автора и его подписок
createEffect(async () => {
const meData = session()?.user?.app_data?.profile as Author
@ -72,6 +71,7 @@ export const AuthorView = (props: AuthorViewProps) => {
if (slug && meData?.slug === slug) {
setAuthor(meData)
setFollowers(myFollowers() || [])
setFollowersLoaded(true)
changeFollowing([...(myFollows?.topics || []), ...(myFollows?.authors || [])])
} else if (slug && !author()) {
await loadAuthor({ slug })
@ -84,11 +84,13 @@ export const AuthorView = (props: AuthorViewProps) => {
.toPromise()
const follows = followsResp?.data?.get_author_followers || {}
changeFollowing([...(follows?.authors || []), ...(follows?.topics || [])])
setFollowingsLoaded(true)
const followersResp = await client()
?.query(getAuthorFollowersQuery, { slug: foundAuthor.slug })
.toPromise()
setFollowers(followersResp?.data?.get_author_followers || [])
setFollowersLoaded(true)
}
}
})
@ -108,7 +110,7 @@ export const AuthorView = (props: AuthorViewProps) => {
})
const handleDeleteComment = (id: number) => {
setCommented((prev) => prev.filter((comment) => comment.id !== id))
setCommented((prev) => (prev||[]).filter((comment) => comment.id !== id))
}
const TabNavigator = () => (
@ -140,16 +142,13 @@ export const AuthorView = (props: AuthorViewProps) => {
const [loadMoreHidden, setLoadMoreHidden] = createSignal(false)
const loadMore = async () => {
saveScrollPosition()
const amountBefore = feedByAuthor()?.[props.authorSlug]?.length || 0
const topicShoutsFetcher = loadShouts({
const authorhoutsFetcher = loadShouts({
filters: { author: props.authorSlug },
limit: SHOUTS_PER_PAGE,
offset: amountBefore
offset: feedByAuthor()?.[props.authorSlug]?.length || 0
})
const result = await topicShoutsFetcher()
const result = await authorhoutsFetcher()
result && addFeed(result)
const amountAfter = feedByAuthor()?.[props.authorSlug].length
setLoadMoreHidden(amountAfter === amountBefore)
restoreScrollPosition()
return result as LoadMoreItems
}
@ -161,10 +160,35 @@ export const AuthorView = (props: AuthorViewProps) => {
setSortedFeed(feed)
},{}))
const [loadMoreCommentsHidden, setLoadMoreCommentsHidden] = createSignal(false)
const { commentsByAuthor, addReactions } = useReactions()
const loadMoreComments = async () => {
if (!author()) return [] as LoadMoreItems
saveScrollPosition()
const aid = author()?.id || 0
const authorCommentsFetcher = loadReactions({
by: {
comment: true,
author: author()?.slug
},
limit: COMMENTS_PER_PAGE,
offset: commentsByAuthor()[aid]?.length || 0
})
const result = await authorCommentsFetcher()
result && addReactions(result)
result && setCommented((prev) => [...new Set([...(prev||[]), ...result])])
restoreScrollPosition()
return result as LoadMoreItems
}
createEffect(() => setCurrentTab(params.tab))
createEffect(on([author, commented], ([a, ccc]) => a && setLoadMoreCommentsHidden(ccc?.length === a.stat?.comments), {}))
createEffect(on([author, feedByAuthor], ([a, feed]) => a && feed[props.authorSlug] && setLoadMoreHidden(feed[props.authorSlug]?.length === a.stat?.shouts), {}))
return (
<div class={styles.authorPage}>
<div class="wide-container">
<Show when={author()} fallback={<Loading />}>
<Show when={author() && followersLoaded() && followingsLoaded()} fallback={<Loading />}>
<>
<div class={styles.authorHeader}>
<AuthorCard
@ -221,6 +245,8 @@ export const AuthorView = (props: AuthorViewProps) => {
</div>
</Show>
<LoadMoreWrapper loadFunction={loadMoreComments} pageSize={COMMENTS_PER_PAGE} hidden={loadMoreCommentsHidden()}>
<div class="wide-container">
<div class="row">
<div class="col-md-20 col-lg-18">
@ -239,6 +265,7 @@ export const AuthorView = (props: AuthorViewProps) => {
</div>
</div>
</div>
</LoadMoreWrapper>
</Match>
<Match when={!currentTab()}>

View File

@ -49,7 +49,10 @@ export const LoadMoreWrapper = (props: LoadMoreProps) => {
restoreScrollPosition()
}
onMount(loadItems)
onMount(() => {
loadItems()
console.debug(`load on mount ${items()}`)
})
return (
<>

View File

@ -1,6 +1,6 @@
import type { JSX } from 'solid-js'
import type { Accessor, JSX } from 'solid-js'
import { createContext, createMemo, onCleanup, useContext } from 'solid-js'
import { createContext, createMemo, createSignal, onCleanup, useContext } from 'solid-js'
import { createStore, reconcile } from 'solid-js/store'
import { coreApiUrl } from '~/config'
import { loadReactions } from '~/graphql/api/public'
@ -21,7 +21,8 @@ import { useSnackbar } from './ui'
type ReactionsContextType = {
reactionEntities: Record<number, Reaction>
reactionsByShout: Record<string, Reaction[]>
reactionsByShout: Record<number, Reaction[]>
commentsByAuthor: Accessor<Record<number, Reaction[]>>
loadReactionsBy: (args: QueryLoad_Reactions_ByArgs) => Promise<Reaction[]>
createReaction: (reaction: MutationCreate_ReactionArgs) => Promise<void>
updateReaction: (reaction: MutationUpdate_ReactionArgs) => Promise<Reaction>
@ -38,24 +39,40 @@ export function useReactions() {
export const ReactionsProvider = (props: { children: JSX.Element }) => {
const [reactionEntities, setReactionEntities] = createStore<Record<number, Reaction>>({})
const [reactionsByShout, setReactionsByShout] = createStore<Record<number, Reaction[]>>({})
const [reactionsByAuthor, setReactionsByAuthor] = createStore<Record<number, Reaction[]>>({})
const [commentsByAuthor, setCommentsByAuthor] = createSignal<Record<number, Reaction[]>>({})
const { t } = useLocalize()
const { showSnackbar } = useSnackbar()
const { session } = useSession()
const client = createMemo(() => graphqlClientCreate(coreApiUrl, session()?.access_token))
const addReactions = (rrr: Reaction[]) => {
const newReactionsByShout: Record<string, Reaction[]> = { ...reactionsByShout }
const newReactionsByShout: Record<number, Reaction[]> = { ...reactionsByShout }
const newReactionsByAuthor: Record<number, Reaction[]> = { ...reactionsByAuthor }
const newReactionEntities = rrr.reduce(
(acc: { [reaction_id: number]: Reaction }, reaction: Reaction) => {
acc[reaction.id] = reaction
if (!newReactionsByShout[reaction.shout.slug]) newReactionsByShout[reaction.shout.slug] = []
newReactionsByShout[reaction.shout.slug].push(reaction)
if (!newReactionsByShout[reaction.shout.id]) newReactionsByShout[reaction.shout.id] = []
newReactionsByShout[reaction.shout.id].push(reaction)
if (!newReactionsByAuthor[reaction.created_by.id]) newReactionsByAuthor[reaction.created_by.id] = []
newReactionsByAuthor[reaction.created_by.id].push(reaction)
return acc
},
{ ...reactionEntities }
)
setReactionEntities(newReactionEntities)
setReactionsByShout(newReactionsByShout)
setReactionsByAuthor(newReactionsByAuthor)
const newCommentsByAuthor = Object.fromEntries(
Object.entries(newReactionsByAuthor).map(([authorId, reactions]) => [
authorId,
reactions.filter((x: Reaction) => x.kind === ReactionKind.Comment),
])
)
setCommentsByAuthor(newCommentsByAuthor)
}
const loadReactionsBy = async (opts: QueryLoad_Reactions_ByArgs): Promise<Reaction[]> => {
@ -132,7 +149,7 @@ export const ReactionsProvider = (props: { children: JSX.Element }) => {
addReactions
}
const value: ReactionsContextType = { reactionEntities, reactionsByShout, ...actions }
const value: ReactionsContextType = { reactionEntities, reactionsByShout, commentsByAuthor, ...actions }
return <ReactionsContext.Provider value={value}>{props.children}</ReactionsContext.Provider>
}