import type { ServiceAlert } from './ridebus'; export function isAlertActive(alert: ServiceAlert, now: number): boolean { return ( Date.parse(alert.startTime) <= now && (alert.endTime === null || now < Date.parse(alert.endTime)) ); } export function getActiveAlerts( alerts: ServiceAlert[], cityId: number, now: number ): ServiceAlert[] { const seen = new Set(); return alerts.filter((alert) => { if ( alert.cityId !== cityId || seen.has(alert.id) || !isAlertActive(alert, now) ) return false; seen.add(alert.id); return true; }); } export function formatAlertRouteNumbers(alert: ServiceAlert): string { return alert.routeNumbers.map((number) => `№${number}`).join(' · '); } // Recheck the current data at each start/end boundary, independently of polling. // Long timers are capped because browsers store their delay as a signed int32. export function watchAlertValidity( alerts: ServiceAlert[], onChange: () => void ) { let timer: ReturnType; function schedule() { const now = Date.now(); const next = alerts .flatMap((alert) => [alert.startTime, alert.endTime]) .filter((time): time is string => time !== null) .map(Date.parse) .filter((time) => time > now) .reduce((earliest, time) => Math.min(earliest, time), Infinity); if (Number.isFinite(next)) timer = setTimeout(refresh, Math.min(next - now, 2147483647)); } function refresh() { onChange(); schedule(); } // Also cover a boundary crossed between render and effect subscription. timer = setTimeout(refresh, 0); return () => clearTimeout(timer); }