Live video on the AT Protocol
1import React, { useCallback, useMemo, useState } from "react";
2import { StoreApi } from "zustand";
3import { PlayerContext } from "./context";
4import { PlayerState } from "./player-state";
5import { makePlayerStore } from "./player-store";
6
7interface PlayerProviderProps {
8 children: React.ReactNode;
9 initialPlayers?: string[];
10 defaultId?: string;
11}
12
13export const PlayerProvider: React.FC<PlayerProviderProps> = ({
14 children,
15 initialPlayers = [],
16 defaultId = Math.random().toString(36).slice(8),
17}) => {
18 const [players, setPlayers] = useState<Record<string, StoreApi<PlayerState>>>(
19 () => {
20 // Initialize with any initial player IDs provided
21 const initialPlayerStores: Record<string, StoreApi<PlayerState>> = {};
22 for (const playerId of initialPlayers) {
23 initialPlayerStores[playerId] = makePlayerStore(playerId);
24 }
25
26 // Always create at least one player by default
27 if (initialPlayers.length === 0) {
28 initialPlayerStores[defaultId] = makePlayerStore(defaultId);
29 }
30
31 return initialPlayerStores;
32 },
33 );
34
35 const createPlayer = useCallback((id?: string) => {
36 console.log("Creating new player");
37 const playerId = id || Math.random().toString(36).slice(8);
38 const playerStore = makePlayerStore(playerId);
39
40 setPlayers((prev) => ({
41 ...prev,
42 [playerId]: playerStore,
43 }));
44
45 return playerId;
46 }, []);
47
48 const removePlayer = useCallback((id: string) => {
49 setPlayers((prev) => {
50 // Don't remove the last player
51 if (Object.keys(prev).length <= 1) {
52 console.warn("Cannot remove the last player");
53 return prev;
54 }
55
56 const newPlayers = { ...prev };
57 delete newPlayers[id];
58 return newPlayers;
59 });
60 }, []);
61
62 const contextValue = useMemo(
63 () => ({
64 players,
65 createPlayer,
66 removePlayer,
67 }),
68 [players, createPlayer, removePlayer],
69 );
70
71 return (
72 <PlayerContext.Provider value={contextValue}>
73 {children}
74 </PlayerContext.Provider>
75 );
76};
77
78// HOC to wrap components that need player context
79export function withPlayerProvider<P extends object>(
80 Component: React.ComponentType<P>,
81): React.FC<P & { initialPlayers?: string[] }> {
82 return function WithPlayerProvider(props: P & { initialPlayers?: string[] }) {
83 const { initialPlayers, ...componentProps } = props;
84 return (
85 <PlayerProvider initialPlayers={initialPlayers}>
86 <Component {...(componentProps as P)} />
87 </PlayerProvider>
88 );
89 };
90}