The post page links previous_in_category and next_in_category (the Journal's opt-in scored related mode) as server-rendered links with the title in the anchor text when the Journal answers one, else the site-wide previous_post and next_post (postNeighbours). Types, a fixture post and expectation lines for both cases.
48 lines
2.1 KiB
TypeScript
48 lines
2.1 KiB
TypeScript
// The post page's SSR payload: the post with its body sanitized (h1 demoted
|
|
// to h2 while the Journal H1 setting is on), without the raw `content`
|
|
// source or `translations`, and with related posts as list rows. Kept apart from view.ts because it loads the sanitizer, which list
|
|
// pages and cards do not need.
|
|
|
|
import { sanitizeJournalHtml } from './htmlGate.ts'
|
|
import type { JournalPostDetail } from './types.ts'
|
|
import { toListPost } from './view.ts'
|
|
|
|
/**
|
|
* True while the post's body h1 headings become h2 (the Journal API's
|
|
* content_h1_to_h2; absent on an older Journal, which counts as on).
|
|
*/
|
|
export function demotesH1(post: { content_h1_to_h2?: boolean | null } | null | undefined): boolean {
|
|
return post?.content_h1_to_h2 !== false
|
|
}
|
|
|
|
/**
|
|
* The previous and next links of a post page: the neighbours within the
|
|
* post's first category when the Journal answers them (its opt-in scored
|
|
* mode) and at least one exists, else the site-wide previous_post and
|
|
* next_post.
|
|
*/
|
|
export function postNeighbours(post: {
|
|
previous_post?: { slug: string; title: string } | null
|
|
next_post?: { slug: string; title: string } | null
|
|
previous_in_category?: { slug: string; title: string } | null
|
|
next_in_category?: { slug: string; title: string } | null
|
|
}): { previous: { slug: string; title: string } | null; next: { slug: string; title: string } | null; inCategory: boolean } {
|
|
if (post.previous_in_category || post.next_in_category) {
|
|
return { previous: post.previous_in_category ?? null, next: post.next_in_category ?? null, inCategory: true }
|
|
}
|
|
return { previous: post.previous_post ?? null, next: post.next_post ?? null, inCategory: false }
|
|
}
|
|
|
|
/** A post page's data: the post with its body sanitized and no source or translations. */
|
|
export type JournalPostView = JournalPostDetail
|
|
|
|
export function toPostView(post: JournalPostDetail): JournalPostView {
|
|
return {
|
|
...post,
|
|
content: null,
|
|
translations: null,
|
|
content_html: sanitizeJournalHtml(post.content_html, { demoteH1: demotesH1(post) }),
|
|
related_posts: (post.related_posts ?? []).map(toListPost),
|
|
}
|
|
}
|