Add widget actions for joining and leaving calls and switching layouts

These actions are processed lazily to ensure that even if the app takes a while to start up, they won't be missed.
This commit is contained in:
Robin Townsend 2022-09-09 02:10:45 -04:00
commit b7be3011da
4 changed files with 141 additions and 36 deletions

View file

@ -19,6 +19,8 @@ import { useHistory } from "react-router-dom";
import { GroupCall, GroupCallState } from "matrix-js-sdk/src/webrtc/groupCall";
import { MatrixClient } from "matrix-js-sdk/src/client";
import type { IWidgetApiRequest } from "matrix-widget-api";
import { widget, ElementWidgetActions, JoinCallData } from "../widget";
import { useGroupCall } from "./useGroupCall";
import { ErrorView, FullScreenView } from "../FullScreenView";
import { LobbyView } from "./LobbyView";
@ -28,6 +30,8 @@ import { CallEndedView } from "./CallEndedView";
import { useRoomAvatar } from "./useRoomAvatar";
import { useSentryGroupCallHandler } from "./useSentryGroupCallHandler";
import { useLocationNavigation } from "../useLocationNavigation";
import { useMediaHandler } from "../settings/useMediaHandler";
declare global {
interface Window {
groupCall?: GroupCall;
@ -38,6 +42,7 @@ interface Props {
client: MatrixClient;
isPasswordlessUser: boolean;
isEmbedded: boolean;
preload: boolean;
hideHeader: boolean;
roomIdOrAlias: string;
groupCall: GroupCall;
@ -47,6 +52,7 @@ export function GroupCallView({
client,
isPasswordlessUser,
isEmbedded,
preload,
hideHeader,
roomIdOrAlias,
groupCall,
@ -73,14 +79,50 @@ export function GroupCallView({
unencryptedEventsFromUsers,
} = useGroupCall(groupCall);
const { setAudioInput, setVideoInput } = useMediaHandler();
const avatarUrl = useRoomAvatar(groupCall.room);
useEffect(() => {
window.groupCall = groupCall;
return () => {
delete window.groupCall;
};
}, [groupCall]);
// In embedded mode, bypass the lobby and just enter the call straight away
if (isEmbedded) groupCall.enter();
}, [groupCall, isEmbedded]);
useEffect(() => {
if (widget && preload) {
// In preload mode, wait for a join action before entering
const onJoin = async (ev: CustomEvent<IWidgetApiRequest>) => {
const { audioInput, videoInput } = ev.detail
.data as unknown as JoinCallData;
if (audioInput !== null) setAudioInput(audioInput);
if (videoInput !== null) setVideoInput(videoInput);
await Promise.all([
groupCall.setMicrophoneMuted(audioInput === null),
groupCall.setLocalVideoMuted(videoInput === null),
]);
await groupCall.enter();
await Promise.all([
widget.api.setAlwaysOnScreen(true),
widget.api.transport.reply(ev.detail, {}),
]);
};
widget.lazyActions.on(ElementWidgetActions.JoinCall, onJoin);
return () => {
widget.lazyActions.off(ElementWidgetActions.JoinCall, onJoin);
};
}
}, [groupCall, preload, setAudioInput, setVideoInput]);
useEffect(() => {
if (isEmbedded && !preload) {
// In embedded mode, bypass the lobby and just enter the call straight away
groupCall.enter();
}
}, [groupCall, isEmbedded, preload]);
useSentryGroupCallHandler(groupCall);
@ -90,13 +132,31 @@ export function GroupCallView({
const history = useHistory();
const onLeave = useCallback(() => {
setLeft(true);
leave();
if (widget) {
widget.api.transport.send(ElementWidgetActions.HangupCall, {});
widget.api.setAlwaysOnScreen(false);
}
if (!isPasswordlessUser) {
if (isPasswordlessUser) {
setLeft(true);
} else if (!isEmbedded) {
history.push("/");
}
}, [leave, isPasswordlessUser, history]);
}, [leave, isPasswordlessUser, isEmbedded, history]);
useEffect(() => {
if (widget && state === GroupCallState.Entered) {
const onHangup = async (ev: CustomEvent<IWidgetApiRequest>) => {
leave();
await widget.api.transport.reply(ev.detail, {});
};
widget.lazyActions.once(ElementWidgetActions.HangupCall, onHangup);
return () => {
widget.lazyActions.off(ElementWidgetActions.HangupCall, onHangup);
};
}
}, [groupCall, state, leave]);
if (error) {
return <ErrorView error={error} />;
@ -148,33 +208,33 @@ export function GroupCallView({
);
} else if (left) {
return <CallEndedView client={client} />;
} else if (preload) {
return null;
} else if (isEmbedded) {
return (
<FullScreenView>
<h1>Loading room...</h1>
</FullScreenView>
);
} else {
if (isEmbedded) {
return (
<FullScreenView>
<h1>Loading room...</h1>
</FullScreenView>
);
} else {
return (
<LobbyView
client={client}
groupCall={groupCall}
roomName={groupCall.room.name}
avatarUrl={avatarUrl}
state={state}
onInitLocalCallFeed={initLocalCallFeed}
localCallFeed={localCallFeed}
onEnter={enter}
microphoneMuted={microphoneMuted}
localVideoMuted={localVideoMuted}
toggleLocalVideoMuted={toggleLocalVideoMuted}
toggleMicrophoneMuted={toggleMicrophoneMuted}
roomIdOrAlias={roomIdOrAlias}
isEmbedded={isEmbedded}
hideHeader={hideHeader}
/>
);
}
return (
<LobbyView
client={client}
groupCall={groupCall}
roomName={groupCall.room.name}
avatarUrl={avatarUrl}
state={state}
onInitLocalCallFeed={initLocalCallFeed}
localCallFeed={localCallFeed}
onEnter={enter}
microphoneMuted={microphoneMuted}
localVideoMuted={localVideoMuted}
toggleLocalVideoMuted={toggleLocalVideoMuted}
toggleMicrophoneMuted={toggleMicrophoneMuted}
roomIdOrAlias={roomIdOrAlias}
isEmbedded={isEmbedded}
hideHeader={hideHeader}
/>
);
}
}