75 lines
3.1 KiB
TypeScript
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)
|
|
}
|