Files
ridebus-website/lib/serviceAlerts.ts
Zakhar Timoshenko 142ec97959 feat: add Open Graph image generation and SEO metadata
- Introduced `PageSeo` component for managing page metadata for social sharing.
- Created Open Graph image templates using SVG and static fonts for better visual representation.
- Implemented service alert management with functions to check active alerts and format route numbers.
- Added utility functions for generating and watching alert validity.
- Developed a script to generate Open Graph images dynamically based on page content.
- Included tests for service alert functionalities to ensure correctness and reliability.
2026-10-05 13:02:06 +03:00

58 lines
1.6 KiB
TypeScript

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<number>();
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<typeof setTimeout>;
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);
}