simon | f929a36 | 2022-11-18 16:53:45 -0500 | [diff] [blame] | 1 | /* |
| 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 | */ |
MichelleSS | 5516420 | 2022-11-25 18:36:14 -0500 | [diff] [blame] | 18 | import { CallAction, CallBegin, WebSocketMessageType } from 'jami-web-common'; |
simon | 5c67796 | 2022-12-02 16:51:54 -0500 | [diff] [blame] | 19 | import { useCallback, useContext, useEffect, useMemo, useState } from 'react'; |
simon | f929a36 | 2022-11-18 16:53:45 -0500 | [diff] [blame] | 20 | |
simon | 5c67796 | 2022-12-02 16:51:54 -0500 | [diff] [blame] | 21 | import { createOptionalContext } from '../hooks/createOptionalContext'; |
idillon | 07d31cc | 2022-12-06 22:40:14 -0500 | [diff] [blame] | 22 | import { ConversationMember } from '../models/conversation-member'; |
MichelleSS | 5516420 | 2022-11-25 18:36:14 -0500 | [diff] [blame] | 23 | import { callTimeoutMs } from '../utils/constants'; |
simon | 1e2bf34 | 2022-12-02 12:19:40 -0500 | [diff] [blame] | 24 | import { AsyncSetState, SetState, WithChildren } from '../utils/utils'; |
idillon | cf42c65 | 2023-01-31 18:56:17 -0500 | [diff] [blame] | 25 | import { useWebRtcManager } from '../webrtc/WebRtcManager'; |
| 26 | import { useAuthContext } from './AuthProvider'; |
simon | 5c67796 | 2022-12-02 16:51:54 -0500 | [diff] [blame] | 27 | import { CallData, CallManagerContext } from './CallManagerProvider'; |
| 28 | import ConditionalContextProvider from './ConditionalContextProvider'; |
idillon | 27dab02 | 2023-02-02 17:55:47 -0500 | [diff] [blame] | 29 | import { useUserMediaContext } from './UserMediaProvider'; |
idillon | a4b96ab | 2023-02-01 15:30:12 -0500 | [diff] [blame] | 30 | import { IWebSocketContext, useWebSocketContext } from './WebSocketProvider'; |
simon | f929a36 | 2022-11-18 16:53:45 -0500 | [diff] [blame] | 31 | |
| 32 | export type CallRole = 'caller' | 'receiver'; |
| 33 | |
| 34 | export enum CallStatus { |
simon | ff1cb35 | 2022-11-24 15:15:26 -0500 | [diff] [blame] | 35 | Default, |
simon | 9076a9a | 2022-11-29 17:13:01 -0500 | [diff] [blame] | 36 | Loading, |
simon | f929a36 | 2022-11-18 16:53:45 -0500 | [diff] [blame] | 37 | Ringing, |
| 38 | Connecting, |
| 39 | InCall, |
simon | 9076a9a | 2022-11-29 17:13:01 -0500 | [diff] [blame] | 40 | PermissionsDenied, |
simon | f929a36 | 2022-11-18 16:53:45 -0500 | [diff] [blame] | 41 | } |
| 42 | |
simon | 1e2bf34 | 2022-12-02 12:19:40 -0500 | [diff] [blame] | 43 | export enum VideoStatus { |
| 44 | Off, |
| 45 | Camera, |
| 46 | ScreenShare, |
| 47 | } |
| 48 | |
simon | f929a36 | 2022-11-18 16:53:45 -0500 | [diff] [blame] | 49 | export interface ICallContext { |
idillon | cf42c65 | 2023-01-31 18:56:17 -0500 | [diff] [blame] | 50 | remoteStreams: readonly MediaStream[]; |
| 51 | |
simon | f929a36 | 2022-11-18 16:53:45 -0500 | [diff] [blame] | 52 | isAudioOn: boolean; |
simon | 9076a9a | 2022-11-29 17:13:01 -0500 | [diff] [blame] | 53 | setIsAudioOn: SetState<boolean>; |
simon | 1e2bf34 | 2022-12-02 12:19:40 -0500 | [diff] [blame] | 54 | videoStatus: VideoStatus; |
| 55 | updateVideoStatus: AsyncSetState<VideoStatus>; |
simon | f9d78f2 | 2022-11-25 15:47:15 -0500 | [diff] [blame] | 56 | isChatShown: boolean; |
| 57 | setIsChatShown: SetState<boolean>; |
simon | 2a5cf14 | 2022-11-25 15:47:35 -0500 | [diff] [blame] | 58 | isFullscreen: boolean; |
| 59 | setIsFullscreen: SetState<boolean>; |
simon | f929a36 | 2022-11-18 16:53:45 -0500 | [diff] [blame] | 60 | callRole: CallRole; |
| 61 | callStatus: CallStatus; |
Misha Krieger-Raynauld | d0cc3e3 | 2022-11-29 19:59:31 -0500 | [diff] [blame] | 62 | callStartTime: number | undefined; |
simon | f929a36 | 2022-11-18 16:53:45 -0500 | [diff] [blame] | 63 | |
MichelleSS | 5516420 | 2022-11-25 18:36:14 -0500 | [diff] [blame] | 64 | acceptCall: (withVideoOn: boolean) => void; |
simon | accd802 | 2022-11-24 15:04:53 -0500 | [diff] [blame] | 65 | endCall: () => void; |
simon | f929a36 | 2022-11-18 16:53:45 -0500 | [diff] [blame] | 66 | } |
| 67 | |
simon | 5c67796 | 2022-12-02 16:51:54 -0500 | [diff] [blame] | 68 | const optionalCallContext = createOptionalContext<ICallContext>('CallContext'); |
| 69 | export const useCallContext = optionalCallContext.useOptionalContext; |
simon | f929a36 | 2022-11-18 16:53:45 -0500 | [diff] [blame] | 70 | |
| 71 | export default ({ children }: WithChildren) => { |
idillon | a4b96ab | 2023-02-01 15:30:12 -0500 | [diff] [blame] | 72 | const webSocket = useWebSocketContext(); |
idillon | 07d31cc | 2022-12-06 22:40:14 -0500 | [diff] [blame] | 73 | const { callMembers, callData, exitCall } = useContext(CallManagerContext); |
simon | f353ef4 | 2022-11-28 23:14:53 -0500 | [diff] [blame] | 74 | |
simon | 5c67796 | 2022-12-02 16:51:54 -0500 | [diff] [blame] | 75 | const dependencies = useMemo( |
| 76 | () => ({ |
| 77 | webSocket, |
idillon | 07d31cc | 2022-12-06 22:40:14 -0500 | [diff] [blame] | 78 | callMembers, |
simon | 5c67796 | 2022-12-02 16:51:54 -0500 | [diff] [blame] | 79 | callData, |
| 80 | exitCall, |
| 81 | conversationId: callData?.conversationId, |
| 82 | }), |
idillon | cf42c65 | 2023-01-31 18:56:17 -0500 | [diff] [blame] | 83 | [webSocket, callMembers, callData, exitCall] |
simon | 5c67796 | 2022-12-02 16:51:54 -0500 | [diff] [blame] | 84 | ); |
simon | f353ef4 | 2022-11-28 23:14:53 -0500 | [diff] [blame] | 85 | |
simon | e35acc2 | 2022-12-02 16:51:12 -0500 | [diff] [blame] | 86 | return ( |
simon | 5c67796 | 2022-12-02 16:51:54 -0500 | [diff] [blame] | 87 | <ConditionalContextProvider |
| 88 | Context={optionalCallContext.Context} |
| 89 | initialValue={undefined} |
| 90 | dependencies={dependencies} |
| 91 | useProviderValue={CallProvider} |
| 92 | > |
simon | e35acc2 | 2022-12-02 16:51:12 -0500 | [diff] [blame] | 93 | {children} |
simon | 5c67796 | 2022-12-02 16:51:54 -0500 | [diff] [blame] | 94 | </ConditionalContextProvider> |
simon | e35acc2 | 2022-12-02 16:51:12 -0500 | [diff] [blame] | 95 | ); |
simon | f353ef4 | 2022-11-28 23:14:53 -0500 | [diff] [blame] | 96 | }; |
| 97 | |
| 98 | const CallProvider = ({ |
idillon | 07d31cc | 2022-12-06 22:40:14 -0500 | [diff] [blame] | 99 | callMembers, |
simon | 5c67796 | 2022-12-02 16:51:54 -0500 | [diff] [blame] | 100 | callData, |
| 101 | exitCall, |
simon | e35acc2 | 2022-12-02 16:51:12 -0500 | [diff] [blame] | 102 | conversationId, |
simon | f353ef4 | 2022-11-28 23:14:53 -0500 | [diff] [blame] | 103 | webSocket, |
simon | 5c67796 | 2022-12-02 16:51:54 -0500 | [diff] [blame] | 104 | }: { |
simon | f353ef4 | 2022-11-28 23:14:53 -0500 | [diff] [blame] | 105 | webSocket: IWebSocketContext; |
idillon | 07d31cc | 2022-12-06 22:40:14 -0500 | [diff] [blame] | 106 | callMembers: ConversationMember[]; |
simon | 5c67796 | 2022-12-02 16:51:54 -0500 | [diff] [blame] | 107 | callData: CallData; |
| 108 | exitCall: () => void; |
simon | e35acc2 | 2022-12-02 16:51:12 -0500 | [diff] [blame] | 109 | conversationId: string; |
simon | 5c67796 | 2022-12-02 16:51:54 -0500 | [diff] [blame] | 110 | }): ICallContext => { |
idillon | 27dab02 | 2023-02-02 17:55:47 -0500 | [diff] [blame] | 111 | const { |
| 112 | localStream, |
| 113 | updateLocalStream, |
| 114 | screenShareLocalStream, |
| 115 | updateScreenShare, |
| 116 | setAudioInputDeviceId, |
| 117 | setVideoDeviceId, |
| 118 | stopMedias, |
| 119 | } = useUserMediaContext(); |
idillon | cf42c65 | 2023-01-31 18:56:17 -0500 | [diff] [blame] | 120 | const { account } = useAuthContext(); |
| 121 | const webRtcManager = useWebRtcManager(); |
| 122 | |
| 123 | // TODO: This logic will have to change to support multiple people in a call. Could we move this logic to the server? |
| 124 | // The client could make a single request with the conversationId, and the server would be tasked with sending |
| 125 | // all the individual requests to the members of the conversation. |
| 126 | const contactUri = callMembers[0]?.contact.uri; |
| 127 | const connectionInfos = webRtcManager.connectionsInfos[contactUri]; |
| 128 | const remoteStreams = connectionInfos?.remoteStreams; |
| 129 | const iceConnectionState = connectionInfos?.iceConnectionState; |
simon | f929a36 | 2022-11-18 16:53:45 -0500 | [diff] [blame] | 130 | |
simon | f929a36 | 2022-11-18 16:53:45 -0500 | [diff] [blame] | 131 | const [isAudioOn, setIsAudioOn] = useState(false); |
simon | 1e2bf34 | 2022-12-02 12:19:40 -0500 | [diff] [blame] | 132 | const [videoStatus, setVideoStatus] = useState(VideoStatus.Off); |
simon | f9d78f2 | 2022-11-25 15:47:15 -0500 | [diff] [blame] | 133 | const [isChatShown, setIsChatShown] = useState(false); |
simon | 2a5cf14 | 2022-11-25 15:47:35 -0500 | [diff] [blame] | 134 | const [isFullscreen, setIsFullscreen] = useState(false); |
simon | e35acc2 | 2022-12-02 16:51:12 -0500 | [diff] [blame] | 135 | const [callStatus, setCallStatus] = useState(CallStatus.Default); |
| 136 | const [callRole] = useState(callData?.role); |
Misha Krieger-Raynauld | d0cc3e3 | 2022-11-29 19:59:31 -0500 | [diff] [blame] | 137 | const [callStartTime, setCallStartTime] = useState<number | undefined>(undefined); |
simon | f929a36 | 2022-11-18 16:53:45 -0500 | [diff] [blame] | 138 | |
idillon | cf42c65 | 2023-01-31 18:56:17 -0500 | [diff] [blame] | 139 | // TODO: Replace this by a callback |
| 140 | useEffect(() => { |
| 141 | if (callData.role === 'receiver' && contactUri && localStream) { |
| 142 | webRtcManager.addConnection(webSocket, account, contactUri, callData, localStream, screenShareLocalStream); |
| 143 | } |
| 144 | }, [account, callData, contactUri, localStream, screenShareLocalStream, webRtcManager, webSocket]); |
| 145 | |
idillon | cf42c65 | 2023-01-31 18:56:17 -0500 | [diff] [blame] | 146 | // TODO: Transform the effect into a callback |
| 147 | const updateLocalStreams = webRtcManager.updateLocalStreams; |
| 148 | useEffect(() => { |
| 149 | if ((!localStream && !screenShareLocalStream) || !updateLocalStreams) { |
| 150 | return; |
| 151 | } |
| 152 | |
| 153 | updateLocalStreams(localStream, screenShareLocalStream); |
| 154 | }, [localStream, screenShareLocalStream, updateLocalStreams]); |
| 155 | |
| 156 | const sendWebRtcOffer = useCallback(async () => { |
| 157 | if (contactUri) { |
| 158 | webRtcManager.addConnection(webSocket, account, contactUri, callData, localStream, screenShareLocalStream); |
| 159 | } |
| 160 | }, [account, callData, contactUri, localStream, screenShareLocalStream, webRtcManager, webSocket]); |
| 161 | |
| 162 | const closeConnection = useCallback(() => { |
idillon | 27dab02 | 2023-02-02 17:55:47 -0500 | [diff] [blame] | 163 | stopMedias(); |
idillon | cf42c65 | 2023-01-31 18:56:17 -0500 | [diff] [blame] | 164 | webRtcManager.clean(); |
idillon | 27dab02 | 2023-02-02 17:55:47 -0500 | [diff] [blame] | 165 | }, [stopMedias, webRtcManager]); |
simon | f929a36 | 2022-11-18 16:53:45 -0500 | [diff] [blame] | 166 | |
idillon | 27dab02 | 2023-02-02 17:55:47 -0500 | [diff] [blame] | 167 | // Tracks logic should be moved into UserMediaProvider |
simon | 492e840 | 2022-11-29 16:48:37 -0500 | [diff] [blame] | 168 | useEffect(() => { |
simon | 9076a9a | 2022-11-29 17:13:01 -0500 | [diff] [blame] | 169 | if (localStream) { |
| 170 | for (const track of localStream.getAudioTracks()) { |
| 171 | track.enabled = isAudioOn; |
simon | 492e840 | 2022-11-29 16:48:37 -0500 | [diff] [blame] | 172 | const deviceId = track.getSettings().deviceId; |
| 173 | if (deviceId) { |
| 174 | setAudioInputDeviceId(deviceId); |
| 175 | } |
simon | 9076a9a | 2022-11-29 17:13:01 -0500 | [diff] [blame] | 176 | } |
simon | feaa1db | 2022-11-26 20:13:18 -0500 | [diff] [blame] | 177 | } |
idillon | 27dab02 | 2023-02-02 17:55:47 -0500 | [diff] [blame] | 178 | }, [isAudioOn, localStream, setAudioInputDeviceId]); |
simon | f929a36 | 2022-11-18 16:53:45 -0500 | [diff] [blame] | 179 | |
idillon | 27dab02 | 2023-02-02 17:55:47 -0500 | [diff] [blame] | 180 | // Tracks logic should be moved into UserMediaProvider |
simon | f929a36 | 2022-11-18 16:53:45 -0500 | [diff] [blame] | 181 | useEffect(() => { |
simon | f353ef4 | 2022-11-28 23:14:53 -0500 | [diff] [blame] | 182 | if (localStream) { |
MichelleSS | 5516420 | 2022-11-25 18:36:14 -0500 | [diff] [blame] | 183 | for (const track of localStream.getVideoTracks()) { |
simon | 1e2bf34 | 2022-12-02 12:19:40 -0500 | [diff] [blame] | 184 | track.enabled = videoStatus === VideoStatus.Camera; |
simon | 492e840 | 2022-11-29 16:48:37 -0500 | [diff] [blame] | 185 | const deviceId = track.getSettings().deviceId; |
| 186 | if (deviceId) { |
| 187 | setVideoDeviceId(deviceId); |
| 188 | } |
MichelleSS | 5516420 | 2022-11-25 18:36:14 -0500 | [diff] [blame] | 189 | } |
simon | ff1cb35 | 2022-11-24 15:15:26 -0500 | [diff] [blame] | 190 | } |
idillon | 27dab02 | 2023-02-02 17:55:47 -0500 | [diff] [blame] | 191 | }, [videoStatus, localStream, setVideoDeviceId]); |
simon | 1e2bf34 | 2022-12-02 12:19:40 -0500 | [diff] [blame] | 192 | |
idillon | 27dab02 | 2023-02-02 17:55:47 -0500 | [diff] [blame] | 193 | // Track logic should be moved into UserMediaProvider |
simon | 1e2bf34 | 2022-12-02 12:19:40 -0500 | [diff] [blame] | 194 | const updateVideoStatus = useCallback( |
| 195 | async (newStatus: ((prevState: VideoStatus) => VideoStatus) | VideoStatus) => { |
| 196 | if (typeof newStatus === 'function') { |
| 197 | newStatus = newStatus(videoStatus); |
| 198 | } |
| 199 | |
| 200 | const stream = await updateScreenShare(newStatus === VideoStatus.ScreenShare); |
| 201 | if (stream) { |
| 202 | for (const track of stream.getTracks()) { |
| 203 | track.addEventListener('ended', () => { |
| 204 | console.warn('Browser ended screen sharing'); |
| 205 | updateVideoStatus(VideoStatus.Off); |
| 206 | }); |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | setVideoStatus(newStatus); |
| 211 | }, |
| 212 | [videoStatus, updateScreenShare] |
| 213 | ); |
simon | f929a36 | 2022-11-18 16:53:45 -0500 | [diff] [blame] | 214 | |
| 215 | useEffect(() => { |
simon | 2a5cf14 | 2022-11-25 15:47:35 -0500 | [diff] [blame] | 216 | const onFullscreenChange = () => { |
| 217 | setIsFullscreen(document.fullscreenElement !== null); |
| 218 | }; |
| 219 | |
| 220 | document.addEventListener('fullscreenchange', onFullscreenChange); |
| 221 | return () => { |
| 222 | document.removeEventListener('fullscreenchange', onFullscreenChange); |
| 223 | }; |
| 224 | }, []); |
| 225 | |
| 226 | useEffect(() => { |
simon | 9076a9a | 2022-11-29 17:13:01 -0500 | [diff] [blame] | 227 | if (callRole === 'caller' && callStatus === CallStatus.Default) { |
simon | e35acc2 | 2022-12-02 16:51:12 -0500 | [diff] [blame] | 228 | const withVideoOn = callData?.withVideoOn ?? false; |
simon | 9076a9a | 2022-11-29 17:13:01 -0500 | [diff] [blame] | 229 | setCallStatus(CallStatus.Loading); |
simon | 492e840 | 2022-11-29 16:48:37 -0500 | [diff] [blame] | 230 | updateLocalStream() |
simon | 9076a9a | 2022-11-29 17:13:01 -0500 | [diff] [blame] | 231 | .then(() => { |
| 232 | const callBegin: CallBegin = { |
| 233 | contactId: contactUri, |
| 234 | conversationId, |
simon | 492e840 | 2022-11-29 16:48:37 -0500 | [diff] [blame] | 235 | withVideoOn, |
simon | 9076a9a | 2022-11-29 17:13:01 -0500 | [diff] [blame] | 236 | }; |
| 237 | |
| 238 | setCallStatus(CallStatus.Ringing); |
simon | 1e2bf34 | 2022-12-02 12:19:40 -0500 | [diff] [blame] | 239 | setVideoStatus(withVideoOn ? VideoStatus.Camera : VideoStatus.Off); |
simon | 9076a9a | 2022-11-29 17:13:01 -0500 | [diff] [blame] | 240 | console.info('Sending CallBegin', callBegin); |
| 241 | webSocket.send(WebSocketMessageType.CallBegin, callBegin); |
| 242 | }) |
| 243 | .catch((e) => { |
| 244 | console.error(e); |
| 245 | setCallStatus(CallStatus.PermissionsDenied); |
| 246 | }); |
| 247 | } |
simon | e35acc2 | 2022-12-02 16:51:12 -0500 | [diff] [blame] | 248 | }, [webSocket, updateLocalStream, callRole, callStatus, contactUri, conversationId, callData]); |
simon | 9076a9a | 2022-11-29 17:13:01 -0500 | [diff] [blame] | 249 | |
| 250 | const acceptCall = useCallback( |
| 251 | (withVideoOn: boolean) => { |
| 252 | setCallStatus(CallStatus.Loading); |
simon | 492e840 | 2022-11-29 16:48:37 -0500 | [diff] [blame] | 253 | updateLocalStream() |
simon | 9076a9a | 2022-11-29 17:13:01 -0500 | [diff] [blame] | 254 | .then(() => { |
| 255 | const callAccept: CallAction = { |
| 256 | contactId: contactUri, |
| 257 | conversationId, |
| 258 | }; |
| 259 | |
simon | 1e2bf34 | 2022-12-02 12:19:40 -0500 | [diff] [blame] | 260 | setVideoStatus(withVideoOn ? VideoStatus.Camera : VideoStatus.Off); |
simon | 9076a9a | 2022-11-29 17:13:01 -0500 | [diff] [blame] | 261 | setCallStatus(CallStatus.Connecting); |
| 262 | console.info('Sending CallAccept', callAccept); |
| 263 | webSocket.send(WebSocketMessageType.CallAccept, callAccept); |
| 264 | }) |
| 265 | .catch((e) => { |
| 266 | console.error(e); |
| 267 | setCallStatus(CallStatus.PermissionsDenied); |
| 268 | }); |
| 269 | }, |
simon | 492e840 | 2022-11-29 16:48:37 -0500 | [diff] [blame] | 270 | [webSocket, updateLocalStream, contactUri, conversationId] |
simon | 9076a9a | 2022-11-29 17:13:01 -0500 | [diff] [blame] | 271 | ); |
| 272 | |
| 273 | useEffect(() => { |
simon | f929a36 | 2022-11-18 16:53:45 -0500 | [diff] [blame] | 274 | if (callRole === 'caller' && callStatus === CallStatus.Ringing) { |
simon | accd802 | 2022-11-24 15:04:53 -0500 | [diff] [blame] | 275 | const callAcceptListener = (data: CallAction) => { |
| 276 | console.info('Received event on CallAccept', data); |
Charlie | c18d640 | 2022-11-27 13:01:04 -0500 | [diff] [blame] | 277 | if (data.conversationId !== conversationId) { |
| 278 | console.warn('Wrong incoming conversationId, ignoring action'); |
| 279 | return; |
| 280 | } |
| 281 | |
simon | f929a36 | 2022-11-18 16:53:45 -0500 | [diff] [blame] | 282 | setCallStatus(CallStatus.Connecting); |
| 283 | |
simon | 9076a9a | 2022-11-29 17:13:01 -0500 | [diff] [blame] | 284 | sendWebRtcOffer(); |
simon | f929a36 | 2022-11-18 16:53:45 -0500 | [diff] [blame] | 285 | }; |
| 286 | |
| 287 | webSocket.bind(WebSocketMessageType.CallAccept, callAcceptListener); |
| 288 | |
| 289 | return () => { |
| 290 | webSocket.unbind(WebSocketMessageType.CallAccept, callAcceptListener); |
| 291 | }; |
| 292 | } |
simon | 9076a9a | 2022-11-29 17:13:01 -0500 | [diff] [blame] | 293 | }, [callRole, webSocket, sendWebRtcOffer, callStatus, conversationId]); |
simon | f929a36 | 2022-11-18 16:53:45 -0500 | [diff] [blame] | 294 | |
simon | 9076a9a | 2022-11-29 17:13:01 -0500 | [diff] [blame] | 295 | const endCall = useCallback(() => { |
| 296 | const callEnd: CallAction = { |
| 297 | contactId: contactUri, |
| 298 | conversationId, |
| 299 | }; |
MichelleSS | 5516420 | 2022-11-25 18:36:14 -0500 | [diff] [blame] | 300 | |
simon | 9076a9a | 2022-11-29 17:13:01 -0500 | [diff] [blame] | 301 | console.info('Sending CallEnd', callEnd); |
| 302 | closeConnection(); |
| 303 | webSocket.send(WebSocketMessageType.CallEnd, callEnd); |
simon | e35acc2 | 2022-12-02 16:51:12 -0500 | [diff] [blame] | 304 | exitCall(); |
simon | 9076a9a | 2022-11-29 17:13:01 -0500 | [diff] [blame] | 305 | // TODO: write in chat that the call ended |
simon | e35acc2 | 2022-12-02 16:51:12 -0500 | [diff] [blame] | 306 | }, [webSocket, contactUri, conversationId, closeConnection, exitCall]); |
simon | accd802 | 2022-11-24 15:04:53 -0500 | [diff] [blame] | 307 | |
| 308 | useEffect(() => { |
simon | accd802 | 2022-11-24 15:04:53 -0500 | [diff] [blame] | 309 | const callEndListener = (data: CallAction) => { |
| 310 | console.info('Received event on CallEnd', data); |
Charlie | c18d640 | 2022-11-27 13:01:04 -0500 | [diff] [blame] | 311 | if (data.conversationId !== conversationId) { |
| 312 | console.warn('Wrong incoming conversationId, ignoring action'); |
| 313 | return; |
| 314 | } |
| 315 | |
simon | 9076a9a | 2022-11-29 17:13:01 -0500 | [diff] [blame] | 316 | closeConnection(); |
simon | e35acc2 | 2022-12-02 16:51:12 -0500 | [diff] [blame] | 317 | exitCall(); |
simon | accd802 | 2022-11-24 15:04:53 -0500 | [diff] [blame] | 318 | // TODO: write in chat that the call ended |
| 319 | }; |
| 320 | |
| 321 | webSocket.bind(WebSocketMessageType.CallEnd, callEndListener); |
| 322 | return () => { |
| 323 | webSocket.unbind(WebSocketMessageType.CallEnd, callEndListener); |
| 324 | }; |
simon | e35acc2 | 2022-12-02 16:51:12 -0500 | [diff] [blame] | 325 | }, [webSocket, exitCall, conversationId, closeConnection]); |
simon | accd802 | 2022-11-24 15:04:53 -0500 | [diff] [blame] | 326 | |
simon | f929a36 | 2022-11-18 16:53:45 -0500 | [diff] [blame] | 327 | useEffect(() => { |
Charlie | b837e8f | 2022-11-28 19:18:46 -0500 | [diff] [blame] | 328 | if ( |
| 329 | callStatus === CallStatus.Connecting && |
| 330 | (iceConnectionState === 'connected' || iceConnectionState === 'completed') |
| 331 | ) { |
simon | f929a36 | 2022-11-18 16:53:45 -0500 | [diff] [blame] | 332 | console.info('Changing call status to InCall'); |
| 333 | setCallStatus(CallStatus.InCall); |
Misha Krieger-Raynauld | d0cc3e3 | 2022-11-29 19:59:31 -0500 | [diff] [blame] | 334 | setCallStartTime(Date.now()); |
simon | f929a36 | 2022-11-18 16:53:45 -0500 | [diff] [blame] | 335 | } |
simon | 9076a9a | 2022-11-29 17:13:01 -0500 | [diff] [blame] | 336 | }, [iceConnectionState, callStatus]); |
simon | accd802 | 2022-11-24 15:04:53 -0500 | [diff] [blame] | 337 | |
MichelleSS | 5516420 | 2022-11-25 18:36:14 -0500 | [diff] [blame] | 338 | useEffect(() => { |
Charlie | 380dc5e | 2022-11-29 16:51:42 -0500 | [diff] [blame] | 339 | if (iceConnectionState === 'disconnected' || iceConnectionState === 'failed') { |
| 340 | console.info('ICE connection disconnected or failed, ending call'); |
Charlie | b837e8f | 2022-11-28 19:18:46 -0500 | [diff] [blame] | 341 | endCall(); |
| 342 | } |
simon | 1e2bf34 | 2022-12-02 12:19:40 -0500 | [diff] [blame] | 343 | }, [iceConnectionState, callStatus, videoStatus, endCall]); |
Charlie | b837e8f | 2022-11-28 19:18:46 -0500 | [diff] [blame] | 344 | |
| 345 | useEffect(() => { |
MichelleSS | 5516420 | 2022-11-25 18:36:14 -0500 | [diff] [blame] | 346 | const checkStatusTimeout = () => { |
| 347 | if (callStatus !== CallStatus.InCall) { |
| 348 | endCall(); |
simon | ff1cb35 | 2022-11-24 15:15:26 -0500 | [diff] [blame] | 349 | } |
MichelleSS | 5516420 | 2022-11-25 18:36:14 -0500 | [diff] [blame] | 350 | }; |
| 351 | const timeoutId = setTimeout(checkStatusTimeout, callTimeoutMs); |
simon | ff1cb35 | 2022-11-24 15:15:26 -0500 | [diff] [blame] | 352 | |
MichelleSS | 5516420 | 2022-11-25 18:36:14 -0500 | [diff] [blame] | 353 | return () => { |
| 354 | clearTimeout(timeoutId); |
| 355 | }; |
| 356 | }, [callStatus, endCall]); |
simon | ff1cb35 | 2022-11-24 15:15:26 -0500 | [diff] [blame] | 357 | |
simon | 5c67796 | 2022-12-02 16:51:54 -0500 | [diff] [blame] | 358 | return useMemo( |
| 359 | () => ({ |
idillon | cf42c65 | 2023-01-31 18:56:17 -0500 | [diff] [blame] | 360 | remoteStreams, |
simon | 5c67796 | 2022-12-02 16:51:54 -0500 | [diff] [blame] | 361 | isAudioOn, |
| 362 | setIsAudioOn, |
| 363 | videoStatus, |
| 364 | updateVideoStatus, |
| 365 | isChatShown, |
| 366 | setIsChatShown, |
| 367 | isFullscreen, |
| 368 | setIsFullscreen, |
| 369 | callRole, |
| 370 | callStatus, |
| 371 | callStartTime, |
| 372 | acceptCall, |
| 373 | endCall, |
| 374 | }), |
| 375 | [ |
idillon | cf42c65 | 2023-01-31 18:56:17 -0500 | [diff] [blame] | 376 | remoteStreams, |
simon | 5c67796 | 2022-12-02 16:51:54 -0500 | [diff] [blame] | 377 | isAudioOn, |
simon | 5c67796 | 2022-12-02 16:51:54 -0500 | [diff] [blame] | 378 | videoStatus, |
idillon | 27dab02 | 2023-02-02 17:55:47 -0500 | [diff] [blame] | 379 | setIsAudioOn, |
simon | 5c67796 | 2022-12-02 16:51:54 -0500 | [diff] [blame] | 380 | updateVideoStatus, |
| 381 | isChatShown, |
idillon | 27dab02 | 2023-02-02 17:55:47 -0500 | [diff] [blame] | 382 | setIsChatShown, |
simon | 5c67796 | 2022-12-02 16:51:54 -0500 | [diff] [blame] | 383 | isFullscreen, |
idillon | 27dab02 | 2023-02-02 17:55:47 -0500 | [diff] [blame] | 384 | setIsFullscreen, |
simon | 5c67796 | 2022-12-02 16:51:54 -0500 | [diff] [blame] | 385 | callRole, |
| 386 | callStatus, |
| 387 | callStartTime, |
| 388 | acceptCall, |
| 389 | endCall, |
| 390 | ] |
simon | f929a36 | 2022-11-18 16:53:45 -0500 | [diff] [blame] | 391 | ); |
| 392 | }; |