commentsByAuthor
This commit is contained in:
parent
e176544e36
commit
7714977391
|
@ -8,8 +8,9 @@ import { useAuthors } from '~/context/authors'
|
||||||
import { SHOUTS_PER_PAGE, useFeed } from '~/context/feed'
|
import { SHOUTS_PER_PAGE, useFeed } from '~/context/feed'
|
||||||
import { useFollowing } from '~/context/following'
|
import { useFollowing } from '~/context/following'
|
||||||
import { useLocalize } from '~/context/localize'
|
import { useLocalize } from '~/context/localize'
|
||||||
|
import { useReactions } from '~/context/reactions'
|
||||||
import { useSession } from '~/context/session'
|
import { useSession } from '~/context/session'
|
||||||
import { loadShouts } from '~/graphql/api/public'
|
import { loadReactions, loadShouts } from '~/graphql/api/public'
|
||||||
import { graphqlClientCreate } from '~/graphql/client'
|
import { graphqlClientCreate } from '~/graphql/client'
|
||||||
import getAuthorFollowersQuery from '~/graphql/query/core/author-followers'
|
import getAuthorFollowersQuery from '~/graphql/query/core/author-followers'
|
||||||
import getAuthorFollowsQuery from '~/graphql/query/core/author-follows'
|
import getAuthorFollowsQuery from '~/graphql/query/core/author-follows'
|
||||||
|
@ -33,6 +34,7 @@ type AuthorViewProps = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PRERENDERED_ARTICLES_COUNT = 12
|
export const PRERENDERED_ARTICLES_COUNT = 12
|
||||||
|
const COMMENTS_PER_PAGE = 12
|
||||||
// const LOAD_MORE_PAGE_SIZE = 9
|
// const LOAD_MORE_PAGE_SIZE = 9
|
||||||
|
|
||||||
export const AuthorView = (props: AuthorViewProps) => {
|
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 [following, changeFollowing] = createSignal<Array<Author | Topic>>([] as Array<Author | Topic>) // flat AuthorFollowsResult
|
||||||
const [showExpandBioControl, setShowExpandBioControl] = createSignal(false)
|
const [showExpandBioControl, setShowExpandBioControl] = createSignal(false)
|
||||||
const [commented, setCommented] = createSignal<Reaction[]>([])
|
const [commented, setCommented] = createSignal<Reaction[]>([])
|
||||||
|
const [followersLoaded, setFollowersLoaded] = createSignal(false)
|
||||||
|
const [followingsLoaded, setFollowingsLoaded] = createSignal(false)
|
||||||
|
|
||||||
// derivatives
|
// derivatives
|
||||||
const me = createMemo<Author>(() => session()?.user?.app_data?.profile as Author)
|
const me = createMemo<Author>(() => session()?.user?.app_data?.profile as Author)
|
||||||
|
|
||||||
// Переход по табам
|
|
||||||
createEffect(() => {
|
|
||||||
setCurrentTab(params.tab)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Объединенный эффект для загрузки автора и его подписок
|
// Объединенный эффект для загрузки автора и его подписок
|
||||||
createEffect(async () => {
|
createEffect(async () => {
|
||||||
const meData = session()?.user?.app_data?.profile as Author
|
const meData = session()?.user?.app_data?.profile as Author
|
||||||
|
@ -72,6 +71,7 @@ export const AuthorView = (props: AuthorViewProps) => {
|
||||||
if (slug && meData?.slug === slug) {
|
if (slug && meData?.slug === slug) {
|
||||||
setAuthor(meData)
|
setAuthor(meData)
|
||||||
setFollowers(myFollowers() || [])
|
setFollowers(myFollowers() || [])
|
||||||
|
setFollowersLoaded(true)
|
||||||
changeFollowing([...(myFollows?.topics || []), ...(myFollows?.authors || [])])
|
changeFollowing([...(myFollows?.topics || []), ...(myFollows?.authors || [])])
|
||||||
} else if (slug && !author()) {
|
} else if (slug && !author()) {
|
||||||
await loadAuthor({ slug })
|
await loadAuthor({ slug })
|
||||||
|
@ -84,11 +84,13 @@ export const AuthorView = (props: AuthorViewProps) => {
|
||||||
.toPromise()
|
.toPromise()
|
||||||
const follows = followsResp?.data?.get_author_followers || {}
|
const follows = followsResp?.data?.get_author_followers || {}
|
||||||
changeFollowing([...(follows?.authors || []), ...(follows?.topics || [])])
|
changeFollowing([...(follows?.authors || []), ...(follows?.topics || [])])
|
||||||
|
setFollowingsLoaded(true)
|
||||||
|
|
||||||
const followersResp = await client()
|
const followersResp = await client()
|
||||||
?.query(getAuthorFollowersQuery, { slug: foundAuthor.slug })
|
?.query(getAuthorFollowersQuery, { slug: foundAuthor.slug })
|
||||||
.toPromise()
|
.toPromise()
|
||||||
setFollowers(followersResp?.data?.get_author_followers || [])
|
setFollowers(followersResp?.data?.get_author_followers || [])
|
||||||
|
setFollowersLoaded(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -108,7 +110,7 @@ export const AuthorView = (props: AuthorViewProps) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleDeleteComment = (id: number) => {
|
const handleDeleteComment = (id: number) => {
|
||||||
setCommented((prev) => prev.filter((comment) => comment.id !== id))
|
setCommented((prev) => (prev||[]).filter((comment) => comment.id !== id))
|
||||||
}
|
}
|
||||||
|
|
||||||
const TabNavigator = () => (
|
const TabNavigator = () => (
|
||||||
|
@ -140,16 +142,13 @@ export const AuthorView = (props: AuthorViewProps) => {
|
||||||
const [loadMoreHidden, setLoadMoreHidden] = createSignal(false)
|
const [loadMoreHidden, setLoadMoreHidden] = createSignal(false)
|
||||||
const loadMore = async () => {
|
const loadMore = async () => {
|
||||||
saveScrollPosition()
|
saveScrollPosition()
|
||||||
const amountBefore = feedByAuthor()?.[props.authorSlug]?.length || 0
|
const authorhoutsFetcher = loadShouts({
|
||||||
const topicShoutsFetcher = loadShouts({
|
|
||||||
filters: { author: props.authorSlug },
|
filters: { author: props.authorSlug },
|
||||||
limit: SHOUTS_PER_PAGE,
|
limit: SHOUTS_PER_PAGE,
|
||||||
offset: amountBefore
|
offset: feedByAuthor()?.[props.authorSlug]?.length || 0
|
||||||
})
|
})
|
||||||
const result = await topicShoutsFetcher()
|
const result = await authorhoutsFetcher()
|
||||||
result && addFeed(result)
|
result && addFeed(result)
|
||||||
const amountAfter = feedByAuthor()?.[props.authorSlug].length
|
|
||||||
setLoadMoreHidden(amountAfter === amountBefore)
|
|
||||||
restoreScrollPosition()
|
restoreScrollPosition()
|
||||||
return result as LoadMoreItems
|
return result as LoadMoreItems
|
||||||
}
|
}
|
||||||
|
@ -161,10 +160,35 @@ export const AuthorView = (props: AuthorViewProps) => {
|
||||||
setSortedFeed(feed)
|
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 (
|
return (
|
||||||
<div class={styles.authorPage}>
|
<div class={styles.authorPage}>
|
||||||
<div class="wide-container">
|
<div class="wide-container">
|
||||||
<Show when={author()} fallback={<Loading />}>
|
<Show when={author() && followersLoaded() && followingsLoaded()} fallback={<Loading />}>
|
||||||
<>
|
<>
|
||||||
<div class={styles.authorHeader}>
|
<div class={styles.authorHeader}>
|
||||||
<AuthorCard
|
<AuthorCard
|
||||||
|
@ -221,6 +245,8 @@ export const AuthorView = (props: AuthorViewProps) => {
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
|
|
||||||
|
<LoadMoreWrapper loadFunction={loadMoreComments} pageSize={COMMENTS_PER_PAGE} hidden={loadMoreCommentsHidden()}>
|
||||||
<div class="wide-container">
|
<div class="wide-container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-20 col-lg-18">
|
<div class="col-md-20 col-lg-18">
|
||||||
|
@ -239,6 +265,7 @@ export const AuthorView = (props: AuthorViewProps) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</LoadMoreWrapper>
|
||||||
</Match>
|
</Match>
|
||||||
|
|
||||||
<Match when={!currentTab()}>
|
<Match when={!currentTab()}>
|
||||||
|
|
|
@ -49,7 +49,10 @@ export const LoadMoreWrapper = (props: LoadMoreProps) => {
|
||||||
restoreScrollPosition()
|
restoreScrollPosition()
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(loadItems)
|
onMount(() => {
|
||||||
|
loadItems()
|
||||||
|
console.debug(`load on mount ${items()}`)
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -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 { createStore, reconcile } from 'solid-js/store'
|
||||||
import { coreApiUrl } from '~/config'
|
import { coreApiUrl } from '~/config'
|
||||||
import { loadReactions } from '~/graphql/api/public'
|
import { loadReactions } from '~/graphql/api/public'
|
||||||
|
@ -21,7 +21,8 @@ import { useSnackbar } from './ui'
|
||||||
|
|
||||||
type ReactionsContextType = {
|
type ReactionsContextType = {
|
||||||
reactionEntities: Record<number, Reaction>
|
reactionEntities: Record<number, Reaction>
|
||||||
reactionsByShout: Record<string, Reaction[]>
|
reactionsByShout: Record<number, Reaction[]>
|
||||||
|
commentsByAuthor: Accessor<Record<number, Reaction[]>>
|
||||||
loadReactionsBy: (args: QueryLoad_Reactions_ByArgs) => Promise<Reaction[]>
|
loadReactionsBy: (args: QueryLoad_Reactions_ByArgs) => Promise<Reaction[]>
|
||||||
createReaction: (reaction: MutationCreate_ReactionArgs) => Promise<void>
|
createReaction: (reaction: MutationCreate_ReactionArgs) => Promise<void>
|
||||||
updateReaction: (reaction: MutationUpdate_ReactionArgs) => Promise<Reaction>
|
updateReaction: (reaction: MutationUpdate_ReactionArgs) => Promise<Reaction>
|
||||||
|
@ -38,24 +39,40 @@ export function useReactions() {
|
||||||
export const ReactionsProvider = (props: { children: JSX.Element }) => {
|
export const ReactionsProvider = (props: { children: JSX.Element }) => {
|
||||||
const [reactionEntities, setReactionEntities] = createStore<Record<number, Reaction>>({})
|
const [reactionEntities, setReactionEntities] = createStore<Record<number, Reaction>>({})
|
||||||
const [reactionsByShout, setReactionsByShout] = 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 { t } = useLocalize()
|
||||||
const { showSnackbar } = useSnackbar()
|
const { showSnackbar } = useSnackbar()
|
||||||
const { session } = useSession()
|
const { session } = useSession()
|
||||||
const client = createMemo(() => graphqlClientCreate(coreApiUrl, session()?.access_token))
|
const client = createMemo(() => graphqlClientCreate(coreApiUrl, session()?.access_token))
|
||||||
|
|
||||||
const addReactions = (rrr: Reaction[]) => {
|
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(
|
const newReactionEntities = rrr.reduce(
|
||||||
(acc: { [reaction_id: number]: Reaction }, reaction: Reaction) => {
|
(acc: { [reaction_id: number]: Reaction }, reaction: Reaction) => {
|
||||||
acc[reaction.id] = reaction
|
acc[reaction.id] = reaction
|
||||||
if (!newReactionsByShout[reaction.shout.slug]) newReactionsByShout[reaction.shout.slug] = []
|
if (!newReactionsByShout[reaction.shout.id]) newReactionsByShout[reaction.shout.id] = []
|
||||||
newReactionsByShout[reaction.shout.slug].push(reaction)
|
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
|
return acc
|
||||||
},
|
},
|
||||||
{ ...reactionEntities }
|
{ ...reactionEntities }
|
||||||
)
|
)
|
||||||
|
|
||||||
setReactionEntities(newReactionEntities)
|
setReactionEntities(newReactionEntities)
|
||||||
setReactionsByShout(newReactionsByShout)
|
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[]> => {
|
const loadReactionsBy = async (opts: QueryLoad_Reactions_ByArgs): Promise<Reaction[]> => {
|
||||||
|
@ -132,7 +149,7 @@ export const ReactionsProvider = (props: { children: JSX.Element }) => {
|
||||||
addReactions
|
addReactions
|
||||||
}
|
}
|
||||||
|
|
||||||
const value: ReactionsContextType = { reactionEntities, reactionsByShout, ...actions }
|
const value: ReactionsContextType = { reactionEntities, reactionsByShout, commentsByAuthor, ...actions }
|
||||||
|
|
||||||
return <ReactionsContext.Provider value={value}>{props.children}</ReactionsContext.Provider>
|
return <ReactionsContext.Provider value={value}>{props.children}</ReactionsContext.Provider>
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user