56 lines
1.9 KiB
TypeScript
56 lines
1.9 KiB
TypeScript
import type { PlayerState } from "@deadhead/schema";
|
|
import { useQuery } from "@tanstack/react-query";
|
|
import { useEffect, useRef } from "react";
|
|
import { ApiError, type ApiClient } from "../api/client";
|
|
import { useApi } from "../api/context";
|
|
import { useClock } from "../clock/clock";
|
|
|
|
export const PLAYER_STATE_KEY = ["player-state"] as const;
|
|
|
|
/** Player state, or null when there is no company on this device yet ("unauthorized"). */
|
|
export async function fetchPlayerState(api: ApiClient): Promise<PlayerState | null> {
|
|
try {
|
|
return await api.call("getPlayerState", {});
|
|
} catch (error) {
|
|
if (error instanceof ApiError && error.code === "unauthorized") return null;
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Server player state (Step 8.5), shared by every screen. Refetched when the app
|
|
* regains focus; `App` also runs `useRefetchOnArrival` once for arrivals.
|
|
*/
|
|
export function usePlayerState() {
|
|
const api = useApi();
|
|
const query = useQuery({
|
|
queryKey: PLAYER_STATE_KEY,
|
|
queryFn: () => fetchPlayerState(api),
|
|
// Screens share this query; fresh for 10 s so mounting a screen does not refetch.
|
|
// Returning to the app, arrivals and actions always refetch.
|
|
staleTime: 10_000,
|
|
refetchOnWindowFocus: "always",
|
|
});
|
|
return query;
|
|
}
|
|
|
|
/**
|
|
* Refetches once the server clock passes the earliest in-flight arrival, so the
|
|
* server resolves it and the UI shows the result. Each dispatch triggers at most
|
|
* one refetch.
|
|
*/
|
|
export function useRefetchOnArrival(state: PlayerState | null, refetch: () => unknown) {
|
|
const now = useClock((s) => s.now);
|
|
const triggered = useRef(new Set<string>());
|
|
|
|
useEffect(() => {
|
|
if (!state) return;
|
|
const arrived = state.activeDispatches.filter(
|
|
(d) => d.arrivalAt <= now && !triggered.current.has(d.id),
|
|
);
|
|
if (arrived.length === 0) return;
|
|
for (const d of arrived) triggered.current.add(d.id);
|
|
void refetch();
|
|
}, [state, now, refetch]);
|
|
}
|