Improve permission handling in call flow
Improve permission handling by asking the user to give mic and camera permissions before sending `CallBegin` or `CallAccept` for the caller and receiver respectively.
Followed the flow described here: https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Connectivity#session_descriptions
CallProvider:
- Change functions order to place listeners under the function that sends the corresponding WebSocket message.
- Replace `Default` CallStatus with `Loading` for when asking user permissions before sending the `CallBegin`/`CallAccept` message.
- Remove `localStream` and `remoteStream` from `CallContext`. They are now available only in `WebRtcContext`.
- Replace `setAudioStatus` and `setVideoStatus` with `setIsAudioOn` and `setIsVideoOn`. A `useEffect` is now used to disable the tracks when the audio/video status changes.
WebRtcProvider:
- Move WebRTC connection close logic to WebRtcProvider
- Remove `webRtcConnection` from `WebRtcContext`. `WebRtcProvider` is now in charge of setting everything related to the WebRTC Connection.
UI:
- Add `CallPermissionDenied` page for when permissions are denied.
- Rework `CallPending` to display `Loading...` when waiting for user permissions
Change-Id: I48153577cca4c73cdb9b81d2fa78cfdfe2e06d69
diff --git a/client/src/pages/CallPending.tsx b/client/src/pages/CallPending.tsx
index 23cbd50..fde276e 100644
--- a/client/src/pages/CallPending.tsx
+++ b/client/src/pages/CallPending.tsx
@@ -19,6 +19,7 @@
import { Box, CircularProgress, Grid, IconButtonProps, Stack, Typography } from '@mui/material';
import { ComponentType, ReactNode, useContext, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
+import { useLocation } from 'react-router-dom';
import {
CallingAnswerAudioButton,
@@ -27,21 +28,12 @@
CallingRefuseButton,
} from '../components/CallButtons';
import ConversationAvatar from '../components/ConversationAvatar';
+import { CallContext, CallStatus } from '../contexts/CallProvider';
import { ConversationContext } from '../contexts/ConversationProvider';
-export type CallPendingProps = {
- pending: PendingStatus;
- caller?: CallerStatus;
- medium?: CommunicationMedium;
-};
-
-type PendingStatus = 'caller' | 'receiver';
-type CallerStatus = 'calling' | 'connecting';
-type CommunicationMedium = 'audio' | 'video';
-
-export const CallPending = (props: CallPendingProps) => {
+export const CallPending = () => {
const { conversation } = useContext(ConversationContext);
-
+ const { callRole } = useContext(CallContext);
return (
<Stack
direction="column"
@@ -91,11 +83,7 @@
/>
</Box>
</Box>
- {props.pending === 'caller' ? (
- <CallPendingCallerInterface {...props} />
- ) : (
- <CallPendingReceiverInterface {...props} />
- )}
+ {callRole === 'caller' ? <CallPendingCallerInterface /> : <CallPendingReceiverInterface />}
</Stack>
);
};
@@ -133,20 +121,28 @@
);
};
-export const CallPendingCallerInterface = ({ caller }: CallPendingProps) => {
+export const CallPendingCallerInterface = () => {
+ const { callStatus } = useContext(CallContext);
const { t } = useTranslation();
const { conversation } = useContext(ConversationContext);
const memberName = useMemo(() => conversation.getFirstMember().contact.getRegisteredName(), [conversation]);
+ let title = t('loading');
+
+ switch (callStatus) {
+ case CallStatus.Ringing:
+ title = t('calling', {
+ member0: memberName,
+ });
+ break;
+ case CallStatus.Connecting:
+ title = t('connecting');
+ break;
+ }
+
return (
<CallPendingDetails
- title={
- caller === 'calling'
- ? t('calling', {
- member0: memberName,
- })
- : t('connecting')
- }
+ title={title}
buttons={[
{
ButtonComponent: CallingCancelButton,
@@ -157,21 +153,31 @@
);
};
-export const CallPendingReceiverInterface = ({ medium, caller }: CallPendingProps) => {
+export const CallPendingReceiverInterface = () => {
+ const { state } = useLocation();
+ const { callStatus } = useContext(CallContext);
+
const { t } = useTranslation();
const { conversation } = useContext(ConversationContext);
const memberName = useMemo(() => conversation.getFirstMember().contact.getRegisteredName(), [conversation]);
+ let title = t('loading');
+
+ switch (callStatus) {
+ case CallStatus.Ringing:
+ title = t('incoming_call', {
+ context: state?.isVideoOn ? 'video' : 'audio',
+ member0: memberName,
+ });
+ break;
+ case CallStatus.Connecting:
+ title = t('connecting');
+ break;
+ }
+
return (
<CallPendingDetails
- title={
- caller === 'connecting'
- ? t('connecting')
- : t('incoming_call', {
- context: medium,
- member0: memberName,
- })
- }
+ title={title}
buttons={[
{
ButtonComponent: CallingRefuseButton,