Live video on the AT Protocol
79
fork

Configure Feed

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

at eli/problem-detection 181 lines 4.8 kB view raw
1import React, { createContext, useContext, useMemo } from "react"; 2import { StoreApi, useStore } from "zustand"; 3import { usePlayerContext } from "../player-store"; 4import { PlayerProtocol, PlayerState } from "./player-state"; 5 6// Context for a single player 7interface SinglePlayerContextType { 8 playerId: string; 9 playerStore: StoreApi<PlayerState>; 10} 11 12const SinglePlayerContext = createContext<SinglePlayerContextType | null>(null); 13 14interface SinglePlayerProviderProps { 15 children: React.ReactNode; 16 playerId?: string; 17 protocol?: PlayerProtocol; 18 rendition?: string; 19} 20 21/** 22 * Provider component for a single player that creates a scoped context 23 * This allows components to access a specific player's state without passing IDs around 24 */ 25export const SinglePlayerProvider: React.FC<SinglePlayerProviderProps> = ({ 26 children, 27 playerId: providedPlayerId, 28 protocol = PlayerProtocol.WEBRTC, 29 rendition = "auto", 30}) => { 31 const { players, createPlayer } = usePlayerContext(); 32 33 // Create or get a player ID 34 const playerId = useMemo(() => { 35 // If a player ID is provided and exists, use it 36 if (providedPlayerId && players[providedPlayerId]) { 37 return providedPlayerId; 38 } 39 40 // If a player ID is provided but doesn't exist, create it 41 if (providedPlayerId) { 42 return createPlayer(providedPlayerId); 43 } 44 45 // Otherwise create a new player 46 return createPlayer(); 47 }, [providedPlayerId, players, createPlayer]); 48 49 // Get the player store 50 const playerStore = useMemo(() => { 51 return players[playerId]; 52 }, [players, playerId]); 53 54 // Set initial protocol and rendition if provided 55 React.useEffect(() => { 56 if (protocol) { 57 playerStore.setState((state) => ({ 58 ...state, 59 protocol, 60 })); 61 } 62 63 if (rendition) { 64 playerStore.setState((state) => ({ 65 ...state, 66 selectedRendition: rendition, 67 })); 68 } 69 }, [playerStore, protocol, rendition]); 70 71 // Create context value 72 const contextValue = useMemo( 73 () => ({ 74 playerId, 75 playerStore, 76 }), 77 [playerId, playerStore], 78 ); 79 80 return ( 81 <SinglePlayerContext.Provider value={contextValue}> 82 {children} 83 </SinglePlayerContext.Provider> 84 ); 85}; 86 87/** 88 * Hook to access the current single player context 89 */ 90export function useSinglePlayerContext() { 91 const context = useContext(SinglePlayerContext); 92 if (!context) { 93 throw new Error( 94 "useSinglePlayerContext must be used within a SinglePlayerProvider", 95 ); 96 } 97 return context; 98} 99 100/** 101 * Hook to access the current player ID from the single player context 102 */ 103export function useCurrentPlayerId(): string { 104 const { playerId } = useSinglePlayerContext(); 105 return playerId; 106} 107 108/** 109 * Hook to access state from the current player without needing to specify the ID 110 */ 111export function useCurrentPlayerStore<U>( 112 selector: (state: PlayerState) => U, 113): U { 114 const { playerStore } = useSinglePlayerContext(); 115 return useStore(playerStore, selector); 116} 117 118/** 119 * Hook to get the protocol of the current player 120 */ 121export function useCurrentPlayerProtocol(): [ 122 PlayerProtocol, 123 (protocol: PlayerProtocol) => void, 124] { 125 return useCurrentPlayerStore( 126 (state) => [state.protocol, state.setProtocol] as const, 127 ); 128} 129 130/** 131 * Hook to get the selected rendition of the current player 132 */ 133export function useCurrentPlayerRendition(): [ 134 string, 135 (rendition: string) => void, 136] { 137 return useCurrentPlayerStore( 138 (state) => [state.selectedRendition, state.setSelectedRendition] as const, 139 ); 140} 141 142/** 143 * Hook to get the ingest state of the current player 144 */ 145export function useCurrentPlayerIngest(): { 146 starting: boolean; 147 setStarting: (starting: boolean) => void; 148 connectionState: RTCPeerConnectionState | null; 149 setConnectionState: (state: RTCPeerConnectionState | null) => void; 150 startedTimestamp: number | null; 151 setStartedTimestamp: (timestamp: number | null) => void; 152} { 153 return useCurrentPlayerStore((state) => ({ 154 starting: state.ingestStarting, 155 setStarting: state.setIngestStarting, 156 connectionState: state.ingestConnectionState, 157 setConnectionState: state.setIngestConnectionState, 158 startedTimestamp: state.ingestStarted, 159 setStartedTimestamp: state.setIngestStarted, 160 })); 161} 162 163/** 164 * HOC to wrap components with a SinglePlayerProvider 165 */ 166export function withSinglePlayer<P extends object>( 167 Component: React.ComponentType<P>, 168): React.FC<P & SinglePlayerProviderProps> { 169 return function WithSinglePlayer(props: P & SinglePlayerProviderProps) { 170 const { playerId, protocol, rendition, ...componentProps } = props; 171 return ( 172 <SinglePlayerProvider 173 playerId={playerId} 174 protocol={protocol} 175 rendition={rendition} 176 > 177 <Component {...(componentProps as P)} /> 178 </SinglePlayerProvider> 179 ); 180 }; 181}