Skip to content

Commit 855da4f

Browse files
committed
fix: distinguish the 2 phases of joining the call: connect to the call AND wait for participants list
Signed-off-by: DorraJaouad <[email protected]>
1 parent 4b30af0 commit 855da4f

File tree

4 files changed

+121
-25
lines changed

4 files changed

+121
-25
lines changed

src/components/CallView/shared/EmptyCallView.vue

+16-2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import IconPhone from 'vue-material-design-icons/Phone.vue'
3333
import { t } from '@nextcloud/l10n'
3434

3535
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
36+
import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
3637

3738
import { CONVERSATION, PARTICIPANT } from '../../../constants.js'
3839
import { copyConversationLinkToClipboard } from '../../../utils/handleUrl.ts'
@@ -43,6 +44,7 @@ export default {
4344

4445
components: {
4546
NcButton,
47+
NcLoadingIcon,
4648
IconAccountMultiple,
4749
IconLinkVariant,
4850
IconPhone,
@@ -66,11 +68,14 @@ export default {
6668
},
6769

6870
computed: {
69-
7071
token() {
7172
return this.$store.getters.getToken()
7273
},
7374

75+
isConnecting() {
76+
return this.$store.getters.isConnecting(this.token)
77+
},
78+
7479
conversation() {
7580
return this.$store.getters.conversation(this.token)
7681
},
@@ -116,14 +121,19 @@ export default {
116121
},
117122

118123
emptyCallViewIcon() {
119-
if (this.isPhoneConversation) {
124+
if (this.isConnecting) {
125+
return NcLoadingIcon
126+
} else if (this.isPhoneConversation) {
120127
return IconPhone
121128
} else {
122129
return this.isPublicConversation ? IconLinkVariant : IconAccountMultiple
123130
}
124131
},
125132

126133
title() {
134+
if (this.isConnecting) {
135+
return t('spreed', 'Connecting …')
136+
}
127137
if (this.isPhoneConversation) {
128138
return t('spreed', 'Calling …')
129139
}
@@ -134,6 +144,10 @@ export default {
134144
},
135145

136146
message() {
147+
if (this.isConnecting) {
148+
return ''
149+
}
150+
137151
if (this.isPasswordRequestConversation || this.isFileConversation) {
138152
return ''
139153
}

src/components/TopBar/CallButton.vue

+5-5
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99
id="call_button"
1010
:title="startCallTitle"
1111
:aria-label="startCallLabel"
12-
:disabled="startCallButtonDisabled || loading || isConnecting"
12+
:disabled="startCallButtonDisabled || loading || isJoiningCall"
1313
:type="startCallButtonType"
1414
@click="handleClick">
1515
<template #icon>
16-
<NcLoadingIcon v-if="isConnecting || loading" />
16+
<NcLoadingIcon v-if="isJoiningCall || loading" />
1717
<IconPhoneDial v-else-if="isPhoneRoom" :size="20" />
1818
<IconPhoneOutline v-else-if="silentCall" :size="20" />
1919
<IconPhone v-else :size="20" />
@@ -276,7 +276,7 @@ export default {
276276
return t('spreed', 'Join call')
277277
}
278278

279-
if (this.isConnecting) {
279+
if (this.isJoiningCall) {
280280
return t('spreed', 'Connecting...')
281281
}
282282

@@ -347,8 +347,8 @@ export default {
347347
return this.$store.getters.isInLobby
348348
},
349349

350-
isConnecting() {
351-
return this.$store.getters.isConnecting(this.token)
350+
isJoiningCall() {
351+
return this.$store.getters.isJoiningCall(this.token)
352352
},
353353

354354
connectionFailed() {

src/store/participantsStore.js

+91-16
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ const state = {
6666
},
6767
inCall: {
6868
},
69+
joiningCall: {
70+
},
6971
connecting: {
7072
},
7173
connectionFailed: {
@@ -93,6 +95,10 @@ const getters = {
9395
return !!(state.inCall[token] && Object.keys(state.inCall[token]).length > 0)
9496
},
9597

98+
isJoiningCall: (state) => (token) => {
99+
return !!(state.joiningCall[token] && Object.keys(state.joiningCall[token]).length > 0)
100+
},
101+
96102
isConnecting: (state) => (token) => {
97103
return !!(state.connecting[token] && Object.keys(state.connecting[token]).length > 0)
98104
},
@@ -354,6 +360,19 @@ const mutations = {
354360
Vue.delete(state.connectionFailed, token)
355361
},
356362

363+
joiningCall(state, { token, sessionId, flags }) {
364+
if (!state.joiningCall[token]) {
365+
Vue.set(state.joiningCall, token, {})
366+
}
367+
Vue.set(state.joiningCall[token], sessionId, flags)
368+
},
369+
370+
finishedJoiningCall(state, { token, sessionId }) {
371+
if (state.joiningCall[token] && state.joiningCall[token][sessionId]) {
372+
Vue.delete(state.joiningCall[token], sessionId)
373+
}
374+
},
375+
357376
connecting(state, { token, sessionId, flags }) {
358377
if (!state.connecting[token]) {
359378
Vue.set(state.connecting, token, {})
@@ -811,10 +830,24 @@ const actions = {
811830
return false
812831
},
813832

814-
async joinCall({ commit, getters }, { token, participantIdentifier, flags, silent, recordingConsent }) {
815-
commit('connecting', { token, sessionId: participantIdentifier.sessionId, flags })
833+
async joinCall({ commit, getters, state }, { token, participantIdentifier, flags, silent, recordingConsent }) {
834+
// SUMMARY: join call process
835+
// There are 2 main steps to join a call:
836+
// 1. Join the call (signaling-join-call)
837+
// 2A. Wait for the users list (signaling-users-in-room) INTERNAL server event
838+
// 2B. Wait for the users list (signaling-users-changed) EXTERNAL server event
839+
// In case of failure, we receive a signaling-join-call-failed event
816840

817-
if (!participantIdentifier?.sessionId) {
841+
// Exception 1: We may receive the users list before the signaling-join-call event
842+
// In this case, we use the isParticipantsListReceived flag to handle this case
843+
844+
// Exception 2: We may receive the users list in a second event of signaling-users-changed or signaling-users-in-room
845+
// In this case, we always check if the list is the updated one (it has the current participant in the call)
846+
847+
const { sessionId } = participantIdentifier
848+
let isParticipantsListReceived = null
849+
850+
if (!sessionId) {
818851
console.error('Trying to join call without sessionId')
819852
return
820853
}
@@ -825,20 +858,62 @@ const actions = {
825858
return
826859
}
827860

828-
// Preparing the event listener for the signaling-join-call event
829-
EventBus.once('signaling-join-call', () => {
830-
commit('setInCall', {
831-
token,
832-
sessionId: participantIdentifier.sessionId,
833-
flags,
834-
})
835-
commit('finishedConnecting', { token, sessionId: participantIdentifier.sessionId })
836-
})
861+
commit('joiningCall', { token, sessionId, flags })
837862

838-
// Preparing the event listener for the signaling-join-call-failed event
839-
EventBus.once('signaling-join-call-failed', () => {
840-
commit('finishedConnecting', { token, sessionId: participantIdentifier.sessionId })
841-
})
863+
const handleJoinCall = () => {
864+
commit('setInCall', { token, sessionId, flags })
865+
commit('finishedJoiningCall', { token, sessionId })
866+
867+
if (isParticipantsListReceived) {
868+
isParticipantsListReceived = null
869+
commit('finishedConnecting', { token, sessionId })
870+
} else {
871+
commit('connecting', { token, sessionId, flags })
872+
}
873+
}
874+
875+
const handleJoinCallFailed = () => {
876+
commit('finishedJoiningCall', { token, sessionId })
877+
isParticipantsListReceived = null
878+
}
879+
880+
const handleUsersInRoom = (payload) => {
881+
const participant = payload[0].find(p => p.sessionId === sessionId)
882+
if (participant && participant.inCall !== PARTICIPANT.CALL_FLAG.DISCONNECTED) {
883+
if (state.joiningCall[token]?.[sessionId]) {
884+
isParticipantsListReceived = true
885+
commit('connecting', { token, sessionId, flags })
886+
return
887+
}
888+
commit('finishedConnecting', { token, sessionId })
889+
EventBus.off('signaling-users-in-room', handleUsersInRoom)
890+
}
891+
}
892+
893+
const handleUsersChanged = (payload) => {
894+
const participant = payload[0].find(p => p.nextcloudSessionId === sessionId)
895+
if (participant && participant.inCall !== PARTICIPANT.CALL_FLAG.DISCONNECTED) {
896+
if (state.joiningCall[token]?.[sessionId]) {
897+
isParticipantsListReceived = true
898+
commit('connecting', { token, sessionId, flags })
899+
return
900+
}
901+
commit('finishedConnecting', { token, sessionId })
902+
EventBus.off('signaling-users-changed', handleUsersChanged)
903+
}
904+
}
905+
906+
// Fallback in case we never receive the users list after joining the call
907+
setTimeout(() => {
908+
// If, by accident, we never receive a users list, just switch to
909+
// "Waiting for others to join the call …" after some seconds.
910+
commit('finishedConnecting', { token, sessionId })
911+
}, 10000)
912+
913+
EventBus.once('signaling-join-call', handleJoinCall)
914+
EventBus.once('signaling-join-call-failed', handleJoinCallFailed)
915+
EventBus.on('signaling-users-in-room', handleUsersInRoom)
916+
EventBus.on('signaling-users-changed', handleUsersChanged)
842917

843918
try {
844919
const actualFlags = await joinCall(token, flags, silent, recordingConsent)

src/store/participantsStore.spec.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,8 @@ describe('participantsStore', () => {
575575
test('joins call', async () => {
576576
// Assert
577577
expect(joinCall).toHaveBeenCalledWith(TOKEN, flags, false, false)
578+
// Mock the signaling event
579+
EventBus.emit('signaling-join-call')
578580
expect(store.getters.isInCall(TOKEN)).toBe(true)
579581
expect(store.getters.isConnecting(TOKEN)).toBe(true)
580582
expect(store.getters.participantsList(TOKEN)).toStrictEqual([
@@ -587,7 +589,12 @@ describe('participantsStore', () => {
587589
])
588590

589591
// Finished connecting to the call
590-
EventBus.emit('signaling-users-in-room')
592+
EventBus.emit('signaling-users-in-room', [[{
593+
attendeeId: 1,
594+
sessionId: 'session-id-1',
595+
inCall: actualFlags,
596+
participantType: PARTICIPANT.TYPE.USER,
597+
}]])
591598

592599
expect(store.getters.isInCall(TOKEN)).toBe(true)
593600
expect(store.getters.isConnecting(TOKEN)).toBe(false)
@@ -906,7 +913,7 @@ describe('participantsStore', () => {
906913
flags,
907914
silent: false,
908915
})
909-
916+
EventBus.emit('signaling-join-call')
910917
expect(store.getters.isInCall(TOKEN)).toBe(true)
911918

912919
leaveConversation.mockResolvedValue()

0 commit comments

Comments
 (0)