Live video on the AT Protocol
79
fork

Configure Feed

Select the types of activity you want to include in your feed.

at natb/command-errors 102 lines 2.8 kB view raw
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 13function randomUUID(): string { 14 let dt = new Date().getTime(); 15 var uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace( 16 /[xy]/g, 17 function (c) { 18 var r = (dt + Math.random() * 16) % 16 | 0; 19 dt = Math.floor(dt / 16); 20 return (c == "x" ? r : (r & 0x3) | 0x8).toString(16); 21 }, 22 ); 23 return uuid; 24} 25 26export const PlayerProvider: React.FC<PlayerProviderProps> = ({ 27 children, 28 initialPlayers = [], 29 defaultId = Math.random().toString(36).slice(8), 30}) => { 31 const [players, setPlayers] = useState<Record<string, StoreApi<PlayerState>>>( 32 () => { 33 // Initialize with any initial player IDs provided 34 const initialPlayerStores: Record<string, StoreApi<PlayerState>> = {}; 35 for (const playerId of initialPlayers) { 36 initialPlayerStores[playerId] = makePlayerStore(playerId); 37 } 38 39 // Always create at least one player by default 40 if (initialPlayers.length === 0) { 41 initialPlayerStores[defaultId] = makePlayerStore(defaultId); 42 } 43 44 return initialPlayerStores; 45 }, 46 ); 47 48 const createPlayer = useCallback((id?: string) => { 49 const playerId = id || randomUUID(); 50 const playerStore = makePlayerStore(playerId); 51 52 setPlayers((prev) => ({ 53 ...prev, 54 [playerId]: playerStore, 55 })); 56 57 return playerId; 58 }, []); 59 60 const removePlayer = useCallback((id: string) => { 61 setPlayers((prev) => { 62 // Don't remove the last player 63 if (Object.keys(prev).length <= 1) { 64 console.warn("Cannot remove the last player"); 65 return prev; 66 } 67 68 const newPlayers = { ...prev }; 69 delete newPlayers[id]; 70 return newPlayers; 71 }); 72 }, []); 73 74 const contextValue = useMemo( 75 () => ({ 76 players, 77 createPlayer, 78 removePlayer, 79 }), 80 [players, createPlayer, removePlayer], 81 ); 82 83 return ( 84 <PlayerContext.Provider value={contextValue}> 85 {children} 86 </PlayerContext.Provider> 87 ); 88}; 89 90// HOC to wrap components that need player context 91export function withPlayerProvider<P extends object>( 92 Component: React.ComponentType<P>, 93): React.FC<P & { initialPlayers?: string[] }> { 94 return function WithPlayerProvider(props: P & { initialPlayers?: string[] }) { 95 const { initialPlayers, ...componentProps } = props; 96 return ( 97 <PlayerProvider initialPlayers={initialPlayers}> 98 <Component {...(componentProps as P)} /> 99 </PlayerProvider> 100 ); 101 }; 102}