Files
controller-v2/client/src/lib/festival.ts
T
2026-09-29 17:01:16 +08:00

63 lines
2.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ============================================================
Festival skin gating — date-driven, URL-overridable.
Only Christmas is implemented today; add more festivals by
extending FESTIVAL_WINDOWS plus the matching components under
components/festival/.
============================================================ */
import { useMemo } from "react";
export type FestivalId = "christmas" | "halloween";
/** Inclusive [month, day] windows. Christmas: Dec 20 – Dec 26; Halloween: Oct 26 – Nov 1. */
const FESTIVAL_WINDOWS: Record<
FestivalId,
{ from: [number, number]; to: [number, number] }
> = {
christmas: { from: [12, 20], to: [12, 26] },
halloween: { from: [10, 26], to: [11, 1] },
};
function inWindow(
month: number,
day: number,
w: { from: [number, number]; to: [number, number] },
) {
const cur = month * 100 + day;
const from = w.from[0] * 100 + w.from[1];
const to = w.to[0] * 100 + w.to[1];
// Support windows that wrap the new year (from > to).
return from <= to ? cur >= from && cur <= to : cur >= from || cur <= to;
}
/**
* Resolve the active festival skin.
* `?festival=christmas` forces it on and `?festival=off` forces it off (QA hook,
* so the skin can be verified without waiting for the real date window).
* Otherwise falls back to the system-date window.
*/
export function getFestival(
now: Date = new Date(),
search: string = typeof location !== "undefined" ? location.search : "",
): FestivalId | null {
const override = new URLSearchParams(search).get("festival");
if (override === "off" || override === "none") return null;
if (override && Object.prototype.hasOwnProperty.call(FESTIVAL_WINDOWS, override)) {
return override as FestivalId;
}
for (const id of Object.keys(FESTIVAL_WINDOWS) as FestivalId[]) {
if (inWindow(now.getMonth() + 1, now.getDate(), FESTIVAL_WINDOWS[id])) return id;
}
return null;
}
/** Stable per-mount festival resolution (the date won't matter mid-session). */
export function useFestival(): FestivalId | null {
return useMemo(() => getFestival(), []);
}
/** 首页 VU 区的节日问候语,键与 FestivalId 对应。 */
export const FESTIVAL_GREETINGS: Record<FestivalId, string> = {
christmas: "Merry Christmas",
halloween: "Happy Halloween",
};