Files
immich-folio/lib/journalNav.ts

58 lines
1.9 KiB
TypeScript
Raw Normal View History

/**
* Sequential journal navigation — the "previous / next entry" pair at the foot
* of a journal entry, mirroring `albumNav.ts` (#483) for the journal (#591).
*
* `siblings` must already be filtered to what the current visitor may see —
* a draft, or a password-protected entry they have not unlocked, must never
* appear here even as a name-only link (GHSA-fvgv-97g3-wjr7 was exactly this
* class of leak for the index and page metadata).
*
* Neighbours are not wrapped around, for the same reason as albums: reaching
* the end of the journal should feel like an ending, not send the reader back
* to its start with no sign that they have been round.
*/
export interface JournalNavCandidate {
slug: string;
title: string;
}
export interface JournalNavLink {
href: string;
title: string;
}
export interface JournalNavPair {
prev?: JournalNavLink;
next?: JournalNavLink;
}
/**
* The neighbours of `currentSlug` within `siblings`, in that list's own order
* (publication order, newest first, same as `/journal` and `listJournalEntries()`).
*
* Returns an empty pair when the entry is not in the list, or when it is the
* only one — both cases mean there is nothing to offer, and the caller renders
* nothing.
*/
export function journalNeighbours(
siblings: readonly JournalNavCandidate[],
currentSlug: string,
): JournalNavPair {
const index = siblings.findIndex((e) => e.slug === currentSlug);
if (index === -1 || siblings.length < 2) return {};
const link = (entry: JournalNavCandidate): JournalNavLink => ({
href: `/journal/${entry.slug}`,
title: entry.title,
});
const previous = index > 0 ? siblings[index - 1] : undefined;
const following = index < siblings.length - 1 ? siblings[index + 1] : undefined;
return {
...(previous ? { prev: link(previous) } : {}),
...(following ? { next: link(following) } : {}),
};
}