mirror of
https://github.com/jakejarvis/sofa.git
synced 2026-08-29 03:55:38 -04:00
- useTimeAgo: replace per-instance intervals with shared useSyncExternalStore ticker - useTiltEffect: gate to fine-pointer devices, skip motion graphs on touch - Carousel: bake WheelGesturesPlugin into primitive, remove from all consumers - TitleSeasons: use memoized Set + precomputed progress map instead of Array.includes - useSearch: stabilize results identity with useMemo - CommandPalette: hoist shortcut grouping to module scope, stabilize effect deps - StarRating: hoist static spring transition to module constant - Settings toggles: use useOptimistic + useTransition for auto-rollback Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
46 lines
1.2 KiB
TypeScript
46 lines
1.2 KiB
TypeScript
import { formatDistanceToNowStrict } from "date-fns";
|
|
import { useSyncExternalStore } from "react";
|
|
|
|
// Shared ticker — one interval regardless of how many components subscribe
|
|
const TICK_MS = 30_000;
|
|
let tick = Date.now();
|
|
const listeners = new Set<() => void>();
|
|
let timerId: ReturnType<typeof setInterval> | null = null;
|
|
|
|
function subscribe(cb: () => void) {
|
|
if (listeners.size === 0) {
|
|
timerId = setInterval(() => {
|
|
tick = Date.now();
|
|
for (const l of listeners) l();
|
|
}, TICK_MS);
|
|
}
|
|
listeners.add(cb);
|
|
return () => {
|
|
listeners.delete(cb);
|
|
if (listeners.size === 0 && timerId) {
|
|
clearInterval(timerId);
|
|
timerId = null;
|
|
}
|
|
};
|
|
}
|
|
|
|
function getSnapshot() {
|
|
return tick;
|
|
}
|
|
|
|
function toTimestamp(date: string | Date | null | undefined): number | null {
|
|
if (!date) return null;
|
|
const t = date instanceof Date ? date.getTime() : new Date(date).getTime();
|
|
return Number.isFinite(t) ? t : null;
|
|
}
|
|
|
|
export function useTimeAgo(
|
|
date: string | Date | null | undefined,
|
|
{ addSuffix = true, fallback = "" } = {},
|
|
): string {
|
|
useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
const ts = toTimestamp(date);
|
|
if (ts === null) return fallback;
|
|
return formatDistanceToNowStrict(ts, { addSuffix });
|
|
}
|