Files
vue-journal-layer/app/utils/view.ts
Jakub Zych c408eeb2ca feat: Journal list, category pages, pagination, tokens and pl/en strings
- index and category pages paginate with ?page=N and 404 out of range
- category content renders above the list through the html gate
- related posts fall back to the latest posts
- --journal-* tokens and neutral base styles
- pl/en strings registered only for locales the app enables
2026-10-09 18:02:48 +02:00

46 lines
1.7 KiB
TypeScript

// View shaping for SSR payloads. Pages run API rows through these in
// useAsyncData's transform, so the serialized payload carries only what the
// page renders: no raw `content` source, no `translations`, and never an HTML
// body that failed the Journal gate.
import { gateJournalHtml } from './htmlGate.ts'
import type { JournalPost, JournalPostDetail } from './types.ts'
/** A post row as a list or card renders it: without body, source or translations. */
export function toListPost(post: JournalPost): JournalPost {
return { ...post, content: null, content_html: null, translations: null }
}
/** A post page's data: the gated body, with `content_blocked` set when the gate refused it. */
export interface JournalPostView extends JournalPostDetail {
content_blocked: boolean
}
/**
* Formats an API timestamp as a long date for `language` (a BCP 47 tag such
* as "pl-PL"). UTC on both server and browser, so hydration matches. Returns
* "" for a missing or invalid value.
*/
export function formatJournalDate(iso: string | null | undefined, language: string): string {
if (!iso) return ''
const d = new Date(iso)
if (Number.isNaN(d.getTime())) return ''
try {
return new Intl.DateTimeFormat(language || 'en', { dateStyle: 'long', timeZone: 'UTC' }).format(d)
} catch {
return new Intl.DateTimeFormat('en', { dateStyle: 'long', timeZone: 'UTC' }).format(d)
}
}
export function toPostView(post: JournalPostDetail): JournalPostView {
const gated = gateJournalHtml(post.content_html)
return {
...post,
content: null,
translations: null,
content_html: gated.html,
content_blocked: !gated.safe,
related_posts: (post.related_posts ?? []).map(toListPost),
}
}