Leave call when participant was remotely disconnected from the call

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 <danxuliu@gmail.com>
This commit is contained in:
Daniel Calviño Sánchez 2021-10-28 19:45:11 +02:00 коммит произвёл Joas Schilling
Родитель a4a0e46219
Коммит 1e1cd5e25f
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 7076EA9751AACDDA
2 изменённых файлов: 40 добавлений и 0 удалений

Просмотреть файл

@ -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,

Просмотреть файл

@ -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