Files
uiogaming.no/src/app/[locale]/blog/[slug]/page.tsx
T
leander 017f5154b1 Rewrite translation logic
Rerendering translations apparently caused issues with safari because
apple fucking sucks. Thus we had to rewrite the logic to use
prefix-based translations. This is the attempt at this. It might be a
mega commit but it's how it ought to be...
2025-09-16 03:31:11 +02:00

156 lines
3.8 KiB
TypeScript

import Layout from "@/app/[locale]/layout"
import Footer from "@/components/page-sections/footer"
import { generateSeoMetadata, generateSeoViewport } from "@/components/seo"
import Back from "@/components/ui/back"
import { urlFor } from "@/lib/imageUrlBuilder"
import { getBlogPost } from "@/lib/sanity"
import { PortableText, PortableTextComponents } from "@portabletext/react"
import { Metadata, Viewport } from "next"
import { notFound } from "next/navigation"
import { getLocale } from "next-intl/server"
import Image from "next/image"
import * as styles from "./page.module.css"
interface BlogPageParams {
params: Promise<{
slug: string
locale: string
}>
}
interface ImageValue {
asset?: {
_id: string
url: string
}
alt?: string
caption?: string
}
interface LinkValue {
href: string
}
export async function generateMetadata({
params,
}: BlogPageParams): Promise<Metadata> {
const { slug } = await params
const data = await getBlogPost(slug)
return generateSeoMetadata({
title: data.title,
description: data.excerpt,
imageURL: data.mainImage?.asset?.url,
author: data.author?.name,
})
}
export function generateViewport(): Viewport {
return generateSeoViewport()
}
const components: PortableTextComponents = {
types: {
image: ({ value }: { value: ImageValue }) => {
if (!value?.asset) return null
return (
<div className={styles.imageContainer}>
<Image
src={urlFor(value).url()}
alt={value.alt || "Blog image"}
width={800}
height={400}
style={{ width: "100%", height: "auto" }}
/>
{value.caption && (
<p className={styles.imageCaption}>{value.caption}</p>
)}
</div>
)
},
},
marks: {
link: ({
children,
value,
}: {
children?: React.ReactNode
value?: LinkValue
}) => (
<a
href={value?.href ?? "#"}
target="_blank"
rel="noopener noreferrer"
className={styles.link}
>
{children}
</a>
),
},
}
const BlogPost = async ({ params }: BlogPageParams) => {
const { slug }: { slug: string } = await params
const locale = await getLocale()
const data = await getBlogPost(slug)
if (!data) return notFound()
const createdAt = new Date(data._createdAt).toLocaleDateString(locale, {
day: "numeric",
month: "long",
year: "numeric",
})
return (
<Layout>
<div className={styles.container}>
<div className={styles.backButton}>
<Back />
</div>
<article className={styles.article}>
<header className={styles.header}>
{data.mainImage && (
<Image
src={urlFor(data.mainImage).url()}
alt={data.title}
width={800}
height={400}
className={styles.headerImage}
/>
)}
<h1 className={styles.title}>{data.title}</h1>
<p className={styles.excerpt}>{data.excerpt}</p>
<div className={styles.author}>
{data.author?.image && (
<Image
className="circular"
alt={data.author.name}
src={urlFor(data.author.image).width(50).height(50).url()}
width={50}
height={50}
/>
)}
<div className={styles.authorNameSection}>
<p>{data.author?.name}</p>
<p>{createdAt}</p>
</div>
</div>
</header>
<div className={styles.content}>
<PortableText value={data.body} components={components} />
</div>
</article>
</div>
<Footer />
</Layout>
)
}
export default BlogPost