blob: ada8c3436169822b24b7dbaf93d3b29e51ea4f77 [file] [log] [blame]
simon2d3b6532022-11-08 21:01:57 -05001/*
2 * Copyright (C) 2022 Savoir-faire Linux Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License as
6 * published by the Free Software Foundation; either version 3 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Affero General Public License for more details.
13 *
14 * You should have received a copy of the GNU Affero General Public
15 * License along with this program. If not, see
16 * <https://www.gnu.org/licenses/>.
17 */
18
simonf929a362022-11-18 16:53:45 -050019import { Box, CircularProgress, Grid, IconButtonProps, Stack, Typography } from '@mui/material';
simon5c677962022-12-02 16:51:54 -050020import { ComponentType, ReactNode, useEffect, useMemo, useRef } from 'react';
simon4e7445c2022-11-16 21:18:46 -050021import { useTranslation } from 'react-i18next';
simon9076a9a2022-11-29 17:13:01 -050022import { useLocation } from 'react-router-dom';
simon2d3b6532022-11-08 21:01:57 -050023
24import {
25 CallingAnswerAudioButton,
26 CallingAnswerVideoButton,
simonf929a362022-11-18 16:53:45 -050027 CallingCancelButton,
simon2d3b6532022-11-08 21:01:57 -050028 CallingRefuseButton,
29} from '../components/CallButtons';
Gabriel Rochon15a5fb22022-11-27 19:25:14 -050030import ConversationAvatar from '../components/ConversationAvatar';
idillon255682e2023-02-06 13:25:26 -050031import { useCallManagerContext } from '../contexts/CallManagerProvider';
simon09fe4822022-11-30 23:36:25 -050032import { useConversationContext } from '../contexts/ConversationProvider';
idillon27dab022023-02-02 17:55:47 -050033import { useUserMediaContext } from '../contexts/UserMediaProvider';
idillonc45a43d2023-02-10 18:12:10 -050034import { CallStatus } from '../services/CallManager';
simon571240f2022-11-29 23:59:27 -050035import { VideoElementWithSinkId } from '../utils/utils';
simon2d3b6532022-11-08 21:01:57 -050036
simon9076a9a2022-11-29 17:13:01 -050037export const CallPending = () => {
idillon07d31cc2022-12-06 22:40:14 -050038 const { conversationDisplayName } = useConversationContext();
idillon27dab022023-02-02 17:55:47 -050039 const { localStream } = useUserMediaContext();
idillon255682e2023-02-06 13:25:26 -050040 const { callData } = useCallManagerContext();
simon571240f2022-11-29 23:59:27 -050041 const localVideoRef = useRef<VideoElementWithSinkId | null>(null);
Misha Krieger-Raynauldabc80e52022-11-29 19:44:41 -050042
43 useEffect(() => {
44 if (localStream && localVideoRef.current) {
45 localVideoRef.current.srcObject = localStream;
46 }
simon492e8402022-11-29 16:48:37 -050047 }, [localStream, localVideoRef]);
Misha Krieger-Raynauldabc80e52022-11-29 19:44:41 -050048
simon2d3b6532022-11-08 21:01:57 -050049 return (
50 <Stack
51 direction="column"
52 justifyContent="center"
53 alignItems="center"
54 height="100%"
55 spacing={4}
simon9f814a32022-11-22 21:40:53 -050056 flexGrow={1}
simon2d3b6532022-11-08 21:01:57 -050057 sx={{
Misha Krieger-Raynauldabc80e52022-11-29 19:44:41 -050058 position: 'relative',
simon2d3b6532022-11-08 21:01:57 -050059 }}
60 >
Misha Krieger-Raynauldabc80e52022-11-29 19:44:41 -050061 <video
62 ref={localVideoRef}
63 autoPlay
64 muted
65 style={{
66 width: '100%',
67 height: '100%',
68 position: 'absolute',
69 objectFit: 'cover',
70 backgroundColor: 'black',
71 zIndex: -1,
72 }}
73 />
simon2d3b6532022-11-08 21:01:57 -050074 <Box
75 sx={{
76 position: 'relative',
77 display: 'flex',
78 alignItems: 'center',
79 justifyContent: 'center',
80 width: '100%',
81 height: '30%',
82 }}
83 >
84 <Box
85 sx={{
86 aspectRatio: '1',
87 height: '100%',
88 position: 'absolute',
89 }}
90 >
91 <CircularProgress
92 disableShrink
93 thickness={1}
94 size="100%"
95 sx={{
96 position: 'absolute',
97 color: 'white',
98 zIndex: 1,
99 }}
100 />
Gabriel Rochon15a5fb22022-11-27 19:25:14 -0500101 <ConversationAvatar
simonf929a362022-11-18 16:53:45 -0500102 alt="contact profile picture"
idillon07d31cc2022-12-06 22:40:14 -0500103 displayName={conversationDisplayName}
simon2d3b6532022-11-08 21:01:57 -0500104 style={{
simon2d3b6532022-11-08 21:01:57 -0500105 width: '100%',
106 height: '100%',
simon2d3b6532022-11-08 21:01:57 -0500107 }}
108 />
109 </Box>
110 </Box>
idillon255682e2023-02-06 13:25:26 -0500111 {callData?.role === 'caller' ? <CallPendingCallerInterface /> : <CallPendingReceiverInterface />}
simon2d3b6532022-11-08 21:01:57 -0500112 </Stack>
113 );
114};
115
simonf929a362022-11-18 16:53:45 -0500116const CallPendingDetails = ({
117 title,
118 buttons,
119}: {
120 title: ReactNode;
121 buttons: {
122 ButtonComponent: ComponentType<IconButtonProps>;
123 title: ReactNode;
124 }[];
125}) => {
simon2d3b6532022-11-08 21:01:57 -0500126 return (
simon4e7445c2022-11-16 21:18:46 -0500127 <>
simon2d3b6532022-11-08 21:01:57 -0500128 <Typography variant="h1" color="white">
simonf929a362022-11-18 16:53:45 -0500129 {title}
simon2d3b6532022-11-08 21:01:57 -0500130 </Typography>
simon4e7445c2022-11-16 21:18:46 -0500131 <Box width="50%">
simonf929a362022-11-18 16:53:45 -0500132 <Grid container justifyContent="center">
133 {buttons.map(({ ButtonComponent, title: buttonTitle }, i) => (
134 <Grid item key={i} xs={4}>
135 <Stack direction="column" alignItems="center" spacing={1} sx={{}}>
136 <ButtonComponent color="inherit" size="large" />
137 <Typography variant="body2" color="white" sx={{ opacity: 0.75 }}>
138 {buttonTitle}
139 </Typography>
140 </Stack>
141 </Grid>
142 ))}
143 </Grid>
simon4e7445c2022-11-16 21:18:46 -0500144 </Box>
145 </>
simon2d3b6532022-11-08 21:01:57 -0500146 );
147};
148
simon9076a9a2022-11-29 17:13:01 -0500149export const CallPendingCallerInterface = () => {
idillon255682e2023-02-06 13:25:26 -0500150 const { callStatus } = useCallManagerContext();
simon4e7445c2022-11-16 21:18:46 -0500151 const { t } = useTranslation();
idillon07d31cc2022-12-06 22:40:14 -0500152 const { members } = useConversationContext();
153 const memberName = useMemo(() => members[0].getDisplayName(), [members]);
simonf929a362022-11-18 16:53:45 -0500154
simon9076a9a2022-11-29 17:13:01 -0500155 let title = t('loading');
156
157 switch (callStatus) {
158 case CallStatus.Ringing:
159 title = t('calling', {
160 member0: memberName,
161 });
162 break;
163 case CallStatus.Connecting:
164 title = t('connecting');
165 break;
166 }
167
simon2d3b6532022-11-08 21:01:57 -0500168 return (
simonf929a362022-11-18 16:53:45 -0500169 <CallPendingDetails
simon9076a9a2022-11-29 17:13:01 -0500170 title={title}
simonf929a362022-11-18 16:53:45 -0500171 buttons={[
172 {
173 ButtonComponent: CallingCancelButton,
174 title: t('end_call'),
175 },
176 ]}
177 />
178 );
179};
180
simon9076a9a2022-11-29 17:13:01 -0500181export const CallPendingReceiverInterface = () => {
182 const { state } = useLocation();
idillon255682e2023-02-06 13:25:26 -0500183 const { callStatus } = useCallManagerContext();
simon9076a9a2022-11-29 17:13:01 -0500184
simonf929a362022-11-18 16:53:45 -0500185 const { t } = useTranslation();
idillon07d31cc2022-12-06 22:40:14 -0500186 const { members } = useConversationContext();
187 const memberName = useMemo(() => members[0].getDisplayName(), [members]);
simonf929a362022-11-18 16:53:45 -0500188
simon9076a9a2022-11-29 17:13:01 -0500189 let title = t('loading');
190
191 switch (callStatus) {
192 case CallStatus.Ringing:
simone35acc22022-12-02 16:51:12 -0500193 case CallStatus.Default:
simon9076a9a2022-11-29 17:13:01 -0500194 title = t('incoming_call', {
195 context: state?.isVideoOn ? 'video' : 'audio',
196 member0: memberName,
197 });
198 break;
199 case CallStatus.Connecting:
200 title = t('connecting');
201 break;
202 }
203
simonf929a362022-11-18 16:53:45 -0500204 return (
205 <CallPendingDetails
simon9076a9a2022-11-29 17:13:01 -0500206 title={title}
simonf929a362022-11-18 16:53:45 -0500207 buttons={[
208 {
209 ButtonComponent: CallingRefuseButton,
210 title: t('refuse_call'),
211 },
212 {
213 ButtonComponent: CallingAnswerAudioButton,
214 title: t('accept_call_audio'),
215 },
216 {
217 ButtonComponent: CallingAnswerVideoButton,
218 title: t('accept_call_video'),
219 },
220 ]}
221 />
simon2d3b6532022-11-08 21:01:57 -0500222 );
223};