NGU-Web/src/lib/timelineRefs.ts

75 lines
3.1 KiB
TypeScript

/* ═══════════════════════════════════════════════════════════════
TIMELINE REFS
Turns a timeline item into a destination and an image. Both were
answered locally here before, which is how the timeline came to
link events at /event/:id while the router only knew about
/retreats/:id — two files holding the same opinion, one of them
wrong, neither aware of the other.
Now this file knows about timeline items and nothing else. Where
a record lives is hrefs.ts; where an image lives is media.ts.
── The shape this reads, from history.js ──
item.href explicit link_url override, may be off-site
item.ref { kind, id, orgKind? } — orgKind only on
organizations, because only they have it
item.logo { file, kind } — v_timeline COALESCEs the
filename across five tables, so the kind is
what says which directory it came from
item.team { id, name, orgId? } on a team ref
item.people[] { id, name, photo?, title? }
Every one of those is optional. An entry is a standalone
milestone until proven otherwise, and the two accessors below
return null rather than assuming a shape that isn't there —
which is the other half of the /organizations/undefined bug:
reaching into a ref that wasn't sent yields undefined, and
undefined interpolates into a path perfectly happily.
═══════════════════════════════════════════════════════════════ */
import { refHref } from './hrefs.ts'
import { logoForKind, personPhoto } from './media.ts'
import type { TimelineItem } from './timeline.ts'
/**
* Where this entry points, or null if nowhere.
*
* An explicit link_url wins: it's the editor deliberately
* overriding the record's own page, usually to send someone to an
* external write-up. TimelineEntry checks for a scheme and renders
* an <a> instead of a <Link>, so this returns it unchanged.
*
* Otherwise the reference decides, and refHref returns null for a
* kind with no page yet ('person', until /people/:id exists) as
* well as for a ref that didn't resolve. Null means the entry
* renders as a plain <div> — right for a milestone, and right for
* a reference that's missing an id, which used to render as a link
* to a 404.
*/
export function hrefFor(item: TimelineItem): string | null {
if (item.href) return item.href
const ref = item.ref
if (!ref) return null
return refHref(ref.kind, ref.id, ref.orgKind)
}
/**
* The entry's image, resolved against the directory for whatever
* kind of record the filename came from.
*/
export function logoSrc(item: TimelineItem): string | null {
if (!item.logo) return null
return logoForKind(item.logo.kind, item.logo.file)
}
/**
* A roster member's photo. Same rule as everywhere else — the API
* sends a bare filename.
*/
export function photoSrc(photo?: string | null): string | null {
return personPhoto(photo)
}