Compare commits
2 Commits
7ee28c7ca3
...
d7f046a4d6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7f046a4d6 | ||
|
|
7c00ae5000 |
@@ -11,6 +11,34 @@ import { PARKS, groupByRegion } from "@/lib/parks";
|
||||
import type { DayData } from "@/lib/db";
|
||||
|
||||
const REFRESH_INTERVAL_MS = 2 * 60 * 1000; // 2 minutes
|
||||
const OPEN_REFRESH_BUFFER_MS = 30_000; // 30s after opening time before hitting the API
|
||||
|
||||
/** Parse the opening hour/minute from a hoursLabel like "10am", "10:30am", "11am". */
|
||||
function parseOpenTime(hoursLabel: string): { hour: number; minute: number } | null {
|
||||
const openPart = hoursLabel.split(" - ")[0].trim();
|
||||
const match = openPart.match(/^(\d+)(?::(\d+))?(am|pm)$/i);
|
||||
if (!match) return null;
|
||||
let hour = parseInt(match[1], 10);
|
||||
const minute = match[2] ? parseInt(match[2], 10) : 0;
|
||||
const period = match[3].toLowerCase();
|
||||
if (period === "pm" && hour !== 12) hour += 12;
|
||||
if (period === "am" && hour === 12) hour = 0;
|
||||
return { hour, minute };
|
||||
}
|
||||
|
||||
/** Milliseconds from now until a given local clock time in a timezone. Negative if already past. */
|
||||
function msUntilLocalTime(hour: number, minute: number, timezone: string): number {
|
||||
const now = new Date();
|
||||
const parts = new Intl.DateTimeFormat("en-US", {
|
||||
timeZone: timezone,
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
hour12: false,
|
||||
}).formatToParts(now);
|
||||
const localHour = parseInt(parts.find(p => p.type === "hour")!.value, 10) % 24;
|
||||
const localMinute = parseInt(parts.find(p => p.type === "minute")!.value, 10);
|
||||
return ((hour * 60 + minute) - (localHour * 60 + localMinute)) * 60_000;
|
||||
}
|
||||
|
||||
const COASTER_MODE_KEY = "coasterMode";
|
||||
|
||||
@@ -54,6 +82,29 @@ export function HomePageClient({
|
||||
return () => clearInterval(id);
|
||||
}, [isCurrentWeek, router]);
|
||||
|
||||
// Schedule a targeted refresh at each park's exact opening time so the
|
||||
// open indicator and ride counts appear immediately rather than waiting
|
||||
// up to 2 minutes for the next polling cycle.
|
||||
useEffect(() => {
|
||||
if (!isCurrentWeek) return;
|
||||
const timeouts: ReturnType<typeof setTimeout>[] = [];
|
||||
|
||||
for (const park of PARKS) {
|
||||
const dayData = data[park.id]?.[today];
|
||||
if (!dayData?.isOpen || !dayData.hoursLabel) continue;
|
||||
const openTime = parseOpenTime(dayData.hoursLabel);
|
||||
if (!openTime) continue;
|
||||
const ms = msUntilLocalTime(openTime.hour, openTime.minute, park.timezone);
|
||||
// Only schedule if opening is still in the future (within the next 24h)
|
||||
if (ms > 0 && ms < 24 * 60 * 60 * 1000) {
|
||||
timeouts.push(setTimeout(() => router.refresh(), ms)); // mark as open
|
||||
timeouts.push(setTimeout(() => router.refresh(), ms + OPEN_REFRESH_BUFFER_MS)); // pick up ride counts
|
||||
}
|
||||
}
|
||||
|
||||
return () => timeouts.forEach(clearTimeout);
|
||||
}, [isCurrentWeek, today, data, router]);
|
||||
|
||||
// Remember the current week so the park page back button returns here.
|
||||
useEffect(() => {
|
||||
localStorage.setItem("lastWeek", weekStart);
|
||||
|
||||
Reference in New Issue
Block a user