SurfSense/surfsense_web/lib/announcements/announcements-utils.ts

81 lines
2.3 KiB
TypeScript
Raw Normal View History

import type { Announcement } from "@/contracts/types/announcement.types";
/**
* Returns true when the current time falls within the announcement's
* [startTime, endTime] window. Returns false for invalid windows
* (endTime before startTime) or when now is outside the range.
*/
export function isAnnouncementActive(announcement: Announcement, now = new Date()): boolean {
const start = new Date(announcement.startTime).getTime();
const end = new Date(announcement.endTime).getTime();
if (Number.isNaN(start) || Number.isNaN(end) || end < start) return false;
const nowMs = now.getTime();
return nowMs >= start && nowMs <= end;
}
/**
* Returns true when the announcement's audience matches the viewer context.
* - `"all"` visible to everyone
* - `"users"` visible only to authenticated users
* - `"web_visitors"` visible only to unauthenticated visitors
*/
export function announcementMatchesAudience(
announcement: Announcement,
2026-02-20 22:44:56 -08:00
isAuthenticated: boolean
): boolean {
switch (announcement.audience) {
case "all":
return true;
case "users":
return isAuthenticated;
case "web_visitors":
return !isAuthenticated;
default:
return false;
}
}
/**
* Filter announcements to only those that are currently active and
* targeted at the given audience.
*/
export function getActiveAnnouncements(
announcements: Announcement[],
isAuthenticated: boolean,
2026-02-20 22:44:56 -08:00
now = new Date()
): Announcement[] {
return announcements.filter(
2026-02-20 22:44:56 -08:00
(a) => isAnnouncementActive(a, now) && announcementMatchesAudience(a, isAuthenticated)
);
}
/**
* Returns the number of milliseconds until the next announcement either
* starts or expires. Returns `null` when there are no upcoming transitions.
* Useful for scheduling re-renders so the UI updates automatically.
*/
export function msUntilNextTransition(
announcements: Announcement[],
2026-02-20 22:44:56 -08:00
now = new Date()
): number | null {
const nowMs = now.getTime();
let nearest: number | null = null;
for (const a of announcements) {
const start = new Date(a.startTime).getTime();
const end = new Date(a.endTime).getTime();
if (Number.isNaN(start) || Number.isNaN(end) || end < start) continue;
for (const edge of [start, end]) {
if (edge > nowMs) {
const diff = edge - nowMs;
if (nearest === null || diff < nearest) nearest = diff;
}
}
}
return nearest;
}