2022-05-04 17:09:48 +01:00
/*
2024-09-06 10:22:13 +02:00
Copyright 2022-2024 New Vector Ltd.
2022-05-04 17:09:48 +01:00
2025-02-18 17:59:58 +00:00
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
2024-09-06 10:22:13 +02:00
Please see LICENSE in the repository root for full details.
2022-05-04 17:09:48 +01:00
*/
2025-02-26 17:20:30 +07:00
import {
type FC ,
type ReactNode ,
useCallback ,
useEffect ,
useMemo ,
useState ,
} from "react" ;
2024-12-11 09:27:55 +00:00
import { type MatrixClient } from "matrix-js-sdk/src/client" ;
2024-04-23 15:15:13 +02:00
import {
isE2EESupported as isE2EESupportedBrowser ,
2025-02-26 14:55:03 +01:00
Room ,
2024-04-23 15:15:13 +02:00
} from "livekit-client" ;
2023-06-23 14:17:51 -04:00
import { logger } from "matrix-js-sdk/src/logger" ;
2024-12-11 09:27:55 +00:00
import { type MatrixRTCSession } from "matrix-js-sdk/src/matrixrtc/MatrixRTCSession" ;
2023-09-27 18:12:04 -04:00
import { JoinRule } from "matrix-js-sdk/src/matrix" ;
2025-01-06 18:00:20 +01:00
import { useNavigate } from "react-router-dom" ;
2022-08-02 00:46:16 +02:00
2022-09-09 02:10:45 -04:00
import type { IWidgetApiRequest } from "matrix-widget-api" ;
2024-12-12 07:33:47 +00:00
import {
ElementWidgetActions ,
type JoinCallData ,
type WidgetHelpers ,
} from "../widget" ;
2022-01-05 15:35:12 -08:00
import { LobbyView } from "./LobbyView" ;
2024-12-11 09:27:55 +00:00
import { type MatrixInfo } from "./VideoPreview" ;
2022-01-05 15:35:12 -08:00
import { CallEndedView } from "./CallEndedView" ;
2023-03-01 13:47:36 +01:00
import { PosthogAnalytics } from "../analytics/PosthogAnalytics" ;
2023-05-26 20:41:32 +02:00
import { useProfile } from "../profile/useProfile" ;
2024-08-27 09:45:39 -04:00
import { findDeviceByName } from "../utils/media" ;
2025-02-26 14:53:15 +01:00
import { ActiveCall } from "./InCallView" ;
2024-12-11 09:27:55 +00:00
import { MUTE_PARTICIPANT_COUNT , type MuteStates } from "./MuteStates" ;
2025-02-24 17:45:40 +07:00
import { useMediaDevices } from "../livekit/MediaDevicesContext" ;
2023-08-16 18:41:27 +01:00
import { useMatrixRTCSessionMemberships } from "../useMatrixRTCSessionMemberships" ;
import { enterRTCSession , leaveRTCSession } from "../rtcSessionHelpers" ;
2024-04-23 15:15:13 +02:00
import { useRoomEncryptionSystem } from "../e2ee/sharedKeyManagement" ;
2023-09-08 15:39:10 -04:00
import { useRoomAvatar } from "./useRoomAvatar" ;
import { useRoomName } from "./useRoomName" ;
import { useJoinRule } from "./useJoinRule" ;
2023-09-27 15:17:04 -04:00
import { InviteModal } from "./InviteModal" ;
2023-10-16 17:45:06 +01:00
import { useUrlParams } from "../UrlParams" ;
2023-10-30 17:06:59 +00:00
import { E2eeType } from "../e2ee/e2eeType" ;
2024-12-12 07:33:47 +00:00
import { useAudioContext } from "../useAudioContext" ;
import { callEventAudioSounds } from "./CallEventAudioRenderer" ;
import { useLatest } from "../useLatest" ;
2025-01-08 13:24:23 +00:00
import { usePageTitle } from "../usePageTitle" ;
2025-02-28 12:17:28 +01:00
import {
2025-03-05 20:50:19 +01:00
E2EENotSupportedError ,
2025-02-28 12:17:28 +01:00
ElementCallError ,
2025-03-05 20:50:19 +01:00
UnknownCallError ,
2025-02-28 12:17:28 +01:00
} from "../utils/errors.ts" ;
2025-03-05 20:50:19 +01:00
import { GroupCallErrorBoundary } from "./GroupCallErrorBoundary.tsx" ;
2025-03-10 15:20:51 +01:00
import { GroupCallErrorBoundaryContextProvider } from "./GroupCallErrorBoundaryContextProvider.tsx" ;
import { useGroupCallErrorBoundary } from "./useCallErrorBoundary.ts" ;
2022-09-09 02:10:45 -04:00
2022-08-02 00:46:16 +02:00
declare global {
interface Window {
2023-08-16 18:41:27 +01:00
rtcSession? : MatrixRTCSession ;
2022-08-02 00:46:16 +02:00
}
}
2022-09-09 02:04:53 -04:00
2022-08-02 00:46:16 +02:00
interface Props {
client : MatrixClient ;
isPasswordlessUser : boolean ;
2023-09-18 20:47:47 -04:00
confineToRoom : boolean ;
2022-09-09 02:10:45 -04:00
preload : boolean ;
2023-10-25 13:49:18 +02:00
skipLobby : boolean ;
2022-09-09 02:04:53 -04:00
hideHeader : boolean ;
2023-08-16 18:41:27 +01:00
rtcSession : MatrixRTCSession ;
2025-01-17 10:30:28 -05:00
isJoined : boolean ;
2024-04-23 15:15:13 +02:00
muteStates : MuteStates ;
2024-12-12 07:33:47 +00:00
widget : WidgetHelpers | null ;
2022-08-02 00:46:16 +02:00
}
2022-09-09 02:04:53 -04:00
2025-03-10 15:20:51 +01:00
export const GroupCallView : FC < Props > = ( props ) => {
return (
< GroupCallErrorBoundaryContextProvider >
< GroupCallViewInner { ...props } />
</ GroupCallErrorBoundaryContextProvider >
);
};
export const GroupCallViewInner : FC < Props > = ({
2022-01-05 15:35:12 -08:00
client ,
isPasswordlessUser ,
2023-09-18 20:47:47 -04:00
confineToRoom ,
2022-09-09 02:10:45 -04:00
preload ,
2023-10-25 13:49:18 +02:00
skipLobby ,
2022-09-09 02:04:53 -04:00
hideHeader ,
2023-08-16 18:41:27 +01:00
rtcSession ,
2025-01-17 10:30:28 -05:00
isJoined ,
2024-04-23 15:15:13 +02:00
muteStates ,
2024-12-12 07:33:47 +00:00
widget ,
2023-09-22 18:05:13 -04:00
}) => {
2023-08-16 18:41:27 +01:00
const memberships = useMatrixRTCSessionMemberships ( rtcSession );
2024-12-12 07:33:47 +00:00
const leaveSoundContext = useLatest (
useAudioContext ({
sounds : callEventAudioSounds ,
latencyHint : "interactive" ,
}),
);
2024-07-18 18:14:29 +02:00
// This should use `useEffectEvent` (only available in experimental versions)
2024-04-23 15:15:13 +02:00
useEffect (() => {
2024-07-18 18:14:29 +02:00
if ( memberships . length >= MUTE_PARTICIPANT_COUNT )
2024-04-23 15:15:13 +02:00
muteStates . audio . setEnabled ? .( false );
2024-07-18 18:14:29 +02:00
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
2024-04-23 15:15:13 +02:00
2022-01-05 15:35:12 -08:00
useEffect (() => {
2023-08-16 18:41:27 +01:00
window . rtcSession = rtcSession ;
2024-06-04 11:20:25 -04:00
return () : void => {
2023-08-16 18:41:27 +01:00
delete window . rtcSession ;
2022-09-09 02:10:45 -04:00
};
2023-08-16 18:41:27 +01:00
}, [ rtcSession ]);
2022-07-08 20:55:18 +01:00
2024-09-11 01:27:02 -04:00
useEffect (() => {
// Sanity check the room object
if ( client . getRoom ( rtcSession . room . roomId ) !== rtcSession . room )
logger . warn (
`We've ended up with multiple rooms for the same ID ( ${ rtcSession . room . roomId } ). This indicates a bug in the group call loading code, and may lead to incomplete room state.` ,
);
}, [ client , rtcSession . room ]);
2023-05-26 20:41:32 +02:00
const { displayName , avatarUrl } = useProfile ( client );
2023-09-12 11:30:46 +01:00
const roomName = useRoomName ( rtcSession . room );
const roomAvatar = useRoomAvatar ( rtcSession . room );
2024-01-26 10:03:08 +01:00
const { perParticipantE2EE , returnToLobby } = useUrlParams ();
2024-04-23 15:15:13 +02:00
const e2eeSystem = useRoomEncryptionSystem ( rtcSession . room . roomId );
2025-01-08 13:24:23 +00:00
usePageTitle ( roomName );
2023-09-08 15:39:10 -04:00
2023-07-04 18:46:27 +01:00
const matrixInfo = useMemo (() : MatrixInfo => {
return {
2023-08-31 15:46:09 +02:00
userId : client.getUserId () ! ,
2023-07-04 18:46:27 +01:00
displayName : displayName ! ,
avatarUrl : avatarUrl ! ,
2023-08-16 18:41:27 +01:00
roomId : rtcSession.room.roomId ,
2023-09-08 15:39:10 -04:00
roomName ,
2023-08-16 18:41:27 +01:00
roomAlias : rtcSession.room.getCanonicalAlias (),
2023-09-08 15:39:10 -04:00
roomAvatar ,
2024-04-23 15:15:13 +02:00
e2eeSystem ,
2023-07-04 18:46:27 +01:00
};
2023-09-08 15:39:10 -04:00
}, [
2024-04-23 15:15:13 +02:00
client ,
2023-09-08 15:39:10 -04:00
displayName ,
avatarUrl ,
2024-04-23 15:15:13 +02:00
rtcSession . room ,
2023-09-08 15:39:10 -04:00
roomName ,
roomAvatar ,
2024-04-23 15:15:13 +02:00
e2eeSystem ,
2023-09-08 15:39:10 -04:00
]);
2023-09-27 18:12:04 -04:00
// Count each member only once, regardless of how many devices they use
const participantCount = useMemo (
2023-10-09 20:49:03 +01:00
() => new Set < string >( memberships . map (( m ) => m . sender ! )). size ,
2023-10-11 10:42:04 -04:00
[ memberships ],
2023-09-27 18:12:04 -04:00
);
2023-05-26 20:41:32 +02:00
2023-08-02 15:29:37 -04:00
const deviceContext = useMediaDevices ();
2025-02-24 17:45:40 +07:00
const latestDevices = useLatest ( deviceContext );
const latestMuteStates = useLatest ( muteStates );
2023-08-02 15:29:37 -04:00
2025-03-10 15:20:51 +01:00
const { showGroupCallErrorBoundary } = useGroupCallErrorBoundary ();
const enterRTCSessionOrError = useCallback (
async (
rtcSession : MatrixRTCSession ,
perParticipantE2EE : boolean ,
) : Promise < void > => {
try {
await enterRTCSession ( rtcSession , perParticipantE2EE );
} catch ( e ) {
if ( e instanceof ElementCallError ) {
showGroupCallErrorBoundary ( e );
} else {
logger . error ( `Unknown Error while entering RTC session` , e );
const error = new UnknownCallError (
e instanceof Error ? e : new Error ( "Unknown error" , { cause : e }),
);
showGroupCallErrorBoundary ( error );
}
2025-02-26 14:55:03 +01:00
}
2025-03-10 15:20:51 +01:00
},
[ showGroupCallErrorBoundary ],
);
2025-02-26 14:55:03 +01:00
2022-09-09 02:10:45 -04:00
useEffect (() => {
2024-11-28 18:05:12 +01:00
const defaultDeviceSetup = async ({
audioInput ,
videoInput ,
} : JoinCallData ) : Promise < void > => {
2023-10-25 13:49:18 +02:00
// XXX: I think this is broken currently - LiveKit *won't* request
// permissions and give you device names unless you specify a kind, but
// here we want all kinds of devices. This needs a fix in livekit-client
// for the following name-matching logic to do anything useful.
const devices = await Room . getLocalDevices ( undefined , true );
2024-11-28 18:05:12 +01:00
if ( audioInput ) {
2024-09-10 09:49:35 +02:00
const deviceId = findDeviceByName ( audioInput , "audioinput" , devices );
2023-10-25 13:49:18 +02:00
if ( ! deviceId ) {
logger . warn ( "Unknown audio input: " + audioInput );
2024-11-28 18:05:12 +01:00
// override the default mute state
2023-08-02 15:29:37 -04:00
latestMuteStates . current ! . audio . setEnabled ? .( false );
} else {
2023-10-25 13:49:18 +02:00
logger . debug (
`Found audio input ID ${ deviceId } for name ${ audioInput } ` ,
2023-06-23 14:17:51 -04:00
);
2023-10-25 13:49:18 +02:00
latestDevices . current ! . audioInput . select ( deviceId );
2023-06-23 14:17:51 -04:00
}
2023-10-25 13:49:18 +02:00
}
2024-11-28 18:05:12 +01:00
if ( videoInput ) {
2024-09-10 09:49:35 +02:00
const deviceId = findDeviceByName ( videoInput , "videoinput" , devices );
2023-10-25 13:49:18 +02:00
if ( ! deviceId ) {
logger . warn ( "Unknown video input: " + videoInput );
2024-11-28 18:05:12 +01:00
// override the default mute state
2023-08-09 17:34:53 -04:00
latestMuteStates . current ! . video . setEnabled ? .( false );
2023-08-02 15:29:37 -04:00
} else {
2023-10-25 13:49:18 +02:00
logger . debug (
`Found video input ID ${ deviceId } for name ${ videoInput } ` ,
2023-06-23 14:17:51 -04:00
);
2023-10-25 13:49:18 +02:00
latestDevices . current ! . videoInput . select ( deviceId );
2023-06-23 14:17:51 -04:00
}
2023-10-25 13:49:18 +02:00
}
};
2024-01-26 10:03:08 +01:00
2024-11-11 18:30:15 +01:00
if ( skipLobby ) {
2024-11-11 12:51:31 -05:00
if ( widget ) {
if ( preload ) {
// In preload mode without lobby we wait for a join action before entering
const onJoin = ( ev : CustomEvent < IWidgetApiRequest >) : void => {
( async () : Promise < void > => {
await defaultDeviceSetup (
ev . detail . data as unknown as JoinCallData ,
);
2025-02-26 14:55:03 +01:00
await enterRTCSessionOrError ( rtcSession , perParticipantE2EE );
2024-12-12 07:33:47 +00:00
widget . api . transport . reply ( ev . detail , {});
2024-11-11 12:51:31 -05:00
})(). catch (( e ) => {
logger . error ( "Error joining RTC session" , e );
});
};
widget . lazyActions . on ( ElementWidgetActions . JoinCall , onJoin );
return () : void => {
2024-12-12 07:33:47 +00:00
widget . lazyActions . off ( ElementWidgetActions . JoinCall , onJoin );
2024-11-11 12:51:31 -05:00
};
} else {
// No lobby and no preload: we enter the rtc session right away
2024-11-11 18:30:15 +01:00
( async () : Promise < void > => {
2025-02-26 14:55:03 +01:00
await enterRTCSessionOrError ( rtcSession , perParticipantE2EE );
2024-11-11 18:30:15 +01:00
})(). catch (( e ) => {
logger . error ( "Error joining RTC session" , e );
});
2024-11-11 12:51:31 -05:00
}
2024-11-11 18:30:15 +01:00
} else {
2025-02-26 14:55:03 +01:00
void enterRTCSessionOrError ( rtcSession , perParticipantE2EE );
2024-11-11 18:30:15 +01:00
}
2022-09-09 02:10:45 -04:00
}
2025-02-24 17:45:40 +07:00
}, [
widget ,
rtcSession ,
preload ,
skipLobby ,
perParticipantE2EE ,
latestDevices ,
latestMuteStates ,
2025-03-10 15:20:51 +01:00
enterRTCSessionOrError ,
2025-02-24 17:45:40 +07:00
]);
2022-09-09 02:10:45 -04:00
2022-01-05 15:35:12 -08:00
const [ left , setLeft ] = useState ( false );
2025-03-10 15:20:51 +01:00
2025-01-06 18:00:20 +01:00
const navigate = useNavigate ();
2022-01-05 15:35:12 -08:00
2023-07-20 17:55:50 +01:00
const onLeave = useCallback (
2025-02-26 17:20:30 +07:00
( cause : "user" | "error" = "user" ) : void => {
2024-12-12 07:33:47 +00:00
const audioPromise = leaveSoundContext . current ? . playSound ( "left" );
2023-07-20 17:55:50 +01:00
// In embedded/widget mode the iFrame will be killed right after the call ended prohibiting the posthog event from getting sent,
// therefore we want the event to be sent instantly without getting queued/batched.
const sendInstantly = !! widget ;
2024-12-12 07:33:47 +00:00
setLeft ( true );
2025-02-17 19:19:31 +07:00
// we need to wait until the callEnded event is tracked on posthog.
// Otherwise the iFrame gets killed before the callEnded event got tracked.
const posthogRequest = new Promise (( resolve ) => {
PosthogAnalytics . instance . eventCallEnded . track (
rtcSession . room . roomId ,
rtcSession . memberships . length ,
sendInstantly ,
rtcSession ,
);
window . setTimeout ( resolve , 10 );
});
2022-11-04 13:07:14 +01:00
2024-12-12 07:33:47 +00:00
leaveRTCSession (
rtcSession ,
2025-02-26 17:20:30 +07:00
cause ,
2024-12-12 07:33:47 +00:00
// Wait for the sound in widget mode (it's not long)
2025-02-17 19:19:31 +07:00
Promise . all ([ audioPromise , posthogRequest ]),
2024-12-12 07:33:47 +00:00
)
// Only sends matrix leave event. The Livekit session will disconnect once the ActiveCall-view unmounts.
2025-01-08 16:42:07 +00:00
. then ( async () => {
2024-09-10 09:49:35 +02:00
if (
! isPasswordlessUser &&
! confineToRoom &&
! PosthogAnalytics . instance . isEnabled ()
) {
2025-01-08 16:42:07 +00:00
await navigate ( "/" );
2024-09-10 09:49:35 +02:00
}
})
. catch (( e ) => {
logger . error ( "Error leaving RTC session" , e );
});
2023-07-20 17:55:50 +01:00
},
2024-12-12 07:33:47 +00:00
[
widget ,
rtcSession ,
isPasswordlessUser ,
confineToRoom ,
leaveSoundContext ,
2025-01-06 18:00:20 +01:00
navigate ,
2024-12-12 07:33:47 +00:00
],
2023-07-20 17:55:50 +01:00
);
2022-09-09 02:10:45 -04:00
useEffect (() => {
2023-08-16 18:41:27 +01:00
if ( widget && isJoined ) {
2024-01-26 10:03:08 +01:00
// set widget to sticky once joined.
2024-12-12 07:33:47 +00:00
widget . api . setAlwaysOnScreen ( true ). catch (( e ) => {
2024-09-10 09:49:35 +02:00
logger . error ( "Error calling setAlwaysOnScreen(true)" , e );
});
2024-01-26 10:03:08 +01:00
2024-09-10 09:49:35 +02:00
const onHangup = ( ev : CustomEvent < IWidgetApiRequest >) : void => {
2024-12-12 07:33:47 +00:00
widget . api . transport . reply ( ev . detail , {});
2023-11-28 19:07:08 +01:00
// Only sends matrix leave event. The Livekit session will disconnect once the ActiveCall-view unmounts.
2025-02-17 19:19:31 +07:00
leaveRTCSession ( rtcSession , "user" ). catch (( e ) => {
2024-09-10 09:49:35 +02:00
logger . error ( "Failed to leave RTC session" , e );
});
2022-09-09 02:10:45 -04:00
};
widget . lazyActions . once ( ElementWidgetActions . HangupCall , onHangup );
2024-06-04 11:20:25 -04:00
return () : void => {
2024-12-12 07:33:47 +00:00
widget . lazyActions . off ( ElementWidgetActions . HangupCall , onHangup );
2022-09-09 02:10:45 -04:00
};
}
2024-12-12 07:33:47 +00:00
}, [ widget , isJoined , rtcSession ]);
2023-06-16 18:07:13 +02:00
2023-09-12 11:30:46 +01:00
const joinRule = useJoinRule ( rtcSession . room );
2023-09-08 15:39:10 -04:00
2023-09-27 15:17:04 -04:00
const [ shareModalOpen , setInviteModalOpen ] = useState ( false );
const onDismissInviteModal = useCallback (
() => setInviteModalOpen ( false ),
2023-10-11 10:42:04 -04:00
[ setInviteModalOpen ],
2023-09-17 14:35:35 -04:00
);
2023-09-08 15:39:10 -04:00
const onShareClickFn = useCallback (
2023-09-27 15:17:04 -04:00
() => setInviteModalOpen ( true ),
2023-10-11 10:42:04 -04:00
[ setInviteModalOpen ],
2023-09-08 15:39:10 -04:00
);
const onShareClick = joinRule === JoinRule . Public ? onShareClickFn : null ;
2024-04-23 15:15:13 +02:00
if ( ! isE2EESupportedBrowser () && e2eeSystem . kind !== E2eeType . NONE ) {
// If we have a encryption system but the browser does not support it.
2025-03-05 20:50:19 +01:00
throw new E2EENotSupportedError ();
2023-08-11 16:59:26 +02:00
}
2023-09-17 14:35:35 -04:00
const shareModal = (
2023-09-27 15:17:04 -04:00
< InviteModal
2023-09-19 18:23:44 +01:00
room = { rtcSession . room }
2023-09-17 14:35:35 -04:00
open = { shareModalOpen }
2023-09-27 15:17:04 -04:00
onDismiss = { onDismissInviteModal }
2023-09-17 14:35:35 -04:00
/>
2023-09-08 15:39:10 -04:00
);
2024-01-26 10:03:08 +01:00
const lobbyView = (
<>
{ shareModal }
< LobbyView
client = { client }
matrixInfo = { matrixInfo }
muteStates = { muteStates }
2025-02-26 14:55:03 +01:00
onEnter = {() =>
void enterRTCSessionOrError ( rtcSession , perParticipantE2EE )
}
2024-01-26 10:03:08 +01:00
confineToRoom = { confineToRoom }
hideHeader = { hideHeader }
participantCount = { participantCount }
onShareClick = { onShareClick }
/>
</>
);
2023-09-08 15:39:10 -04:00
2025-02-26 17:20:30 +07:00
let body : ReactNode ;
2025-03-10 15:20:51 +01:00
if ( isJoined ) {
2025-02-26 17:20:30 +07:00
body = (
2023-09-12 11:30:46 +01:00
<>
2023-09-08 15:39:10 -04:00
{ shareModal }
2023-06-30 18:12:58 +01:00
< ActiveCall
client = { client }
2023-09-08 15:39:10 -04:00
matrixInfo = { matrixInfo }
2024-12-19 15:54:28 +00:00
rtcSession = { rtcSession as MatrixRTCSession }
2023-09-27 18:12:04 -04:00
participantCount = { participantCount }
2023-06-30 18:12:58 +01:00
onLeave = { onLeave }
hideHeader = { hideHeader }
2023-08-02 15:29:37 -04:00
muteStates = { muteStates }
2024-04-23 15:15:13 +02:00
e2eeSystem = { e2eeSystem }
2023-09-12 11:30:46 +01:00
//otelGroupCallMembership={otelGroupCallMembership}
2023-09-08 15:39:10 -04:00
onShareClick = { onShareClick }
2023-06-30 18:12:58 +01:00
/>
2023-09-12 11:30:46 +01:00
</>
2023-05-26 20:41:32 +02:00
);
2024-01-26 10:03:08 +01:00
} else if ( left && widget === null ) {
// Left in SPA mode:
2023-06-07 14:19:53 -04:00
// The call ended view is shown for two reasons: prompting guests to create
// an account, and prompting users that have opted into analytics to provide
// feedback. We don't show a feedback prompt to widget users however (at
// least for now), because we don't yet have designs that would allow widget
// users to dismiss the feedback prompt and close the call window without
// submitting anything.
if (
isPasswordlessUser ||
2025-02-26 17:20:30 +07:00
( PosthogAnalytics . instance . isEnabled () && widget === null )
2023-06-07 14:19:53 -04:00
) {
2025-02-26 17:20:30 +07:00
body = (
< CallEndedView
endedCallId = { rtcSession . room . roomId }
client = { client }
isPasswordlessUser = { isPasswordlessUser }
confineToRoom = { confineToRoom }
/>
2023-06-07 16:22:44 +02:00
);
2022-09-23 15:35:05 +01:00
} else {
// If the user is a regular user, we'll have sent them back to the homepage,
// so just sit here & do nothing: otherwise we would (briefly) mount the
// LobbyView again which would open capture devices again.
2025-02-26 17:20:30 +07:00
body = null ;
2022-09-23 15:35:05 +01:00
}
2024-01-26 10:03:08 +01:00
} else if ( left && widget !== null ) {
// Left in widget mode:
if ( ! returnToLobby ) {
2025-02-26 17:20:30 +07:00
body = null ;
2024-01-26 10:03:08 +01:00
}
} else if ( preload || skipLobby ) {
2025-02-26 17:20:30 +07:00
body = null ;
} else {
body = lobbyView ;
2022-01-05 15:35:12 -08:00
}
2024-01-26 10:03:08 +01:00
2025-03-05 20:50:19 +01:00
return (
< GroupCallErrorBoundary
recoveryActionHandler = {( action ) => {
if ( action == "reconnect" ) {
setLeft ( false );
enterRTCSessionOrError ( rtcSession , perParticipantE2EE ). catch (( e ) => {
logger . error ( "Error re-entering RTC session" , e );
});
}
}}
onError = {
( /**error*/ ) => {
if ( rtcSession . isJoined ()) onLeave ( "error" );
}
}
>
{ body }
</ GroupCallErrorBoundary >
);
2023-09-22 18:05:13 -04:00
};