@-routing-fix

This commit is contained in:
Untone 2024-07-13 12:36:23 +03:00
parent ef1408327f
commit fde2335a02
9 changed files with 61 additions and 85 deletions

View File

@ -1,4 +1,4 @@
import { A, useLocation, useParams } from '@solidjs/router' import { A, useLocation } from '@solidjs/router'
import { clsx } from 'clsx' import { clsx } from 'clsx'
import { For, Match, Show, Switch, createEffect, createMemo, createSignal, on, onMount } from 'solid-js' import { For, Match, Show, Switch, createEffect, createMemo, createSignal, on, onMount } from 'solid-js'
import { Loading } from '~/components/_shared/Loading' import { Loading } from '~/components/_shared/Loading'
@ -26,7 +26,7 @@ import { Row2 } from '../../Feed/Row2'
import { Row3 } from '../../Feed/Row3' import { Row3 } from '../../Feed/Row3'
import styles from './Author.module.scss' import styles from './Author.module.scss'
type Props = { type AuthorViewProps = {
authorSlug: string authorSlug: string
selectedTab: string selectedTab: string
shouts?: Shout[] shouts?: Shout[]
@ -37,11 +37,10 @@ type Props = {
export const PRERENDERED_ARTICLES_COUNT = 12 export const PRERENDERED_ARTICLES_COUNT = 12
const LOAD_MORE_PAGE_SIZE = 9 const LOAD_MORE_PAGE_SIZE = 9
export const AuthorView = (props: Props) => { export const AuthorView = (props: AuthorViewProps) => {
// contexts // contexts
const { t } = useLocalize() const { t } = useLocalize()
const loc = useLocation() const loc = useLocation()
const params = useParams()
const { session } = useSession() const { session } = useSession()
const { query } = useGraphQL() const { query } = useGraphQL()
const { sortedFeed } = useFeed() const { sortedFeed } = useFeed()
@ -173,19 +172,19 @@ export const AuthorView = (props: Props) => {
<div class={clsx(styles.groupControls, 'row')}> <div class={clsx(styles.groupControls, 'row')}>
<div class="col-md-16"> <div class="col-md-16">
<ul class="view-switcher"> <ul class="view-switcher">
<li classList={{ 'view-switcher__item--selected': params.tab === '' }}> <li classList={{ 'view-switcher__item--selected': !props.selectedTab }}>
<A href={`/author/${props.authorSlug}`}>{t('Publications')}</A> <A href={`/author/${props.authorSlug}`}>{t('Publications')}</A>
<Show when={author()?.stat}> <Show when={author()?.stat}>
<span class="view-switcher__counter">{author()?.stat?.shouts || 0}</span> <span class="view-switcher__counter">{author()?.stat?.shouts || 0}</span>
</Show> </Show>
</li> </li>
<li classList={{ 'view-switcher__item--selected': params.tab === 'comment' }}> <li classList={{ 'view-switcher__item--selected': props.selectedTab === 'comment' }}>
<A href={`/author/${props.authorSlug}/comments`}>{t('Comments')}</A> <A href={`/author/${props.authorSlug}/comments`}>{t('Comments')}</A>
<Show when={author()?.stat}> <Show when={author()?.stat}>
<span class="view-switcher__counter">{author()?.stat?.comments || 0}</span> <span class="view-switcher__counter">{author()?.stat?.comments || 0}</span>
</Show> </Show>
</li> </li>
<li classList={{ 'view-switcher__item--selected': params.tab === 'about' }}> <li classList={{ 'view-switcher__item--selected': props.selectedTab === 'about' }}>
<A onClick={() => checkBioHeight()} href={`/author/${props.authorSlug}/about`}> <A onClick={() => checkBioHeight()} href={`/author/${props.authorSlug}/about`}>
{t('About the author')} {t('About the author')}
</A> </A>
@ -206,7 +205,7 @@ export const AuthorView = (props: Props) => {
</div> </div>
<Switch> <Switch>
<Match when={params.tab === 'about'}> <Match when={props.selectedTab === '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">
@ -230,7 +229,7 @@ export const AuthorView = (props: Props) => {
</div> </div>
</div> </div>
</Match> </Match>
<Match when={params.tab === 'comments'}> <Match when={props.selectedTab === '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" />
@ -256,7 +255,7 @@ export const AuthorView = (props: Props) => {
</div> </div>
</div> </div>
</Match> </Match>
<Match when={params.tab === ''}> <Match when={!props.selectedTab}>
<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" />

View File

@ -16,6 +16,7 @@ import styles from './PageLayout.module.scss'
type PageLayoutProps = { type PageLayoutProps = {
title: string title: string
desc?: string desc?: string
keywords?: string
headerTitle?: string headerTitle?: string
slug?: string slug?: string
article?: Shout article?: Shout
@ -46,12 +47,10 @@ export const PageLayout = (props: PageLayoutProps) => {
}) })
: imageUrl : imageUrl
) )
const description = createMemo(() => (props.desc ? t(props.desc) : '')) const description = createMemo(() => props.desc || (props.article && descFromBody(props.article.body)))
const keypath = createMemo(() => (props.key || loc?.pathname.split('/')[0]) as keyof typeof ruKeywords) const keypath = createMemo(() => (props.key || loc?.pathname.split('/')[0]) as keyof typeof ruKeywords)
const keywords = createMemo( const keywords = createMemo(
() => () => props.keywords || (lang() === 'ru' ? ruKeywords[keypath()] : enKeywords[keypath()])
(props.article && descFromBody(props.article.body)) ||
(lang() === 'ru' ? ruKeywords[keypath()] : enKeywords[keypath()])
) )
const [scrollToComments, setScrollToComments] = createSignal<boolean>(false) const [scrollToComments, setScrollToComments] = createSignal<boolean>(false)
createEffect(() => props.scrollToComments?.(scrollToComments())) createEffect(() => props.scrollToComments?.(scrollToComments()))

View File

@ -92,12 +92,13 @@ export const AuthorsProvider = (props: { children: JSX.Element }) => {
const loadAuthor = async (opts: QueryGet_AuthorArgs): Promise<void> => { const loadAuthor = async (opts: QueryGet_AuthorArgs): Promise<void> => {
try { try {
console.debug('[context.authors] load author', opts)
const fetcher = await getAuthor(opts) const fetcher = await getAuthor(opts)
const author = await fetcher() const author = await fetcher()
if (author) addAuthor(author as Author) if (author) addAuthor(author as Author)
console.debug('Loaded author:', author) console.debug('[context.authors]', author)
} catch (error) { } catch (error) {
console.error('Error loading author:', error) console.error('[context.authors] Error loading author:', error)
throw error throw error
} }
} }

View File

@ -1,4 +1,4 @@
import { RouteDefinition, RouteSectionProps, createAsync, useLocation, useParams } from '@solidjs/router' import { RouteDefinition, RouteSectionProps, createAsync, useLocation } from '@solidjs/router'
import { HttpStatusCode } from '@solidjs/start' import { HttpStatusCode } from '@solidjs/start'
import { import {
ErrorBoundary, ErrorBoundary,
@ -14,7 +14,7 @@ import { FourOuFourView } from '~/components/Views/FourOuFour'
import { Loading } from '~/components/_shared/Loading' import { Loading } from '~/components/_shared/Loading'
import { gaIdentity } from '~/config' import { gaIdentity } from '~/config'
import { useLocalize } from '~/context/localize' import { useLocalize } from '~/context/localize'
import { getAuthor, getShout } from '~/graphql/api/public' import { getShout } from '~/graphql/api/public'
import type { Author, Reaction, Shout } from '~/graphql/schema/core.gen' import type { Author, Reaction, Shout } from '~/graphql/schema/core.gen'
import { initGA, loadGAScript } from '~/utils/ga' import { initGA, loadGAScript } from '~/utils/ga'
import { descFromBody, keywordsFromTopics } from '~/utils/meta' import { descFromBody, keywordsFromTopics } from '~/utils/meta'
@ -29,12 +29,6 @@ const fetchShout = async (slug: string): Promise<Shout | undefined> => {
return result return result
} }
const fetchAuthor = async (slug: string): Promise<Author | undefined> => {
const authorLoader = getAuthor({ slug })
const result = await authorLoader()
return result
}
export const route: RouteDefinition = { export const route: RouteDefinition = {
load: async ({ params }) => ({ load: async ({ params }) => ({
article: await fetchShout(params.slug) article: await fetchShout(params.slug)
@ -44,21 +38,23 @@ export const route: RouteDefinition = {
type SlugPageProps = { article?: Shout; comments?: Reaction[]; votes?: Reaction[]; author?: Author } type SlugPageProps = { article?: Shout; comments?: Reaction[]; votes?: Reaction[]; author?: Author }
export default (props: RouteSectionProps<SlugPageProps>) => { export default (props: RouteSectionProps<SlugPageProps>) => {
const params = useParams() if (props.params.slug.startsWith('@')) {
if (params.slug.startsWith('@')) return AuthorPage(props as RouteSectionProps<AuthorPageProps>) console.debug('[slug] @ found, render as author page')
const patchedProps = {
...props,
params: {
...props.params,
slug: props.params.slug.slice(1, props.params.slug.length)
}
} as RouteSectionProps<AuthorPageProps>
return AuthorPage(patchedProps)
}
const loc = useLocation() const loc = useLocation()
const { t } = useLocalize() const { t } = useLocalize()
const [scrollToComments, setScrollToComments] = createSignal<boolean>(false) const [scrollToComments, setScrollToComments] = createSignal<boolean>(false)
const article = createAsync(async () => props.data.article || (await fetchShout(params.slug))) const article = createAsync(async () => props.data.article || (await fetchShout(props.params.slug)))
const author = createAsync(async () => const titleSuffix = createMemo(() => (article()?.title ? ` :: ${article()?.title || ''}` : ''))
params.slug.startsWith('@')
? props.data.author || (await fetchAuthor(params.slug))
: article()?.authors?.[0]
)
const titleSuffix = createMemo(
() => (article()?.title || author()?.name) ?? ` :: ${article()?.title || author()?.name || ''}`
)
onMount(async () => { onMount(async () => {
if (gaIdentity && article()?.id) { if (gaIdentity && article()?.id) {
@ -98,35 +94,19 @@ export default (props: RouteSectionProps<SlugPageProps>) => {
</PageLayout> </PageLayout>
} }
> >
<Show <PageLayout
when={params.slug.startsWith('@')} title={`${t('Discours')}${titleSuffix()}`}
fallback={ desc={descFromBody(article()?.body || '')}
<PageLayout keywords={keywordsFromTopics(article()?.topics as { title: string }[])}
title={`${t('Discours')}${titleSuffix() || ''}`} headerTitle={article()?.title || ''}
desc={keywordsFromTopics(article()?.topics as { title: string }[])} slug={article()?.slug}
headerTitle={article()?.title || ''} cover={article()?.cover || ''}
slug={article()?.slug} scrollToComments={(value) => setScrollToComments(value)}
cover={article()?.cover || ''}
scrollToComments={(value) => setScrollToComments(value)}
>
<ReactionsProvider>
<FullArticle article={article() as Shout} scrollToComments={scrollToComments()} />
</ReactionsProvider>
</PageLayout>
}
> >
<PageLayout <ReactionsProvider>
title={`${t('Discours')}${titleSuffix() || ''}`} <FullArticle article={article() as Shout} scrollToComments={scrollToComments()} />
desc={descFromBody(author()?.about || author()?.bio || '')} </ReactionsProvider>
headerTitle={author()?.name || ''} </PageLayout>
slug={author()?.slug}
cover={author()?.pic || ''}
>
<ReactionsProvider>
<FullArticle article={article() as Shout} scrollToComments={scrollToComments()} />
</ReactionsProvider>
</PageLayout>
</Show>
</Show> </Show>
</Suspense> </Suspense>
</ErrorBoundary> </ErrorBoundary>

View File

@ -1,4 +1,4 @@
import { RouteSectionProps, createAsync, useParams } from '@solidjs/router' import { RouteSectionProps, createAsync } from '@solidjs/router'
import { ErrorBoundary, Suspense, createEffect, createMemo } from 'solid-js' import { ErrorBoundary, Suspense, createEffect, createMemo } 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'
@ -50,13 +50,12 @@ export const route = {
export type AuthorPageProps = { articles?: Shout[]; author?: Author; topics?: Topic[] } export type AuthorPageProps = { articles?: Shout[]; author?: Author; topics?: Topic[] }
export const AuthorPage = (props: RouteSectionProps<AuthorPageProps>) => { export const AuthorPage = (props: RouteSectionProps<AuthorPageProps>) => {
const params = useParams()
const { addAuthor } = useAuthors() const { addAuthor } = useAuthors()
const articles = createAsync( const articles = createAsync(
async () => props.data.articles || (await fetchAuthorShouts(params.slug)) || [] async () => props.data.articles || (await fetchAuthorShouts(props.params.slug)) || []
) )
const author = createAsync(async () => { const author = createAsync(async () => {
const a = props.data.author || (await fetchAuthor(params.slug)) const a = props.data.author || (await fetchAuthor(props.params.slug))
a && addAuthor(a) a && addAuthor(a)
return a return a
}) })
@ -81,7 +80,9 @@ export const AuthorPage = (props: RouteSectionProps<AuthorPageProps>) => {
: getImageUrl('production/image/logo_image.png') : getImageUrl('production/image/logo_image.png')
) )
const selectedTab = createMemo(() => (params.tab in ['followers', 'shouts'] ? params.tab : 'name')) const selectedTab = createMemo(() =>
props.params.tab in ['followers', 'shouts'] ? props.params.tab : 'name'
)
return ( return (
<ErrorBoundary fallback={(_err) => <FourOuFourView />}> <ErrorBoundary fallback={(_err) => <FourOuFourView />}>
<Suspense fallback={<Loading />}> <Suspense fallback={<Loading />}>
@ -96,7 +97,7 @@ export const AuthorPage = (props: RouteSectionProps<AuthorPageProps>) => {
<AuthorView <AuthorView
author={author() as Author} author={author() as Author}
selectedTab={selectedTab()} selectedTab={selectedTab()}
authorSlug={params.slug} authorSlug={props.params.slug}
shouts={articles() as Shout[]} shouts={articles() as Shout[]}
topics={topics()} topics={topics()}
/> />

View File

@ -1,4 +1,4 @@
import { redirect, useParams } from '@solidjs/router' import { RouteSectionProps, redirect } from '@solidjs/router'
import { createEffect, createMemo, createSignal, lazy, on } from 'solid-js' import { createEffect, createMemo, createSignal, lazy, on } from 'solid-js'
import { AuthGuard } from '~/components/AuthGuard' import { AuthGuard } from '~/components/AuthGuard'
import { PageLayout } from '~/components/_shared/PageLayout' import { PageLayout } from '~/components/_shared/PageLayout'
@ -27,7 +27,7 @@ export const getContentTypeTitle = (layout: LayoutType) => {
} }
} }
export default () => { export default (props: RouteSectionProps) => {
const { t } = useLocalize() const { t } = useLocalize()
const { session } = useSession() const { session } = useSession()
const snackbar = useSnackbar() const snackbar = useSnackbar()
@ -38,13 +38,12 @@ export default () => {
redirect('/edit') // all drafts page redirect('/edit') // all drafts page
} }
const [shout, setShout] = createSignal<Shout>() const [shout, setShout] = createSignal<Shout>()
const params = useParams()
const client = useGraphQL() const client = useGraphQL()
createEffect(on(session, (s) => s?.access_token && loadDraft(), { defer: true })) createEffect(on(session, (s) => s?.access_token && loadDraft(), { defer: true }))
const loadDraft = async () => { const loadDraft = async () => {
const result = await client.query(getShoutDraft, { shout_id: params.id }).toPromise() const result = await client.query(getShoutDraft, { shout_id: props.params.id }).toPromise()
if (result) { if (result) {
const { shout: loadedShout, error } = result.data.get_my_shout const { shout: loadedShout, error } = result.data.get_my_shout
if (error) { if (error) {

View File

@ -1,4 +1,4 @@
import { useParams } from '@solidjs/router' import { RouteSectionProps } from '@solidjs/router'
import { createEffect, createSignal, on } from 'solid-js' import { createEffect, createSignal, on } from 'solid-js'
import { AuthGuard } from '~/components/AuthGuard' import { AuthGuard } from '~/components/AuthGuard'
import EditSettingsView from '~/components/Views/EditView/EditSettingsView' import EditSettingsView from '~/components/Views/EditView/EditSettingsView'
@ -9,15 +9,14 @@ import { useSession } from '~/context/session'
import getShoutDraft from '~/graphql/query/core/article-my' import getShoutDraft from '~/graphql/query/core/article-my'
import { Shout } from '~/graphql/schema/core.gen' import { Shout } from '~/graphql/schema/core.gen'
export default () => { export default (props: RouteSectionProps) => {
const { t } = useLocalize() const { t } = useLocalize()
const params = useParams()
const client = useGraphQL() const client = useGraphQL()
const { session } = useSession() const { session } = useSession()
createEffect(on(session, (s) => s?.access_token && loadDraft(), { defer: true })) createEffect(on(session, (s) => s?.access_token && loadDraft(), { defer: true }))
const [shout, setShout] = createSignal<Shout>() const [shout, setShout] = createSignal<Shout>()
const loadDraft = async () => { const loadDraft = async () => {
const result = await client.query(getShoutDraft, { shout_id: params.id }).toPromise() const result = await client.query(getShoutDraft, { shout_id: props.params.id }).toPromise()
if (result) { if (result) {
const { shout: loadedShout, error } = result.data.get_my_shout const { shout: loadedShout, error } = result.data.get_my_shout
if (error) throw new Error(error) if (error) throw new Error(error)

View File

@ -1,4 +1,4 @@
import { Params, RouteSectionProps, createAsync, useParams } from '@solidjs/router' import { Params, RouteSectionProps, createAsync } from '@solidjs/router'
import { createEffect, createMemo, on } from 'solid-js' import { createEffect, createMemo, on } from 'solid-js'
import { TopicsNav } from '~/components/Nav/TopicsNav' import { TopicsNav } from '~/components/Nav/TopicsNav'
import { Expo } from '~/components/Views/Expo' import { Expo } from '~/components/Views/Expo'
@ -28,15 +28,14 @@ export const route = {
export default (props: RouteSectionProps<Shout[]>) => { export default (props: RouteSectionProps<Shout[]>) => {
const { t } = useLocalize() const { t } = useLocalize()
const params = useParams()
const shouts = createAsync( const shouts = createAsync(
async () => async () =>
props.data || props.data ||
(await fetchExpoShouts( (await fetchExpoShouts(
params.layout ? [params.layout] : ['audio', 'literature', 'article', 'video', 'image'] props.params.layout ? [props.params.layout] : ['audio', 'literature', 'article', 'video', 'image']
)) ))
) )
const layout = createMemo(() => params.layout) const layout = createMemo(() => props.params.layout)
const title = createMemo(() => { const title = createMemo(() => {
switch (layout()) { switch (layout()) {
case 'audio': { case 'audio': {

View File

@ -1,4 +1,4 @@
import { RouteSectionProps, createAsync, useParams } from '@solidjs/router' import { RouteSectionProps, createAsync } from '@solidjs/router'
import { HttpStatusCode } from '@solidjs/start' import { HttpStatusCode } from '@solidjs/start'
import { Show, Suspense, createEffect, createMemo, createSignal } from 'solid-js' import { Show, Suspense, createEffect, createMemo, createSignal } from 'solid-js'
import { FourOuFourView } from '~/components/Views/FourOuFour' import { FourOuFourView } from '~/components/Views/FourOuFour'
@ -37,7 +37,6 @@ export const route = {
export default (props: RouteSectionProps<{ articles: Shout[]; topics: Topic[] }>) => { export default (props: RouteSectionProps<{ articles: Shout[]; topics: Topic[] }>) => {
const { t } = useLocalize() const { t } = useLocalize()
const params = useParams()
const { addTopics } = useTopics() const { addTopics } = useTopics()
const [loadingError, setLoadingError] = createSignal(false) const [loadingError, setLoadingError] = createSignal(false)
@ -46,7 +45,7 @@ export default (props: RouteSectionProps<{ articles: Shout[]; topics: Topic[] }>
const ttt: Topic[] = props.data.topics || (await fetchAllTopics()) || [] const ttt: Topic[] = props.data.topics || (await fetchAllTopics()) || []
addTopics(ttt) addTopics(ttt)
console.debug('[route.topic] all topics loaded') console.debug('[route.topic] all topics loaded')
const t = ttt.find((x) => x.slug === params.slug) const t = ttt.find((x) => x.slug === props.params.slug)
return t return t
} catch (_error) { } catch (_error) {
setLoadingError(true) setLoadingError(true)
@ -55,7 +54,7 @@ export default (props: RouteSectionProps<{ articles: Shout[]; topics: Topic[] }>
}) })
const articles = createAsync( const articles = createAsync(
async () => props.data.articles || (await fetchTopicShouts(params.slug)) || [] async () => props.data.articles || (await fetchTopicShouts(props.params.slug)) || []
) )
const title = createMemo(() => `${t('Discours')} :: ${topic()?.title || ''}`) const title = createMemo(() => `${t('Discours')} :: ${topic()?.title || ''}`)