tab-navigation-fix

This commit is contained in:
Untone 2024-08-28 12:50:04 +03:00
parent 7ca7acc487
commit 22f44ee0ec
2 changed files with 110 additions and 62 deletions

View File

@ -1,6 +1,6 @@
import { A, useLocation } from '@solidjs/router'
import { A, useLocation, useParams } from '@solidjs/router'
import { clsx } from 'clsx'
import { For, Match, Show, Switch, createEffect, createMemo, createSignal } from 'solid-js'
import { For, Match, Show, Switch, createEffect, createMemo, createSignal, on } from 'solid-js'
import { Loading } from '~/components/_shared/Loading'
import { coreApiUrl } from '~/config'
import { useAuthors } from '~/context/authors'
@ -37,6 +37,8 @@ export const AuthorView = (props: AuthorViewProps) => {
// contexts
const { t } = useLocalize()
const loc = useLocation()
const params = useParams()
const [currentTab, setCurrentTab] = createSignal<string>(props.selectedTab)
const { session } = useSession()
const client = createMemo(() => graphqlClientCreate(coreApiUrl, session()?.access_token))
@ -58,6 +60,18 @@ export const AuthorView = (props: AuthorViewProps) => {
paginate((props.shouts || []).slice(1), PRERENDERED_ARTICLES_COUNT, LOAD_MORE_PAGE_SIZE)
)
// Переход по табам
createEffect(
on(
() => params.tab,
(tab: string) => {
// Обновляем текущую вкладку
setCurrentTab(tab || '')
},
{}
)
)
// Объединенный эффект для загрузки автора и его подписок
createEffect(async () => {
const meData = session()?.user?.app_data?.profile as Author
@ -105,6 +119,30 @@ export const AuthorView = (props: AuthorViewProps) => {
setCommented((prev) => prev.filter((comment) => comment.id !== id))
}
const TabNavigator = () => (
<div class="col-md-16">
<ul class="view-switcher">
<li classList={{ 'view-switcher__item--selected': !currentTab() }}>
<A href={`/@${props.authorSlug}`}>{t('Publications')}</A>
<Show when={author()?.stat}>
<span class="view-switcher__counter">{author()?.stat?.shouts || 0}</span>
</Show>
</li>
<li classList={{ 'view-switcher__item--selected': currentTab() === 'comments' }}>
<A href={`/@${props.authorSlug}/comments`}>{t('Comments')}</A>
<Show when={author()?.stat}>
<span class="view-switcher__counter">{author()?.stat?.comments || 0}</span>
</Show>
</li>
<li classList={{ 'view-switcher__item--selected': currentTab() === 'about' }}>
<A onClick={() => checkBioHeight()} href={`/@${props.authorSlug}/about`}>
{t('About the author')}
</A>
</li>
</ul>
</div>
)
return (
<div class={styles.authorPage}>
<div class="wide-container">
@ -118,27 +156,7 @@ export const AuthorView = (props: AuthorViewProps) => {
/>
</div>
<div class={clsx(styles.groupControls, 'row')}>
<div class="col-md-16">
<ul class="view-switcher">
<li classList={{ 'view-switcher__item--selected': !props.selectedTab }}>
<A href={`/@${props.authorSlug}`}>{t('Publications')}</A>
<Show when={author()?.stat}>
<span class="view-switcher__counter">{author()?.stat?.shouts || 0}</span>
</Show>
</li>
<li classList={{ 'view-switcher__item--selected': props.selectedTab === 'comment' }}>
<A href={`/@${props.authorSlug}/comments`}>{t('Comments')}</A>
<Show when={author()?.stat}>
<span class="view-switcher__counter">{author()?.stat?.comments || 0}</span>
</Show>
</li>
<li classList={{ 'view-switcher__item--selected': props.selectedTab === 'about' }}>
<A onClick={() => checkBioHeight()} href={`/@${props.authorSlug}/about`}>
{t('About the author')}
</A>
</li>
</ul>
</div>
<TabNavigator />
<div class={clsx('col-md-8', styles.additionalControls)}>
<Show when={author()?.stat?.rating || author()?.stat?.rating === 0}>
<div class={styles.ratingContainer}>
@ -153,7 +171,8 @@ export const AuthorView = (props: AuthorViewProps) => {
</div>
<Switch>
<Match when={props.selectedTab === 'about'}>
<Match when={currentTab() === 'about'}>
<div class="wide-container">
<div class="row">
<div class="col-md-20 col-lg-18">
@ -177,7 +196,9 @@ export const AuthorView = (props: AuthorViewProps) => {
</div>
</div>
</Match>
<Match when={props.selectedTab === 'comments'}>
<Match when={currentTab() === 'comments'}>
<Show when={me()?.slug === props.authorSlug && !me().stat?.comments}>
<div class="wide-container">
<Placeholder type={loc?.pathname} mode="profile" />
@ -203,7 +224,8 @@ export const AuthorView = (props: AuthorViewProps) => {
</div>
</div>
</Match>
<Match when={!props.selectedTab}>
<Match when={!currentTab()}>
<Show when={me()?.slug === props.authorSlug && !me().stat?.shouts}>
<div class="wide-container">
<Placeholder type={loc?.pathname} mode="profile" />
@ -239,6 +261,7 @@ export const AuthorView = (props: AuthorViewProps) => {
</Show>
</Show>
</Match>
</Switch>
</div>
)

View File

@ -1,5 +1,5 @@
import { RouteSectionProps } from '@solidjs/router'
import { ErrorBoundary, Suspense, createEffect, createMemo, createSignal, on } from 'solid-js'
import { RouteSectionProps, createAsync } from '@solidjs/router'
import { ErrorBoundary, Suspense, createEffect, createSignal, on } from 'solid-js'
import { AuthorView } from '~/components/Views/Author'
import { FourOuFourView } from '~/components/Views/FourOuFour'
import { LoadMoreItems, LoadMoreWrapper } from '~/components/_shared/LoadMoreWrapper'
@ -39,9 +39,10 @@ const fetchAuthor = async (slug: string) => {
export const route = {
load: async ({ params, location: { query } }: RouteSectionProps<{ articles: Shout[] }>) => {
const offset: number = Number.parseInt(query.offset, 10)
console.debug('route loading with offset', offset)
return {
author: await fetchAuthor(params.slug),
shouts: await fetchAuthorShouts(params.slug, offset),
articles: await fetchAuthorShouts(params.slug, offset),
topics: await fetchAllTopics()
}
}
@ -50,39 +51,17 @@ export const route = {
export type AuthorPageProps = { articles?: Shout[]; author?: Author; topics?: Topic[] }
export default function AuthorPage(props: RouteSectionProps<AuthorPageProps>) {
const { t } = useLocalize()
// load author's profile
const { addAuthor, authorsEntities } = useAuthors()
const [author, setAuthor] = createSignal<Author | undefined>(undefined)
const { t } = useLocalize()
const title = createMemo(() => `${author()?.name || ''}`)
createEffect(() => {
if (author()) {
console.debug('[routes] author/[slug] author loaded fx')
window?.gtag?.('event', 'page_view', {
page_title: author()?.name || '',
page_location: window?.location.href || '',
page_path: window?.location.pathname || ''
})
}
})
const cover = createMemo(() =>
author()?.pic
? getImageUrl(author()?.pic || '', { width: 1200 })
: getImageUrl('production/image/logo_image.png')
)
// author shouts
const { addFeed, feedByAuthor } = useFeed()
const shoutsByAuthor = createMemo(() => feedByAuthor()[props.params.slug])
createEffect(
on(
[() => props.params.slug || '', author],
async ([slug, profile]) => {
on(author,
async (profile) => {
// update only if no profile loaded
if (!profile) {
const loadedAuthor = authorsEntities()[slug] || (await fetchAuthor(slug))
const loadedAuthor = authorsEntities()[props.params.slug] || (await fetchAuthor(props.params.slug))
if (loadedAuthor) {
addAuthor(loadedAuthor)
setAuthor(loadedAuthor)
@ -93,6 +72,48 @@ export default function AuthorPage(props: RouteSectionProps<AuthorPageProps>) {
)
)
// author's data, view counter
const [title, setTitle] = createSignal<string>('')
const [desc, setDesc] = createSignal<string>('')
const [cover, setCover] = createSignal<string>('')
const [viewed, setViewed] = createSignal(false)
createEffect(
on(
[author, () => window],
([a, win]) => {
if (a && win) {
console.debug('[routes] author/[slug] author loaded fx')
if (!a) return
setTitle(() => `${t('Discours')}${a.name ? ` :: ${a.name}` : ''}`)
setDesc(() => a.about || a.bio || '')
setCover(() => (a.pic ? getImageUrl(a.pic || '', { width: 1200 }) : 'log.png'))
// views google counter increment
if (!viewed()) {
window?.gtag?.('event', 'page_view', {
page_title: author()?.name || '',
page_location: window?.location.href || '',
page_path: window?.location.pathname || ''
})
setViewed(true)
}
}
},
{}
)
)
// author's shouts
const { addFeed, feedByAuthor } = useFeed()
const [loadMoreHidden, setLoadMoreHidden] = createSignal(true)
const authorShouts = createAsync(async () => {
const sss: Shout[] = props.data.articles as Shout[] || feedByAuthor()[props.params.slug] || []
const result = sss || (await fetchAuthorShouts(props.params.slug, 0))
if (!result) setLoadMoreHidden(true)
return result
})
// load more shouts
const loadAuthorShoutsMore = async (offset: number) => {
const loadedShouts = await fetchAuthorShouts(props.params.slug, offset)
loadedShouts && addFeed(loadedShouts)
@ -103,19 +124,23 @@ export default function AuthorPage(props: RouteSectionProps<AuthorPageProps>) {
<ErrorBoundary fallback={(_err) => <FourOuFourView />}>
<Suspense fallback={<Loading />}>
<PageLayout
title={`${t('Discours')} :: ${title()}`}
title={title()}
headerTitle={author()?.name || ''}
slug={author()?.slug}
desc={author()?.about || author()?.bio || ''}
desc={desc()}
cover={cover()}
>
<ReactionsProvider>
<LoadMoreWrapper loadFunction={loadAuthorShoutsMore} pageSize={SHOUTS_PER_PAGE}>
<LoadMoreWrapper
loadFunction={loadAuthorShoutsMore}
pageSize={SHOUTS_PER_PAGE}
hidden={loadMoreHidden()}
>
<AuthorView
author={author() as Author}
selectedTab={props.params.tab}
authorSlug={props.params.slug}
shouts={shoutsByAuthor()}
shouts={authorShouts()}
/>
</LoadMoreWrapper>
</ReactionsProvider>