86 lines
3.3 KiB
TypeScript
86 lines
3.3 KiB
TypeScript
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<BroadcastChannel | null>(null);
|
|
const [snapshot, setSnapshot] = useState<CompanionSnapshot>(() => ({
|
|
...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<DualScreenMessage>) => {
|
|
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 <main className="bottom-display-root">{snapshot.phase === "game"
|
|
? <RemoteDungeonHud snapshot={snapshot} send={send} />
|
|
: <CompanionPanel snapshot={snapshot} send={send} preferControllerBinds />}</main>;
|
|
}
|