blob: 02f06e4d11bab37ac27e0ed46bea67a1898c9e54 [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';
simon492e8402022-11-29 16:48:37 -050020import { ComponentType, ReactNode, useContext, useEffect, useMemo } 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';
simon9076a9a2022-11-29 17:13:01 -050031import { CallContext, CallStatus } from '../contexts/CallProvider';
Gabriel Rochon61f82af2022-11-22 22:28:02 -050032import { ConversationContext } from '../contexts/ConversationProvider';
Misha Krieger-Raynauldabc80e52022-11-29 19:44:41 -050033import { WebRtcContext } from '../contexts/WebRtcProvider';
simon2d3b6532022-11-08 21:01:57 -050034
simon9076a9a2022-11-29 17:13:01 -050035export const CallPending = () => {
Misha Krieger-Raynauldabc80e52022-11-29 19:44:41 -050036 const { localStream } = useContext(WebRtcContext);
Gabriel Rochon15a5fb22022-11-27 19:25:14 -050037 const { conversation } = useContext(ConversationContext);
simon492e8402022-11-29 16:48:37 -050038 const { callRole, localVideoRef } = useContext(CallContext);
Misha Krieger-Raynauldabc80e52022-11-29 19:44:41 -050039
40 useEffect(() => {
41 if (localStream && localVideoRef.current) {
42 localVideoRef.current.srcObject = localStream;
43 }
simon492e8402022-11-29 16:48:37 -050044 }, [localStream, localVideoRef]);
Misha Krieger-Raynauldabc80e52022-11-29 19:44:41 -050045
simon2d3b6532022-11-08 21:01:57 -050046 return (
47 <Stack
48 direction="column"
49 justifyContent="center"
50 alignItems="center"
51 height="100%"
52 spacing={4}
simon9f814a32022-11-22 21:40:53 -050053 flexGrow={1}
simon2d3b6532022-11-08 21:01:57 -050054 sx={{
Misha Krieger-Raynauldabc80e52022-11-29 19:44:41 -050055 position: 'relative',
simon2d3b6532022-11-08 21:01:57 -050056 }}
57 >
Misha Krieger-Raynauldabc80e52022-11-29 19:44:41 -050058 <video
59 ref={localVideoRef}
60 autoPlay
61 muted
62 style={{
63 width: '100%',
64 height: '100%',
65 position: 'absolute',
66 objectFit: 'cover',
67 backgroundColor: 'black',
68 zIndex: -1,
69 }}
70 />
simon2d3b6532022-11-08 21:01:57 -050071 <Box
72 sx={{
73 position: 'relative',
74 display: 'flex',
75 alignItems: 'center',
76 justifyContent: 'center',
77 width: '100%',
78 height: '30%',
79 }}
80 >
81 <Box
82 sx={{
83 aspectRatio: '1',
84 height: '100%',
85 position: 'absolute',
86 }}
87 >
88 <CircularProgress
89 disableShrink
90 thickness={1}
91 size="100%"
92 sx={{
93 position: 'absolute',
94 color: 'white',
95 zIndex: 1,
96 }}
97 />
Gabriel Rochon15a5fb22022-11-27 19:25:14 -050098 <ConversationAvatar
simonf929a362022-11-18 16:53:45 -050099 alt="contact profile picture"
Gabriel Rochon15a5fb22022-11-27 19:25:14 -0500100 displayName={conversation.getDisplayNameNoFallback()}
simon2d3b6532022-11-08 21:01:57 -0500101 style={{
simon2d3b6532022-11-08 21:01:57 -0500102 width: '100%',
103 height: '100%',
simon2d3b6532022-11-08 21:01:57 -0500104 }}
105 />
106 </Box>
107 </Box>
simon9076a9a2022-11-29 17:13:01 -0500108 {callRole === 'caller' ? <CallPendingCallerInterface /> : <CallPendingReceiverInterface />}
simon2d3b6532022-11-08 21:01:57 -0500109 </Stack>
110 );
111};
112
simonf929a362022-11-18 16:53:45 -0500113const CallPendingDetails = ({
114 title,
115 buttons,
116}: {
117 title: ReactNode;
118 buttons: {
119 ButtonComponent: ComponentType<IconButtonProps>;
120 title: ReactNode;
121 }[];
122}) => {
simon2d3b6532022-11-08 21:01:57 -0500123 return (
simon4e7445c2022-11-16 21:18:46 -0500124 <>
simon2d3b6532022-11-08 21:01:57 -0500125 <Typography variant="h1" color="white">
simonf929a362022-11-18 16:53:45 -0500126 {title}
simon2d3b6532022-11-08 21:01:57 -0500127 </Typography>
simon4e7445c2022-11-16 21:18:46 -0500128 <Box width="50%">
simonf929a362022-11-18 16:53:45 -0500129 <Grid container justifyContent="center">
130 {buttons.map(({ ButtonComponent, title: buttonTitle }, i) => (
131 <Grid item key={i} xs={4}>
132 <Stack direction="column" alignItems="center" spacing={1} sx={{}}>
133 <ButtonComponent color="inherit" size="large" />
134 <Typography variant="body2" color="white" sx={{ opacity: 0.75 }}>
135 {buttonTitle}
136 </Typography>
137 </Stack>
138 </Grid>
139 ))}
140 </Grid>
simon4e7445c2022-11-16 21:18:46 -0500141 </Box>
142 </>
simon2d3b6532022-11-08 21:01:57 -0500143 );
144};
145
simon9076a9a2022-11-29 17:13:01 -0500146export const CallPendingCallerInterface = () => {
147 const { callStatus } = useContext(CallContext);
simon4e7445c2022-11-16 21:18:46 -0500148 const { t } = useTranslation();
Gabriel Rochon61f82af2022-11-22 22:28:02 -0500149 const { conversation } = useContext(ConversationContext);
150 const memberName = useMemo(() => conversation.getFirstMember().contact.getRegisteredName(), [conversation]);
simonf929a362022-11-18 16:53:45 -0500151
simon9076a9a2022-11-29 17:13:01 -0500152 let title = t('loading');
153
154 switch (callStatus) {
155 case CallStatus.Ringing:
156 title = t('calling', {
157 member0: memberName,
158 });
159 break;
160 case CallStatus.Connecting:
161 title = t('connecting');
162 break;
163 }
164
simon2d3b6532022-11-08 21:01:57 -0500165 return (
simonf929a362022-11-18 16:53:45 -0500166 <CallPendingDetails
simon9076a9a2022-11-29 17:13:01 -0500167 title={title}
simonf929a362022-11-18 16:53:45 -0500168 buttons={[
169 {
170 ButtonComponent: CallingCancelButton,
171 title: t('end_call'),
172 },
173 ]}
174 />
175 );
176};
177
simon9076a9a2022-11-29 17:13:01 -0500178export const CallPendingReceiverInterface = () => {
179 const { state } = useLocation();
180 const { callStatus } = useContext(CallContext);
181
simonf929a362022-11-18 16:53:45 -0500182 const { t } = useTranslation();
Gabriel Rochon61f82af2022-11-22 22:28:02 -0500183 const { conversation } = useContext(ConversationContext);
184 const memberName = useMemo(() => conversation.getFirstMember().contact.getRegisteredName(), [conversation]);
simonf929a362022-11-18 16:53:45 -0500185
simon9076a9a2022-11-29 17:13:01 -0500186 let title = t('loading');
187
188 switch (callStatus) {
189 case CallStatus.Ringing:
190 title = t('incoming_call', {
191 context: state?.isVideoOn ? 'video' : 'audio',
192 member0: memberName,
193 });
194 break;
195 case CallStatus.Connecting:
196 title = t('connecting');
197 break;
198 }
199
simonf929a362022-11-18 16:53:45 -0500200 return (
201 <CallPendingDetails
simon9076a9a2022-11-29 17:13:01 -0500202 title={title}
simonf929a362022-11-18 16:53:45 -0500203 buttons={[
204 {
205 ButtonComponent: CallingRefuseButton,
206 title: t('refuse_call'),
207 },
208 {
209 ButtonComponent: CallingAnswerAudioButton,
210 title: t('accept_call_audio'),
211 },
212 {
213 ButtonComponent: CallingAnswerVideoButton,
214 title: t('accept_call_video'),
215 },
216 ]}
217 />
simon2d3b6532022-11-08 21:01:57 -0500218 );
219};