load-more-main-ok
This commit is contained in:
parent
f6f012449d
commit
4fe2768329
|
@ -51,7 +51,7 @@ const data: PlaceholderData = {
|
|||
text: 'Placeholder feedDiscussions',
|
||||
buttonLabelAuthor: 'Current discussions',
|
||||
buttonLabelFeed: 'Enter',
|
||||
href: '/feed?by=last_comment'
|
||||
href: '/feed/hot'
|
||||
},
|
||||
author: {
|
||||
image: 'placeholder-join.webp',
|
||||
|
@ -71,7 +71,7 @@ const data: PlaceholderData = {
|
|||
header: 'Join discussions',
|
||||
text: 'Placeholder feedDiscussions',
|
||||
buttonLabel: 'Go to discussions',
|
||||
href: '/feed?by=last_comment',
|
||||
href: '/feed/hot',
|
||||
profileLinks: [
|
||||
{
|
||||
href: '/debate',
|
||||
|
|
|
@ -500,14 +500,14 @@ export const Header = (props: Props) => {
|
|||
</span>
|
||||
</A>
|
||||
</li>
|
||||
<li>
|
||||
{/* <li>
|
||||
<A href={'/feed/bookmarked'}>
|
||||
<span class={styles.subnavigationItemName}>
|
||||
<Icon name="bookmark" class={styles.icon} />
|
||||
{t('Bookmarks')}
|
||||
</span>
|
||||
</A>
|
||||
</li>
|
||||
</li> */}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { A, createAsync, useLocation, useNavigate, useSearchParams } from '@solidjs/router'
|
||||
import { clsx } from 'clsx'
|
||||
import { For, Show, createEffect, createMemo, createSignal, on, onMount } from 'solid-js'
|
||||
import { For, Show, createEffect, createMemo, createSignal, on } from 'solid-js'
|
||||
import { DropDown } from '~/components/_shared/DropDown'
|
||||
import { Option } from '~/components/_shared/DropDown/DropDown'
|
||||
import { Icon } from '~/components/_shared/Icon'
|
||||
|
@ -18,7 +18,7 @@ import { useUI } from '~/context/ui'
|
|||
import { loadUnratedShouts } from '~/graphql/api/private'
|
||||
import type { Author, Reaction, Shout } from '~/graphql/schema/core.gen'
|
||||
import { byCreated } from '~/lib/sort'
|
||||
import { FeedSearchParams } from '~/routes/feed/(feed)'
|
||||
import { FeedSearchParams } from '~/routes/feed/[...order]'
|
||||
import { CommentDate } from '../../Article/CommentDate'
|
||||
import { getShareUrl } from '../../Article/SharePopup'
|
||||
import { AuthorBadge } from '../../Author/AuthorBadge'
|
||||
|
@ -36,6 +36,7 @@ export type PeriodType = 'week' | 'month' | 'year'
|
|||
|
||||
export type FeedProps = {
|
||||
shouts?: Shout[]
|
||||
mode?: '' | 'likes' | 'hot'
|
||||
}
|
||||
|
||||
export const FeedView = (props: FeedProps) => {
|
||||
|
@ -53,7 +54,7 @@ export const FeedView = (props: FeedProps) => {
|
|||
const [isLoading, setIsLoading] = createSignal(false)
|
||||
const [isRightColumnLoaded, setIsRightColumnLoaded] = createSignal(false)
|
||||
const { session } = useSession()
|
||||
const { nonfeaturedFeed, setNonFeaturedFeed } = useFeed()
|
||||
const { feed, setFeed } = useFeed()
|
||||
const { loadReactionsBy } = useReactions()
|
||||
const { topTopics } = useTopics()
|
||||
const { topAuthors } = useAuthors()
|
||||
|
@ -67,20 +68,13 @@ export const FeedView = (props: FeedProps) => {
|
|||
setTopComments(comments.sort(byCreated).reverse())
|
||||
}
|
||||
|
||||
onMount(
|
||||
() =>
|
||||
props.shouts &&
|
||||
Array.isArray(props.shouts) &&
|
||||
setNonFeaturedFeed((prev) => [...prev, ...(props.shouts || [])]) && console.info(nonfeaturedFeed())
|
||||
)
|
||||
|
||||
createEffect(
|
||||
on(
|
||||
() => nonfeaturedFeed(),
|
||||
feed,
|
||||
(sss?: Shout[]) => {
|
||||
if (sss && Array.isArray(sss)) {
|
||||
setIsLoading(true)
|
||||
setNonFeaturedFeed((prev) => [...prev, ...sss])
|
||||
setFeed((prev) => [...prev, ...sss])
|
||||
Promise.all([
|
||||
loadTopComments(),
|
||||
loadReactionsBy({ by: { shouts: sss.map((s: Shout) => s.slug) } })
|
||||
|
@ -113,40 +107,33 @@ export const FeedView = (props: FeedProps) => {
|
|||
<Placeholder type={loc?.pathname} mode="feed" />
|
||||
</Show>
|
||||
|
||||
<Show when={(session() || loc?.pathname === 'feed') && nonfeaturedFeed()?.length}>
|
||||
<Show when={(session() || loc?.pathname === 'feed') && feed()?.length}>
|
||||
<div class={styles.filtersContainer}>
|
||||
<ul class={clsx('view-switcher', styles.feedFilter)}>
|
||||
<li
|
||||
class={clsx({
|
||||
'view-switcher__item--selected': searchParams?.by === 'after' || !searchParams?.by
|
||||
})}
|
||||
>
|
||||
<li class={clsx({ 'view-switcher__item--selected': !props.mode })}>
|
||||
<A href={loc.pathname}>{t('Recent')}</A>
|
||||
</li>
|
||||
{/*<li>*/}
|
||||
{/* <a href="/feed/?by=views">{t('Most read')}</a>*/}
|
||||
{/*</li>*/}
|
||||
<li
|
||||
class={clsx({
|
||||
'view-switcher__item--selected': searchParams?.by === 'likes'
|
||||
'view-switcher__item--selected': props.mode === 'likes'
|
||||
})}
|
||||
>
|
||||
<span class="link" onClick={() => changeSearchParams({ by: 'likes' })}>
|
||||
{t('Top rated')}
|
||||
</span>
|
||||
<A class="link" href={'/feed/likes'}>
|
||||
{t('Liked')}
|
||||
</A>
|
||||
</li>
|
||||
<li
|
||||
class={clsx({
|
||||
'view-switcher__item--selected': searchParams?.by === 'last_comment'
|
||||
'view-switcher__item--selected': props.mode === 'hot'
|
||||
})}
|
||||
>
|
||||
<span class="link" onClick={() => changeSearchParams({ by: 'last_comment' })}>
|
||||
<A class="link" href={'/feed/hot'}>
|
||||
{t('Commented')}
|
||||
</span>
|
||||
</A>
|
||||
</li>
|
||||
</ul>
|
||||
<div class={styles.dropdowns}>
|
||||
<Show when={searchParams?.by && searchParams?.by !== 'after'}>
|
||||
<Show when={searchParams?.period}>
|
||||
<DropDown
|
||||
popupProps={{ horizontalAnchor: 'right' }}
|
||||
options={asOptions(['week', 'month', 'year'])}
|
||||
|
@ -157,7 +144,7 @@ export const FeedView = (props: FeedProps) => {
|
|||
</Show>
|
||||
<DropDown
|
||||
popupProps={{ horizontalAnchor: 'right' }}
|
||||
options={asOptions(['followed', 'unrated', 'discussed', 'bookmarked', 'coauthored'])}
|
||||
options={asOptions(['followed', 'unrated', 'discussed', 'coauthored'])}
|
||||
currentOption={asOption(loc.pathname.split('/').pop() || '')}
|
||||
triggerCssClass={styles.periodSwitcher}
|
||||
onChange={(mode: Option) => navigate(`/feed/${mode.value}`)}
|
||||
|
@ -166,8 +153,8 @@ export const FeedView = (props: FeedProps) => {
|
|||
</div>
|
||||
|
||||
<Show when={!isLoading()} fallback={<Loading />}>
|
||||
<Show when={(nonfeaturedFeed() || []).length > 0}>
|
||||
<For each={(nonfeaturedFeed() || []).slice(0, 4)}>
|
||||
<Show when={(feed() || []).length > 0}>
|
||||
<For each={(feed() || []).slice(0, 4)}>
|
||||
{(article) => (
|
||||
<ArticleCard
|
||||
onShare={(shared) => handleShare(shared)}
|
||||
|
@ -199,7 +186,7 @@ export const FeedView = (props: FeedProps) => {
|
|||
</ul>
|
||||
</div>
|
||||
|
||||
<For each={(nonfeaturedFeed() || []).slice(4)}>
|
||||
<For each={(feed() || []).slice(4)}>
|
||||
{(article) => (
|
||||
<ArticleCard article={article} settings={{ isFeedMode: true }} desktopCoverSize="M" />
|
||||
)}
|
||||
|
|
|
@ -4,17 +4,17 @@ import { useLocalize } from '~/context/localize'
|
|||
import { Author, Reaction, Shout } from '~/graphql/schema/core.gen'
|
||||
import { restoreScrollPosition, saveScrollPosition } from '~/utils/scroll'
|
||||
|
||||
export type LoadMoreItems = Shout[] | Author[] | Reaction[]
|
||||
|
||||
type LoadMoreProps = {
|
||||
loadFunction: (offset?: number) => void
|
||||
loadFunction: (offset?: number) => Promise<LoadMoreItems>
|
||||
pageSize: number
|
||||
children: JSX.Element
|
||||
}
|
||||
|
||||
type Items = Shout[] | Author[] | Reaction[]
|
||||
|
||||
export const LoadMoreWrapper = (props: LoadMoreProps) => {
|
||||
const { t } = useLocalize()
|
||||
const [items, setItems] = createSignal<Items>([])
|
||||
const [items, setItems] = createSignal<LoadMoreItems>([])
|
||||
const [offset, setOffset] = createSignal(0)
|
||||
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(true)
|
||||
const [isLoading, setIsLoading] = createSignal(false)
|
||||
|
@ -25,7 +25,7 @@ export const LoadMoreWrapper = (props: LoadMoreProps) => {
|
|||
const newItems = await props.loadFunction(offset())
|
||||
if (!Array.isArray(newItems)) return
|
||||
console.debug('[_share] load more items', newItems)
|
||||
setItems((prev) => [...prev, ...newItems])
|
||||
setItems((prev) => [...prev, ...newItems] as LoadMoreItems)
|
||||
setOffset((prev) => prev + props.pageSize)
|
||||
setIsLoadMoreButtonVisible(newItems.length >= props.pageSize - 1)
|
||||
setIsLoading(false)
|
||||
|
|
|
@ -34,9 +34,9 @@ type FeedContextType = {
|
|||
seen: Accessor<{ [slug: string]: number }>
|
||||
addSeen: (slug: string) => void
|
||||
|
||||
// featured
|
||||
nonfeaturedFeed: Accessor<Shout[] | undefined>
|
||||
setNonFeaturedFeed: Setter<Shout[]>
|
||||
// all
|
||||
feed: Accessor<Shout[] | undefined>
|
||||
setFeed: Setter<Shout[]>
|
||||
|
||||
// featured
|
||||
featuredFeed: Accessor<Shout[] | undefined>
|
||||
|
@ -62,7 +62,7 @@ export const useFeed = () => useContext(FeedContext)
|
|||
export const FeedProvider = (props: { children: JSX.Element }) => {
|
||||
const [sortedFeed, setSortedFeed] = createSignal<Shout[]>([])
|
||||
const [articleEntities, setArticleEntities] = createSignal<{ [articleSlug: string]: Shout }>({})
|
||||
const [nonfeaturedFeed, setNonFeaturedFeed] = createSignal<Shout[]>([])
|
||||
const [feed, setFeed] = createSignal<Shout[]>([])
|
||||
const [featuredFeed, setFeaturedFeed] = createSignal<Shout[]>([])
|
||||
const [expoFeed, setExpoFeed] = createSignal<Shout[]>([])
|
||||
const [topFeed, setTopFeed] = createSignal<Shout[]>([])
|
||||
|
@ -260,8 +260,8 @@ export const FeedProvider = (props: { children: JSX.Element }) => {
|
|||
setFeaturedFeed,
|
||||
expoFeed,
|
||||
setExpoFeed,
|
||||
nonfeaturedFeed,
|
||||
setNonFeaturedFeed
|
||||
feed,
|
||||
setFeed
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
|
|
|
@ -263,6 +263,7 @@
|
|||
"Lists": "Списки",
|
||||
"Literature": "Литература",
|
||||
"Load more": "Показать ещё",
|
||||
"loaded": "загружено",
|
||||
"Loading": "Загрузка",
|
||||
"Login and security": "Вход и безопасность",
|
||||
"Logout": "Выход",
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
import { type RouteDefinition, type RouteSectionProps, createAsync } from '@solidjs/router'
|
||||
import { Show, createEffect } from 'solid-js'
|
||||
import { LoadMoreWrapper } from '~/components/_shared/LoadMoreWrapper'
|
||||
import { LoadMoreItems, LoadMoreWrapper } from '~/components/_shared/LoadMoreWrapper'
|
||||
import { useFeed } from '~/context/feed'
|
||||
import { useTopics } from '~/context/topics'
|
||||
import { loadShouts, loadTopics } from '~/graphql/api/public'
|
||||
import { LoadShoutsOptions, Shout } from '~/graphql/schema/core.gen'
|
||||
import { byStat } from '~/lib/sort'
|
||||
import { SortFunction } from '~/types/common'
|
||||
import { HomeView, HomeViewProps } from '../components/Views/Home'
|
||||
import { Loading } from '../components/_shared/Loading'
|
||||
import { PageLayout } from '../components/_shared/PageLayout'
|
||||
|
@ -74,7 +72,6 @@ export const route = {
|
|||
} satisfies RouteDefinition
|
||||
|
||||
export default function HomePage(props: RouteSectionProps<HomeViewProps>) {
|
||||
const { addTopics } = useTopics()
|
||||
const { t } = useLocalize()
|
||||
const {
|
||||
setFeaturedFeed,
|
||||
|
@ -85,46 +82,38 @@ export default function HomePage(props: RouteSectionProps<HomeViewProps>) {
|
|||
topFeed: topRatedFeed
|
||||
} = useFeed()
|
||||
|
||||
const data = createAsync(async (prev?: HomeViewProps) => {
|
||||
const topics = props.data?.topics || (await fetchAllTopics())
|
||||
const offset = prev?.featuredShouts?.length || 0
|
||||
const featuredShoutsLoader = featuredLoader(offset)
|
||||
const loaded = await featuredShoutsLoader()
|
||||
setFeaturedFeed((prev) => [...prev, ...loaded||[]])
|
||||
const featuredShouts = [
|
||||
...(prev?.featuredShouts || []),
|
||||
...(loaded || props.data?.featuredShouts || [])
|
||||
]
|
||||
const sortFn = byStat('viewed')
|
||||
const topViewedShouts = featuredShouts.sort(sortFn as SortFunction<Shout>)
|
||||
return {
|
||||
...prev,
|
||||
...props.data,
|
||||
topViewedShouts,
|
||||
featuredShouts,
|
||||
topics
|
||||
}
|
||||
})
|
||||
|
||||
// preload all topics
|
||||
const { addTopics, sortedTopics } = useTopics()
|
||||
createEffect(() => {
|
||||
if (data()?.topics) {
|
||||
console.debug('[routes.main] topics update')
|
||||
addTopics(data()?.topics || [])
|
||||
}
|
||||
!sortedTopics() && props.data.topics && addTopics(props.data.topics)
|
||||
})
|
||||
|
||||
// load more faetured shouts
|
||||
const loadMoreFeatured = async (offset?: number) => {
|
||||
const shoutsLoader = featuredLoader(offset)
|
||||
const loaded = await shoutsLoader()
|
||||
loaded && setFeaturedFeed((prev: Shout[]) => [...prev, ...loaded])
|
||||
return loaded as LoadMoreItems
|
||||
}
|
||||
|
||||
// preload featured shouts
|
||||
const shouts = createAsync(async () => {
|
||||
if (props.data.featuredShouts) {
|
||||
setFeaturedFeed(props.data.featuredShouts)
|
||||
console.debug('[routes.main] featured feed preloaded')
|
||||
return props.data.featuredShouts
|
||||
}
|
||||
return await loadMoreFeatured()
|
||||
})
|
||||
|
||||
const SHOUTS_PER_PAGE = 20
|
||||
|
||||
return (
|
||||
<PageLayout withPadding={true} title={t('Discours')} key="home">
|
||||
<Show when={(featuredFeed() || []).length > 0} fallback={<Loading />}>
|
||||
<LoadMoreWrapper loadFunction={loadMoreFeatured} pageSize={SHOUTS_PER_PAGE}>
|
||||
<HomeView
|
||||
featuredShouts={featuredFeed() as Shout[]}
|
||||
featuredShouts={featuredFeed() || shouts() as Shout[]}
|
||||
topMonthShouts={topMonthFeed() as Shout[]}
|
||||
topViewedShouts={topViewedFeed() as Shout[]}
|
||||
topRatedShouts={topRatedFeed() as Shout[]}
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
import { RouteSectionProps, createAsync, useSearchParams } from '@solidjs/router'
|
||||
import { Client } from '@urql/core'
|
||||
import { createSignal } from 'solid-js'
|
||||
import { createEffect } from 'solid-js'
|
||||
import { AUTHORS_PER_PAGE } from '~/components/Views/AllAuthors/AllAuthors'
|
||||
import { Feed } from '~/components/Views/Feed'
|
||||
import { LoadMoreWrapper } from '~/components/_shared/LoadMoreWrapper'
|
||||
import { LoadMoreItems, LoadMoreWrapper } from '~/components/_shared/LoadMoreWrapper'
|
||||
import { PageLayout } from '~/components/_shared/PageLayout'
|
||||
import { useFeed } from '~/context/feed'
|
||||
import { useLocalize } from '~/context/localize'
|
||||
import { ReactionsProvider } from '~/context/reactions'
|
||||
import { useTopics } from '~/context/topics'
|
||||
import { loadShouts } from '~/graphql/api/public'
|
||||
import { LoadShoutsOptions, Shout } from '~/graphql/schema/core.gen'
|
||||
import { LoadShoutsOptions, Shout, Topic } from '~/graphql/schema/core.gen'
|
||||
import { SHOUTS_PER_PAGE } from '../(main)'
|
||||
|
||||
export type FeedPeriod = 'week' | 'month' | 'year'
|
||||
|
@ -20,7 +21,6 @@ export type PeriodItem = {
|
|||
}
|
||||
|
||||
export type FeedSearchParams = {
|
||||
by: 'after' | 'likes' | 'last_comment'
|
||||
period: FeedPeriod
|
||||
}
|
||||
|
||||
|
@ -44,42 +44,67 @@ const getFromDate = (period: FeedPeriod): number => {
|
|||
return Math.floor(d.getTime() / 1000)
|
||||
}
|
||||
|
||||
const fetchPublishedShouts = async (offset?: number, _client?: Client) => {
|
||||
const shoutsLoader = loadShouts({ filters: { featured: undefined }, limit: SHOUTS_PER_PAGE, offset })
|
||||
const feedLoader = async (options: Partial<LoadShoutsOptions>, _client?: Client) => {
|
||||
const shoutsLoader = loadShouts({ ...options, limit: SHOUTS_PER_PAGE } as LoadShoutsOptions)
|
||||
return await shoutsLoader()
|
||||
}
|
||||
|
||||
export const route = {
|
||||
load: async ({ location: { query } }: RouteSectionProps<{ articles: Shout[] }>) => {
|
||||
const offset: number = Number.parseInt(query.offset, 10)
|
||||
const result = await fetchPublishedShouts(offset)
|
||||
const result = await feedLoader({ offset })
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
export default (props: RouteSectionProps<Shout[]>) => {
|
||||
const [searchParams] = useSearchParams<FeedSearchParams>()
|
||||
export default (props: RouteSectionProps<{ shouts: Shout[]; topics: Topic[] }>) => {
|
||||
const [searchParams] = useSearchParams<FeedSearchParams>() // ?period=month
|
||||
const { t } = useLocalize()
|
||||
const {setNonFeaturedFeed} = useFeed()
|
||||
const [offset, setOffset] = createSignal<number>(0)
|
||||
const loadMore = async () => {
|
||||
const newOffset = offset() + SHOUTS_PER_PAGE
|
||||
setOffset(newOffset)
|
||||
const { setFeed } = useFeed()
|
||||
|
||||
// preload all topics
|
||||
const { addTopics, sortedTopics } = useTopics()
|
||||
createEffect(() => {
|
||||
!sortedTopics() && props.data.topics && addTopics(props.data.topics)
|
||||
})
|
||||
|
||||
// load more feed
|
||||
const loadMoreFeed = async (offset?: number) => {
|
||||
// /feed/:order: - select order setting
|
||||
const paramPattern = /^(hot|likes)$/
|
||||
const order =
|
||||
(props.params.order && paramPattern.test(props.params.order)
|
||||
? props.params.order === 'hot'
|
||||
? 'last_comment'
|
||||
: props.params.order
|
||||
: 'created_at') || 'created_at'
|
||||
|
||||
const options: LoadShoutsOptions = {
|
||||
limit: SHOUTS_PER_PAGE,
|
||||
offset: newOffset,
|
||||
order_by: searchParams?.by
|
||||
offset,
|
||||
order_by: order
|
||||
}
|
||||
|
||||
if (searchParams?.by === 'after') {
|
||||
const period = searchParams?.by || 'month'
|
||||
// ?period=month - time period filter
|
||||
if (searchParams?.period) {
|
||||
const period = searchParams?.period || 'month'
|
||||
options.filters = { after: getFromDate(period as FeedPeriod) }
|
||||
}
|
||||
const result = await fetchPublishedShouts(newOffset)
|
||||
result && setNonFeaturedFeed(result)
|
||||
return
|
||||
|
||||
const loaded = await feedLoader(options)
|
||||
loaded && setFeed((prev: Shout[]) => [...prev, ...loaded])
|
||||
return loaded as LoadMoreItems
|
||||
}
|
||||
const shouts = createAsync(async () => props.data || await loadMore())
|
||||
|
||||
// preload shouts
|
||||
const shouts = createAsync(async () => {
|
||||
if (props.data.shouts) {
|
||||
setFeed(props.data.shouts)
|
||||
console.debug('[routes.main] feed preloaded')
|
||||
return props.data.shouts
|
||||
}
|
||||
return (await loadMoreFeed()) as Shout[]
|
||||
})
|
||||
|
||||
return (
|
||||
<PageLayout
|
||||
|
@ -88,9 +113,9 @@ export default (props: RouteSectionProps<Shout[]>) => {
|
|||
key="feed"
|
||||
desc="Independent media project about culture, science, art and society with horizontal editing"
|
||||
>
|
||||
<LoadMoreWrapper loadFunction={loadMore} pageSize={AUTHORS_PER_PAGE}>
|
||||
<LoadMoreWrapper loadFunction={loadMoreFeed} pageSize={AUTHORS_PER_PAGE}>
|
||||
<ReactionsProvider>
|
||||
<Feed shouts={shouts() || []} />
|
||||
<Feed />
|
||||
</ReactionsProvider>
|
||||
</LoadMoreWrapper>
|
||||
</PageLayout>
|
114
src/routes/feed/my/[...mode]/[...order].tsx
Normal file
114
src/routes/feed/my/[...mode]/[...order].tsx
Normal file
|
@ -0,0 +1,114 @@
|
|||
import { RouteSectionProps, useSearchParams } from '@solidjs/router'
|
||||
import { createEffect } from 'solid-js'
|
||||
import { AUTHORS_PER_PAGE } from '~/components/Views/AllAuthors/AllAuthors'
|
||||
import { Feed } from '~/components/Views/Feed'
|
||||
import { LoadMoreItems, LoadMoreWrapper } from '~/components/_shared/LoadMoreWrapper'
|
||||
import { PageLayout } from '~/components/_shared/PageLayout'
|
||||
import { useFeed } from '~/context/feed'
|
||||
import { useGraphQL } from '~/context/graphql'
|
||||
import { useLocalize } from '~/context/localize'
|
||||
import { ReactionsProvider } from '~/context/reactions'
|
||||
import { useTopics } from '~/context/topics'
|
||||
import {
|
||||
loadCoauthoredShouts,
|
||||
loadDiscussedShouts,
|
||||
loadFollowedShouts,
|
||||
loadUnratedShouts
|
||||
} from '~/graphql/api/private'
|
||||
import { LoadShoutsOptions, Shout, Topic } from '~/graphql/schema/core.gen'
|
||||
|
||||
const feeds = {
|
||||
followed: loadFollowedShouts,
|
||||
discussed: loadDiscussedShouts,
|
||||
coauthored: loadCoauthoredShouts,
|
||||
unrated: loadUnratedShouts
|
||||
}
|
||||
|
||||
export type FeedPeriod = 'week' | 'month' | 'year'
|
||||
export type FeedSearchParams = { period?: FeedPeriod }
|
||||
|
||||
const getFromDate = (period: FeedPeriod): number => {
|
||||
const now = new Date()
|
||||
let d: Date = now
|
||||
switch (period) {
|
||||
case 'week': {
|
||||
d = new Date(now.setDate(now.getDate() - 7))
|
||||
break
|
||||
}
|
||||
case 'month': {
|
||||
d = new Date(now.setMonth(now.getMonth() - 1))
|
||||
break
|
||||
}
|
||||
case 'year': {
|
||||
d = new Date(now.setFullYear(now.getFullYear() - 1))
|
||||
break
|
||||
}
|
||||
}
|
||||
return Math.floor(d.getTime() / 1000)
|
||||
}
|
||||
|
||||
// /feed/my/followed/hot
|
||||
|
||||
export default (props: RouteSectionProps<{ shouts: Shout[]; topics: Topic[] }>) => {
|
||||
const [searchParams] = useSearchParams<FeedSearchParams>() // ?period=month
|
||||
const { t } = useLocalize()
|
||||
const { setFeed } = useFeed()
|
||||
// TODO: use const { requireAuthentication } = useSession()
|
||||
const client = useGraphQL()
|
||||
|
||||
// preload all topics
|
||||
const { addTopics, sortedTopics } = useTopics()
|
||||
createEffect(() => {
|
||||
!sortedTopics() && props.data.topics && addTopics(props.data.topics)
|
||||
})
|
||||
|
||||
// load more my feed
|
||||
const loadMoreMyFeed = async (offset?: number) => {
|
||||
// /feed/my/:mode:
|
||||
const paramModePattern = /^(followed|discussed|liked|coauthored|unrated)$/
|
||||
const mode =
|
||||
props.params.mode && paramModePattern.test(props.params.mode) ? props.params.mode : 'followed'
|
||||
const gqlHandler = feeds[mode as keyof typeof feeds]
|
||||
|
||||
// /feed/my/:mode:/:order: - select order setting
|
||||
const paramOrderPattern = /^(hot|likes)$/
|
||||
const order =
|
||||
(paramOrderPattern.test(props.params.order)
|
||||
? props.params.order === 'hot'
|
||||
? 'last_comment'
|
||||
: props.params.order
|
||||
: 'created_at') || 'created_at'
|
||||
|
||||
const options: LoadShoutsOptions = {
|
||||
limit: 20,
|
||||
offset,
|
||||
order_by: order
|
||||
}
|
||||
|
||||
// ?period=month - time period filter
|
||||
if (searchParams?.period) {
|
||||
const period = searchParams?.period || 'month'
|
||||
options.filters = { after: getFromDate(period as FeedPeriod) }
|
||||
}
|
||||
|
||||
const shoutsLoader = gqlHandler(client, options)
|
||||
const loaded = await shoutsLoader()
|
||||
loaded && setFeed((prev: Shout[]) => [...prev, ...loaded])
|
||||
return loaded as LoadMoreItems
|
||||
}
|
||||
|
||||
return (
|
||||
<PageLayout
|
||||
withPadding={true}
|
||||
title={`${t('Discours')} :: ${t('Feed')}`}
|
||||
key="feed"
|
||||
desc="Independent media project about culture, science, art and society with horizontal editing"
|
||||
>
|
||||
<LoadMoreWrapper loadFunction={loadMoreMyFeed} pageSize={AUTHORS_PER_PAGE}>
|
||||
<ReactionsProvider>
|
||||
<Feed />
|
||||
</ReactionsProvider>
|
||||
</LoadMoreWrapper>
|
||||
</PageLayout>
|
||||
)
|
||||
}
|
Loading…
Reference in New Issue
Block a user