diff --git a/src/components/RightSidebar/Participants/ParticipantsTab.vue b/src/components/RightSidebar/Participants/ParticipantsTab.vue
index f8b1b2b4891..0f80c5d4bfc 100644
--- a/src/components/RightSidebar/Participants/ParticipantsTab.vue
+++ b/src/components/RightSidebar/Participants/ParticipantsTab.vue
@@ -70,7 +70,6 @@ import getParticipants from '../../../mixins/getParticipants.js'
import { searchPossibleConversations } from '../../../services/conversationsService.js'
import { EventBus } from '../../../services/EventBus.js'
import { addParticipant } from '../../../services/participantsService.js'
-import { useGuestNameStore } from '../../../stores/guestName.js'
import CancelableRequest from '../../../utils/cancelableRequest.js'
export default {
@@ -99,11 +98,8 @@ export default {
setup() {
const { sortParticipants } = useSortParticipants()
- // FIXME move to getParticipants when replace with composable
- const guestNameStore = useGuestNameStore()
return {
- guestNameStore,
sortParticipants,
}
},
diff --git a/src/components/RightSidebar/RightSidebar.vue b/src/components/RightSidebar/RightSidebar.vue
index 727777f0577..af4eb0d2900 100644
--- a/src/components/RightSidebar/RightSidebar.vue
+++ b/src/components/RightSidebar/RightSidebar.vue
@@ -325,9 +325,14 @@ export default {
}
},
- // Switch tab for guest if he is demoted from moderators
isModeratorOrUser(newValue) {
- if (!newValue) {
+ if (newValue) {
+ // Fetch participants list if guest was promoted to moderators
+ this.$nextTick(() => {
+ emit('guest-promoted', { token: this.token })
+ })
+ } else {
+ // Switch active tab to chat if guest was demoted from moderators
this.activeTab = 'chat'
}
},
diff --git a/src/components/TopBar/TopBar.vue b/src/components/TopBar/TopBar.vue
index 373a87b8a64..ffaa256ffda 100644
--- a/src/components/TopBar/TopBar.vue
+++ b/src/components/TopBar/TopBar.vue
@@ -164,9 +164,8 @@ import TopBarMediaControls from './TopBarMediaControls.vue'
import TopBarMenu from './TopBarMenu.vue'
import { CONVERSATION } from '../../constants.js'
-import getParticipants from '../../mixins/getParticipants.js'
+import isInLobby from '../../mixins/isInLobby.js'
import BrowserStorage from '../../services/BrowserStorage.js'
-import { useGuestNameStore } from '../../stores/guestName.js'
import { getStatusMessage } from '../../utils/userStatus.js'
import { localCallParticipantModel, localMediaModel } from '../../utils/webrtc/index.js'
@@ -196,7 +195,7 @@ export default {
mixins: [
richEditor,
- getParticipants,
+ isInLobby,
],
props: {
@@ -214,15 +213,6 @@ export default {
},
},
- setup() {
- // FIXME move to getParticipants when replace with composable
- const guestNameStore = useGuestNameStore()
-
- return {
- guestNameStore,
- }
- },
-
data: () => {
return {
unreadNotificationHandle: null,
@@ -353,18 +343,6 @@ export default {
this.notifyUnreadMessages(null)
}
},
-
- isModeratorOrUser(newValue) {
- if (newValue) {
- // fetch participants immediately when becomes available
- this.cancelableGetParticipants()
- }
- },
- },
-
- beforeMount() {
- // Initialises the get participants mixin for participants counter
- this.initialiseGetParticipantsMixin()
},
mounted() {
@@ -382,8 +360,6 @@ export default {
document.removeEventListener('MSFullscreenChange', this.fullScreenChanged, false)
document.removeEventListener('webkitfullscreenchange', this.fullScreenChanged, false)
document.body.classList.remove('has-topbar')
-
- this.stopGetParticipantsMixin()
},
methods: {
diff --git a/src/mixins/getParticipants.js b/src/mixins/getParticipants.js
index d7ae961e457..3a27393ab76 100644
--- a/src/mixins/getParticipants.js
+++ b/src/mixins/getParticipants.js
@@ -20,18 +20,11 @@
* along with this program. If not, see .
*
*/
-import Hex from 'crypto-js/enc-hex.js'
-import SHA1 from 'crypto-js/sha1.js'
import debounce from 'debounce'
-import Axios from '@nextcloud/axios'
-import { showError } from '@nextcloud/dialogs'
-import { emit } from '@nextcloud/event-bus'
+import { subscribe, unsubscribe } from '@nextcloud/event-bus'
-import { PARTICIPANT } from '../constants.js'
import { EventBus } from '../services/EventBus.js'
-import { fetchParticipants } from '../services/participantsService.js'
-import CancelableRequest from '../utils/cancelableRequest.js'
import isInLobby from './isInLobby.js'
const getParticipants = {
@@ -41,10 +34,6 @@ const getParticipants = {
data() {
return {
participantsInitialised: false,
- /**
- * Stores the cancel function for cancelableGetParticipants
- */
- cancelGetParticipants: () => {},
fetchingParticipants: false,
}
},
@@ -65,12 +54,16 @@ const getParticipants = {
// Then we have to search for another solution. Maybe the room list which we update
// periodically gets a hash of all online sessions?
EventBus.$on('signaling-participant-list-changed', this.debounceUpdateParticipants)
+
+ subscribe('guest-promoted', this.onJoinedConversation)
},
stopGetParticipantsMixin() {
EventBus.$off('route-change', this.onRouteChange)
EventBus.$off('joined-conversation', this.onJoinedConversation)
EventBus.$off('signaling-participant-list-changed', this.debounceUpdateParticipants)
+
+ unsubscribe('guest-promoted', this.onJoinedConversation)
},
onRouteChange() {
@@ -116,53 +109,13 @@ const getParticipants = {
return
}
- try {
- // The token must be stored in a local variable to ensure that
- // the same token is used after waiting.
- const token = this.token
- // Clear previous requests if there's one pending
- this.cancelGetParticipants('Cancel get participants')
- // Get a new cancelable request function and cancel function pair
- this.fetchingParticipants = true
- const { request, cancel } = CancelableRequest(fetchParticipants)
- this.cancelGetParticipants = cancel
- const participants = await request(token)
- this.$store.dispatch('purgeParticipantsStore', token)
-
- const hasUserStatuses = !!participants.headers['x-nextcloud-has-user-statuses']
- participants.data.ocs.data.forEach(participant => {
- this.$store.dispatch('addParticipant', {
- token,
- participant,
- })
- if (participant.participantType === PARTICIPANT.TYPE.GUEST
- || participant.participantType === PARTICIPANT.TYPE.GUEST_MODERATOR) {
- // FIXME replace mixin with composable. until then
- // guestNameStore should be set up at component level
- this.guestNameStore.addGuestName({
- token,
- actorId: Hex.stringify(SHA1(participant.sessionIds[0])),
- actorDisplayName: participant.displayName,
- }, { noUpdate: false })
- } else if (participant.actorType === 'users' && hasUserStatuses) {
- emit('user_status:status.updated', {
- status: participant.status,
- message: participant.statusMessage,
- icon: participant.statusIcon,
- clearAt: participant.statusClearAt,
- userId: participant.actorId,
- })
- }
- })
+ this.fetchingParticipants = true
+
+ const response = await this.$store.dispatch('fetchParticipants', { token: this.token })
+ if (response) {
this.participantsInitialised = true
- } catch (exception) {
- if (!Axios.isCancel(exception)) {
- console.error(exception)
- showError(t('spreed', 'An error occurred while fetching the participants'))
- }
- } finally {
- this.fetchingParticipants = false
}
+ this.fetchingParticipants = false
},
},
}
diff --git a/src/store/participantsStore.js b/src/store/participantsStore.js
index a0c09a33026..40fe7a6b3cd 100644
--- a/src/store/participantsStore.js
+++ b/src/store/participantsStore.js
@@ -19,9 +19,12 @@
* along with this program. If not, see .
*
*/
+import Hex from 'crypto-js/enc-hex.js'
+import SHA1 from 'crypto-js/sha1.js'
import Vue from 'vue'
import { showError } from '@nextcloud/dialogs'
+import { emit } from '@nextcloud/event-bus'
import { generateUrl } from '@nextcloud/router'
import { PARTICIPANT } from '../constants.js'
@@ -43,9 +46,12 @@ import {
removeAllPermissionsFromParticipant,
setPermissions,
setTyping,
+ fetchParticipants,
} from '../services/participantsService.js'
import SessionStorage from '../services/SessionStorage.js'
import { talkBroadcastChannel } from '../services/talkBroadcastChannel.js'
+import { useGuestNameStore } from '../stores/guestName.js'
+import CancelableRequest from '../utils/cancelableRequest.js'
const state = {
attendees: {
@@ -60,6 +66,12 @@ const state = {
},
speaking: {
},
+ /**
+ * Stores the cancel function returned by `cancelableFetchParticipants`,
+ * which allows to cancel the previous request for participants
+ * when quickly switching to a new conversation.
+ */
+ cancelFetchParticipants: null,
}
const getters = {
@@ -414,6 +426,10 @@ const mutations = {
Vue.delete(state.peers, token)
}
},
+
+ setCancelFetchParticipants(state, cancelFunction) {
+ state.cancelFetchParticipants = cancelFunction
+ },
}
const actions = {
@@ -536,6 +552,81 @@ const actions = {
commit('updateParticipant', { token, attendeeId: attendee.attendeeId, updatedData })
},
+ /**
+ * Fetches participants that belong to a particular conversation
+ * specified with its token.
+ *
+ * @param {object} context default store context;
+ * @param {object} data the wrapping object;
+ * @param {string} data.token the conversation token;
+ * @return {object|null}
+ */
+ async fetchParticipants(context, { token }) {
+ const guestNameStore = useGuestNameStore()
+ // Cancel a previous request
+ context.dispatch('cancelFetchParticipants')
+ // Get a new cancelable request function and cancel function pair
+ const { request, cancel } = CancelableRequest(fetchParticipants)
+ // Assign the new cancel function to our data value
+ context.commit('setCancelFetchParticipants', cancel)
+
+ try {
+ const response = await request(token)
+ context.dispatch('purgeParticipantsStore', token)
+
+ const hasUserStatuses = !!response.headers['x-nextcloud-has-user-statuses']
+
+ response.data.ocs.data.forEach(participant => {
+ context.dispatch('addParticipant', { token, participant })
+
+ if (participant.participantType === PARTICIPANT.TYPE.GUEST
+ || participant.participantType === PARTICIPANT.TYPE.GUEST_MODERATOR) {
+ guestNameStore.addGuestName({
+ token,
+ actorId: Hex.stringify(SHA1(participant.sessionIds[0])),
+ actorDisplayName: participant.displayName,
+ }, { noUpdate: false })
+ } else if (participant.actorType === 'users' && hasUserStatuses) {
+ emit('user_status:status.updated', {
+ status: participant.status,
+ message: participant.statusMessage,
+ icon: participant.statusIcon,
+ clearAt: participant.statusClearAt,
+ userId: participant.actorId,
+ })
+ }
+ })
+
+ // Discard current cancel function
+ context.commit('setCancelFetchParticipants', null)
+
+ return response
+ } catch (exception) {
+ if (exception?.response.status === 403) {
+ context.dispatch('fetchConversation', { token })
+ } else if (!CancelableRequest.isCancel(exception)) {
+ console.error(exception)
+ showError(t('spreed', 'An error occurred while fetching the participants'))
+ }
+ return null
+ }
+ },
+
+ /**
+ * Cancels a previously running "fetchParticipants" action if applicable.
+ *
+ * @param {object} context default store context;
+ * @return {boolean} true if a request got cancelled, false otherwise
+ */
+ cancelFetchParticipants(context) {
+ if (context.state.cancelFetchParticipants) {
+ context.state.cancelFetchParticipants('canceled')
+ context.commit('setCancelFetchParticipants', null)
+ return true
+ }
+ return false
+ },
+
async joinCall({ commit, getters }, { token, participantIdentifier, flags, silent }) {
if (!participantIdentifier?.sessionId) {
console.error('Trying to join call without sessionId')
diff --git a/src/store/participantsStore.spec.js b/src/store/participantsStore.spec.js
index 67018fa41aa..d01d4e269c7 100644
--- a/src/store/participantsStore.spec.js
+++ b/src/store/participantsStore.spec.js
@@ -1,13 +1,19 @@
import { createLocalVue } from '@vue/test-utils'
+import Hex from 'crypto-js/enc-hex.js'
+import SHA1 from 'crypto-js/sha1.js'
import mockConsole from 'jest-mock-console'
import { cloneDeep } from 'lodash'
+import { createPinia, setActivePinia } from 'pinia'
import Vuex from 'vuex'
+import { emit } from '@nextcloud/event-bus'
+
import { PARTICIPANT } from '../constants.js'
import {
joinCall,
leaveCall,
} from '../services/callsService.js'
+import { fetchConversation } from '../services/conversationsService.js'
import { EventBus } from '../services/EventBus.js'
import {
promoteToModerator,
@@ -16,12 +22,15 @@ import {
resendInvitations,
joinConversation,
leaveConversation,
+ fetchParticipants,
removeCurrentUserFromConversation,
grantAllPermissionsToParticipant,
removeAllPermissionsFromParticipant,
} from '../services/participantsService.js'
+import { useGuestNameStore } from '../stores/guestName.js'
import { generateOCSErrorResponse, generateOCSResponse } from '../test-helpers.js'
import participantsStore from './participantsStore.js'
+import storeConfig from './storeConfig.js'
jest.mock('../services/participantsService', () => ({
promoteToModerator: jest.fn(),
@@ -30,6 +39,7 @@ jest.mock('../services/participantsService', () => ({
resendInvitations: jest.fn(),
joinConversation: jest.fn(),
leaveConversation: jest.fn(),
+ fetchParticipants: jest.fn(),
removeCurrentUserFromConversation: jest.fn(),
grantAllPermissionsToParticipant: jest.fn(),
removeAllPermissionsFromParticipant: jest.fn(),
@@ -38,16 +48,27 @@ jest.mock('../services/callsService', () => ({
joinCall: jest.fn(),
leaveCall: jest.fn(),
}))
+jest.mock('../services/conversationsService', () => ({
+ fetchConversation: jest.fn(),
+}))
+
+jest.mock('@nextcloud/event-bus', () => ({
+ emit: jest.fn(),
+ subscribe: jest.fn(),
+}))
describe('participantsStore', () => {
const TOKEN = 'XXTOKENXX'
let testStoreConfig = null
let localVue = null
let store = null
+ let guestNameStore = null
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
+ setActivePinia(createPinia())
+ guestNameStore = useGuestNameStore()
testStoreConfig = cloneDeep(participantsStore)
store = new Vuex.Store(testStoreConfig)
@@ -359,12 +380,124 @@ describe('participantsStore', () => {
})
})
- describe('call handling', () => {
- beforeEach(() => {
+ describe('fetch participants', () => {
+ test('populates store for the fetched conversation', async () => {
+ // Arrange
+ const payload = [{
+ attendeeId: 1,
+ sessionId: 'session-id-1',
+ inCall: PARTICIPANT.CALL_FLAG.DISCONNECTED,
+ }]
+
+ fetchParticipants.mockResolvedValue(generateOCSResponse({ payload }))
+
+ // Act
+ await store.dispatch('fetchParticipants', { token: TOKEN })
+
+ // Assert
+ expect(store.getters.participantsList(TOKEN)).toMatchObject(payload)
+ })
+
+ test('saves a guest name from response', async () => {
+ // Arrange
+ const payload = [{
+ attendeeId: 1,
+ sessionIds: ['guest-session-id'],
+ actorId: 'guest-actor-id',
+ displayName: 'guest-name',
+ participantType: PARTICIPANT.TYPE.GUEST,
+ inCall: PARTICIPANT.CALL_FLAG.DISCONNECTED,
+ }]
+ const id = Hex.stringify(SHA1('guest-session-id'))
+
+ fetchParticipants.mockResolvedValue(generateOCSResponse({ payload }))
+
+ // Act
+ await store.dispatch('fetchParticipants', { token: TOKEN })
+
+ // Assert
+ expect(guestNameStore.getGuestName(TOKEN, id)).toBe('guest-name')
+ })
+
+ test('emits an user status update', async () => {
+ // Arrange
+ const payload = [{
+ attendeeId: 1,
+ actorId: 'actor-id',
+ displayName: 'guest-name',
+ actorType: 'users',
+ inCall: PARTICIPANT.CALL_FLAG.DISCONNECTED,
+ status: 'status',
+ statusMessage: 'statusMessage',
+ statusIcon: 'statusIcon',
+ statusClearAt: 'statusClearAt',
+ }]
+
+ fetchParticipants.mockResolvedValue(generateOCSResponse(
+ {
+ headers: { 'x-nextcloud-has-user-statuses': true },
+ payload,
+ }))
+
+ // Act
+ await store.dispatch('fetchParticipants', { token: TOKEN })
+
+ // Assert
+ expect(emit).toHaveBeenCalledWith('user_status:status.updated',
+ {
+ clearAt: 'statusClearAt',
+ icon: 'statusIcon',
+ message: 'statusMessage',
+ status: 'status',
+ userId: 'actor-id',
+ })
+ })
+
+ test('updates conversation if fail to fetch participants', async () => {
+ // Arrange
+ testStoreConfig = cloneDeep(storeConfig)
store = new Vuex.Store(testStoreConfig)
+ fetchParticipants.mockRejectedValue(generateOCSErrorResponse({
+ status: 403,
+ payload: [],
+ }))
+ fetchConversation.mockResolvedValue(generateOCSResponse(
+ {
+ payload: {},
+ }))
+ // Act
+ await store.dispatch('fetchParticipants', { token: TOKEN })
+
+ // Assert
+ expect(fetchConversation).toHaveBeenCalled()
})
- test('joins call', async () => {
+ test('cancels old request', async () => {
+ // Arrange
+ const payload = [{
+ attendeeId: 1,
+ sessionId: 'session-id-1',
+ inCall: PARTICIPANT.CALL_FLAG.DISCONNECTED,
+ }]
+ fetchParticipants.mockResolvedValue(generateOCSResponse({ payload }))
+
+ // Act
+ store.dispatch('fetchParticipants', { token: TOKEN })
+ await store.dispatch('fetchParticipants', { token: TOKEN })
+
+ // Assert
+ expect(fetchParticipants).toHaveBeenCalledTimes(2)
+ expect(fetchParticipants).toHaveBeenNthCalledWith(1, TOKEN, { cancelToken: { promise: expect.anything(), reason: expect.anything() } })
+ expect(fetchParticipants).toHaveBeenNthCalledWith(2, TOKEN, { cancelToken: { promise: expect.anything() } })
+ })
+ })
+
+ describe('call handling', () => {
+ const actualFlags = PARTICIPANT.CALL_FLAG.WITH_AUDIO
+ const flags = PARTICIPANT.CALL_FLAG.WITH_AUDIO | PARTICIPANT.CALL_FLAG.WITH_VIDEO
+
+ beforeEach(async () => {
+ store = new Vuex.Store(testStoreConfig)
store.dispatch('addParticipant', {
token: TOKEN,
participant: {
@@ -377,13 +510,9 @@ describe('participantsStore', () => {
// The requested flags and the actual flags can be different if some
// media device is not available.
- const actualFlags = PARTICIPANT.CALL_FLAG.WITH_AUDIO
joinCall.mockResolvedValue(actualFlags)
+ leaveCall.mockResolvedValue()
- expect(store.getters.isInCall(TOKEN)).toBe(false)
- expect(store.getters.isConnecting(TOKEN)).toBe(false)
-
- const flags = PARTICIPANT.CALL_FLAG.WITH_AUDIO | PARTICIPANT.CALL_FLAG.WITH_VIDEO
await store.dispatch('joinCall', {
token: TOKEN,
participantIdentifier: {
@@ -393,9 +522,13 @@ describe('participantsStore', () => {
flags,
silent: false,
})
+ })
+ test('joins call', async () => {
+ // Assert
expect(joinCall).toHaveBeenCalledWith(TOKEN, flags, false)
expect(store.getters.isInCall(TOKEN)).toBe(true)
+ expect(store.getters.isConnecting(TOKEN)).toBe(true)
expect(store.getters.participantsList(TOKEN)).toStrictEqual([
{
attendeeId: 1,
@@ -405,84 +538,36 @@ describe('participantsStore', () => {
},
])
- expect(store.getters.isConnecting(TOKEN)).toBe(true)
-
+ // Finished connecting to the call
EventBus.$emit('signaling-users-in-room')
expect(store.getters.isInCall(TOKEN)).toBe(true)
expect(store.getters.isConnecting(TOKEN)).toBe(false)
})
- })
-
- test('joins and leaves call', async () => {
- store.dispatch('addParticipant', {
- token: TOKEN,
- participant: {
- attendeeId: 1,
- sessionId: 'session-id-1',
- participantType: PARTICIPANT.TYPE.USER,
- inCall: PARTICIPANT.CALL_FLAG.DISCONNECTED,
- },
- })
-
- // The requested flags and the actual flags can be different if some
- // media device is not available.
- const actualFlags = PARTICIPANT.CALL_FLAG.WITH_AUDIO
- joinCall.mockResolvedValue(actualFlags)
-
- expect(store.getters.isInCall(TOKEN)).toBe(false)
- expect(store.getters.isConnecting(TOKEN)).toBe(false)
-
- const flags = PARTICIPANT.CALL_FLAG.WITH_AUDIO | PARTICIPANT.CALL_FLAG.WITH_VIDEO
- await store.dispatch('joinCall', {
- token: TOKEN,
- participantIdentifier: {
- attendeeId: 1,
- sessionId: 'session-id-1',
- },
- flags,
- silent: false,
- })
- expect(joinCall).toHaveBeenCalledWith(TOKEN, flags, false)
- expect(store.getters.isInCall(TOKEN)).toBe(true)
- expect(store.getters.participantsList(TOKEN)).toStrictEqual([
- {
- attendeeId: 1,
- sessionId: 'session-id-1',
- inCall: actualFlags,
- participantType: PARTICIPANT.TYPE.USER,
- },
- ])
-
- expect(store.getters.isConnecting(TOKEN)).toBe(true)
-
- EventBus.$emit('signaling-users-in-room')
-
- expect(store.getters.isInCall(TOKEN)).toBe(true)
- expect(store.getters.isConnecting(TOKEN)).toBe(false)
-
- leaveCall.mockResolvedValue()
+ test('leaves call', async () => {
+ // Act
+ await store.dispatch('leaveCall', {
+ token: TOKEN,
+ participantIdentifier: {
+ attendeeId: 1,
+ sessionId: 'session-id-1',
+ },
+ })
- await store.dispatch('leaveCall', {
- token: TOKEN,
- participantIdentifier: {
- attendeeId: 1,
- sessionId: 'session-id-1',
- },
+ // Assert
+ expect(leaveCall).toHaveBeenCalledWith(TOKEN, false)
+ expect(store.getters.isInCall(TOKEN)).toBe(false)
+ expect(store.getters.isConnecting(TOKEN)).toBe(false)
+ expect(store.getters.participantsList(TOKEN)).toStrictEqual([
+ {
+ attendeeId: 1,
+ sessionId: 'session-id-1',
+ inCall: PARTICIPANT.CALL_FLAG.DISCONNECTED,
+ participantType: PARTICIPANT.TYPE.USER,
+ },
+ ])
})
-
- expect(leaveCall).toHaveBeenCalledWith(TOKEN, false)
- expect(store.getters.isInCall(TOKEN)).toBe(false)
- expect(store.getters.isConnecting(TOKEN)).toBe(false)
- expect(store.getters.participantsList(TOKEN)).toStrictEqual([
- {
- attendeeId: 1,
- sessionId: 'session-id-1',
- inCall: PARTICIPANT.CALL_FLAG.DISCONNECTED,
- participantType: PARTICIPANT.TYPE.USER,
- },
- ])
})
test('resends invitations', async () => {
@@ -662,68 +747,70 @@ describe('participantsStore', () => {
})
})
- test('leaves conversation', async () => {
- leaveConversation.mockResolvedValue()
+ describe('leaving conversation', () => {
+ test('leaves conversation', async () => {
+ leaveConversation.mockResolvedValue()
- await store.dispatch('leaveConversation', { token: TOKEN })
-
- expect(leaveCall).not.toHaveBeenCalled()
- expect(leaveConversation).toHaveBeenCalledWith(TOKEN)
- })
+ await store.dispatch('leaveConversation', { token: TOKEN })
- test('leaves conversation while in call', async () => {
- testStoreConfig.getters.getParticipantIdentifier = () => jest.fn().mockReturnValue({
- attendeeId: 1,
- sessionId: 'session-id-1',
+ expect(leaveCall).not.toHaveBeenCalled()
+ expect(leaveConversation).toHaveBeenCalledWith(TOKEN)
})
- store = new Vuex.Store(testStoreConfig)
- store.dispatch('addParticipant', {
- token: TOKEN,
- participant: {
+ test('leaves conversation while in call', async () => {
+ testStoreConfig.getters.getParticipantIdentifier = () => jest.fn().mockReturnValue({
attendeeId: 1,
sessionId: 'session-id-1',
- participantType: PARTICIPANT.TYPE.USER,
- inCall: PARTICIPANT.CALL_FLAG.DISCONNECTED,
- },
- })
+ })
+ store = new Vuex.Store(testStoreConfig)
- const flags = PARTICIPANT.CALL_FLAG.WITH_AUDIO | PARTICIPANT.CALL_FLAG.WITH_VIDEO
- await store.dispatch('joinCall', {
- token: TOKEN,
- participantIdentifier: {
- attendeeId: 1,
- sessionId: 'session-id-1',
- },
- flags,
- silent: false,
- })
+ store.dispatch('addParticipant', {
+ token: TOKEN,
+ participant: {
+ attendeeId: 1,
+ sessionId: 'session-id-1',
+ participantType: PARTICIPANT.TYPE.USER,
+ inCall: PARTICIPANT.CALL_FLAG.DISCONNECTED,
+ },
+ })
- expect(store.getters.isInCall(TOKEN)).toBe(true)
+ const flags = PARTICIPANT.CALL_FLAG.WITH_AUDIO | PARTICIPANT.CALL_FLAG.WITH_VIDEO
+ await store.dispatch('joinCall', {
+ token: TOKEN,
+ participantIdentifier: {
+ attendeeId: 1,
+ sessionId: 'session-id-1',
+ },
+ flags,
+ silent: false,
+ })
- leaveConversation.mockResolvedValue()
+ expect(store.getters.isInCall(TOKEN)).toBe(true)
- await store.dispatch('leaveConversation', { token: TOKEN })
+ leaveConversation.mockResolvedValue()
- expect(store.getters.isInCall(TOKEN)).toBe(false)
- expect(leaveCall).toHaveBeenCalledWith(TOKEN, false)
- expect(leaveConversation).toHaveBeenCalledWith(TOKEN)
- })
+ await store.dispatch('leaveConversation', { token: TOKEN })
- test('removes current user from conversation', async () => {
- removeCurrentUserFromConversation.mockResolvedValue()
+ expect(store.getters.isInCall(TOKEN)).toBe(false)
+ expect(leaveCall).toHaveBeenCalledWith(TOKEN, false)
+ expect(leaveConversation).toHaveBeenCalledWith(TOKEN)
+ })
- testStoreConfig = cloneDeep(participantsStore)
- testStoreConfig.actions.deleteConversation = jest.fn()
- store = new Vuex.Store(testStoreConfig)
+ test('removes current user from conversation', async () => {
+ removeCurrentUserFromConversation.mockResolvedValue()
+
+ testStoreConfig = cloneDeep(participantsStore)
+ testStoreConfig.actions.deleteConversation = jest.fn()
+ store = new Vuex.Store(testStoreConfig)
- await store.dispatch('removeCurrentUserFromConversation', { token: TOKEN })
+ await store.dispatch('removeCurrentUserFromConversation', { token: TOKEN })
- expect(removeCurrentUserFromConversation).toHaveBeenCalledWith(TOKEN)
- expect(testStoreConfig.actions.deleteConversation).toHaveBeenCalledWith(expect.anything(), TOKEN)
+ expect(removeCurrentUserFromConversation).toHaveBeenCalledWith(TOKEN)
+ expect(testStoreConfig.actions.deleteConversation).toHaveBeenCalledWith(expect.anything(), TOKEN)
+ })
})
- describe('participantsStore', () => {
+ describe('participant permissions', () => {
beforeEach(() => {
store.dispatch('addParticipant', {
token: TOKEN,