expo-opening-fix

This commit is contained in:
Untone 2024-10-12 00:39:17 +03:00
parent 424af47b38
commit 83c660235a
3 changed files with 152 additions and 134 deletions

View File

@ -1,7 +1,6 @@
import { A } from '@solidjs/router' import { A } from '@solidjs/router'
import clsx from 'clsx' import clsx from 'clsx'
import { For, Show, createEffect, createSignal, on } from 'solid-js' import { For, Show, createEffect, createSignal, on } from 'solid-js'
import { ConditionalWrapper } from '~/components/_shared/ConditionalWrapper'
import { Loading } from '~/components/_shared/Loading' import { Loading } from '~/components/_shared/Loading'
import { ArticleCardSwiper } from '~/components/_shared/SolidSwiper/ArticleCardSwiper' import { ArticleCardSwiper } from '~/components/_shared/SolidSwiper/ArticleCardSwiper'
import { EXPO_LAYOUTS, SHOUTS_PER_PAGE } from '~/context/feed' import { EXPO_LAYOUTS, SHOUTS_PER_PAGE } from '~/context/feed'
@ -18,20 +17,24 @@ import styles from '~/styles/views/Expo.module.scss'
export const ExpoNav = (props: { layout: ExpoLayoutType | '' }) => { export const ExpoNav = (props: { layout: ExpoLayoutType | '' }) => {
const { t } = useLocalize() const { t } = useLocalize()
return ( return (
<div class="wide-container"> <div class="wide-container">
<ul class={clsx('view-switcher')}> <ul class={clsx('view-switcher')}>
<For each={[...EXPO_LAYOUTS, '']}> <For each={[...EXPO_LAYOUTS, '']}>
{(layoutKey) => ( {(layoutKey) => (
<li class={clsx({ 'view-switcher__item--selected': props.layout === layoutKey })}> <li class={clsx({ 'view-switcher__item--selected': props.layout === layoutKey })}>
<ConditionalWrapper {props.layout !== layoutKey ? (
condition={props.layout !== layoutKey} <A href={`/expo/${layoutKey}`}>
wrapper={(children) => <A href={`/expo/${layoutKey}`}>{children}</A>} <span class="linkReplacement">
> {layoutKey in EXPO_TITLES ? t(EXPO_TITLES[layoutKey as ExpoLayoutType]) : t('All')}
</span>
</A>
) : (
<span class="linkReplacement"> <span class="linkReplacement">
{layoutKey in EXPO_TITLES ? t(EXPO_TITLES[layoutKey as ExpoLayoutType]) : t('All')} {layoutKey in EXPO_TITLES ? t(EXPO_TITLES[layoutKey as ExpoLayoutType]) : t('All')}
</span> </span>
</ConditionalWrapper> )}
</li> </li>
)} )}
</For> </For>
@ -88,36 +91,41 @@ export const Expo = (props: Props) => {
) )
) )
return ( try {
<div class={styles.Expo}> return (
<Show when={props.shouts} fallback={<Loading />} keyed> <div class={styles.Expo}>
{(feed: Shout[]) => ( <Show when={props.shouts} fallback={<Loading />} keyed>
<div class="wide-container"> {(feed) => (
<div class="row"> <div class="wide-container">
<For each={feed.slice(0, SHOUTS_PER_PAGE) || []}> <div class="row">
{(shout) => ( <For each={Array.from(feed || []).slice(0, SHOUTS_PER_PAGE)}>
<div id={`shout-${shout.id}`} class="col-md-6 mt-md-5 col-sm-8 mt-sm-3"> {(shout) => (
<ArticleCard <div id={`shout-${shout.id}`} class="col-md-6 mt-md-5 col-sm-8 mt-sm-3">
article={shout} <ArticleCard
settings={{ nodate: true, nosubtitle: true, noAuthorLink: true }} article={shout}
desktopCoverSize="XS" settings={{ nodate: true, nosubtitle: true, noAuthorLink: true }}
withAspectRatio={true} desktopCoverSize="XS"
/> withAspectRatio={true}
</div> />
)} </div>
</For> )}
</For>
</div>
<Show when={reactedTopMonthArticles()?.length > 0}>
<ArticleCardSwiper title={t('Top month')} slides={reactedTopMonthArticles()} />
</Show>
<Show when={favoriteTopArticles()?.length > 0}>
<ArticleCardSwiper title={t('Favorite')} slides={favoriteTopArticles()} />
</Show>
</div> </div>
)}
<Show when={reactedTopMonthArticles()?.length > 0}> </Show>
<ArticleCardSwiper title={t('Top month')} slides={reactedTopMonthArticles()} /> </div>
</Show> )
} catch (error) {
<Show when={favoriteTopArticles()?.length > 0}> console.error('Error in Expo component:', error)
<ArticleCardSwiper title={t('Favorite')} slides={favoriteTopArticles()} /> return <div>An error occurred. Please try again later.</div>
</Show> }
</div>
)}
</Show>
</div>
)
} }

View File

@ -32,83 +32,85 @@ export const ArticleCardSwiper = (props: Props) => {
}) })
return ( return (
<ShowOnlyOnClient> <Show when={props.slides && props.slides.length > 0}>
<div <ShowOnlyOnClient>
class={clsx({ <div
[styles.Swiper]: props.slides?.length > 1, class={clsx({
[styles.articleMode]: true, [styles.Swiper]: props.slides?.length > 1,
[styles.ArticleCardSwiper]: props.slides?.length > 1 [styles.articleMode]: true,
})} [styles.ArticleCardSwiper]: props.slides?.length > 1
> })}
<Show when={props.title}> >
<div class="wide-container"> <Show when={props.title}>
<div class="row"> <div class="wide-container">
<div class="col-md-12"> <div class="row">
<h2 class={styles.sliderTitle}>{props.title}</h2> <div class="col-md-12">
<h2 class={styles.sliderTitle}>{props.title}</h2>
</div>
</div> </div>
</div> </div>
</div> </Show>
</Show> <div class={styles.container}>
<div class={styles.container}> <Show when={props.slides?.length > 0}>
<Show when={props.slides?.length > 0}> <Show when={props.slides.length !== 1} fallback={<Row1 article={props.slides[0]} />}>
<Show when={props.slides.length !== 1} fallback={<Row1 article={props.slides[0]} />}> <Show when={props.slides.length !== 2} fallback={<Row2 articles={props.slides} />}>
<Show when={props.slides.length !== 2} fallback={<Row2 articles={props.slides} />}> <div class={styles.holder}>
<div class={styles.holder}> <swiper-container
<swiper-container ref={(el) => (mainSwipeRef = el)}
ref={(el) => (mainSwipeRef = el)} centered-slides={true}
centered-slides={true} observer={true}
observer={true} space-between={10}
space-between={10} breakpoints={{
breakpoints={{ 576: { spaceBetween: 20, slidesPerView: 1.5 },
576: { spaceBetween: 20, slidesPerView: 1.5 }, 992: { spaceBetween: 52, slidesPerView: 1.5 }
992: { spaceBetween: 52, slidesPerView: 1.5 } }}
}} round-lengths={true}
round-lengths={true} loop={true}
loop={true} speed={800}
speed={800} autoplay={{
autoplay={{ disableOnInteraction: false,
disableOnInteraction: false, delay: 6000,
delay: 6000, pauseOnMouseEnter: true
pauseOnMouseEnter: true }}
}} >
> <For each={props.slides}>
<For each={props.slides}> {(slide, index) => (
{(slide, index) => ( // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore
// @ts-ignore <swiper-slide virtual-index={index()}>
<swiper-slide virtual-index={index()}> <ArticleCard
<ArticleCard article={slide}
article={slide} settings={{
settings={{ additionalClass: 'swiper-slide',
additionalClass: 'swiper-slide', isFloorImportant: true,
isFloorImportant: true, isWithCover: true,
isWithCover: true, nodate: true
nodate: true }}
}} desktopCoverSize="L"
desktopCoverSize="L" />
/> </swiper-slide>
</swiper-slide> )}
)} </For>
</For> </swiper-container>
</swiper-container> <div
<div class={clsx(styles.navigation, styles.prev)}
class={clsx(styles.navigation, styles.prev)} onClick={() => mainSwipeRef?.swiper.slidePrev()}
onClick={() => mainSwipeRef?.swiper.slidePrev()} >
> <Icon name="swiper-l-arr" class={styles.icon} />
<Icon name="swiper-l-arr" class={styles.icon} /> </div>
<div
class={clsx(styles.navigation, styles.next)}
onClick={() => mainSwipeRef?.swiper.slideNext()}
>
<Icon name="swiper-r-arr" class={styles.icon} />
</div>
</div> </div>
<div </Show>
class={clsx(styles.navigation, styles.next)}
onClick={() => mainSwipeRef?.swiper.slideNext()}
>
<Icon name="swiper-r-arr" class={styles.icon} />
</div>
</div>
</Show> </Show>
</Show> </Show>
</Show> </div>
</div> </div>
</div> </ShowOnlyOnClient>
</ShowOnlyOnClient> </Show>
) )
} }

View File

@ -1,8 +1,10 @@
import { Params, RouteSectionProps, createAsync } from '@solidjs/router' import { Params, RouteSectionProps, createAsync } from '@solidjs/router'
import { Show, createEffect, createSignal, on } from 'solid-js' import { Show, createEffect, createMemo, createSignal, on } from 'solid-js'
import { isServer } from 'solid-js/web'
import { TopicsNav } from '~/components/HeaderNav/TopicsNav' import { TopicsNav } from '~/components/HeaderNav/TopicsNav'
import { Expo, ExpoNav } from '~/components/Views/ExpoView' import { Expo, ExpoNav } from '~/components/Views/ExpoView'
import { LoadMoreItems, LoadMoreWrapper } from '~/components/_shared/LoadMoreWrapper' import { LoadMoreItems, LoadMoreWrapper } from '~/components/_shared/LoadMoreWrapper'
import { Loading } from '~/components/_shared/Loading'
import { PageLayout } from '~/components/_shared/PageLayout' import { PageLayout } from '~/components/_shared/PageLayout'
import { EXPO_LAYOUTS, EXPO_TITLES, SHOUTS_PER_PAGE, useFeed } from '~/context/feed' import { EXPO_LAYOUTS, EXPO_TITLES, SHOUTS_PER_PAGE, useFeed } from '~/context/feed'
import { useLocalize } from '~/context/localize' import { useLocalize } from '~/context/localize'
@ -18,7 +20,7 @@ const fetchExpoShouts = async (layouts: string[]) => {
limit: SHOUTS_PER_PAGE, limit: SHOUTS_PER_PAGE,
offset: 0 offset: 0
} as LoadShoutsOptions) } as LoadShoutsOptions)
return result || [] return result
} }
export const route = { export const route = {
@ -33,14 +35,14 @@ export default (props: RouteSectionProps<Shout[]>) => {
const { t } = useLocalize() const { t } = useLocalize()
const { expoFeed, setExpoFeed, feedByLayout } = useFeed() const { expoFeed, setExpoFeed, feedByLayout } = useFeed()
const [loadMoreVisible, setLoadMoreVisible] = createSignal(false) const [loadMoreVisible, setLoadMoreVisible] = createSignal(false)
const getTitle = (l?: string) => EXPO_TITLES[(l as ExpoLayoutType) || ''] const getTitle = createMemo(() => (l?: string) => EXPO_TITLES[(l as ExpoLayoutType) || ''])
const shouts = createAsync( const shouts = createAsync(async () =>
async () => isServer
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 loadMore = async () => { const loadMore = async () => {
saveScrollPosition() saveScrollPosition()
const limit = SHOUTS_PER_PAGE const limit = SHOUTS_PER_PAGE
@ -48,46 +50,52 @@ export default (props: RouteSectionProps<Shout[]>) => {
const offset = expoFeed()?.length || 0 const offset = expoFeed()?.length || 0
const filters: LoadShoutsFilters = { layouts, featured: true } const filters: LoadShoutsFilters = { layouts, featured: true }
const options: LoadShoutsOptions = { filters, limit, offset } const options: LoadShoutsOptions = { filters, limit, offset }
const shoutsFetcher = loadShouts(options) const fetcher = await loadShouts(options)
const result = await shoutsFetcher() const result = (await fetcher()) || []
setLoadMoreVisible(Boolean(result?.length)) setLoadMoreVisible(Boolean(result?.length))
if (result) { if (result && Array.isArray(result)) {
setExpoFeed((prev) => Array.from(new Set([...(prev || []), ...result])).sort(byCreated)) setExpoFeed((prev) => Array.from(new Set([...(prev || []), ...result])).sort(byCreated))
} }
restoreScrollPosition() restoreScrollPosition()
return result as LoadMoreItems return result as LoadMoreItems
} }
// Эффект для загрузки данных при изменении layout
createEffect( createEffect(
on( on(
() => props.params.layout as ExpoLayoutType, () => props.params.layout,
async (layout?: ExpoLayoutType) => { async (currentLayout) => {
const layouts = layout ? [layout] : EXPO_LAYOUTS const layouts = currentLayout ? [currentLayout] : EXPO_LAYOUTS
const offset = (layout ? feedByLayout()[layout]?.length : expoFeed()?.length) || 0 const offset = (currentLayout ? feedByLayout()[currentLayout]?.length : expoFeed()?.length) || 0
const options: LoadShoutsOptions = { const options: LoadShoutsOptions = {
filters: { layouts, featured: true }, filters: { layouts, featured: true },
limit: SHOUTS_PER_PAGE, limit: SHOUTS_PER_PAGE,
offset offset
} }
const shoutsFetcher = loadShouts(options) const result = await loadShouts(options)
const result = await shoutsFetcher() if (result && Array.isArray(result)) {
setExpoFeed(result || []) setExpoFeed(result)
} else {
setExpoFeed([])
}
} }
) )
) )
return ( return (
<PageLayout <PageLayout
withPadding={true} withPadding={true}
zeroBottomPadding={true} zeroBottomPadding={true}
title={`${t('Discours')} :: ${getTitle(props.params.layout || '')}`} title={`${t('Discours')} :: ${getTitle()((props.params.layout as ExpoLayoutType) || '')}`}
> >
<TopicsNav /> <TopicsNav />
<ExpoNav layout={(props.params.layout || '') as ExpoLayoutType | ''} /> <ExpoNav layout={(props.params.layout as ExpoLayoutType) || ''} />
<LoadMoreWrapper loadFunction={loadMore} pageSize={SHOUTS_PER_PAGE} hidden={!loadMoreVisible()}> <Show when={shouts()} fallback={<Loading />} keyed>
<Show when={shouts()} keyed> {(sss) => (
{(sss: Shout[]) => <Expo shouts={sss} layout={props.params.layout as ExpoLayoutType} />} <LoadMoreWrapper loadFunction={loadMore} pageSize={SHOUTS_PER_PAGE} hidden={!loadMoreVisible()}>
</Show> <Expo shouts={sss as Shout[]} layout={(props.params.layout as ExpoLayoutType) || ''} />
</LoadMoreWrapper> </LoadMoreWrapper>
)}
</Show>
</PageLayout> </PageLayout>
) )
} }