load more (Feed, Author, Topic, Home update)

This commit is contained in:
Igor Lobanov 2022-10-28 23:21:47 +02:00
parent d491ae741e
commit 25fd5bf42f
23 changed files with 299 additions and 150 deletions

View File

@ -2,7 +2,7 @@ import type { Author } from '../../graphql/types.gen'
import { AuthorCard } from './Card'
import './Full.scss'
export default (props: { author: Author }) => {
export const AuthorFull = (props: { author: Author }) => {
return (
<div class="container">
<div class="row">

View File

@ -21,7 +21,7 @@ interface BesideProps {
iconButton?: boolean
}
export default (props: BesideProps) => {
export const Beside = (props: BesideProps) => {
return (
<Show when={!!props.beside?.slug && props.values?.length > 0}>
<div class="floor floor--9">

View File

@ -1,7 +1,7 @@
import { For, Suspense } from 'solid-js/web'
import OneWide from './Row1'
import Row2 from './Row2'
import Row3 from './Row3'
import { Row1 } from './Row1'
import { Row2 } from './Row2'
import { Row3 } from './Row3'
import { shuffle } from '../../utils'
import { createMemo, createSignal } from 'solid-js'
import type { JSX } from 'solid-js'
@ -10,7 +10,7 @@ import './List.scss'
import { t } from '../../utils/intl'
export const Block6 = (props: { articles: Shout[] }) => {
const dice = createMemo(() => shuffle([OneWide, Row2, Row3]))
const dice = createMemo(() => shuffle([Row1, Row2, Row3]))
return (
<>

View File

@ -2,7 +2,7 @@ import { Show } from 'solid-js'
import type { Shout } from '../../graphql/types.gen'
import { ArticleCard } from './Card'
export default (props: { article: Shout }) => (
export const Row1 = (props: { article: Shout }) => (
<Show when={!!props.article}>
<div class="floor floor--one-article">
<div class="wide-container row">

View File

@ -2,13 +2,14 @@ import { createComputed, createSignal, Show } from 'solid-js'
import { For } from 'solid-js/web'
import type { Shout } from '../../graphql/types.gen'
import { ArticleCard } from './Card'
const x = [
['6', '6'],
['4', '8'],
['8', '4']
]
export default (props: { articles: Shout[] }) => {
export const Row2 = (props: { articles: Shout[] }) => {
const [y, setY] = createSignal(0)
createComputed(() => setY(Math.floor(Math.random() * x.length)))

View File

@ -2,7 +2,7 @@ import { For } from 'solid-js/web'
import type { Shout } from '../../graphql/types.gen'
import { ArticleCard } from './Card'
export default (props: { articles: Shout[]; header?: any }) => {
export const Row3 = (props: { articles: Shout[]; header?: any }) => {
return (
<div class="floor">
<div class="wide-container row">

View File

@ -1,8 +1,8 @@
import { MainLayout } from '../Layouts/MainLayout'
import { AuthorView } from '../Views/Author'
import { AuthorView, PRERENDERED_ARTICLES_COUNT } from '../Views/Author'
import type { PageProps } from '../types'
import { createMemo, createSignal, onCleanup, onMount, Show } from 'solid-js'
import { loadArticlesForAuthors, resetSortedArticles } from '../../stores/zine/articles'
import { loadAuthorArticles, resetSortedArticles } from '../../stores/zine/articles'
import { useRouter } from '../../stores/router'
import { loadAuthor } from '../../stores/zine/authors'
import { Loading } from '../Loading'
@ -27,7 +27,7 @@ export const AuthorPage = (props: PageProps) => {
return
}
await loadArticlesForAuthors({ authorSlugs: [slug()] })
await loadAuthorArticles({ authorSlug: slug(), limit: PRERENDERED_ARTICLES_COUNT })
await loadAuthor({ slug: slug() })
setIsLoaded(true)

View File

@ -1,30 +1,14 @@
import { MainLayout } from '../Layouts/MainLayout'
import { FeedView } from '../Views/Feed'
import type { PageProps } from '../types'
import { createSignal, onCleanup, onMount, Show } from 'solid-js'
import { loadRecentArticles, resetSortedArticles } from '../../stores/zine/articles'
import { Loading } from '../Loading'
export const FeedPage = (props: PageProps) => {
const [isLoaded, setIsLoaded] = createSignal(Boolean(props.feedArticles))
onMount(async () => {
if (isLoaded()) {
return
}
await loadRecentArticles({ limit: 50, offset: 0 })
setIsLoaded(true)
})
import { onCleanup } from 'solid-js'
import { resetSortedArticles } from '../../stores/zine/articles'
export const FeedPage = () => {
onCleanup(() => resetSortedArticles())
return (
<MainLayout>
<Show when={isLoaded()} fallback={<Loading />}>
<FeedView articles={props.feedArticles} />
</Show>
<FeedView />
</MainLayout>
)
}

View File

@ -1,4 +1,4 @@
import { HomeView } from '../Views/Home'
import { HomeView, PRERENDERED_ARTICLES_COUNT } from '../Views/Home'
import { MainLayout } from '../Layouts/MainLayout'
import type { PageProps } from '../types'
import { createSignal, onCleanup, onMount, Show } from 'solid-js'
@ -14,7 +14,7 @@ export const HomePage = (props: PageProps) => {
return
}
await loadPublishedArticles({ limit: 5, offset: 0 })
await loadPublishedArticles({ limit: PRERENDERED_ARTICLES_COUNT, offset: 0 })
await loadRandomTopics()
setIsLoaded(true)

View File

@ -1,8 +1,8 @@
import { MainLayout } from '../Layouts/MainLayout'
import { TopicView } from '../Views/Topic'
import { PRERENDERED_ARTICLES_COUNT, TopicView } from '../Views/Topic'
import type { PageProps } from '../types'
import { createMemo, createSignal, onCleanup, onMount, Show } from 'solid-js'
import { loadArticlesForTopics, resetSortedArticles } from '../../stores/zine/articles'
import { loadTopicArticles, resetSortedArticles } from '../../stores/zine/articles'
import { useRouter } from '../../stores/router'
import { loadTopic } from '../../stores/zine/topics'
import { Loading } from '../Loading'
@ -27,7 +27,7 @@ export const TopicPage = (props: PageProps) => {
return
}
await loadArticlesForTopics({ topicSlugs: [slug()] })
await loadTopicArticles({ topicSlug: slug(), limit: PRERENDERED_ARTICLES_COUNT, offset: 0 })
await loadTopic({ slug: slug() })
setIsLoaded(true)

View File

@ -1,17 +1,18 @@
import { Show, createMemo } from 'solid-js'
import { Show, createMemo, createSignal, For, onMount } from 'solid-js'
import type { Author, Shout } from '../../graphql/types.gen'
import Row2 from '../Feed/Row2'
import Row3 from '../Feed/Row3'
// import Beside from '../Feed/Beside'
import AuthorFull from '../Author/Full'
import { Row2 } from '../Feed/Row2'
import { Row3 } from '../Feed/Row3'
import { AuthorFull } from '../Author/Full'
import { t } from '../../utils/intl'
import { useAuthorsStore } from '../../stores/zine/authors'
import { useArticlesStore } from '../../stores/zine/articles'
import { loadAuthorArticles, useArticlesStore } from '../../stores/zine/articles'
import '../../styles/Topic.scss'
import { useTopicsStore } from '../../stores/zine/topics'
import { useRouter } from '../../stores/router'
import Beside from '../Feed/Beside'
import { Beside } from '../Feed/Beside'
import { restoreScrollPosition, saveScrollPosition } from '../../utils/scroll'
import { splitToPages } from '../../utils/splitToPages'
// TODO: load reactions on client
type AuthorProps = {
@ -26,16 +27,37 @@ type AuthorPageSearchParams = {
by: '' | 'viewed' | 'rating' | 'commented' | 'recent'
}
export const PRERENDERED_ARTICLES_COUNT = 12
const LOAD_MORE_PAGE_SIZE = 9 // Row3 + Row3 + Row3
export const AuthorView = (props: AuthorProps) => {
const { sortedArticles } = useArticlesStore({
sortedArticles: props.authorArticles
})
const { authorEntities } = useAuthorsStore({ authors: [props.author] })
const { topicsByAuthor } = useTopicsStore()
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
const author = createMemo(() => authorEntities()[props.authorSlug])
const { searchParams, changeSearchParam } = useRouter<AuthorPageSearchParams>()
const loadMore = async () => {
saveScrollPosition()
const { hasMore } = await loadAuthorArticles({
authorSlug: author().slug,
limit: LOAD_MORE_PAGE_SIZE,
offset: sortedArticles().length
})
setIsLoadMoreButtonVisible(hasMore)
restoreScrollPosition()
}
onMount(async () => {
if (sortedArticles().length === PRERENDERED_ARTICLES_COUNT) {
loadMore()
}
})
const title = createMemo(() => {
const m = searchParams().by
if (m === 'viewed') return t('Top viewed')
@ -44,6 +66,10 @@ export const AuthorView = (props: AuthorProps) => {
return t('Top recent')
})
const pages = createMemo<Shout[][]>(() =>
splitToPages(sortedArticles(), PRERENDERED_ARTICLES_COUNT, LOAD_MORE_PAGE_SIZE)
)
return (
<div class="container author-page">
<Show when={author()} fallback={<div class="center">{t('Loading')}</div>}>
@ -83,8 +109,8 @@ export const AuthorView = (props: AuthorProps) => {
</div>
<h3 class="col-12">{title()}</h3>
<div class="row">
<Show when={sortedArticles().length > 0}>
<Beside
title={t('Topics which supported by author')}
values={topicsByAuthor()[author().slug].slice(0, 5)}
@ -96,18 +122,26 @@ export const AuthorView = (props: AuthorProps) => {
iconButton={true}
/>
<Row3 articles={sortedArticles().slice(1, 4)} />
<Show when={sortedArticles().length > 4}>
<Row2 articles={sortedArticles().slice(4, 6)} />
</Show>
<Show when={sortedArticles().length > 6}>
<Row3 articles={sortedArticles().slice(6, 9)} />
</Show>
<Show when={sortedArticles().length > 9}>
<Row3 articles={sortedArticles().slice(9, 12)} />
</Show>
<For each={pages()}>
{(page) => (
<>
<Row3 articles={page.slice(0, 3)} />
<Row3 articles={page.slice(3, 6)} />
<Row3 articles={page.slice(6, 9)} />
</>
)}
</For>
<Show when={isLoadMoreButtonVisible()}>
<p class="load-more-container">
<button class="button" onClick={loadMore}>
{t('Load more')}
</button>
</p>
</Show>
</div>
</Show>

View File

@ -1,5 +1,4 @@
import { createMemo, For, Show } from 'solid-js'
import type { Shout, Reaction } from '../../graphql/types.gen'
import { createMemo, createSignal, For, onMount, Show } from 'solid-js'
import '../../styles/Feed.scss'
import stylesBeside from '../../components/Feed/Beside.module.scss'
import { Icon } from '../Nav/Icon'
@ -17,11 +16,6 @@ import { useAuthorsStore } from '../../stores/zine/authors'
import { useTopicsStore } from '../../stores/zine/topics'
import { useTopAuthorsStore } from '../../stores/zine/topAuthors'
interface FeedProps {
articles: Shout[]
reactions?: Reaction[]
}
// const AUTHORSHIP_REACTIONS = [
// ReactionKind.Accept,
// ReactionKind.Reject,
@ -29,9 +23,11 @@ interface FeedProps {
// ReactionKind.Ask
// ]
export const FeedView = (props: FeedProps) => {
export const FEED_PAGE_SIZE = 20
export const FeedView = () => {
// state
const { sortedArticles } = useArticlesStore({ sortedArticles: props.articles })
const { sortedArticles } = useArticlesStore()
const reactions = useReactionsStore()
const { sortedAuthors } = useAuthorsStore()
const { topTopics } = useTopicsStore()
@ -40,6 +36,8 @@ export const FeedView = (props: FeedProps) => {
const topReactions = createMemo(() => sortBy(reactions(), byCreated))
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
// const expectingFocus = createMemo<Shout[]>(() => {
// // 1 co-author notifications needs
// // TODO: list of articles where you are co-author
@ -53,13 +51,15 @@ export const FeedView = (props: FeedProps) => {
// return []
// })
// eslint-disable-next-line unicorn/consistent-function-scoping
const loadMore = () => {
// const limit = props.limit || 50
// const offset = props.offset || 0
// FIXME
loadRecentArticles({ limit: 50, offset: 0 })
const loadMore = async () => {
const { hasMore } = await loadRecentArticles({ limit: FEED_PAGE_SIZE, offset: sortedArticles().length })
setIsLoadMoreButtonVisible(hasMore)
}
onMount(() => {
loadMore()
})
return (
<>
<div class="container feed">
@ -113,10 +113,6 @@ export const FeedView = (props: FeedProps) => {
{(article) => <ArticleCard article={article} settings={{ isFeedMode: true }} />}
</For>
</Show>
<p class="load-more-container">
<button class="button">{t('Load more')}</button>
</p>
</div>
<aside class="col-md-3">
@ -136,12 +132,13 @@ export const FeedView = (props: FeedProps) => {
</Show>
</aside>
</div>
<Show when={isLoadMoreButtonVisible()}>
<p class="load-more-container">
<button class="button" onClick={loadMore}>
{t('Load more')}
</button>
</p>
</Show>
</div>
</>
)

View File

@ -1,12 +1,12 @@
import { createMemo, For, onMount, Show } from 'solid-js'
import { createMemo, createSignal, For, onMount, Show } from 'solid-js'
import Banner from '../Discours/Banner'
import { NavTopics } from '../Nav/Topics'
import { Row5 } from '../Feed/Row5'
import Row3 from '../Feed/Row3'
import Row2 from '../Feed/Row2'
import Row1 from '../Feed/Row1'
import { Row3 } from '../Feed/Row3'
import { Row2 } from '../Feed/Row2'
import { Row1 } from '../Feed/Row1'
import Hero from '../Discours/Hero'
import Beside from '../Feed/Beside'
import { Beside } from '../Feed/Beside'
import RowShort from '../Feed/RowShort'
import Slider from '../Feed/Slider'
import Group from '../Feed/Group'
@ -24,6 +24,7 @@ import {
import { useTopAuthorsStore } from '../../stores/zine/topAuthors'
import { locale } from '../../stores/ui'
import { restoreScrollPosition, saveScrollPosition } from '../../utils/scroll'
import { splitToPages } from '../../utils/splitToPages'
const log = getLogger('home view')
@ -31,7 +32,8 @@ type HomeProps = {
randomTopics: Topic[]
recentPublishedArticles: Shout[]
}
const PRERENDERED_ARTICLES_COUNT = 5
export const PRERENDERED_ARTICLES_COUNT = 5
const CLIENT_LOAD_ARTICLES_COUNT = 29
const LOAD_MORE_PAGE_SIZE = 16 // Row1 + Row3 + Row2 + Beside (3 + 1) + Row1 + Row 2 + Row3
@ -49,14 +51,20 @@ export const HomeView = (props: HomeProps) => {
const { randomTopics, topTopics } = useTopicsStore({
randomTopics: props.randomTopics
})
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
const { topAuthors } = useTopAuthorsStore()
onMount(() => {
onMount(async () => {
loadTopArticles()
loadTopMonthArticles()
if (sortedArticles().length < PRERENDERED_ARTICLES_COUNT + CLIENT_LOAD_ARTICLES_COUNT) {
loadPublishedArticles({ limit: CLIENT_LOAD_ARTICLES_COUNT, offset: sortedArticles().length })
const { hasMore } = await loadPublishedArticles({
limit: CLIENT_LOAD_ARTICLES_COUNT,
offset: sortedArticles().length
})
setIsLoadMoreButtonVisible(hasMore)
}
})
@ -85,22 +93,23 @@ export const HomeView = (props: HomeProps) => {
const loadMore = async () => {
saveScrollPosition()
await loadPublishedArticles({ limit: LOAD_MORE_PAGE_SIZE, offset: sortedArticles().length })
const { hasMore } = await loadPublishedArticles({
limit: LOAD_MORE_PAGE_SIZE,
offset: sortedArticles().length
})
setIsLoadMoreButtonVisible(hasMore)
restoreScrollPosition()
}
const pages = createMemo<Shout[][]>(() => {
return sortedArticles()
.slice(PRERENDERED_ARTICLES_COUNT + CLIENT_LOAD_ARTICLES_COUNT)
.reduce((acc, article, index) => {
if (index % LOAD_MORE_PAGE_SIZE === 0) {
acc.push([])
}
acc[acc.length - 1].push(article)
return acc
}, [] as Shout[][])
})
const pages = createMemo<Shout[][]>(() =>
splitToPages(
sortedArticles(),
PRERENDERED_ARTICLES_COUNT + CLIENT_LOAD_ARTICLES_COUNT,
LOAD_MORE_PAGE_SIZE
)
)
return (
<Show when={locale() && sortedArticles().length > 0}>
@ -173,11 +182,13 @@ export const HomeView = (props: HomeProps) => {
)}
</For>
<Show when={isLoadMoreButtonVisible()}>
<p class="load-more-container">
<button class="button" onClick={loadMore}>
{t('Load more')}
</button>
</p>
</Show>
</Show>
)
}

View File

@ -1,16 +1,18 @@
import { For, Show, createMemo } from 'solid-js'
import { For, Show, createMemo, onMount, createSignal } from 'solid-js'
import type { Shout, Topic } from '../../graphql/types.gen'
import Row3 from '../Feed/Row3'
import Row2 from '../Feed/Row2'
import Beside from '../Feed/Beside'
import { Row3 } from '../Feed/Row3'
import { Row2 } from '../Feed/Row2'
import { Beside } from '../Feed/Beside'
import { ArticleCard } from '../Feed/Card'
import '../../styles/Topic.scss'
import { FullTopic } from '../Topic/Full'
import { t } from '../../utils/intl'
import { useRouter } from '../../stores/router'
import { useTopicsStore } from '../../stores/zine/topics'
import { useArticlesStore } from '../../stores/zine/articles'
import { loadPublishedArticles, useArticlesStore } from '../../stores/zine/articles'
import { useAuthorsStore } from '../../stores/zine/authors'
import { restoreScrollPosition, saveScrollPosition } from '../../utils/scroll'
import { splitToPages } from '../../utils/splitToPages'
type TopicsPageSearchParams = {
by: 'comments' | '' | 'recent' | 'viewed' | 'rating' | 'commented'
@ -22,9 +24,14 @@ interface TopicProps {
topicSlug: string
}
export const PRERENDERED_ARTICLES_COUNT = 21
const LOAD_MORE_PAGE_SIZE = 9 // Row3 + Row3 + Row3
export const TopicView = (props: TopicProps) => {
const { searchParams, changeSearchParam } = useRouter<TopicsPageSearchParams>()
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
const { sortedArticles } = useArticlesStore({ sortedArticles: props.topicArticles })
const { topicEntities } = useTopicsStore({ topics: [props.topic] })
@ -32,6 +39,24 @@ export const TopicView = (props: TopicProps) => {
const topic = createMemo(() => topicEntities()[props.topicSlug])
const loadMore = async () => {
saveScrollPosition()
const { hasMore } = await loadPublishedArticles({
limit: LOAD_MORE_PAGE_SIZE,
offset: sortedArticles().length
})
setIsLoadMoreButtonVisible(hasMore)
restoreScrollPosition()
}
onMount(async () => {
if (sortedArticles().length === PRERENDERED_ARTICLES_COUNT) {
loadMore()
}
})
const title = createMemo(() => {
const m = searchParams().by
if (m === 'viewed') return t('Top viewed')
@ -40,6 +65,10 @@ export const TopicView = (props: TopicProps) => {
return t('Top recent')
})
const pages = createMemo<Shout[][]>(() =>
splitToPages(sortedArticles(), PRERENDERED_ARTICLES_COUNT, LOAD_MORE_PAGE_SIZE)
)
return (
<div class="topic-page container">
<Show when={topic()}>
@ -110,6 +139,24 @@ export const TopicView = (props: TopicProps) => {
<Row3 articles={sortedArticles().slice(15, 18)} />
<Row3 articles={sortedArticles().slice(18, 21)} />
</Show>
<For each={pages()}>
{(page) => (
<>
<Row3 articles={page.slice(0, 3)} />
<Row3 articles={page.slice(3, 6)} />
<Row3 articles={page.slice(6, 9)} />
</>
)}
</For>
<Show when={isLoadMoreButtonVisible()}>
<p class="load-more-container">
<button class="button" onClick={loadMore}>
{t('Load more')}
</button>
</p>
</Show>
</div>
</Show>
</div>

View File

@ -8,7 +8,6 @@ export type PageProps = {
authorArticles?: Shout[]
topicArticles?: Shout[]
homeArticles?: Shout[]
feedArticles?: Shout[]
author?: Author
allAuthors?: Author[]
topic?: Topic

View File

@ -3,9 +3,10 @@ import { Root } from '../../../components/Root'
import Zine from '../../../layouts/zine.astro'
import { apiClient } from '../../../utils/apiClient'
import { initRouter } from '../../../stores/router'
import { PRERENDERED_ARTICLES_COUNT } from '../../../components/Views/Author'
const slug = Astro.params.slug.toString()
const articles = await apiClient.getArticlesForAuthors({ authorSlugs: [slug], limit: 50 })
const articles = await apiClient.getArticlesForAuthors({ authorSlugs: [slug], limit: PRERENDERED_ARTICLES_COUNT })
const author = articles[0].authors.find((a) => a.slug === slug)
const { pathname, search } = Astro.url

View File

@ -1,16 +1,12 @@
---
import { Root } from '../../components/Root'
import Zine from '../../layouts/zine.astro'
import { apiClient } from '../../utils/apiClient'
import { initRouter } from '../../stores/router'
const { pathname, search } = Astro.url
initRouter(pathname, search)
const articles = await apiClient.getRecentArticles({ limit: 50 })
---
<Zine>
<Root feedArticles={articles} client:load />
<Root client:load />
</Zine>

View File

@ -3,14 +3,14 @@ import Zine from '../layouts/zine.astro'
import { Root } from '../components/Root'
import { apiClient } from '../utils/apiClient'
import { initRouter } from '../stores/router'
import { PRERENDERED_ARTICLES_COUNT } from '../components/Views/Home'
const randomTopics = await apiClient.getRandomTopics({ amount: 12 })
const articles = await apiClient.getRecentPublishedArticles({ limit: 5 })
const articles = await apiClient.getRecentPublishedArticles({ limit: PRERENDERED_ARTICLES_COUNT })
const { pathname, search } = Astro.url
initRouter(pathname, search)
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
---

View File

@ -2,9 +2,10 @@
import { Root } from '../../components/Root'
import Zine from '../../layouts/zine.astro'
import { apiClient } from '../../utils/apiClient'
import { PRERENDERED_ARTICLES_COUNT } from '../../components/Views/Topic'
const slug = Astro.params.slug?.toString() || ''
const articles = await apiClient.getArticlesForTopics({ topicSlugs: [slug], limit: 50 })
const articles = await apiClient.getArticlesForTopics({ topicSlugs: [slug], limit: PRERENDERED_ARTICLES_COUNT })
const topic = articles[0].topics.find(({ slug: topicSlug }) => topicSlug === slug)
import { initRouter } from '../../stores/router'

View File

@ -127,40 +127,109 @@ const addSortedArticles = (articles: Shout[]) => {
setSortedArticles((prevSortedArticles) => [...prevSortedArticles, ...articles])
}
export const loadFeed = async ({
limit,
offset
}: {
limit: number
offset?: number
}): Promise<{ hasMore: boolean }> => {
// TODO: load actual feed
return await loadRecentArticles({ limit, offset })
}
export const loadRecentArticles = async ({
limit,
offset
}: {
limit?: number
limit: number
offset?: number
}): Promise<void> => {
const newArticles = await apiClient.getRecentArticles({ limit, offset })
}): Promise<{ hasMore: boolean }> => {
const newArticles = await apiClient.getRecentArticles({ limit: limit + 1, offset })
const hasMore = newArticles.length === limit + 1
if (hasMore) {
newArticles.splice(-1)
}
addArticles(newArticles)
addSortedArticles(newArticles)
return { hasMore }
}
export const loadPublishedArticles = async ({
limit,
offset
offset = 0
}: {
limit?: number
limit: number
offset?: number
}): Promise<void> => {
const newArticles = await apiClient.getPublishedArticles({ limit, offset })
}): Promise<{ hasMore: boolean }> => {
const newArticles = await apiClient.getPublishedArticles({ limit: limit + 1, offset })
const hasMore = newArticles.length === limit + 1
if (hasMore) {
newArticles.splice(-1)
}
addArticles(newArticles)
addSortedArticles(newArticles)
return { hasMore }
}
export const loadArticlesForAuthors = async ({ authorSlugs }: { authorSlugs: string[] }): Promise<void> => {
const articles = await apiClient.getArticlesForAuthors({ authorSlugs, limit: 50 })
addArticles(articles)
setSortedArticles(articles)
export const loadAuthorArticles = async ({
authorSlug,
limit,
offset = 0
}: {
authorSlug: string
limit: number
offset?: number
}): Promise<{ hasMore: boolean }> => {
const newArticles = await apiClient.getArticlesForAuthors({
authorSlugs: [authorSlug],
limit: limit + 1,
offset
})
const hasMore = newArticles.length === limit + 1
if (hasMore) {
newArticles.splice(-1)
}
addArticles(newArticles)
addSortedArticles(newArticles)
return { hasMore }
}
export const loadArticlesForTopics = async ({ topicSlugs }: { topicSlugs: string[] }): Promise<void> => {
const articles = await apiClient.getArticlesForTopics({ topicSlugs, limit: 50 })
addArticles(articles)
setSortedArticles(articles)
export const loadTopicArticles = async ({
topicSlug,
limit,
offset
}: {
topicSlug: string
limit: number
offset: number
}): Promise<{ hasMore: boolean }> => {
const newArticles = await apiClient.getArticlesForTopics({
topicSlugs: [topicSlug],
limit: limit + 1,
offset
})
const hasMore = newArticles.length === limit + 1
if (hasMore) {
newArticles.splice(-1)
}
addArticles(newArticles)
addSortedArticles(newArticles)
return { hasMore }
}
export const resetSortedArticles = () => {

View File

@ -1,6 +1,5 @@
import { apiClient } from '../../utils/apiClient'
import type { Author } from '../../graphql/types.gen'
import { byCreated, byStat, byTopicStatDesc } from '../../utils/sortby'
import { getLogger } from '../../utils/logger'
import { createSignal } from 'solid-js'

View File

@ -184,7 +184,7 @@ export const apiClient = {
},
getArticlesForTopics: async ({
topicSlugs,
limit = FEED_SIZE,
limit,
offset = 0
}: {
topicSlugs: string[]
@ -207,7 +207,7 @@ export const apiClient = {
},
getArticlesForAuthors: async ({
authorSlugs,
limit = FEED_SIZE,
limit,
offset = 0
}: {
authorSlugs: string[]

10
src/utils/splitToPages.ts Normal file
View File

@ -0,0 +1,10 @@
export function splitToPages<T>(arr: T[], startIndex: number, pageSize: number): T[][] {
return arr.slice(startIndex).reduce((acc, article, index) => {
if (index % pageSize === 0) {
acc.push([])
}
acc[acc.length - 1].push(article)
return acc
}, [] as T[][])
}