author-feed+comments-paginate
This commit is contained in:
parent
82904bd1da
commit
8fbc85615c
64
package.json
64
package.json
|
@ -32,40 +32,40 @@
|
||||||
"@solid-primitives/pagination": "^0.3.0",
|
"@solid-primitives/pagination": "^0.3.0",
|
||||||
"@solid-primitives/script-loader": "^2.2.0",
|
"@solid-primitives/script-loader": "^2.2.0",
|
||||||
"@solid-primitives/share": "^2.0.6",
|
"@solid-primitives/share": "^2.0.6",
|
||||||
"@solid-primitives/storage": "^3.7.1",
|
"@solid-primitives/storage": "^3.8.0",
|
||||||
"@solid-primitives/upload": "^0.0.117",
|
"@solid-primitives/upload": "^0.0.117",
|
||||||
"@solidjs/meta": "^0.29.4",
|
"@solidjs/meta": "^0.29.4",
|
||||||
"@solidjs/router": "^0.13.6",
|
"@solidjs/router": "^0.13.6",
|
||||||
"@solidjs/start": "^1.0.4",
|
"@solidjs/start": "^1.0.4",
|
||||||
"@tiptap/core": "^2.4.0",
|
"@tiptap/core": "^2.5.1",
|
||||||
"@tiptap/extension-blockquote": "^2.4.0",
|
"@tiptap/extension-blockquote": "^2.5.1",
|
||||||
"@tiptap/extension-bold": "^2.4.0",
|
"@tiptap/extension-bold": "^2.5.1",
|
||||||
"@tiptap/extension-bubble-menu": "^2.4.0",
|
"@tiptap/extension-bubble-menu": "^2.5.1",
|
||||||
"@tiptap/extension-bullet-list": "^2.4.0",
|
"@tiptap/extension-bullet-list": "^2.5.1",
|
||||||
"@tiptap/extension-character-count": "^2.4.0",
|
"@tiptap/extension-character-count": "^2.5.1",
|
||||||
"@tiptap/extension-collaboration": "^2.4.0",
|
"@tiptap/extension-collaboration": "^2.5.1",
|
||||||
"@tiptap/extension-collaboration-cursor": "^2.4.0",
|
"@tiptap/extension-collaboration-cursor": "^2.5.1",
|
||||||
"@tiptap/extension-document": "^2.4.0",
|
"@tiptap/extension-document": "^2.5.1",
|
||||||
"@tiptap/extension-dropcursor": "^2.4.0",
|
"@tiptap/extension-dropcursor": "^2.5.1",
|
||||||
"@tiptap/extension-floating-menu": "^2.4.0",
|
"@tiptap/extension-floating-menu": "^2.5.1",
|
||||||
"@tiptap/extension-focus": "^2.4.0",
|
"@tiptap/extension-focus": "^2.5.1",
|
||||||
"@tiptap/extension-gapcursor": "^2.4.0",
|
"@tiptap/extension-gapcursor": "^2.5.1",
|
||||||
"@tiptap/extension-hard-break": "^2.4.0",
|
"@tiptap/extension-hard-break": "^2.5.1",
|
||||||
"@tiptap/extension-heading": "^2.4.0",
|
"@tiptap/extension-heading": "^2.5.1",
|
||||||
"@tiptap/extension-highlight": "^2.4.0",
|
"@tiptap/extension-highlight": "^2.5.1",
|
||||||
"@tiptap/extension-history": "^2.4.0",
|
"@tiptap/extension-history": "^2.5.1",
|
||||||
"@tiptap/extension-horizontal-rule": "^2.4.0",
|
"@tiptap/extension-horizontal-rule": "^2.5.1",
|
||||||
"@tiptap/extension-image": "^2.4.0",
|
"@tiptap/extension-image": "^2.5.1",
|
||||||
"@tiptap/extension-italic": "^2.4.0",
|
"@tiptap/extension-italic": "^2.5.1",
|
||||||
"@tiptap/extension-link": "^2.4.0",
|
"@tiptap/extension-link": "^2.5.1",
|
||||||
"@tiptap/extension-list-item": "^2.4.0",
|
"@tiptap/extension-list-item": "^2.5.1",
|
||||||
"@tiptap/extension-ordered-list": "^2.4.0",
|
"@tiptap/extension-ordered-list": "^2.5.1",
|
||||||
"@tiptap/extension-paragraph": "^2.4.0",
|
"@tiptap/extension-paragraph": "^2.5.1",
|
||||||
"@tiptap/extension-placeholder": "^2.4.0",
|
"@tiptap/extension-placeholder": "^2.5.1",
|
||||||
"@tiptap/extension-strike": "^2.4.0",
|
"@tiptap/extension-strike": "^2.5.1",
|
||||||
"@tiptap/extension-text": "^2.4.0",
|
"@tiptap/extension-text": "^2.5.1",
|
||||||
"@tiptap/extension-underline": "^2.4.0",
|
"@tiptap/extension-underline": "^2.5.1",
|
||||||
"@tiptap/extension-youtube": "^2.4.0",
|
"@tiptap/extension-youtube": "^2.5.1",
|
||||||
"@types/cookie": "^0.6.0",
|
"@types/cookie": "^0.6.0",
|
||||||
"@types/cookie-signature": "^1.1.2",
|
"@types/cookie-signature": "^1.1.2",
|
||||||
"@types/node": "^20.14.10",
|
"@types/node": "^20.14.10",
|
||||||
|
@ -79,7 +79,7 @@
|
||||||
"extended-eventsource": "^1.4.9",
|
"extended-eventsource": "^1.4.9",
|
||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
"graphql": "^16.9.0",
|
"graphql": "^16.9.0",
|
||||||
"i18next": "^23.11.5",
|
"i18next": "^23.12.1",
|
||||||
"i18next-http-backend": "^2.5.2",
|
"i18next-http-backend": "^2.5.2",
|
||||||
"i18next-icu": "^2.3.0",
|
"i18next-icu": "^2.3.0",
|
||||||
"intl-messageformat": "^10.5.14",
|
"intl-messageformat": "^10.5.14",
|
||||||
|
@ -99,7 +99,7 @@
|
||||||
"stylelint-config-standard-scss": "^13.1.0",
|
"stylelint-config-standard-scss": "^13.1.0",
|
||||||
"stylelint-order": "^6.0.4",
|
"stylelint-order": "^6.0.4",
|
||||||
"stylelint-scss": "^6.4.1",
|
"stylelint-scss": "^6.4.1",
|
||||||
"swiper": "^11.1.4",
|
"swiper": "^11.1.5",
|
||||||
"throttle-debounce": "^5.0.2",
|
"throttle-debounce": "^5.0.2",
|
||||||
"tslib": "^2.6.3",
|
"tslib": "^2.6.3",
|
||||||
"typescript": "^5.5.3",
|
"typescript": "^5.5.3",
|
||||||
|
|
|
@ -8,14 +8,11 @@ import { useFollowing } from '~/context/following'
|
||||||
import { useGraphQL } from '~/context/graphql'
|
import { useGraphQL } from '~/context/graphql'
|
||||||
import { useLocalize } from '~/context/localize'
|
import { useLocalize } from '~/context/localize'
|
||||||
import { useSession } from '~/context/session'
|
import { useSession } from '~/context/session'
|
||||||
import { loadReactions } from '~/graphql/api/public'
|
|
||||||
import loadShoutsQuery from '~/graphql/query/core/articles-load-by'
|
|
||||||
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'
|
||||||
import type { Author, Reaction, Shout, Topic } from '~/graphql/schema/core.gen'
|
import type { Author, Reaction, Shout, Topic } from '~/graphql/schema/core.gen'
|
||||||
import { byCreated } from '~/lib/sort'
|
import { byCreated } from '~/lib/sort'
|
||||||
import { paginate } from '~/utils/paginate'
|
import { paginate } from '~/utils/paginate'
|
||||||
import { restoreScrollPosition, saveScrollPosition } from '~/utils/scroll'
|
|
||||||
import stylesArticle from '../../Article/Article.module.scss'
|
import stylesArticle from '../../Article/Article.module.scss'
|
||||||
import { Comment } from '../../Article/Comment'
|
import { Comment } from '../../Article/Comment'
|
||||||
import { AuthorCard } from '../../Author/AuthorCard'
|
import { AuthorCard } from '../../Author/AuthorCard'
|
||||||
|
@ -48,7 +45,6 @@ export const AuthorView = (props: AuthorViewProps) => {
|
||||||
const { followers: myFollowers, follows: myFollows } = useFollowing()
|
const { followers: myFollowers, follows: myFollows } = useFollowing()
|
||||||
|
|
||||||
// signals
|
// signals
|
||||||
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
|
|
||||||
const [isBioExpanded, setIsBioExpanded] = createSignal(false)
|
const [isBioExpanded, setIsBioExpanded] = createSignal(false)
|
||||||
const [author, setAuthor] = createSignal<Author>()
|
const [author, setAuthor] = createSignal<Author>()
|
||||||
const [followers, setFollowers] = createSignal<Author[]>([] as Author[])
|
const [followers, setFollowers] = createSignal<Author[]>([] as Author[])
|
||||||
|
@ -62,20 +58,6 @@ export const AuthorView = (props: AuthorViewProps) => {
|
||||||
paginate(sortedFeed(), PRERENDERED_ARTICLES_COUNT, LOAD_MORE_PAGE_SIZE)
|
paginate(sortedFeed(), PRERENDERED_ARTICLES_COUNT, LOAD_MORE_PAGE_SIZE)
|
||||||
)
|
)
|
||||||
|
|
||||||
// fx
|
|
||||||
// пагинация загрузки ленты постов
|
|
||||||
const loadMore = async () => {
|
|
||||||
saveScrollPosition()
|
|
||||||
const resp = await query(loadShoutsQuery, {
|
|
||||||
filters: { author: props.authorSlug },
|
|
||||||
limit: LOAD_MORE_PAGE_SIZE,
|
|
||||||
offset: sortedFeed().length
|
|
||||||
})
|
|
||||||
const hasMore = resp?.data?.load_shouts_by?.hasMore
|
|
||||||
setIsLoadMoreButtonVisible(hasMore)
|
|
||||||
restoreScrollPosition()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1 // проверяет не собственный ли это профиль, иначе - загружает
|
// 1 // проверяет не собственный ли это профиль, иначе - загружает
|
||||||
const [isFetching, setIsFetching] = createSignal(false)
|
const [isFetching, setIsFetching] = createSignal(false)
|
||||||
createEffect(
|
createEffect(
|
||||||
|
@ -122,24 +104,6 @@ export const AuthorView = (props: AuthorViewProps) => {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
// 3 // догружает ленту и комментарии
|
|
||||||
createEffect(
|
|
||||||
on(
|
|
||||||
() => author() as Author,
|
|
||||||
async (profile: Author) => {
|
|
||||||
if (!commented() && profile) {
|
|
||||||
await loadMore()
|
|
||||||
const commentsFetcher = loadReactions({
|
|
||||||
by: { comment: true, created_by: profile.id }
|
|
||||||
})
|
|
||||||
const ccc = await commentsFetcher()
|
|
||||||
if (ccc) setCommented((_) => ccc || [])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// { defer: true },
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
// event handlers
|
// event handlers
|
||||||
let bioContainerRef: HTMLDivElement
|
let bioContainerRef: HTMLDivElement
|
||||||
let bioWrapperRef: HTMLDivElement
|
let bioWrapperRef: HTMLDivElement
|
||||||
|
@ -290,14 +254,6 @@ export const AuthorView = (props: AuthorViewProps) => {
|
||||||
</Match>
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
<Show when={isLoadMoreButtonVisible()}>
|
|
||||||
<p class="load-more-container">
|
|
||||||
<button class="button" onClick={loadMore}>
|
|
||||||
{t('Load more')}
|
|
||||||
</button>
|
|
||||||
</p>
|
|
||||||
</Show>
|
|
||||||
</Show>
|
</Show>
|
||||||
</Match>
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
|
|
|
@ -9,6 +9,7 @@ export type LoadMoreItems = Shout[] | Author[] | Reaction[]
|
||||||
type LoadMoreProps = {
|
type LoadMoreProps = {
|
||||||
loadFunction: (offset?: number) => Promise<LoadMoreItems>
|
loadFunction: (offset?: number) => Promise<LoadMoreItems>
|
||||||
pageSize: number
|
pageSize: number
|
||||||
|
hidden?: boolean
|
||||||
children: JSX.Element
|
children: JSX.Element
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +38,7 @@ export const LoadMoreWrapper = (props: LoadMoreProps) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{props.children}
|
{props.children}
|
||||||
<Show when={isLoadMoreButtonVisible()}>
|
<Show when={isLoadMoreButtonVisible() && !props.hidden}>
|
||||||
<div class="load-more-container">
|
<div class="load-more-container">
|
||||||
<Button
|
<Button
|
||||||
onClick={loadItems}
|
onClick={loadItems}
|
||||||
|
|
|
@ -24,6 +24,7 @@ type ReactionsContextType = {
|
||||||
createReaction: (reaction: MutationCreate_ReactionArgs) => Promise<void>
|
createReaction: (reaction: MutationCreate_ReactionArgs) => Promise<void>
|
||||||
updateReaction: (reaction: MutationUpdate_ReactionArgs) => Promise<Reaction>
|
updateReaction: (reaction: MutationUpdate_ReactionArgs) => Promise<Reaction>
|
||||||
deleteReaction: (id: number) => Promise<{ error: string } | null>
|
deleteReaction: (id: number) => Promise<{ error: string } | null>
|
||||||
|
addReactions: (rrr: Reaction[]) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const ReactionsContext = createContext<ReactionsContextType>({} as ReactionsContextType)
|
const ReactionsContext = createContext<ReactionsContextType>({} as ReactionsContextType)
|
||||||
|
@ -38,24 +39,27 @@ export const ReactionsProvider = (props: { children: JSX.Element }) => {
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
const { showSnackbar } = useSnackbar()
|
const { showSnackbar } = useSnackbar()
|
||||||
const { mutation } = useGraphQL()
|
const { mutation } = useGraphQL()
|
||||||
|
const addReactions = (rrr: Reaction[]) => {
|
||||||
const loadReactionsBy = async (opts: QueryLoad_Reactions_ByArgs): Promise<Reaction[]> => {
|
const newReactionsByShout: Record<string, Reaction[]> = { ...reactionsByShout }
|
||||||
!opts.by && console.warn('reactions provider got wrong opts')
|
const newReactionEntities = rrr.reduce(
|
||||||
const fetcher = await loadReactions(opts)
|
|
||||||
const result = (await fetcher()) || []
|
|
||||||
console.debug('[context.reactions] loaded', result)
|
|
||||||
const newReactionsByShout: Record<string, Reaction[]> = {}
|
|
||||||
const newReactionEntities = result.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.slug]) newReactionsByShout[reaction.shout.slug] = []
|
||||||
newReactionsByShout[reaction.shout.slug].push(reaction)
|
newReactionsByShout[reaction.shout.slug].push(reaction)
|
||||||
return acc
|
return acc
|
||||||
},
|
},
|
||||||
{}
|
{ ...reactionEntities }
|
||||||
)
|
)
|
||||||
setReactionsByShout(newReactionsByShout)
|
|
||||||
setReactionEntities(newReactionEntities)
|
setReactionEntities(newReactionEntities)
|
||||||
|
setReactionsByShout(newReactionsByShout)
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadReactionsBy = async (opts: QueryLoad_Reactions_ByArgs): Promise<Reaction[]> => {
|
||||||
|
!opts.by && console.warn('reactions provider got wrong opts')
|
||||||
|
const fetcher = await loadReactions(opts)
|
||||||
|
const result = (await fetcher()) || []
|
||||||
|
console.debug('[context.reactions] loaded', result)
|
||||||
|
result && addReactions(result)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,7 +124,8 @@ export const ReactionsProvider = (props: { children: JSX.Element }) => {
|
||||||
loadReactionsBy,
|
loadReactionsBy,
|
||||||
createReaction,
|
createReaction,
|
||||||
updateReaction,
|
updateReaction,
|
||||||
deleteReaction
|
deleteReaction,
|
||||||
|
addReactions
|
||||||
}
|
}
|
||||||
|
|
||||||
const value: ReactionsContextType = { reactionEntities, reactionsByShout, ...actions }
|
const value: ReactionsContextType = { reactionEntities, reactionsByShout, ...actions }
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import { RouteSectionProps, createAsync } from '@solidjs/router'
|
import { RouteSectionProps, createAsync } from '@solidjs/router'
|
||||||
import { ErrorBoundary, Suspense, createEffect, createMemo } from 'solid-js'
|
import { ErrorBoundary, createEffect, createMemo } from 'solid-js'
|
||||||
import { AuthorView } from '~/components/Views/Author'
|
import { AuthorView } from '~/components/Views/Author'
|
||||||
import { FourOuFourView } from '~/components/Views/FourOuFour'
|
import { FourOuFourView } from '~/components/Views/FourOuFour'
|
||||||
import { Loading } from '~/components/_shared/Loading'
|
import { LoadMoreItems, LoadMoreWrapper } from '~/components/_shared/LoadMoreWrapper'
|
||||||
import { PageLayout } from '~/components/_shared/PageLayout'
|
import { PageLayout } from '~/components/_shared/PageLayout'
|
||||||
import { useAuthors } from '~/context/authors'
|
import { useAuthors } from '~/context/authors'
|
||||||
|
import { useFeed } from '~/context/feed'
|
||||||
import { useLocalize } from '~/context/localize'
|
import { useLocalize } from '~/context/localize'
|
||||||
import { ReactionsProvider } from '~/context/reactions'
|
import { ReactionsProvider, useReactions } from '~/context/reactions'
|
||||||
import { loadAuthors, loadShouts, loadTopics } from '~/graphql/api/public'
|
import { loadAuthors, loadReactions, loadShouts, loadTopics } from '~/graphql/api/public'
|
||||||
import {
|
import {
|
||||||
Author,
|
Author,
|
||||||
LoadShoutsOptions,
|
LoadShoutsOptions,
|
||||||
|
@ -38,10 +39,9 @@ const fetchAuthor = async (slug: string) => {
|
||||||
export const route = {
|
export const route = {
|
||||||
load: async ({ params, location: { query } }: RouteSectionProps<{ articles: Shout[] }>) => {
|
load: async ({ params, location: { query } }: RouteSectionProps<{ articles: Shout[] }>) => {
|
||||||
const offset: number = Number.parseInt(query.offset, 10)
|
const offset: number = Number.parseInt(query.offset, 10)
|
||||||
const result = await fetchAuthorShouts(params.slug, offset)
|
|
||||||
return {
|
return {
|
||||||
author: await fetchAuthor(params.slug),
|
author: await fetchAuthor(params.slug),
|
||||||
shouts: result || [],
|
shouts: await fetchAuthorShouts(params.slug, offset),
|
||||||
topics: await fetchAllTopics()
|
topics: await fetchAllTopics()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,11 +50,14 @@ export const route = {
|
||||||
export type AuthorPageProps = { articles?: Shout[]; author?: Author; topics?: Topic[] }
|
export type AuthorPageProps = { articles?: Shout[]; author?: Author; topics?: Topic[] }
|
||||||
|
|
||||||
export default function AuthorPage(props: RouteSectionProps<AuthorPageProps>) {
|
export default function AuthorPage(props: RouteSectionProps<AuthorPageProps>) {
|
||||||
const { addAuthor } = useAuthors()
|
const { addAuthor, authorsEntities } = useAuthors()
|
||||||
const articles = createAsync(
|
const articles = createAsync(
|
||||||
async () => props.data.articles || (await fetchAuthorShouts(props.params.slug)) || []
|
async () => props.data.articles || (await fetchAuthorShouts(props.params.slug)) || []
|
||||||
)
|
)
|
||||||
const author = createAsync(async () => {
|
const author = createAsync(async () => {
|
||||||
|
const loadedBefore = authorsEntities()[props.params.slug]
|
||||||
|
if (loadedBefore) return loadedBefore
|
||||||
|
|
||||||
const a = props.data.author || (await fetchAuthor(props.params.slug))
|
const a = props.data.author || (await fetchAuthor(props.params.slug))
|
||||||
a && addAuthor(a)
|
a && addAuthor(a)
|
||||||
return a
|
return a
|
||||||
|
@ -80,12 +83,32 @@ export default function AuthorPage(props: RouteSectionProps<AuthorPageProps>) {
|
||||||
: getImageUrl('production/image/logo_image.png')
|
: getImageUrl('production/image/logo_image.png')
|
||||||
)
|
)
|
||||||
|
|
||||||
const selectedTab = createMemo(() =>
|
const selectedTab = createMemo(() => (props.params.tab in ['comments', 'about'] ? props.params.tab : ''))
|
||||||
props.params.tab in ['followers', 'shouts'] ? props.params.tab : 'name'
|
const { addReactions } = useReactions()
|
||||||
)
|
const loadMoreComments = async () => {
|
||||||
|
const commentsFetcher = loadReactions({
|
||||||
|
by: { comment: true, created_by: author()?.id }
|
||||||
|
})
|
||||||
|
const ccc = await commentsFetcher()
|
||||||
|
ccc && addReactions(ccc)
|
||||||
|
return ccc as LoadMoreItems
|
||||||
|
}
|
||||||
|
const { addFeed, feedByAuthor } = useFeed()
|
||||||
|
const loadMoreAuthorShouts = async () => {
|
||||||
|
const slug = author()?.slug
|
||||||
|
const offset = feedByAuthor()[props.params.slug].length
|
||||||
|
const shoutsFetcher = loadShouts({
|
||||||
|
filters: { author: slug },
|
||||||
|
offset,
|
||||||
|
limit: SHOUTS_PER_PAGE
|
||||||
|
})
|
||||||
|
const sss = await shoutsFetcher()
|
||||||
|
sss && addFeed(sss)
|
||||||
|
return sss as LoadMoreItems
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ErrorBoundary fallback={(_err) => <FourOuFourView />}>
|
<ErrorBoundary fallback={(_err) => <FourOuFourView />}>
|
||||||
<Suspense fallback={<Loading />}>
|
|
||||||
<PageLayout
|
<PageLayout
|
||||||
title={`${t('Discours')} :: ${title()}`}
|
title={`${t('Discours')} :: ${title()}`}
|
||||||
headerTitle={author()?.name || ''}
|
headerTitle={author()?.name || ''}
|
||||||
|
@ -94,16 +117,21 @@ export default function AuthorPage(props: RouteSectionProps<AuthorPageProps>) {
|
||||||
cover={cover()}
|
cover={cover()}
|
||||||
>
|
>
|
||||||
<ReactionsProvider>
|
<ReactionsProvider>
|
||||||
|
<LoadMoreWrapper
|
||||||
|
loadFunction={(selectedTab() === 'comments' ? loadMoreComments : loadMoreAuthorShouts)}
|
||||||
|
pageSize={SHOUTS_PER_PAGE}
|
||||||
|
hidden={selectedTab() !== '' || selectedTab() !== 'comments'}
|
||||||
|
>
|
||||||
<AuthorView
|
<AuthorView
|
||||||
author={author() as Author}
|
author={author() as Author}
|
||||||
selectedTab={selectedTab()}
|
selectedTab={selectedTab()}
|
||||||
authorSlug={props.params.slug}
|
authorSlug={props.params.slug}
|
||||||
shouts={articles() as Shout[]}
|
shouts={feedByAuthor()[props.params.slug] || articles() as Shout[]}
|
||||||
topics={topics()}
|
topics={topics()}
|
||||||
/>
|
/>
|
||||||
|
</LoadMoreWrapper>
|
||||||
</ReactionsProvider>
|
</ReactionsProvider>
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
</Suspense>
|
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,22 @@
|
||||||
|
/**
|
||||||
|
* Делит массив на меньшие массивы (страницы) заданного размера.
|
||||||
|
*
|
||||||
|
* @template T - Тип элементов в массиве.
|
||||||
|
* @param {T[]} arr - Массив, который нужно разделить на страницы.
|
||||||
|
* @param {number} startIndex - Индекс, с которого начинается пагинация.
|
||||||
|
* @param {number} pageSize - Размер каждой страницы.
|
||||||
|
* @returns {T[][]} - Массив массивов, где каждый подмассив является страницей заданного размера.
|
||||||
|
*/
|
||||||
export function paginate<T>(arr: T[], startIndex: number, pageSize: number): T[][] {
|
export function paginate<T>(arr: T[], startIndex: number, pageSize: number): T[][] {
|
||||||
return arr.slice(startIndex).reduce((acc, item, index) => {
|
return arr.slice(startIndex).reduce((acc, item, index) => {
|
||||||
|
// Начинаем новую страницу, когда индекс кратен размеру страницы
|
||||||
if (index % pageSize === 0) {
|
if (index % pageSize === 0) {
|
||||||
acc.push([])
|
// Создаем новый подмассив (страницу)
|
||||||
|
acc.push([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
acc?.at(-1)?.push(item)
|
// Добавляем текущий элемент на последнюю страницу
|
||||||
return acc
|
acc.at(-1)?.push(item);
|
||||||
}, [] as T[][])
|
return acc;
|
||||||
|
}, [] as T[][]); // Инициализируем аккумулятор как пустой массив массивов
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user