page-titles

This commit is contained in:
Untone 2024-07-05 11:11:57 +03:00
parent 6354bb8d47
commit 3433ba0aac
19 changed files with 38 additions and 29 deletions

View File

@ -9,6 +9,7 @@
"build": "vinxi build", "build": "vinxi build",
"start": "vinxi start", "start": "vinxi start",
"codegen": "graphql-codegen", "codegen": "graphql-codegen",
"e2e": "npm run e2e:tests",
"e2e:tests": "npx playwright test --project=webkit", "e2e:tests": "npx playwright test --project=webkit",
"e2e:tests:ci": "CI=true npx playwright test --project=webkit", "e2e:tests:ci": "CI=true npx playwright test --project=webkit",
"e2e:install": "npx playwright install webkit && npx playwright install-deps ", "e2e:install": "npx playwright install webkit && npx playwright install-deps ",

View File

@ -56,7 +56,7 @@ export const AllAuthors = (props: Props) => {
}) })
const sortedKeys = createMemo<string[]>(() => { const sortedKeys = createMemo<string[]>(() => {
const keys = Object.keys(byLetterFiltered()) const keys = Object.keys(byLetterFiltered()||{})
keys.sort() keys.sort()
const fk = keys.shift() || '' const fk = keys.shift() || ''
fk && keys.push(fk) fk && keys.push(fk)
@ -69,16 +69,16 @@ export const AllAuthors = (props: Props) => {
return ( return (
<div class={clsx(styles.allAuthorsPage, 'wide-container')}> <div class={clsx(styles.allAuthorsPage, 'wide-container')}>
<Meta name="descprition" content={description()} /> <Meta name="descprition" content={description() || ''} />
<Meta name="keywords" content={lang() === 'ru' ? ruKeywords[''] : enKeywords['']} /> <Meta name="keywords" content={lang() === 'ru' ? ruKeywords[''] : enKeywords['']} />
<Meta name="og:type" content="article" /> <Meta name="og:type" content="article" />
<Meta name="og:title" content={ogTitle()} /> <Meta name="og:title" content={ogTitle() || ''} />
<Meta name="og:image" content={ogImage()} /> <Meta name="og:image" content={ogImage() || ''} />
<Meta name="twitter:image" content={ogImage()} /> <Meta name="twitter:image" content={ogImage() || ''} />
<Meta name="og:description" content={description()} /> <Meta name="og:description" content={description() || ''} />
<Meta name="twitter:card" content="summary_large_image" /> <Meta name="twitter:card" content="summary_large_image" />
<Meta name="twitter:title" content={ogTitle()} /> <Meta name="twitter:title" content={ogTitle() || ''} />
<Meta name="twitter:description" content={description()} /> <Meta name="twitter:description" content={description() || ''} />
<Show when={props.isLoaded} fallback={<Loading />}> <Show when={props.isLoaded} fallback={<Loading />}>
<div class="offset-md-5"> <div class="offset-md-5">
<div class="row"> <div class="row">

View File

@ -83,7 +83,13 @@ export const LocalizeProvider = (props: { children: JSX.Element }) => {
const formatTimeAgo = (date: Date) => timeAgo().format(date) const formatTimeAgo = (date: Date) => timeAgo().format(date)
const value: LocalizeContextType = { const value: LocalizeContextType = {
t: i18next.t, t: ((s: string) => {
try {
return i18next.t(s)
} catch(_) {
return s
}
}) as i18n['t'],
lang, lang,
setLang, setLang,
formatTime, formatTime,

View File

@ -1,4 +1,4 @@
import { RouteSectionProps, createAsync, useParams } from '@solidjs/router' import { RouteSectionProps, createAsync, useLocation, useParams } from '@solidjs/router'
import { ErrorBoundary, Suspense, createMemo, createReaction, createSignal, onMount } from 'solid-js' import { ErrorBoundary, Suspense, createMemo, createReaction, createSignal, onMount } from 'solid-js'
import { FourOuFourView } from '~/components/Views/FourOuFour' import { FourOuFourView } from '~/components/Views/FourOuFour'
import { Loading } from '~/components/_shared/Loading' import { Loading } from '~/components/_shared/Loading'
@ -22,11 +22,12 @@ export const route = {
export const ArticlePage = (props: RouteSectionProps<{ article: Shout }>) => { export const ArticlePage = (props: RouteSectionProps<{ article: Shout }>) => {
const params = useParams() const params = useParams()
const loc = useLocation()
const article = createAsync(async () => props.data.article || (await fetchShout(params.slug))) const article = createAsync(async () => props.data.article || (await fetchShout(params.slug)))
const { t } = useLocalize() const { t } = useLocalize()
const [scrollToComments, setScrollToComments] = createSignal<boolean>(false) const [scrollToComments, setScrollToComments] = createSignal<boolean>(false)
const title = createMemo( const title = createMemo(
() => `${article()?.authors?.[0]?.name || t('Discours')}: ${article()?.title || ''}` () => `${article()?.authors?.[0]?.name || t('Discours')} :: ${article()?.title || ''}`
) )
onMount(async () => { onMount(async () => {
if (gaIdentity) { if (gaIdentity) {
@ -49,7 +50,7 @@ export const ArticlePage = (props: RouteSectionProps<{ article: Shout }>) => {
window.gtag?.('event', 'page_view', { window.gtag?.('event', 'page_view', {
page_title: article()?.title, page_title: article()?.title,
page_location: window.location.href, page_location: window.location.href,
page_path: window.location.pathname page_path: loc.pathname
}) })
} }
}) })

View File

@ -46,13 +46,13 @@ export const route = {
} }
} satisfies RouteDefinition } satisfies RouteDefinition
export default function AllTopicsPage(props: RouteSectionProps<{ authors: Author[] }>) { export default function AllAuthorsPage(props: RouteSectionProps<{ authors: Author[] }>) {
const { t } = useLocalize() const { t } = useLocalize()
const authors = createAsync<Author[]>(async () => props.data.authors || (await fetchData()) || []) const authors = createAsync<Author[]>(async () => props.data.authors || (await fetchData()) || [])
const { addAuthors } = useAuthors() const { addAuthors } = useAuthors()
createEffect(() => addAuthors(authors() || [])) createEffect(() => addAuthors(authors() || []))
return ( return (
<PageLayout withPadding={true} title={`${t('Discours')}:${t('All topics')}`}> <PageLayout withPadding={true} title={`${t('Discours')} :: ${t('All authors')}`}>
<ReactionsProvider> <ReactionsProvider>
<Suspense fallback={<Loading />}> <Suspense fallback={<Loading />}>
<AllAuthors authors={authors() || []} isLoaded={Boolean(authors())} /> <AllAuthors authors={authors() || []} isLoaded={Boolean(authors())} />

View File

@ -33,7 +33,7 @@ export const TopicPage = (props: RouteSectionProps<{ articles: Shout[] }>) => {
const { authorsEntities } = useAuthors() const { authorsEntities } = useAuthors()
const { t } = useLocalize() const { t } = useLocalize()
const author = createMemo(() => authorsEntities?.()[params.slug]) const author = createMemo(() => authorsEntities?.()[params.slug])
const title = createMemo(() => `${t('Discours')}: ${author()?.name || ''}`) const title = createMemo(() => `${author()?.name || ''}`)
// docs: `a side effect that is run the first time the expression // docs: `a side effect that is run the first time the expression
// wrapped by the returned tracking function is notified of a change` // wrapped by the returned tracking function is notified of a change`
@ -51,7 +51,7 @@ export const TopicPage = (props: RouteSectionProps<{ articles: Shout[] }>) => {
<ErrorBoundary fallback={(_err) => <FourOuFourView />}> <ErrorBoundary fallback={(_err) => <FourOuFourView />}>
<Suspense fallback={<Loading />}> <Suspense fallback={<Loading />}>
<PageLayout <PageLayout
title={title()} title={`${t('Discours')} :: ${title()}`}
headerTitle={author()?.name || ''} headerTitle={author()?.name || ''}
slug={author()?.slug} slug={author()?.slug}
articleBody={author()?.about || author()?.bio || ''} articleBody={author()?.about || author()?.bio || ''}

View File

@ -20,7 +20,7 @@ export const DraftsPage = () => {
const drafts = createAsync(async () => await fetchDrafts(client)) const drafts = createAsync(async () => await fetchDrafts(client))
return ( return (
<PageLayout title={t('Drafts')}> <PageLayout title={`${t('Discours')} :: ${t('Drafts')}`}>
<AuthGuard> <AuthGuard>
<DraftsView drafts={drafts() || []} /> <DraftsView drafts={drafts() || []} />
</AuthGuard> </AuthGuard>

View File

@ -64,7 +64,7 @@ export const EditPage = () => {
}) })
return ( return (
<PageLayout title={title()}> <PageLayout title={`${t('Discours')} :: ${title()}`}>
<AuthGuard> <AuthGuard>
<EditView shout={shout() as Shout} /> <EditView shout={shout() as Shout} />
</AuthGuard> </AuthGuard>

View File

@ -25,7 +25,8 @@ export const EditSettingsPage = () => {
} }
} }
return ( return (
<PageLayout title={t('Publication settings')}>
<PageLayout title={`${t('Discours')} :: ${t('Publication settings')}`}>
<AuthGuard> <AuthGuard>
<EditSettingsView shout={shout() as Shout} /> <EditSettingsView shout={shout() as Shout} />
</AuthGuard> </AuthGuard>

View File

@ -32,7 +32,7 @@ export default () => {
} }
} }
return ( return (
<PageLayout title={ogTitle()}> <PageLayout title={`${t('Discours')} :: ${ogTitle()}`}>
<Meta name="descprition" content={description()} /> <Meta name="descprition" content={description()} />
<Meta name="keywords" content={lang() === 'ru' ? ruKeywords[''] : enKeywords['']} /> <Meta name="keywords" content={lang() === 'ru' ? ruKeywords[''] : enKeywords['']} />
<Meta name="og:type" content="article" /> <Meta name="og:type" content="article" />

View File

@ -60,7 +60,7 @@ export const ExpoPage = (props: RouteSectionProps<Shout[]>) => {
createEffect(on(title, (ttl) => (document.title = ttl), { defer: true })) createEffect(on(title, (ttl) => (document.title = ttl), { defer: true }))
return ( return (
<PageLayout withPadding={true} zeroBottomPadding={true} title={title()}> <PageLayout withPadding={true} zeroBottomPadding={true} title={`${t('Discours')} :: ${title()}`}>
<Topics /> <Topics />
<Expo shouts={shouts() || []} layout={layout() as LayoutType} /> <Expo shouts={shouts() || []} layout={layout() as LayoutType} />
</PageLayout> </PageLayout>

View File

@ -125,7 +125,7 @@ export const FeedPage = (props: RouteSectionProps<Shout[]>) => {
} }
createEffect(() => setIsLoadMoreButtonVisible(offset() < (shouts()?.length || 0))) createEffect(() => setIsLoadMoreButtonVisible(offset() < (shouts()?.length || 0)))
return ( return (
<PageLayout title={t('Feed')}> <PageLayout withPadding={true} title={`${t('Discours')} :: ${t('Feed')}`}>
<ReactionsProvider> <ReactionsProvider>
<Feed shouts={shouts() || []} /> <Feed shouts={shouts() || []} />
</ReactionsProvider> </ReactionsProvider>

View File

@ -8,7 +8,7 @@ export const ProfileSettingsPage = () => {
const { t } = useLocalize() const { t } = useLocalize()
return ( return (
<PageLayout title={t('Profile')}> <PageLayout withPadding={true} title={`${t('Discours')} :: ${t('Profile')}`}>
<AuthGuard> <AuthGuard>
<ProfileProvider> <ProfileProvider>
<ProfileSettings /> <ProfileSettings />

View File

@ -135,7 +135,7 @@ export const ProfileSecurityPage = () => {
} }
return ( return (
<PageLayout title={t('Profile')}> <PageLayout withPadding={true} title={`${t('Discours')} :: ${t('Security')}`}>
<AuthGuard> <AuthGuard>
<Show when={isSessionLoaded()} fallback={<Loading />}> <Show when={isSessionLoaded()} fallback={<Loading />}>
<div class="wide-container"> <div class="wide-container">

View File

@ -7,7 +7,7 @@ export const ProfileSubscriptionsPage = () => {
const { t } = useLocalize() const { t } = useLocalize()
return ( return (
<PageLayout title={t('Profile')}> <PageLayout withPadding={true} title={`${t('Discours')} :: ${t('Subscriptions')}`}>
<AuthGuard> <AuthGuard>
<ProfileSubscriptions /> <ProfileSubscriptions />
</AuthGuard> </AuthGuard>

View File

@ -48,7 +48,7 @@ export const SearchPage = () => {
}) })
return ( return (
<PageLayout title={t('Search')}> <PageLayout withPadding={true} title={`${t('Discours')} :: ${t('Search')}`}>
<ReactionsProvider> <ReactionsProvider>
<Suspense fallback={<Loading />}> <Suspense fallback={<Loading />}>
<Show when={isLoaded()} fallback={<Loading />}> <Show when={isLoaded()} fallback={<Loading />}>

View File

@ -22,7 +22,7 @@ export default function AllTopicsPage(props: RouteSectionProps<{ topics: Topic[]
const { addTopics } = useTopics() const { addTopics } = useTopics()
createEffect(() => addTopics(topics() || [])) createEffect(() => addTopics(topics() || []))
return ( return (
<PageLayout withPadding={true} title={`${t('Discours')}:${t('All topics')}`}> <PageLayout withPadding={true} title={`${t('Discours')} :: ${t('All topics')}`}>
<ReactionsProvider> <ReactionsProvider>
<Suspense fallback={<Loading />}> <Suspense fallback={<Loading />}>
<AllTopics topics={topics() as Topic[]} /> <AllTopics topics={topics() as Topic[]} />

View File

@ -33,7 +33,7 @@ export const TopicPage = (props: RouteSectionProps<{ articles: Shout[] }>) => {
const { topicEntities } = useTopics() const { topicEntities } = useTopics()
const { t } = useLocalize() const { t } = useLocalize()
const topic = createMemo(() => topicEntities?.()[params.slug]) const topic = createMemo(() => topicEntities?.()[params.slug])
const title = createMemo(() => `${t('Discours')}: ${topic()?.title || ''}`) const title = createMemo(() => `${t('Discours')} :: ${topic()?.title || ''}`)
// docs: `a side effect that is run the first time the expression // docs: `a side effect that is run the first time the expression
// wrapped by the returned tracking function is notified of a change` // wrapped by the returned tracking function is notified of a change`

View File

@ -69,7 +69,7 @@ test.describe('Pages open', () => {
Object.keys(pagesTitles).forEach((res: string) => { Object.keys(pagesTitles).forEach((res: string) => {
test(`Open Page ${res}`, async ({ page }) => { test(`Open Page ${res}`, async ({ page }) => {
await page.goto(`${res}`) await page.goto(`${res}`)
const title = pagesTitles[res as keyof typeof pagesTitles] || '' const title = pagesTitles[res as keyof typeof pagesTitles] || '00000000000'
await expect(page).toHaveTitle(title) await expect(page).toHaveTitle(title)
}) })
}) })