From 1e1cd5e25f785c32f33fc8af63b9d887d9d2a408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Thu, 28 Oct 2021 19:45:11 +0200 Subject: [PATCH] Leave call when participant was remotely disconnected from the call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently whether the local user is in the call or not from the point of view of the Nextcloud server is mostly ignored; the UI is based only on whether the user explicitly joined or left the call. The reason is that after the user joined the response from a previous request to get the room information done when the user had not joined yet could be received, so honouring that could make the UI jump between "in the call" and "not in the call" (as it happened in older versions). However, in the WebRTC related code whether the local user is in the call or not is only based on the user events sent by the signaling server, so in that case the state is always up to date. Due to this, it is possible to detect whether the local user was kicked out from the call by a moderator (for example, because the call was ended for everyone) by comparing the local state ("localUserInCall", which is updated when locally joining and leaving a call) with the remote state provided by the signaling server. Note that there is no rollback of "localUserInCall" if the join or leave call request fails; the error is currently ignored by other parts of the code, so handling it here does not provide any benefit. An alternative approach would have been to ignore the "in call" state provided by the server while a "join call" request is on-going, so the local state and the server state would match except when the user was kicked out from the call. This would have required deeper changes, so even if it could be a better approach in the end for now the simpler (but less clean) approach was used. Signed-off-by: Daniel Calviño Sánchez --- src/utils/signaling.js | 4 ++++ src/utils/webrtc/webrtc.js | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/utils/signaling.js b/src/utils/signaling.js index 28b699c10..98edcb0bb 100644 --- a/src/utils/signaling.js +++ b/src/utils/signaling.js @@ -266,6 +266,8 @@ Signaling.Base.prototype._joinCallSuccess = function(/* token */) { Signaling.Base.prototype.joinCall = function(token, flags) { return new Promise((resolve, reject) => { + this._trigger('beforeJoinCall', [token]) + axios.post(generateOcsUrl('apps/spreed/api/v4/call/{token}', { token }), { flags, }) @@ -318,6 +320,8 @@ Signaling.Base.prototype.leaveCall = function(token, keepToken, all = false) { return } + this._trigger('beforeLeaveCall', [token, keepToken]) + axios.delete(generateOcsUrl('apps/spreed/api/v4/call/{token}', { token }), { data: { all, diff --git a/src/utils/webrtc/webrtc.js b/src/utils/webrtc/webrtc.js index 20f5c1e62..e06bba446 100644 --- a/src/utils/webrtc/webrtc.js +++ b/src/utils/webrtc/webrtc.js @@ -50,6 +50,10 @@ let usersInCallMapping = {} let ownPeer = null let ownScreenPeer = null let selfInCall = PARTICIPANT.CALL_FLAG.DISCONNECTED +// Special variable to know when the local user explicitly joined and left the +// call; this is needed to know when the user was kicked out from the call by a +// moderator. +let localUserInCall = false const delayedConnectionToPeer = [] let callParticipantCollection = null let localCallParticipantModel = null @@ -469,6 +473,24 @@ function usersInCallChanged(signaling, users) { Sounds.playLeave(true) } + // Besides the participant state it also needs to be checked whether the + // local user left the call already or not (either explicitly or due to a + // forced reconnection) to avoid trying to leave the call twice in the + // store. + if (previousSelfInCall !== PARTICIPANT.CALL_FLAG.DISCONNECTED + && selfInCall === PARTICIPANT.CALL_FLAG.DISCONNECTED + && localUserInCall) { + console.info('Force leaving the call for current participant') + + store.dispatch('leaveCall', { + token: store.getters.getToken(), + participantIdentifier: store.getters.getParticipantIdentifier(), + }) + + // Do not return to disconnect already from the other participants + // without waiting for another signaling event about changed users. + } + if (selfInCall === PARTICIPANT.CALL_FLAG.DISCONNECTED) { // Own session is no longer in the call, disconnect from all others. usersChanged(signaling, [], previousUsersInRoom) @@ -534,6 +556,20 @@ export default function initWebRtc(signaling, _callParticipantCollection, _local }) usersInCallChanged(signaling, usersInCallMapping) }) + signaling.on('beforeJoinCall', function(token, reconnect) { + // The user needs to be set as in the call before the request is + // actually done to also cover the (unlikely) case that the request + // takes too long to return and the associated signaling message + // is received before the "join call" request ends. + localUserInCall = true + }) + signaling.on('beforeLeaveCall', function(token, reconnect) { + // The user needs to be set as not in the call before the request is + // actually done to also cover the (unlikely) case that the request + // takes too long to return and the associated signaling message + // is received before the "leave call" request ends. + localUserInCall = false + }) signaling.on('leaveCall', function(token, reconnect) { // When the MCU is used and there is a connection error the call is // left and then joined again to perform the reconnection. In those