diff --git a/src/components/Views/Author/Author.tsx b/src/components/Views/Author/Author.tsx index 4bd6f3e7..bccbcf97 100644 --- a/src/components/Views/Author/Author.tsx +++ b/src/components/Views/Author/Author.tsx @@ -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>([] as Array) // flat AuthorFollowsResult const [showExpandBioControl, setShowExpandBioControl] = createSignal(false) const [commented, setCommented] = createSignal([]) + const [followersLoaded, setFollowersLoaded] = createSignal(false) + const [followingsLoaded, setFollowingsLoaded] = createSignal(false) // derivatives const me = createMemo(() => 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 (
- }> + }> <>
{
+ + diff --git a/src/components/_shared/LoadMoreWrapper.tsx b/src/components/_shared/LoadMoreWrapper.tsx index ff53e9bd..5254f3c1 100644 --- a/src/components/_shared/LoadMoreWrapper.tsx +++ b/src/components/_shared/LoadMoreWrapper.tsx @@ -49,7 +49,10 @@ export const LoadMoreWrapper = (props: LoadMoreProps) => { restoreScrollPosition() } - onMount(loadItems) + onMount(() => { + loadItems() + console.debug(`load on mount ${items()}`) + }) return ( <> diff --git a/src/context/reactions.tsx b/src/context/reactions.tsx index a0f4ce55..6d456719 100644 --- a/src/context/reactions.tsx +++ b/src/context/reactions.tsx @@ -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 - reactionsByShout: Record + reactionsByShout: Record + commentsByAuthor: Accessor> loadReactionsBy: (args: QueryLoad_Reactions_ByArgs) => Promise createReaction: (reaction: MutationCreate_ReactionArgs) => Promise updateReaction: (reaction: MutationUpdate_ReactionArgs) => Promise @@ -38,24 +39,40 @@ export function useReactions() { export const ReactionsProvider = (props: { children: JSX.Element }) => { const [reactionEntities, setReactionEntities] = createStore>({}) const [reactionsByShout, setReactionsByShout] = createStore>({}) + const [reactionsByAuthor, setReactionsByAuthor] = createStore>({}) + const [commentsByAuthor, setCommentsByAuthor] = createSignal>({}) const { t } = useLocalize() const { showSnackbar } = useSnackbar() const { session } = useSession() const client = createMemo(() => graphqlClientCreate(coreApiUrl, session()?.access_token)) const addReactions = (rrr: Reaction[]) => { - const newReactionsByShout: Record = { ...reactionsByShout } + const newReactionsByShout: Record = { ...reactionsByShout } + const newReactionsByAuthor: Record = { ...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 => { @@ -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 {props.children} }