import { createContext, createElement, type ReactNode, useCallback, useContext, useEffect, useMemo, useState, } from "react"; import type { PartySocketEvent, PartyState, } from "../../../api/src/party-types"; import { usePartySocket } from "./use-party-socket"; import { useUser } from "./user"; const emptyPartyState: PartyState = { party: null, members: [], }; type PartyContextValue = PartyState & { connectionState: "disconnected" | "connecting" | "connected" | "reconnecting"; isConnected: boolean; isConnecting: boolean; isReconnecting: boolean; setPartyState: (state: PartyState) => void; resetParty: () => void; }; const PartyContext = createContext(null); function reducePartyState( state: PartyState, event: PartySocketEvent, userId: string, ): PartyState { switch (event.type) { case "party_status": { if (!event.party) return emptyPartyState; const isMember = event.members.some((member) => member.userId === userId); if (!isMember) return emptyPartyState; return { party: event.party, members: event.members }; } case "member_payload": case "pong": case "error": return state; } } function getApiUrl(): string | null { if (typeof window === "undefined") return null; const envUrl = import.meta.env.VITE_BETTER_AUTH_URL; if (envUrl) return envUrl; return `${window.location.protocol}//${window.location.host}`; } export function PartyProvider({ children }: { children: ReactNode }) { const { user } = useUser(); const userId = user?.id ?? null; const [state, setState] = useState(emptyPartyState); const handleMessage = useCallback( (event: PartySocketEvent) => { if (!userId) return; setState((prev: PartyState) => reducePartyState(prev, event, userId)); }, [userId], ); const apiUrl = useMemo(() => { const url = getApiUrl(); if (!url) return null; return url; }, []); const resetParty = useCallback(() => { setState(emptyPartyState); }, []); useEffect(() => { if (!userId) resetParty(); }, [resetParty, userId]); const wsState = usePartySocket({ apiUrl: userId ? apiUrl : null, onMessage: userId ? handleMessage : null, }); const value = useMemo( () => ({ ...state, ...wsState, setPartyState: setState, resetParty, }), [state, wsState, resetParty], ); return createElement(PartyContext.Provider, { value }, children); } export function useParty() { const context = useContext(PartyContext); if (!context) { throw new Error("useParty must be used within PartyProvider"); } return context; }