The H:MM:SS clock-time formatter is copy-pasted in at least five places in apps/web, with slightly diverging details (one copy lacks the isFinite guard):
components/player/player-bar.tsx — formatTime()
components/player/chapter-drawer.tsx — formatDuration() (identical copy)
app/(authenticated)/audiobooks/[id]/page.tsx — formatChapterTime() (no isFinite guard)
components/users/session-log.tsx — formatPosition() + formatSessionDuration()
The shared lib/format-duration.ts only covers humanized durations ("5h 30m"), not clock-style timestamps.
The bookmarks feature (#64) introduces a shared apps/web/lib/format-timestamp.ts with formatTimestamp(seconds) (and parseTimestamp() for input). Once that lands:
Task: migrate the five local copies to the shared formatTimestamp() util and delete the local helpers. Behavior should stay identical (H:MM:SS above one hour, M:SS below, "0:00" fallback for non-finite input).
Acceptance criteria:
The H:MM:SS clock-time formatter is copy-pasted in at least five places in
apps/web, with slightly diverging details (one copy lacks theisFiniteguard):components/player/player-bar.tsx—formatTime()components/player/chapter-drawer.tsx—formatDuration()(identical copy)app/(authenticated)/audiobooks/[id]/page.tsx—formatChapterTime()(noisFiniteguard)components/users/session-log.tsx—formatPosition()+formatSessionDuration()The shared
lib/format-duration.tsonly covers humanized durations ("5h 30m"), not clock-style timestamps.The bookmarks feature (#64) introduces a shared
apps/web/lib/format-timestamp.tswithformatTimestamp(seconds)(andparseTimestamp()for input). Once that lands:Task: migrate the five local copies to the shared
formatTimestamp()util and delete the local helpers. Behavior should stay identical (H:MM:SS above one hour, M:SS below, "0:00" fallback for non-finite input).Acceptance criteria:
lib/format-timestamp.tsformat-timestamptests cover the fallback cases the local copies handled