import { useCallback, useEffect, useRef, useState } from "react"; import { getControllerMovement, subscribeControllerMovement, subscribeControllerToken, } from "../input/controller"; import { CompanionPanel } from "../ui/CompanionPanel"; import { createDualScreenChannel, currentCompanionSnapshot, type CompanionCommand, type CompanionSnapshot, type DualScreenMessage, } from "./dualScreenSync"; import { normalizeInputPreferences } from "../input/keybinds"; import { publishInputPreferences } from "../input/keybindRuntime"; import { useForcedThorDisplays } from "./useThorDualScreen"; import { RemoteDungeonHud } from "./RemoteDungeonHud"; export function BottomDisplayApp() { useForcedThorDisplays(); const channelRef = useRef(null); const [snapshot, setSnapshot] = useState(() => ({ ...currentCompanionSnapshot(), notice: "Linking the upper display...", })); const send = useCallback((command: CompanionCommand) => { channelRef.current?.postMessage({ type: "command", command } satisfies DualScreenMessage); }, []); useEffect(() => { const channel = createDualScreenChannel(); if (!channel) return; channelRef.current = channel; const role = new URLSearchParams(window.location.search).get("role"); const forwardsController = role === "activity"; const ready = () => channel.postMessage({ type: "companion-ready" } satisfies DualScreenMessage); const closing = () => channel.postMessage({ type: "companion-closing" } satisfies DualScreenMessage); const unsubscribeToken = forwardsController ? subscribeControllerToken((event) => channel.postMessage({ type: "controller-token", event } satisfies DualScreenMessage)) : () => undefined; const unsubscribeMovement = forwardsController ? subscribeControllerMovement((movement) => channel.postMessage({ type: "controller-motion", movement: { ...movement }, } satisfies DualScreenMessage)) : () => undefined; channel.onmessage = (event: MessageEvent) => { if (event.data.type === "app-state") { setSnapshot(event.data.snapshot); if (event.data.snapshot.keybinds) publishInputPreferences(normalizeInputPreferences(event.data.snapshot.keybinds)); } else if (event.data.type === "authoritative-ready") ready(); }; window.addEventListener("pageshow", ready); window.addEventListener("pagehide", closing); ready(); if (forwardsController) { channel.postMessage({ type: "controller-motion", movement: { ...getControllerMovement() }, } satisfies DualScreenMessage); } return () => { window.removeEventListener("pageshow", ready); window.removeEventListener("pagehide", closing); unsubscribeToken(); unsubscribeMovement(); if (forwardsController) { channel.postMessage({ type: "controller-motion", movement: { moveX: 0, moveY: 0, lookX: 0, lookY: 0 }, } satisfies DualScreenMessage); } closing(); channelRef.current = null; channel.close(); }; }, []); return
{snapshot.phase === "game" ? : }
; }