expo-showup

This commit is contained in:
Untone 2024-09-24 14:56:16 +03:00
parent a52ee5a90f
commit 22575cc7fa
2 changed files with 48 additions and 37 deletions

View File

@ -1,6 +1,7 @@
import { A } from '@solidjs/router' import { A } from '@solidjs/router'
import { clsx } from 'clsx' import { clsx } from 'clsx'
import { For, Show, createEffect, createMemo, createSignal, on, onCleanup, onMount } from 'solid-js' import { For, Show, createEffect, createMemo, createSignal, on, onCleanup, onMount } from 'solid-js'
import { ConditionalWrapper } from '~/components/_shared/ConditionalWrapper' import { ConditionalWrapper } from '~/components/_shared/ConditionalWrapper'
import { LoadMoreItems, LoadMoreWrapper } from '~/components/_shared/LoadMoreWrapper' import { LoadMoreItems, LoadMoreWrapper } from '~/components/_shared/LoadMoreWrapper'
import { Loading } from '~/components/_shared/Loading' import { Loading } from '~/components/_shared/Loading'
@ -13,6 +14,8 @@ import getRandomTopShoutsQuery from '~/graphql/query/core/articles-load-random-t
import { LoadShoutsFilters, LoadShoutsOptions, Shout } from '~/graphql/schema/core.gen' import { LoadShoutsFilters, LoadShoutsOptions, Shout } from '~/graphql/schema/core.gen'
import { LayoutType } from '~/types/common' import { LayoutType } from '~/types/common'
import { getUnixtime } from '~/utils/date' import { getUnixtime } from '~/utils/date'
import { restoreScrollPosition, saveScrollPosition } from '~/utils/scroll'
import { byCreated } from '~/utils/sort'
import { ArticleCard } from '../../Feed/ArticleCard' import { ArticleCard } from '../../Feed/ArticleCard'
import styles from './Expo.module.scss' import styles from './Expo.module.scss'
@ -33,21 +36,9 @@ export const Expo = (props: Props) => {
const [favoriteTopArticles, setFavoriteTopArticles] = createSignal<Shout[]>([]) const [favoriteTopArticles, setFavoriteTopArticles] = createSignal<Shout[]>([])
const [reactedTopMonthArticles, setReactedTopMonthArticles] = createSignal<Shout[]>([]) const [reactedTopMonthArticles, setReactedTopMonthArticles] = createSignal<Shout[]>([])
const [expoShouts, setExpoShouts] = createSignal<Shout[]>([])
const { feedByLayout, expoFeed, setExpoFeed } = useFeed() const { feedByLayout, expoFeed, setExpoFeed } = useFeed()
const layouts = createMemo<LayoutType[]>(() => (props.layout ? [props.layout] : EXPO_LAYOUTS)) const layouts = createMemo<LayoutType[]>(() => (props.layout ? [props.layout] : EXPO_LAYOUTS))
const loadMoreFiltered = async () => {
const limit = SHOUTS_PER_PAGE
const offset = (props.layout ? feedByLayout()[props.layout] : expoFeed())?.length
const filters: LoadShoutsFilters = { layouts: layouts(), featured: true }
const options: LoadShoutsOptions = { filters, limit, offset }
const shoutsFetcher = loadShouts(options)
const result = await shoutsFetcher()
result && setExpoFeed(result)
return result as LoadMoreItems
}
const loadRandomTopArticles = async () => { const loadRandomTopArticles = async () => {
const options: LoadShoutsOptions = { const options: LoadShoutsOptions = {
filters: { layouts: layouts(), featured: true }, filters: { layouts: layouts(), featured: true },
@ -76,20 +67,15 @@ export const Expo = (props: Props) => {
}) })
createEffect( createEffect(
on( on(layouts, (lll) => {
() => props.layout, console.debug('layouts changed', lll)
() => { loadRandomTopArticles()
setExpoShouts([]) loadRandomTopMonthArticles()
setFavoriteTopArticles([]) })
setReactedTopMonthArticles([])
loadRandomTopArticles()
loadRandomTopMonthArticles()
}
)
) )
onCleanup(() => { onCleanup(() => {
setExpoShouts([]) setExpoFeed([])
}) })
const ExpoTabs = () => ( const ExpoTabs = () => (
<div class="wide-container"> <div class="wide-container">
@ -134,10 +120,10 @@ export const Expo = (props: Props) => {
</ul> </ul>
</div> </div>
) )
const ExpoGrid = () => ( const ExpoGrid = (props: Props) => (
<div class="wide-container"> <div class="wide-container">
<div class="row"> <div class="row">
<For each={props.shouts.slice(0, LOAD_MORE_PAGE_SIZE)}> <For each={expoFeed()?.slice(0, LOAD_MORE_PAGE_SIZE) || []}>
{(shout) => ( {(shout) => (
<div class="col-md-6 mt-md-5 col-sm-8 mt-sm-3"> <div class="col-md-6 mt-md-5 col-sm-8 mt-sm-3">
<ArticleCard <ArticleCard
@ -167,7 +153,7 @@ export const Expo = (props: Props) => {
<Show when={favoriteTopArticles()?.length > 0} keyed={true}> <Show when={favoriteTopArticles()?.length > 0} keyed={true}>
<ArticleCardSwiper title={t('Favorite')} slides={favoriteTopArticles()} /> <ArticleCardSwiper title={t('Favorite')} slides={favoriteTopArticles()} />
</Show> </Show>
<For each={props.topRatedShouts?.slice(LOAD_MORE_PAGE_SIZE * 2, expoShouts().length)}> <For each={props.topRatedShouts?.slice(LOAD_MORE_PAGE_SIZE * 2, expoFeed()?.length || 0)}>
{(shout) => ( {(shout) => (
<div class="col-md-6 mt-md-5 col-sm-8 mt-sm-3"> <div class="col-md-6 mt-md-5 col-sm-8 mt-sm-3">
<ArticleCard <ArticleCard
@ -183,13 +169,32 @@ export const Expo = (props: Props) => {
</div> </div>
) )
const [loadMoreVisible, setLoadMoreVisible] = createSignal(false)
// дозагрузка
const loadMore = async () => {
saveScrollPosition()
const limit = SHOUTS_PER_PAGE
const offset = (props.layout ? feedByLayout()[props.layout] : expoFeed())?.length
const filters: LoadShoutsFilters = { layouts: layouts(), featured: true }
const options: LoadShoutsOptions = { filters, limit, offset }
const shoutsFetcher = loadShouts(options)
const result = await shoutsFetcher()
setLoadMoreVisible(Boolean(result?.length))
const expoFeedUpdater = (layout?: LayoutType) => (prev: Shout[]) =>
Array.from(new Set((layout ? prev || [] : expoFeed())?.concat(result || [])))?.sort(byCreated)
result && setExpoFeed(expoFeedUpdater(props.layout))
restoreScrollPosition()
return result as LoadMoreItems
}
return ( return (
<div class={styles.Expo}> <div class={styles.Expo}>
<ExpoTabs /> <ExpoTabs />
<Show when={expoShouts().length > 0} fallback={<Loading />}> <Show when={expoFeed()} fallback={<Loading />}>
<LoadMoreWrapper loadFunction={loadMoreFiltered} pageSize={LOAD_MORE_PAGE_SIZE}> <LoadMoreWrapper loadFunction={loadMore} pageSize={LOAD_MORE_PAGE_SIZE} hidden={!loadMoreVisible()}>
<ExpoGrid /> <ExpoGrid {...props} />
</LoadMoreWrapper> </LoadMoreWrapper>
</Show> </Show>
</div> </div>

View File

@ -1,5 +1,5 @@
import { Params, RouteSectionProps, createAsync } from '@solidjs/router' import { Params, RouteSectionProps, createAsync } from '@solidjs/router'
import { Show, createEffect, createMemo, on } from 'solid-js' import { Show, onMount } from 'solid-js'
import { TopicsNav } from '~/components/TopicsNav' import { TopicsNav } from '~/components/TopicsNav'
import { Expo } from '~/components/Views/Expo' import { Expo } from '~/components/Views/Expo'
import { PageLayout } from '~/components/_shared/PageLayout' import { PageLayout } from '~/components/_shared/PageLayout'
@ -32,9 +32,9 @@ export default (props: RouteSectionProps<Shout[]>) => {
async () => async () =>
props.data || (await fetchExpoShouts(props.params.layout ? [props.params.layout] : EXPO_LAYOUTS)) props.data || (await fetchExpoShouts(props.params.layout ? [props.params.layout] : EXPO_LAYOUTS))
) )
const layout = createMemo(() => props.params.layout)
const title = createMemo(() => { const getTitle = (l: string) => {
switch (layout()) { switch (l) {
case 'audio': { case 'audio': {
return t('Audio') return t('Audio')
} }
@ -51,15 +51,21 @@ export default (props: RouteSectionProps<Shout[]>) => {
return t('Art') return t('Art')
} }
} }
}
onMount(() => {
document.title = getTitle(props.params.layout || '')
}) })
createEffect(on(title, (ttl) => (document.title = ttl), { defer: true }))
return ( return (
<PageLayout withPadding={true} zeroBottomPadding={true} title={`${t('Discours')} :: ${title()}`}> <PageLayout
withPadding={true}
zeroBottomPadding={true}
title={`${t('Discours')} :: ${getTitle(props.params.layout || '')}`}
>
<TopicsNav /> <TopicsNav />
<Show when={shouts()} keyed> <Show when={shouts()} keyed>
{(sss) => <Expo shouts={sss} layout={layout() as LayoutType} />} {(sss) => <Expo shouts={sss} layout={props.params.layout as LayoutType} />}
</Show> </Show>
</PageLayout> </PageLayout>
) )