tab-navigation-fix
This commit is contained in:
parent
7ca7acc487
commit
22f44ee0ec
|
@ -1,6 +1,6 @@
|
||||||
import { A, useLocation } from '@solidjs/router'
|
import { A, useLocation, useParams } from '@solidjs/router'
|
||||||
import { clsx } from 'clsx'
|
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 { Loading } from '~/components/_shared/Loading'
|
||||||
import { coreApiUrl } from '~/config'
|
import { coreApiUrl } from '~/config'
|
||||||
import { useAuthors } from '~/context/authors'
|
import { useAuthors } from '~/context/authors'
|
||||||
|
@ -37,6 +37,8 @@ export const AuthorView = (props: AuthorViewProps) => {
|
||||||
// contexts
|
// contexts
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
const loc = useLocation()
|
const loc = useLocation()
|
||||||
|
const params = useParams()
|
||||||
|
const [currentTab, setCurrentTab] = createSignal<string>(props.selectedTab)
|
||||||
|
|
||||||
const { session } = useSession()
|
const { session } = useSession()
|
||||||
const client = createMemo(() => graphqlClientCreate(coreApiUrl, session()?.access_token))
|
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)
|
paginate((props.shouts || []).slice(1), PRERENDERED_ARTICLES_COUNT, LOAD_MORE_PAGE_SIZE)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Переход по табам
|
||||||
|
createEffect(
|
||||||
|
on(
|
||||||
|
() => params.tab,
|
||||||
|
(tab: string) => {
|
||||||
|
// Обновляем текущую вкладку
|
||||||
|
setCurrentTab(tab || '')
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
// Объединенный эффект для загрузки автора и его подписок
|
// Объединенный эффект для загрузки автора и его подписок
|
||||||
createEffect(async () => {
|
createEffect(async () => {
|
||||||
const meData = session()?.user?.app_data?.profile as Author
|
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))
|
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 (
|
return (
|
||||||
<div class={styles.authorPage}>
|
<div class={styles.authorPage}>
|
||||||
<div class="wide-container">
|
<div class="wide-container">
|
||||||
|
@ -118,27 +156,7 @@ export const AuthorView = (props: AuthorViewProps) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class={clsx(styles.groupControls, 'row')}>
|
<div class={clsx(styles.groupControls, 'row')}>
|
||||||
<div class="col-md-16">
|
<TabNavigator />
|
||||||
<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>
|
|
||||||
<div class={clsx('col-md-8', styles.additionalControls)}>
|
<div class={clsx('col-md-8', styles.additionalControls)}>
|
||||||
<Show when={author()?.stat?.rating || author()?.stat?.rating === 0}>
|
<Show when={author()?.stat?.rating || author()?.stat?.rating === 0}>
|
||||||
<div class={styles.ratingContainer}>
|
<div class={styles.ratingContainer}>
|
||||||
|
@ -153,7 +171,8 @@ export const AuthorView = (props: AuthorViewProps) => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Switch>
|
<Switch>
|
||||||
<Match when={props.selectedTab === 'about'}>
|
|
||||||
|
<Match when={currentTab() === 'about'}>
|
||||||
<div class="wide-container">
|
<div class="wide-container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-20 col-lg-18">
|
<div class="col-md-20 col-lg-18">
|
||||||
|
@ -177,7 +196,9 @@ export const AuthorView = (props: AuthorViewProps) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={props.selectedTab === 'comments'}>
|
|
||||||
|
<Match when={currentTab() === 'comments'}>
|
||||||
|
|
||||||
<Show when={me()?.slug === props.authorSlug && !me().stat?.comments}>
|
<Show when={me()?.slug === props.authorSlug && !me().stat?.comments}>
|
||||||
<div class="wide-container">
|
<div class="wide-container">
|
||||||
<Placeholder type={loc?.pathname} mode="profile" />
|
<Placeholder type={loc?.pathname} mode="profile" />
|
||||||
|
@ -203,7 +224,8 @@ export const AuthorView = (props: AuthorViewProps) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={!props.selectedTab}>
|
|
||||||
|
<Match when={!currentTab()}>
|
||||||
<Show when={me()?.slug === props.authorSlug && !me().stat?.shouts}>
|
<Show when={me()?.slug === props.authorSlug && !me().stat?.shouts}>
|
||||||
<div class="wide-container">
|
<div class="wide-container">
|
||||||
<Placeholder type={loc?.pathname} mode="profile" />
|
<Placeholder type={loc?.pathname} mode="profile" />
|
||||||
|
@ -239,6 +261,7 @@ export const AuthorView = (props: AuthorViewProps) => {
|
||||||
</Show>
|
</Show>
|
||||||
</Show>
|
</Show>
|
||||||
</Match>
|
</Match>
|
||||||
|
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { RouteSectionProps } from '@solidjs/router'
|
import { RouteSectionProps, createAsync } from '@solidjs/router'
|
||||||
import { ErrorBoundary, Suspense, createEffect, createMemo, createSignal, on } from 'solid-js'
|
import { ErrorBoundary, Suspense, createEffect, createSignal, on } from 'solid-js'
|
||||||
import { AuthorView } from '~/components/Views/Author'
|
import { AuthorView } from '~/components/Views/Author'
|
||||||
import { FourOuFourView } from '~/components/Views/FourOuFour'
|
import { FourOuFourView } from '~/components/Views/FourOuFour'
|
||||||
import { LoadMoreItems, LoadMoreWrapper } from '~/components/_shared/LoadMoreWrapper'
|
import { LoadMoreItems, LoadMoreWrapper } from '~/components/_shared/LoadMoreWrapper'
|
||||||
|
@ -39,9 +39,10 @@ const fetchAuthor = async (slug: string) => {
|
||||||
export const route = {
|
export const route = {
|
||||||
load: async ({ params, location: { query } }: RouteSectionProps<{ articles: Shout[] }>) => {
|
load: async ({ params, location: { query } }: RouteSectionProps<{ articles: Shout[] }>) => {
|
||||||
const offset: number = Number.parseInt(query.offset, 10)
|
const offset: number = Number.parseInt(query.offset, 10)
|
||||||
|
console.debug('route loading with offset', offset)
|
||||||
return {
|
return {
|
||||||
author: await fetchAuthor(params.slug),
|
author: await fetchAuthor(params.slug),
|
||||||
shouts: await fetchAuthorShouts(params.slug, offset),
|
articles: await fetchAuthorShouts(params.slug, offset),
|
||||||
topics: await fetchAllTopics()
|
topics: await fetchAllTopics()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,39 +51,17 @@ export const route = {
|
||||||
export type AuthorPageProps = { articles?: Shout[]; author?: Author; topics?: Topic[] }
|
export type AuthorPageProps = { articles?: Shout[]; author?: Author; topics?: Topic[] }
|
||||||
|
|
||||||
export default function AuthorPage(props: RouteSectionProps<AuthorPageProps>) {
|
export default function AuthorPage(props: RouteSectionProps<AuthorPageProps>) {
|
||||||
|
const { t } = useLocalize()
|
||||||
|
|
||||||
|
// load author's profile
|
||||||
const { addAuthor, authorsEntities } = useAuthors()
|
const { addAuthor, authorsEntities } = useAuthors()
|
||||||
const [author, setAuthor] = createSignal<Author | undefined>(undefined)
|
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(
|
createEffect(
|
||||||
on(
|
on(author,
|
||||||
[() => props.params.slug || '', author],
|
async (profile) => {
|
||||||
async ([slug, profile]) => {
|
// update only if no profile loaded
|
||||||
if (!profile) {
|
if (!profile) {
|
||||||
const loadedAuthor = authorsEntities()[slug] || (await fetchAuthor(slug))
|
const loadedAuthor = authorsEntities()[props.params.slug] || (await fetchAuthor(props.params.slug))
|
||||||
if (loadedAuthor) {
|
if (loadedAuthor) {
|
||||||
addAuthor(loadedAuthor)
|
addAuthor(loadedAuthor)
|
||||||
setAuthor(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 loadAuthorShoutsMore = async (offset: number) => {
|
||||||
const loadedShouts = await fetchAuthorShouts(props.params.slug, offset)
|
const loadedShouts = await fetchAuthorShouts(props.params.slug, offset)
|
||||||
loadedShouts && addFeed(loadedShouts)
|
loadedShouts && addFeed(loadedShouts)
|
||||||
|
@ -103,19 +124,23 @@ export default function AuthorPage(props: RouteSectionProps<AuthorPageProps>) {
|
||||||
<ErrorBoundary fallback={(_err) => <FourOuFourView />}>
|
<ErrorBoundary fallback={(_err) => <FourOuFourView />}>
|
||||||
<Suspense fallback={<Loading />}>
|
<Suspense fallback={<Loading />}>
|
||||||
<PageLayout
|
<PageLayout
|
||||||
title={`${t('Discours')} :: ${title()}`}
|
title={title()}
|
||||||
headerTitle={author()?.name || ''}
|
headerTitle={author()?.name || ''}
|
||||||
slug={author()?.slug}
|
slug={author()?.slug}
|
||||||
desc={author()?.about || author()?.bio || ''}
|
desc={desc()}
|
||||||
cover={cover()}
|
cover={cover()}
|
||||||
>
|
>
|
||||||
<ReactionsProvider>
|
<ReactionsProvider>
|
||||||
<LoadMoreWrapper loadFunction={loadAuthorShoutsMore} pageSize={SHOUTS_PER_PAGE}>
|
<LoadMoreWrapper
|
||||||
|
loadFunction={loadAuthorShoutsMore}
|
||||||
|
pageSize={SHOUTS_PER_PAGE}
|
||||||
|
hidden={loadMoreHidden()}
|
||||||
|
>
|
||||||
<AuthorView
|
<AuthorView
|
||||||
author={author() as Author}
|
author={author() as Author}
|
||||||
selectedTab={props.params.tab}
|
selectedTab={props.params.tab}
|
||||||
authorSlug={props.params.slug}
|
authorSlug={props.params.slug}
|
||||||
shouts={shoutsByAuthor()}
|
shouts={authorShouts()}
|
||||||
/>
|
/>
|
||||||
</LoadMoreWrapper>
|
</LoadMoreWrapper>
|
||||||
</ReactionsProvider>
|
</ReactionsProvider>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user