webapp/src/routes/[slug].tsx

108 lines
3.1 KiB
TypeScript
Raw Normal View History

2024-07-07 14:07:11 +00:00
import {
RouteDefinition,
RouteSectionProps,
createAsync,
redirect,
useLocation,
useParams
} from '@solidjs/router'
2024-07-07 13:48:53 +00:00
import { HttpStatusCode } from '@solidjs/start'
import { ErrorBoundary, Show, createEffect, createMemo, createSignal, on, onMount } from 'solid-js'
2024-06-26 08:22:05 +00:00
import { Loading } from '~/components/_shared/Loading'
2024-07-05 14:08:12 +00:00
import { gaIdentity } from '~/config'
2024-07-07 13:48:53 +00:00
import { useFeed } from '~/context/feed'
2024-06-26 08:22:05 +00:00
import { useLocalize } from '~/context/localize'
import { getShout } from '~/graphql/api/public'
import type { Shout } from '~/graphql/schema/core.gen'
2024-06-26 08:22:05 +00:00
import { initGA, loadGAScript } from '~/utils/ga'
import { FullArticle } from '../components/Article/FullArticle'
import { PageLayout } from '../components/_shared/PageLayout'
import { ReactionsProvider } from '../context/reactions'
2024-07-07 13:48:53 +00:00
const fetchShout = async (slug: string): Promise<Shout> => {
2024-06-26 08:22:05 +00:00
const shoutLoader = getShout({ slug })
2024-07-07 13:48:53 +00:00
const shout = await shoutLoader()
if (!shout) {
throw new Error('Shout not found')
}
return shout
2024-06-26 08:22:05 +00:00
}
2024-07-07 13:48:53 +00:00
export const route: RouteDefinition = {
load: async ({ params }) => {
try {
return await fetchShout(params.slug)
} catch (error) {
console.error('Error loading shout:', error)
throw new Response(null, {
status: 404,
statusText: 'Not Found'
})
}
}
2024-06-26 08:22:05 +00:00
}
2024-07-05 19:40:54 +00:00
export default (props: RouteSectionProps<{ article: Shout }>) => {
2024-07-03 17:38:43 +00:00
const params = useParams()
2024-07-05 08:11:57 +00:00
const loc = useLocation()
2024-07-07 13:48:53 +00:00
const { articleEntities } = useFeed()
2024-06-26 08:22:05 +00:00
const { t } = useLocalize()
const [scrollToComments, setScrollToComments] = createSignal<boolean>(false)
2024-07-07 13:48:53 +00:00
const article = createAsync(async () => {
if (params.slug && articleEntities?.()) {
2024-07-07 14:07:11 +00:00
return articleEntities()?.[params.slug] || props.data.article || (await fetchShout(params.slug))
2024-07-07 13:48:53 +00:00
}
throw redirect('/404', { status: 404 })
})
2024-07-07 14:07:11 +00:00
const title = createMemo(
() => `${article()?.authors?.[0]?.name || t('Discours')} :: ${article()?.title || ''}`
)
2024-07-07 13:48:53 +00:00
2024-06-26 08:22:05 +00:00
onMount(async () => {
2024-07-07 13:48:53 +00:00
if (gaIdentity && article()?.id) {
2024-06-26 11:42:35 +00:00
try {
await loadGAScript(gaIdentity)
initGA(gaIdentity)
} catch (error) {
2024-07-07 13:48:53 +00:00
console.warn('Failed to connect Google Analytics:', error)
2024-06-26 11:42:35 +00:00
}
}
})
2024-07-07 14:07:11 +00:00
createEffect(
on(
article,
(a?: Shout) => {
if (!a) return
window?.gtag?.('event', 'page_view', {
page_title: a.title,
page_location: window?.location.href || '',
page_path: loc.pathname
})
},
{ defer: true }
)
)
2024-07-05 19:40:54 +00:00
2024-06-26 08:22:05 +00:00
return (
2024-07-07 13:48:53 +00:00
<ErrorBoundary fallback={() => <HttpStatusCode code={404} />}>
<Show when={article()?.id} fallback={<Loading />}>
2024-06-26 08:22:05 +00:00
<PageLayout
title={title()}
headerTitle={article()?.title || ''}
slug={article()?.slug}
articleBody={article()?.body}
cover={article()?.cover || ''}
2024-07-07 13:48:53 +00:00
scrollToComments={(value) => setScrollToComments(value)}
2024-06-26 08:22:05 +00:00
>
<ReactionsProvider>
<FullArticle article={article() as Shout} scrollToComments={scrollToComments()} />
</ReactionsProvider>
</PageLayout>
2024-07-07 13:48:53 +00:00
</Show>
2024-06-26 08:22:05 +00:00
</ErrorBoundary>
)
}