From b5a9c5915c4232e3b62c19a3055d9d8e3217704d Mon Sep 17 00:00:00 2001 From: Pierre Lehnen <55164754+pierre-lehnen-rc@users.noreply.github.com> Date: Mon, 4 Jul 2022 17:16:34 -0300 Subject: [PATCH] Regression: [VideoConference] Callee client behaves improperly when accepting a call from someone who lost the connection (#26101) --- apps/meteor/client/lib/VideoConfManager.ts | 116 ++++++++++++--------- 1 file changed, 64 insertions(+), 52 deletions(-) diff --git a/apps/meteor/client/lib/VideoConfManager.ts b/apps/meteor/client/lib/VideoConfManager.ts index c994660fb4b9d..00c819da2640b 100644 --- a/apps/meteor/client/lib/VideoConfManager.ts +++ b/apps/meteor/client/lib/VideoConfManager.ts @@ -23,6 +23,7 @@ export type DirectCallParams = { rid: IRoom['_id']; callId: string; dismissed?: boolean; + acceptTimeout?: ReturnType | undefined; // TODO: improve this, nowadays there is not possible check if the video call has finished, but ist a nice improvement // state: 'incoming' | 'outgoing' | 'connected' | 'disconnected' | 'dismissed'; }; @@ -99,10 +100,6 @@ export const VideoConfManager = new (class VideoConfManager extends Emitter; - private acceptingCallId: string | undefined; - - private acceptingCallTimeout = 0; - private _preferences: CallPreferences; private _capabilities: ProviderCapabilities; @@ -143,7 +140,7 @@ export const VideoConfManager = new (class VideoConfManager extends Emitter ({ ...call })); + return [...this.incomingDirectCalls.values()].map(({ timeout: _, ...call }) => ({ ...call })).filter((call) => !call.acceptTimeout); } public async startCall(roomId: IRoom['_id'], title?: string): Promise { @@ -186,31 +183,37 @@ export const VideoConfManager = new (class VideoConfManager extends Emitter { - if (this.acceptingCallId !== callId) { - debug && console.warn(`[VideoConf] Accepting call timeout not properly cleared.`); - return; - } - - debug && console.log(`[VideoConf] Attempt to accept call has timed out.`); - this.acceptingCallId = undefined; - this.acceptingCallTimeout = 0; - - this.removeIncomingCall(callId); - - this.emit('direct/failed', { callId, uid: callData.uid, rid: callData.rid }); - }, ACCEPT_TIMEOUT) as unknown as number; + this.setIncomingCallAttribute( + callId, + 'acceptTimeout', + setTimeout(() => { + const updatedCallData = this.incomingDirectCalls.get(callId); + if (!updatedCallData?.acceptTimeout) { + return; + } + + debug && console.log(`[VideoConf] Attempt to accept call has timed out.`); + this.removeIncomingCall(callId); + + this.emit('direct/failed', { callId, uid: callData.uid, rid: callData.rid }); + }, ACCEPT_TIMEOUT), + ); + this.emit('incoming/changed'); debug && console.log(`[VideoConf] Notifying user ${callData.uid} that we accept their call.`); Notifications.notifyUser(callData.uid, 'video-conference.accepted', { callId, uid: this.userId, rid: callData.rid }); @@ -247,23 +250,37 @@ export const VideoConfManager = new (class VideoConfManager extends Emitter( + callId: string, + attributeName: T, + value: IncomingDirectCall[T] | undefined, + ): void { + const callData = this.incomingDirectCalls.get(callId); + if (!callData) { + return; + } + + const newData: IncomingDirectCall = { + ...callData, + }; + + if (value === undefined) { + delete newData[attributeName]; + } else { + newData[attributeName] = value; + } + + this.incomingDirectCalls.set(callId, newData); + } + private dismissedIncomingCallHelper(callId: string): boolean { // Muting will stop a callId from ringing, but it doesn't affect any part of the existing workflow const callData = this.incomingDirectCalls.get(callId); if (!callData) { return false; } - this.incomingDirectCalls.set(callId, { ...callData, dismissed: true }); - setTimeout(() => { - const callData = this.incomingDirectCalls.get(callId); - - if (!callData) { - return; - } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { dismissed, ...rest } = callData; - this.incomingDirectCalls.set(callId, { ...rest }); - }, CALL_TIMEOUT * 20); + this.setIncomingCallAttribute(callId, 'dismissed', true); + setTimeout(() => this.setIncomingCallAttribute(callId, 'dismissed', undefined), CALL_TIMEOUT * 20); return true; } @@ -314,13 +331,12 @@ export const VideoConfManager = new (class VideoConfManager extends Emitter { debug && console.log(`[VideoConf] Joining call ${callId}.`); - if (this.acceptingCallTimeout && this.acceptingCallId === callId) { - clearTimeout(this.acceptingCallTimeout); - this.acceptingCallTimeout = 0; - this.acceptingCallId = undefined; - } - if (this.incomingDirectCalls.has(callId)) { + const data = this.incomingDirectCalls.get(callId); + if (data?.acceptTimeout) { + debug && console.log('[VideoConf] Clearing acceptance timeout'); + clearTimeout(data.acceptTimeout); + } this.removeIncomingCall(callId); } @@ -429,19 +445,16 @@ export const VideoConfManager = new (class VideoConfManager extends Emitter { if (call.timeout) { clearTimeout(call.timeout); } + if (call.acceptTimeout) { + clearTimeout(call.acceptTimeout); + } }); this.incomingDirectCalls.clear(); this.currentCallData = undefined; - this.acceptingCallId = undefined; this._preferences = {}; this.emit('incoming/changed'); this.emit('ringing/changed'); @@ -466,7 +479,7 @@ export const VideoConfManager = new (class VideoConfManager extends Emitter