import { useEffect } from "react"; import { selectedCharacter, useShellStore } from "./shellStore"; import { useOnlineGroupStore } from "./onlineGroupStore"; import { classCanFillPartyRole, defaultPartyRoleForClass, type PartyRole, } from "../game/partyRoles"; import { isManastormPartySize } from "../game/manastormProgress"; const GROUP_POLL_MS = 2_500; const ACTIVITY_JOIN_WINDOW_MS = 10 * 60 * 1000; export function OnlineGroupCoordinator() { const session = useShellStore((state) => state.session); const character = useShellStore(selectedCharacter); const phase = useShellStore((state) => state.phase); const snapshot = useOnlineGroupStore((state) => state.snapshot); const consumedActivityIds = useOnlineGroupStore((state) => state.consumedActivityIds); const refresh = useOnlineGroupStore((state) => state.refresh); const updateMember = useOnlineGroupStore((state) => state.updateMember); const consumeActivity = useOnlineGroupStore((state) => state.consumeActivity); const reset = useOnlineGroupStore((state) => state.reset); useEffect(() => { if (!session || session.kind !== "account" || !session.accessToken) { reset(); return undefined; } void refresh(session); const timer = window.setInterval(() => void refresh(session, true), GROUP_POLL_MS); const onVisibility = () => { if (document.visibilityState === "visible") void refresh(session, true); }; document.addEventListener("visibilitychange", onVisibility); return () => { window.clearInterval(timer); document.removeEventListener("visibilitychange", onVisibility); }; }, [refresh, reset, session?.accessToken, session?.ownerId]); useEffect(() => { const group = snapshot.group; if (!session || !character || !group || group.activity) return; const member = group.members.find((candidate) => candidate.accountId === session.ownerId); if (!member) return; const role: PartyRole = member.role && classCanFillPartyRole(character.classId, member.role) ? member.role : defaultPartyRoleForClass(character.classId); const characterChanged = member.character?.id !== character.id || member.character?.level !== character.level || member.character?.classId !== character.classId; if (!characterChanged && member.role === role) return; void updateMember(session, character, role); }, [character?.classId, character?.id, character?.level, session?.ownerId, snapshot.group?.id, snapshot.group?.updatedAt, updateMember]); useEffect(() => { const activity = snapshot.group?.activity; if (!session || !activity || phase === "game" || consumedActivityIds.includes(activity.id)) return; if (Date.now() - activity.startedAt > ACTIVITY_JOIN_WINDOW_MS) return; const member = snapshot.group?.members.find((candidate) => candidate.accountId === session.ownerId); if (!member?.character || !member.role) return; consumeActivity(activity.id); void (async () => { if (activity.type === "dungeon") { const { installDungeonShellRuntime } = await import("./dungeonShellRuntime"); installDungeonShellRuntime(); useShellStore.getState().enterDungeon(activity.contentId, member.role ?? undefined); return; } const { installManastormShellRuntime } = await import("./manastormShellRuntime"); installManastormShellRuntime(); const partySize = isManastormPartySize(activity.partySize) ? activity.partySize : 5; useShellStore.getState().enterManastorm( member.role ?? undefined, partySize, activity.startingLevel ?? undefined, ); })().catch((error) => { useShellStore.getState().setNotice(error instanceof Error ? error.message : "The group activity could not be opened."); }); }, [consumeActivity, consumedActivityIds, phase, session?.ownerId, snapshot.group?.activity?.id]); return null; }