2025-04-17 02:31:34 -05:00
import React , {
createContext ,
useState ,
useContext ,
useMemo ,
useCallback ,
ReactNode ,
useEffect ,
} from 'react' ;
2025-04-15 22:14:43 -05:00
import { logger } from 'matrix-js-sdk/lib/logger' ;
2025-04-29 15:28:13 -05:00
import { WidgetApiToWidgetAction , WidgetApiAction , ClientWidgetApi } from 'matrix-widget-api' ;
2025-05-01 16:24:36 -05:00
import { useParams } from 'react-router-dom' ;
import { useMatrixClient } from '../../hooks/useMatrixClient' ;
2025-04-17 02:31:34 -05:00
interface MediaStatePayload {
2025-04-29 15:28:13 -05:00
data ? : {
audio_enabled? : boolean ;
video_enabled? : boolean ;
} ;
2025-04-17 02:31:34 -05:00
}
const WIDGET_MEDIA_STATE_UPDATE_ACTION = 'io.element.device_mute' ;
2025-04-29 15:28:13 -05:00
const WIDGET_HANGUP_ACTION = 'im.vector.hangup' ;
2025-05-01 16:24:36 -05:00
const WIDGET_ON_SCREEN_ACTION = 'set_always_on_screen' ;
const WIDGET_JOIN_ACTION = 'io.element.join' ;
2025-04-15 22:14:43 -05:00
interface CallContextState {
activeCallRoomId : string | null ;
setActiveCallRoomId : ( roomId : string | null ) = > void ;
2025-05-10 20:41:57 -05:00
viewedCallRoomId : string | null ;
setViewedCallRoomId : ( roomId : string | null ) = > void ;
2025-04-15 22:14:43 -05:00
hangUp : ( ) = > void ;
2025-04-29 15:28:13 -05:00
activeClientWidgetApi : ClientWidgetApi | null ;
registerActiveClientWidgetApi : (
roomId : string | null ,
clientWidgetApi : ClientWidgetApi | null
) = > void ;
2025-05-10 20:41:57 -05:00
registerViewedClientWidgetApi : (
roomId : string | null ,
clientWidgetApi : ClientWidgetApi | null
) = > void ;
2025-04-15 22:14:43 -05:00
sendWidgetAction : < T = unknown > (
2025-04-16 19:49:11 -05:00
action : WidgetApiToWidgetAction | string ,
2025-04-15 22:14:43 -05:00
data : T
2025-04-16 19:49:11 -05:00
) = > Promise < void > ;
2025-04-17 02:31:34 -05:00
isAudioEnabled : boolean ;
isVideoEnabled : boolean ;
2025-04-18 03:01:44 -05:00
isChatOpen : boolean ;
2025-05-01 16:24:36 -05:00
isCallActive : boolean ;
2025-05-03 00:06:36 -05:00
isPrimaryIframe : boolean ;
2025-04-17 02:31:34 -05:00
toggleAudio : ( ) = > Promise < void > ;
toggleVideo : ( ) = > Promise < void > ;
2025-04-18 03:01:44 -05:00
toggleChat : ( ) = > Promise < void > ;
2025-05-03 00:06:36 -05:00
toggleIframe : ( ) = > Promise < void > ;
2025-04-15 22:14:43 -05:00
}
const CallContext = createContext < CallContextState | undefined > ( undefined ) ;
interface CallProviderProps {
children : ReactNode ;
}
2025-04-22 22:34:25 -04:00
const DEFAULT_AUDIO_ENABLED = true ;
2025-04-17 02:31:34 -05:00
const DEFAULT_VIDEO_ENABLED = false ;
2025-04-18 03:01:44 -05:00
const DEFAULT_CHAT_OPENED = false ;
2025-05-01 16:24:36 -05:00
const DEFAULT_CALL_ACTIVE = false ;
2025-05-10 20:41:57 -05:00
const DEFAULT_PRIMARY_IFRAME = true ;
2025-04-17 02:31:34 -05:00
2025-04-15 22:14:43 -05:00
export function CallProvider ( { children } : CallProviderProps ) {
const [ activeCallRoomId , setActiveCallRoomIdState ] = useState < string | null > ( null ) ;
2025-05-10 20:41:57 -05:00
const [ viewedCallRoomId , setViewedCallRoomIdState ] = useState < string | null > ( null ) ;
2025-04-29 15:28:13 -05:00
const [ activeClientWidgetApi , setActiveClientWidgetApiState ] = useState < ClientWidgetApi | null > (
null
) ;
2025-05-10 20:41:57 -05:00
const [ activeClientWidgetApiRoomId , setActiveClientWidgetApiRoomId ] = useState < string | null > (
null
) ;
const [ viewedClientWidgetApi , setViewedClientWidgetApiState ] = useState < ClientWidgetApi | null > (
null
) ;
const [ viewedClientWidgetApiRoomId , setViewedClientWidgetApiRoomId ] = useState < string | null > (
null
) ;
2025-04-15 22:14:43 -05:00
2025-04-17 02:31:34 -05:00
const [ isAudioEnabled , setIsAudioEnabledState ] = useState < boolean > ( DEFAULT_AUDIO_ENABLED ) ;
const [ isVideoEnabled , setIsVideoEnabledState ] = useState < boolean > ( DEFAULT_VIDEO_ENABLED ) ;
2025-04-18 03:01:44 -05:00
const [ isChatOpen , setIsChatOpenState ] = useState < boolean > ( DEFAULT_CHAT_OPENED ) ;
2025-05-01 16:24:36 -05:00
const [ isCallActive , setIsCallActive ] = useState < boolean > ( DEFAULT_CALL_ACTIVE ) ;
2025-05-03 00:06:36 -05:00
const [ isPrimaryIframe , setIsPrimaryIframe ] = useState < boolean > ( DEFAULT_PRIMARY_IFRAME ) ;
2025-05-01 16:24:36 -05:00
const { roomIdOrAlias : viewedRoomId } = useParams < { roomIdOrAlias : string } > ( ) ;
const mx = useMatrixClient ( ) ;
const room = mx . getRoom ( viewedRoomId ) ;
2025-04-17 02:31:34 -05:00
const resetMediaState = useCallback ( ( ) = > {
logger . debug ( 'CallContext: Resetting media state to defaults.' ) ;
setIsAudioEnabledState ( DEFAULT_AUDIO_ENABLED ) ;
setIsVideoEnabledState ( DEFAULT_VIDEO_ENABLED ) ;
} , [ ] ) ;
2025-04-15 22:14:43 -05:00
const setActiveCallRoomId = useCallback (
( roomId : string | null ) = > {
2025-04-17 02:31:34 -05:00
logger . warn ( ` CallContext: Setting activeCallRoomId to ${ roomId } ` ) ;
const previousRoomId = activeCallRoomId ;
2025-04-15 22:14:43 -05:00
setActiveCallRoomIdState ( roomId ) ;
2025-04-17 02:31:34 -05:00
if ( roomId !== previousRoomId ) {
logger . debug ( ` CallContext: Active call room changed, resetting media state. ` ) ;
resetMediaState ( ) ;
}
2025-05-10 20:41:57 -05:00
if ( roomId === null || roomId !== activeClientWidgetApiRoomId ) {
2025-04-17 02:31:34 -05:00
logger . warn (
2025-04-29 15:28:13 -05:00
` CallContext: Clearing active clientWidgetApi because active room changed to ${ roomId } or was cleared. `
2025-04-15 22:14:43 -05:00
) ;
}
} ,
2025-05-10 20:41:57 -05:00
[ activeClientWidgetApiRoomId , resetMediaState , activeCallRoomId ]
) ;
const setViewedCallRoomId = useCallback (
( roomId : string | null ) = > {
logger . warn ( ` CallContext: Setting activeCallRoomId to ${ roomId } ` ) ;
setViewedCallRoomIdState ( roomId ) ;
} ,
[ setViewedCallRoomIdState ]
2025-04-16 19:49:11 -05:00
) ;
2025-04-15 22:14:43 -05:00
const hangUp = useCallback ( ( ) = > {
logger . debug ( ` CallContext: Hang up called. ` ) ;
2025-05-02 17:06:54 -05:00
activeClientWidgetApi ? . transport . send ( ` ${ WIDGET_HANGUP_ACTION } ` , { } ) ;
2025-05-11 19:01:47 -05:00
setActiveCallRoomIdState ( null ) ;
setIsCallActive ( false ) ;
2025-05-11 17:55:04 -05:00
} , [ activeClientWidgetApi ? . transport ] ) ;
2025-04-17 02:31:34 -05:00
2025-04-29 15:28:13 -05:00
const setActiveClientWidgetApi = useCallback (
( clientWidgetApi : ClientWidgetApi | null , roomId : string | null ) = > {
setActiveClientWidgetApiState ( clientWidgetApi ) ;
2025-05-10 20:41:57 -05:00
setActiveClientWidgetApiRoomId ( roomId ) ;
2025-04-29 15:28:13 -05:00
} ,
[ ]
) ;
2025-04-15 22:14:43 -05:00
2025-04-29 15:28:13 -05:00
const registerActiveClientWidgetApi = useCallback (
( roomId : string | null , clientWidgetApi : ClientWidgetApi | null ) = > {
if ( activeClientWidgetApi && activeClientWidgetApi !== clientWidgetApi ) {
logger . debug ( ` CallContext: Cleaning up listeners for previous clientWidgetApi instance. ` ) ;
2025-04-17 02:31:34 -05:00
}
2025-04-29 15:28:13 -05:00
if ( roomId && clientWidgetApi ) {
logger . debug ( ` CallContext: Registering active clientWidgetApi for room ${ roomId } . ` ) ;
setActiveClientWidgetApi ( clientWidgetApi , roomId ) ;
2025-05-10 20:41:57 -05:00
} else if ( roomId === activeClientWidgetApiRoomId || roomId === null ) {
2025-04-29 15:28:13 -05:00
logger . debug (
2025-05-10 20:41:57 -05:00
` CallContext: Clearing active clientWidgetApi for room ${ activeClientWidgetApiRoomId } . `
2025-04-29 15:28:13 -05:00
) ;
setActiveClientWidgetApi ( null , null ) ;
2025-04-17 02:31:34 -05:00
resetMediaState ( ) ;
2025-04-15 22:14:43 -05:00
} else {
2025-04-16 19:49:11 -05:00
logger . debug (
2025-05-10 20:41:57 -05:00
` CallContext: Ignoring clientWidgetApi registration/clear request for room ${ roomId } , as current clientWidgetApi belongs to ${ activeClientWidgetApiRoomId } . `
) ;
}
} ,
[ activeClientWidgetApi , activeClientWidgetApiRoomId , setActiveClientWidgetApi , resetMediaState ]
) ;
const setViewedClientWidgetApi = useCallback (
( clientWidgetApi : ClientWidgetApi | null , roomId : string | null ) = > {
setViewedClientWidgetApiState ( clientWidgetApi ) ;
2025-05-11 19:01:47 -05:00
setViewedClientWidgetApiRoomId ( roomId ) ;
2025-05-10 20:41:57 -05:00
} ,
[ ]
) ;
const registerViewedClientWidgetApi = useCallback (
( roomId : string | null , clientWidgetApi : ClientWidgetApi | null ) = > {
if ( viewedClientWidgetApi && viewedClientWidgetApi !== clientWidgetApi ) {
logger . error ( ` CallContext: Cleaning up listeners for previous clientWidgetApi instance. ` ) ;
}
if ( roomId && clientWidgetApi ) {
2025-05-11 17:55:04 -05:00
logger . error ( ` CallContext: Registering viewed clientWidgetApi for room ${ roomId } . ` ) ;
2025-05-10 20:41:57 -05:00
setViewedClientWidgetApi ( clientWidgetApi , roomId ) ;
} else if ( roomId === viewedClientWidgetApiRoomId || roomId === null ) {
logger . error (
2025-05-11 17:55:04 -05:00
` CallContext: Clearing viewed clientWidgetApi for room ${ viewedClientWidgetApiRoomId } . `
2025-05-10 20:41:57 -05:00
) ;
setViewedClientWidgetApi ( null , null ) ;
//resetMediaState();
} else {
logger . debug (
` CallContext: Ignoring clientWidgetApi registration/clear request for room ${ roomId } , as current clientWidgetApi belongs to ${ viewedClientWidgetApiRoomId } . `
2025-04-16 19:49:11 -05:00
) ;
2025-04-15 22:14:43 -05:00
}
} ,
2025-05-10 20:41:57 -05:00
[ viewedClientWidgetApi , viewedClientWidgetApiRoomId , setViewedClientWidgetApi ]
2025-04-15 22:14:43 -05:00
) ;
2025-04-17 02:31:34 -05:00
useEffect ( ( ) = > {
2025-05-11 17:55:04 -05:00
if ( ! activeCallRoomId || ! viewedCallRoomId ) {
2025-04-17 02:31:34 -05:00
return ;
}
const handleHangup = ( ev : CustomEvent ) = > {
2025-04-29 15:28:13 -05:00
ev . preventDefault ( ) ;
2025-05-10 20:41:57 -05:00
activeClientWidgetApi ? . transport . reply ( ev . detail , { } ) ;
viewedClientWidgetApi ? . transport . reply ( ev . detail , { } ) ;
2025-04-17 02:31:34 -05:00
logger . warn (
` CallContext: Received hangup action from widget in room ${ activeCallRoomId } . ` ,
ev
) ;
2025-05-11 19:01:47 -05:00
setIsCallActive ( false ) ;
2025-04-17 02:31:34 -05:00
} ;
const handleMediaStateUpdate = ( ev : CustomEvent < MediaStatePayload > ) = > {
ev . preventDefault ( ) ;
logger . debug (
` CallContext: Received media state update from widget in room ${ activeCallRoomId } : ` ,
ev . detail
) ;
2025-04-29 15:28:13 -05:00
const { audio_enabled , video_enabled } = ev . detail . data ;
2025-04-22 00:27:31 -04:00
if ( typeof audio_enabled === 'boolean' && audio_enabled !== isAudioEnabled ) {
logger . debug ( ` CallContext: Updating audio enabled state from widget: ${ audio_enabled } ` ) ;
setIsAudioEnabledState ( audio_enabled ) ;
2025-04-17 02:31:34 -05:00
}
2025-04-22 00:27:31 -04:00
if ( typeof video_enabled === 'boolean' && video_enabled !== isVideoEnabled ) {
logger . debug ( ` CallContext: Updating video enabled state from widget: ${ video_enabled } ` ) ;
setIsVideoEnabledState ( video_enabled ) ;
2025-04-17 02:31:34 -05:00
}
} ;
2025-05-01 16:24:36 -05:00
const handleOnScreenStateUpdate = ( ev : CustomEvent ) = > {
ev . preventDefault ( ) ;
2025-05-10 20:41:57 -05:00
if ( isPrimaryIframe ) {
activeClientWidgetApi ? . transport . reply ( ev . detail , { } ) ;
} else {
viewedClientWidgetApi ? . transport . reply ( ev . detail , { } ) ;
}
2025-05-01 16:24:36 -05:00
} ;
const handleJoin = ( ev : CustomEvent ) = > {
ev . preventDefault ( ) ;
2025-05-11 17:55:04 -05:00
logger . error ( isCallActive . toString ( ) ) ;
logger . error ( activeClientWidgetApi ) ;
logger . error ( viewedClientWidgetApi ) ;
activeClientWidgetApi ? . transport . reply ( ev . detail , { } ) ;
if ( isCallActive && activeClientWidgetApi && viewedClientWidgetApi ) {
activeClientWidgetApi ? . transport . send ( WIDGET_HANGUP_ACTION , { } ) . then ( ( ) = > { } ) ;
setActiveCallRoomIdState ( viewedCallRoomId ) ;
setActiveClientWidgetApi ( viewedClientWidgetApi , viewedCallRoomId ) ;
setIsPrimaryIframe ( ! isPrimaryIframe ) ;
2025-05-03 00:06:36 -05:00
}
2025-05-11 17:55:04 -05:00
setIsCallActive ( true ) ;
2025-05-01 16:24:36 -05:00
} ;
2025-04-29 15:28:13 -05:00
logger . debug (
` CallContext: Setting up listeners for clientWidgetApi in room ${ activeCallRoomId } `
) ;
2025-05-10 20:41:57 -05:00
activeClientWidgetApi ? . on ( ` action: ${ WIDGET_HANGUP_ACTION } ` , handleHangup ) ;
activeClientWidgetApi ? . on ( ` action: ${ WIDGET_MEDIA_STATE_UPDATE_ACTION } ` , handleMediaStateUpdate ) ;
activeClientWidgetApi ? . on ( ` action: ${ WIDGET_ON_SCREEN_ACTION } ` , handleOnScreenStateUpdate ) ;
activeClientWidgetApi ? . on ( ` action: ${ WIDGET_JOIN_ACTION } ` , handleJoin ) ;
viewedClientWidgetApi ? . on ( ` action: ${ WIDGET_JOIN_ACTION } ` , handleJoin ) ;
viewedClientWidgetApi ? . on ( ` action: ${ WIDGET_MEDIA_STATE_UPDATE_ACTION } ` , handleMediaStateUpdate ) ;
viewedClientWidgetApi ? . on ( ` action: ${ WIDGET_ON_SCREEN_ACTION } ` , handleOnScreenStateUpdate ) ;
viewedClientWidgetApi ? . on ( ` action: ${ WIDGET_HANGUP_ACTION } ` , handleHangup ) ;
2025-04-17 02:31:34 -05:00
} , [
2025-04-29 15:28:13 -05:00
activeClientWidgetApi ,
2025-04-17 02:31:34 -05:00
activeCallRoomId ,
2025-05-10 20:41:57 -05:00
activeClientWidgetApiRoomId ,
2025-04-17 02:31:34 -05:00
hangUp ,
2025-04-18 03:01:44 -05:00
isChatOpen ,
2025-04-17 02:31:34 -05:00
isAudioEnabled ,
isVideoEnabled ,
2025-05-01 16:24:36 -05:00
isCallActive ,
2025-05-10 20:41:57 -05:00
viewedRoomId ,
viewedClientWidgetApi ,
isPrimaryIframe ,
viewedCallRoomId ,
setViewedClientWidgetApi ,
setActiveClientWidgetApi ,
2025-04-17 02:31:34 -05:00
] ) ;
2025-04-15 22:14:43 -05:00
const sendWidgetAction = useCallback (
2025-04-16 19:49:11 -05:00
async < T = unknown , > ( action : WidgetApiToWidgetAction | string , data : T ) : Promise < void > = > {
2025-04-29 15:28:13 -05:00
if ( ! activeClientWidgetApi ) {
2025-04-15 22:14:43 -05:00
logger . warn (
2025-04-29 15:28:13 -05:00
` CallContext: Cannot send action ' ${ action } ', no active API clientWidgetApi registered. `
2025-04-15 22:14:43 -05:00
) ;
2025-04-29 15:28:13 -05:00
return Promise . reject ( new Error ( 'No active call clientWidgetApi' ) ) ;
2025-04-15 22:14:43 -05:00
}
2025-05-10 20:41:57 -05:00
if ( ! activeClientWidgetApiRoomId || activeClientWidgetApiRoomId !== activeCallRoomId ) {
2025-04-29 15:28:13 -05:00
logger . debug (
2025-05-10 20:41:57 -05:00
` CallContext: Cannot send action ' ${ action } ', clientWidgetApi room ( ${ activeClientWidgetApiRoomId } ) does not match active call room ( ${ activeCallRoomId } ). Stale clientWidgetApi? `
2025-04-15 22:14:43 -05:00
) ;
2025-04-29 15:28:13 -05:00
return Promise . reject ( new Error ( 'Mismatched active call clientWidgetApi' ) ) ;
2025-04-15 22:14:43 -05:00
}
try {
logger . debug (
2025-05-10 20:41:57 -05:00
` CallContext: Sending action ' ${ action } ' via active clientWidgetApi (room: ${ activeClientWidgetApiRoomId } ) with data: ` ,
2025-04-15 22:14:43 -05:00
data
) ;
2025-04-29 15:28:13 -05:00
await activeClientWidgetApi . transport . send ( action as WidgetApiAction , data ) ;
2025-04-15 22:14:43 -05:00
} catch ( error ) {
logger . error ( ` CallContext: Error sending action ' ${ action } ': ` , error ) ;
2025-04-17 02:31:34 -05:00
throw error ;
2025-04-15 22:14:43 -05:00
}
} ,
2025-05-10 20:41:57 -05:00
[ activeClientWidgetApi , activeCallRoomId , activeClientWidgetApiRoomId ]
2025-04-15 22:14:43 -05:00
) ;
2025-04-17 02:31:34 -05:00
const toggleAudio = useCallback ( async ( ) = > {
const newState = ! isAudioEnabled ;
logger . debug ( ` CallContext: Toggling audio. New state: enabled= ${ newState } ` ) ;
setIsAudioEnabledState ( newState ) ;
try {
2025-05-01 16:24:36 -05:00
await sendWidgetAction ( WIDGET_MEDIA_STATE_UPDATE_ACTION , {
2025-04-22 00:27:31 -04:00
audio_enabled : newState ,
video_enabled : isVideoEnabled ,
2025-04-17 02:31:34 -05:00
} ) ;
logger . debug ( ` CallContext: Successfully sent audio toggle action. ` ) ;
} catch ( error ) {
logger . error ( ` CallContext: Failed to send audio toggle action. Reverting state. ` , error ) ;
setIsAudioEnabledState ( ! newState ) ;
throw error ;
}
} , [ isAudioEnabled , isVideoEnabled , sendWidgetAction ] ) ;
const toggleVideo = useCallback ( async ( ) = > {
const newState = ! isVideoEnabled ;
logger . debug ( ` CallContext: Toggling video. New state: enabled= ${ newState } ` ) ;
setIsVideoEnabledState ( newState ) ;
try {
2025-05-01 16:24:36 -05:00
await sendWidgetAction ( WIDGET_MEDIA_STATE_UPDATE_ACTION , {
2025-04-22 00:27:31 -04:00
audio_enabled : isAudioEnabled ,
video_enabled : newState ,
2025-04-17 02:31:34 -05:00
} ) ;
logger . debug ( ` CallContext: Successfully sent video toggle action. ` ) ;
} catch ( error ) {
logger . error ( ` CallContext: Failed to send video toggle action. Reverting state. ` , error ) ;
setIsVideoEnabledState ( ! newState ) ;
throw error ;
}
} , [ isVideoEnabled , isAudioEnabled , sendWidgetAction ] ) ;
2025-04-18 03:01:44 -05:00
const toggleChat = useCallback ( async ( ) = > {
const newState = ! isChatOpen ;
2025-04-22 22:29:07 -04:00
setIsChatOpenState ( newState ) ;
2025-04-18 03:01:44 -05:00
} , [ isChatOpen ] ) ;
2025-05-03 00:06:36 -05:00
const toggleIframe = useCallback ( async ( ) = > {
const newState = ! isPrimaryIframe ;
setIsPrimaryIframe ( newState ) ;
} , [ isPrimaryIframe ] ) ;
2025-04-15 22:14:43 -05:00
const contextValue = useMemo < CallContextState > (
( ) = > ( {
activeCallRoomId ,
setActiveCallRoomId ,
2025-05-10 20:41:57 -05:00
viewedCallRoomId ,
setViewedCallRoomId ,
2025-04-15 22:14:43 -05:00
hangUp ,
2025-04-29 15:28:13 -05:00
activeClientWidgetApi ,
registerActiveClientWidgetApi ,
2025-05-10 20:41:57 -05:00
registerViewedClientWidgetApi ,
2025-04-15 22:14:43 -05:00
sendWidgetAction ,
2025-04-18 03:01:44 -05:00
isChatOpen ,
2025-04-17 02:31:34 -05:00
isAudioEnabled ,
isVideoEnabled ,
2025-05-02 02:35:51 -05:00
isCallActive ,
2025-05-03 00:06:36 -05:00
isPrimaryIframe ,
2025-04-17 02:31:34 -05:00
toggleAudio ,
toggleVideo ,
2025-04-18 03:01:44 -05:00
toggleChat ,
2025-05-03 00:06:36 -05:00
toggleIframe ,
2025-04-15 22:14:43 -05:00
} ) ,
[
activeCallRoomId ,
setActiveCallRoomId ,
2025-05-10 20:41:57 -05:00
viewedCallRoomId ,
setViewedCallRoomId ,
2025-04-15 22:14:43 -05:00
hangUp ,
2025-04-29 15:28:13 -05:00
activeClientWidgetApi ,
registerActiveClientWidgetApi ,
2025-05-10 20:41:57 -05:00
registerViewedClientWidgetApi ,
2025-04-15 22:14:43 -05:00
sendWidgetAction ,
2025-04-22 00:27:31 -04:00
isChatOpen ,
2025-04-17 02:31:34 -05:00
isAudioEnabled ,
isVideoEnabled ,
2025-05-02 02:35:51 -05:00
isCallActive ,
2025-05-03 00:06:36 -05:00
isPrimaryIframe ,
2025-04-17 02:31:34 -05:00
toggleAudio ,
toggleVideo ,
2025-04-22 00:27:31 -04:00
toggleChat ,
2025-05-03 00:06:36 -05:00
toggleIframe ,
2025-04-15 22:14:43 -05:00
]
) ;
return < CallContext.Provider value = { contextValue } > { children } < / CallContext.Provider > ;
}
export function useCallState ( ) : CallContextState {
const context = useContext ( CallContext ) ;
if ( context === undefined ) {
throw new Error ( 'useCallState must be used within a CallProvider' ) ;
}
return context ;
}