1
0
Fork 0
polaris-task-force/src/components/frontend/realtime/GameTickRealtime.tsx
2026-09-11 03:25:43 -04:00

100 lines
3 KiB
TypeScript

"use client";
import { useEffect } from "react";
import { useRouter } from "next/navigation";
import {
GAME_EVENT_EVENT,
GAME_TICK_EVENT,
NOTIFICATION_EVENT,
POKER_STATE_EVENT,
POKER_TABLES_EVENT,
POKER_TERMINATED_EVENT,
PROFILE_UPDATE_EVENT,
type GameTickDetail,
type PokerStateDetail,
type PokerTablesDetail,
type PokerTerminatedDetail,
type ProfileUpdateDetail,
} from "@/hooks/useGameTick";
/**
* Opens a Server-Sent Events connection to the game tick stream.
* When a game tick completes, refreshes server-rendered data via
* router.refresh() and dispatches a window event that client-side
* components (e.g. ShipmentToasts, EventLedger) listen to for refetch.
*/
export function GameTickRealtime() {
const router = useRouter();
useEffect(() => {
const source = new EventSource("/api/realtime");
let refreshTimer: ReturnType<typeof setTimeout> | null = null;
const refresh = (delay = 250) => {
if (refreshTimer) return;
refreshTimer = setTimeout(() => {
refreshTimer = null;
router.refresh();
}, delay);
};
source.addEventListener("game-tick", (event) => {
let detail: GameTickDetail = {};
try {
detail = JSON.parse(event.data ?? "{}");
} catch {
// ignore malformed payload
}
refresh();
window.dispatchEvent(new CustomEvent(GAME_TICK_EVENT, { detail }));
});
source.addEventListener("profile-update", (event) => {
let detail: ProfileUpdateDetail = {};
try {
detail = JSON.parse(event.data ?? "{}");
} catch {
// ignore malformed payload
}
refresh(detail.xpGained ? 1700 : 250);
window.dispatchEvent(new CustomEvent(PROFILE_UPDATE_EVENT, { detail }));
});
source.addEventListener("game-event", () => {
refresh();
window.dispatchEvent(new CustomEvent(GAME_EVENT_EVENT));
});
source.addEventListener("notification", (event) => {
window.dispatchEvent(new CustomEvent(NOTIFICATION_EVENT, { detail: event.data }));
});
source.addEventListener("poker-state", (event) => {
let detail: PokerStateDetail = {};
try {
detail = JSON.parse(event.data ?? "{}");
} catch {
// ignore malformed payload
}
window.dispatchEvent(new CustomEvent(POKER_STATE_EVENT, { detail }));
});
source.addEventListener("poker-tables", (event) => {
let detail: PokerTablesDetail = {};
try {
detail = JSON.parse(event.data ?? "{}");
} catch {
// ignore malformed payload
}
window.dispatchEvent(new CustomEvent(POKER_TABLES_EVENT, { detail }));
});
source.addEventListener("poker-terminated", (event) => {
let detail: PokerTerminatedDetail = {};
try {
detail = JSON.parse(event.data ?? "{}");
} catch {
// ignore malformed payload
}
window.dispatchEvent(new CustomEvent(POKER_TERMINATED_EVENT, { detail }));
});
return () => {
if (refreshTimer) clearTimeout(refreshTimer);
source.close();
};
}, [router]);
return null;
}