From 79a5af7db53e73563c92f1f57935942552aeff46 Mon Sep 17 00:00:00 2001 From: Maksim Sukharev Date: Tue, 1 Oct 2024 17:59:29 +0200 Subject: [PATCH] fix: refactor actions - handleJoinCall - setCallViewMode - startPresentation - stopPresentation Signed-off-by: Maksim Sukharev --- src/components/CallView/CallView.vue | 16 ++-- src/components/CallView/Grid/Grid.vue | 4 +- src/components/CallView/shared/LocalVideo.vue | 2 +- .../CallView/shared/VideoBottomBar.spec.js | 29 +++----- .../CallView/shared/VideoBottomBar.vue | 2 +- src/components/TopBar/TopBarMenu.vue | 2 +- src/store/participantsStore.js | 4 + src/store/participantsStore.spec.js | 5 ++ src/stores/callView.js | 73 +++++++------------ 9 files changed, 59 insertions(+), 78 deletions(-) diff --git a/src/components/CallView/CallView.vue b/src/components/CallView/CallView.vue index 35ed75b2202..0e6de1e5f93 100644 --- a/src/components/CallView/CallView.vue +++ b/src/components/CallView/CallView.vue @@ -425,19 +425,19 @@ export default { callParticipantModelsWithScreen(newValue, previousValue) { // Everytime a new screen is shared, switch to promoted view if (newValue.length > previousValue.length) { - this.$store.dispatch('startPresentation') + this.callViewStore.startPresentation() } else if (newValue.length === 0 && previousValue.length > 0 && !this.hasLocalScreen && !this.selectedVideoPeerId) { // last screen share stopped and no selected video, restoring previous state - this.$store.dispatch('stopPresentation') + this.callViewStore.stopPresentation() } }, showLocalScreen(showLocalScreen) { // Everytime the local screen is shared, switch to promoted view if (showLocalScreen) { - this.$store.dispatch('startPresentation') + this.callViewStore.startPresentation() } else if (this.callParticipantModelsWithScreen.length === 0 && !this.selectedVideoPeerId) { // last screen share stopped and no selected video, restoring previous state - this.$store.dispatch('stopPresentation') + this.callViewStore.stopPresentation() } }, hasLocalVideo(newValue) { @@ -656,13 +656,13 @@ export default { } if (this.callViewStore.presentationStarted) { - this.$store.dispatch('setCallViewMode', { + this.callViewStore.setCallViewMode({ isGrid: false, isStripeOpen: false, clearLast: false, }) } else { - this.$store.dispatch('startPresentation') + this.callViewStore.startPresentation() } this.callViewStore.setSelectedVideoPeerId(null) this.screens.splice(index, 1) @@ -694,7 +694,7 @@ export default { return } this.callViewStore.setSelectedVideoPeerId(peerId) - this.$store.dispatch('startPresentation') + this.callViewStore.startPresentation() }, handleClickLocalVideo() { // DO nothing if no video @@ -703,7 +703,7 @@ export default { } // Deselect possible selected video this.callViewStore.setSelectedVideoPeerId('local') - this.$store.dispatch('startPresentation') + this.callViewStore.startPresentation() }, async fetchPeers() { diff --git a/src/components/CallView/Grid/Grid.vue b/src/components/CallView/Grid/Grid.vue index 92e6efcf3ce..692ea6e93fc 100644 --- a/src/components/CallView/Grid/Grid.vue +++ b/src/components/CallView/Grid/Grid.vue @@ -557,7 +557,7 @@ export default { return this.isStripe }, set(value) { - this.$store.dispatch('setCallViewMode', { isGrid: !value, clearLast: false }) + this.callViewStore.setCallViewMode({ isGrid: !value, clearLast: false }) }, }, }, @@ -852,7 +852,7 @@ export default { }, handleClickStripeCollapse() { - this.$store.dispatch('setCallViewMode', { isStripeOpen: !this.stripeOpen, clearLast: false }) + this.callViewStore.setCallViewMode({ isStripeOpen: !this.stripeOpen, clearLast: false }) }, handleMovement() { diff --git a/src/components/CallView/shared/LocalVideo.vue b/src/components/CallView/shared/LocalVideo.vue index 7d1777dae3b..61e6beb318d 100644 --- a/src/components/CallView/shared/LocalVideo.vue +++ b/src/components/CallView/shared/LocalVideo.vue @@ -339,7 +339,7 @@ export default { handleStopFollowing() { this.callViewStore.setSelectedVideoPeerId(null) - this.$store.dispatch('stopPresentation') + this.callViewStore.stopPresentation() }, updateContainerAspectRatio([{ target }]) { diff --git a/src/components/CallView/shared/VideoBottomBar.spec.js b/src/components/CallView/shared/VideoBottomBar.spec.js index 1a4efa32f8e..ea598bca939 100644 --- a/src/components/CallView/shared/VideoBottomBar.spec.js +++ b/src/components/CallView/shared/VideoBottomBar.spec.js @@ -4,6 +4,7 @@ */ import { createLocalVue, shallowMount } from '@vue/test-utils' import { cloneDeep } from 'lodash' +import { createPinia, setActivePinia } from 'pinia' import Vuex, { Store } from 'vuex' import AlertCircle from 'vue-material-design-icons/AlertCircle.vue' @@ -20,6 +21,7 @@ import VideoBottomBar from './VideoBottomBar.vue' import { CONVERSATION, PARTICIPANT } from '../../../constants.js' import storeConfig from '../../../store/storeConfig.js' +import { useCallViewStore } from '../../../stores/callView.js' import { findNcButton } from '../../../test-helpers.js' import { ConnectionState } from '../../../utils/webrtc/models/CallParticipantModel.js' @@ -35,10 +37,10 @@ describe('VideoBottomBar.vue', () => { const USER_ID = 'user-id-1' let localVue let store + let callViewStore let testStoreConfig let componentProps let conversationProps - let selectedVideoPeerIdMock const audioIndicatorAriaLabels = [t('spreed', 'Mute'), t('spreed', 'Muted')] const videoIndicatorAriaLabels = [t('spreed', 'Disable video'), t('spreed', 'Enable video')] @@ -48,6 +50,8 @@ describe('VideoBottomBar.vue', () => { beforeEach(() => { localVue = createLocalVue() localVue.use(Vuex) + setActivePinia(createPinia()) + callViewStore = useCallViewStore() conversationProps = { token: TOKEN, @@ -85,12 +89,6 @@ describe('VideoBottomBar.vue', () => { testStoreConfig = cloneDeep(storeConfig) testStoreConfig.modules.conversationsStore.getters.conversation = jest.fn().mockReturnValue((token) => conversationProps) testStoreConfig.modules.actorStore.getters.getUserId = jest.fn().mockReturnValue(() => USER_ID) - - selectedVideoPeerIdMock = jest.fn().mockReturnValue(() => null) - testStoreConfig.modules.callViewStore.getters.selectedVideoPeerId = selectedVideoPeerIdMock() - testStoreConfig.modules.callViewStore.actions.stopPresentation = jest.fn() - testStoreConfig.modules.callViewStore.actions.selectedVideoPeerId = jest.fn() - store = new Store(testStoreConfig) }) @@ -515,10 +513,7 @@ describe('VideoBottomBar.vue', () => { }) test('button is rendered when source is selected', () => { - selectedVideoPeerIdMock = jest.fn().mockReturnValue(() => PEER_ID) - testStoreConfig.modules.callViewStore.getters.selectedVideoPeerId = selectedVideoPeerIdMock() - store = new Store(testStoreConfig) - + callViewStore.setSelectedVideoPeerId(PEER_ID) componentProps.isBig = true const wrapper = shallowMount(VideoBottomBar, { localVue, @@ -531,9 +526,10 @@ describe('VideoBottomBar.vue', () => { }) test('method is called after click', async () => { - selectedVideoPeerIdMock = jest.fn().mockReturnValue(() => PEER_ID) - testStoreConfig.modules.callViewStore.getters.selectedVideoPeerId = selectedVideoPeerIdMock() - store = new Store(testStoreConfig) + callViewStore.setSelectedVideoPeerId(PEER_ID) + callViewStore.startPresentation() + expect(callViewStore.selectedVideoPeerId).toBe(PEER_ID) + expect(callViewStore.presentationStarted).toBeTruthy() componentProps.isBig = true const wrapper = shallowMount(VideoBottomBar, { @@ -548,9 +544,8 @@ describe('VideoBottomBar.vue', () => { const followingButton = findNcButton(wrapper, followingButtonAriaLabel) await followingButton.trigger('click') - expect(testStoreConfig.modules.callViewStore.actions.stopPresentation).toHaveBeenCalled() - expect(testStoreConfig.modules.callViewStore.actions.selectedVideoPeerId).toHaveBeenCalled() - expect(testStoreConfig.modules.callViewStore.actions.selectedVideoPeerId).toHaveBeenCalledWith(expect.anything(), null) + expect(callViewStore.selectedVideoPeerId).toBe(null) + expect(callViewStore.presentationStarted).toBeFalsy() }) }) }) diff --git a/src/components/CallView/shared/VideoBottomBar.vue b/src/components/CallView/shared/VideoBottomBar.vue index 841cd3d5861..2f9a2814ef4 100644 --- a/src/components/CallView/shared/VideoBottomBar.vue +++ b/src/components/CallView/shared/VideoBottomBar.vue @@ -286,7 +286,7 @@ export default { }, handleStopFollowing() { - this.$store.dispatch('stopPresentation') + this.callViewStore.stopPresentation() this.callViewStore.setSelectedVideoPeerId(null) }, }, diff --git a/src/components/TopBar/TopBarMenu.vue b/src/components/TopBar/TopBarMenu.vue index 9a5656a47ca..18791370182 100644 --- a/src/components/TopBar/TopBarMenu.vue +++ b/src/components/TopBar/TopBarMenu.vue @@ -437,7 +437,7 @@ export default { }, changeView() { - this.$store.dispatch('setCallViewMode', { isGrid: !this.isGrid, clearLast: false }) + this.callViewStore.setCallViewMode({ isGrid: !this.isGrid, clearLast: false }) this.callViewStore.setSelectedVideoPeerId(null) }, diff --git a/src/store/participantsStore.js b/src/store/participantsStore.js index d6803b3770a..66b3ef4ca3b 100644 --- a/src/store/participantsStore.js +++ b/src/store/participantsStore.js @@ -36,6 +36,7 @@ import { } from '../services/participantsService.js' import SessionStorage from '../services/SessionStorage.js' import { talkBroadcastChannel } from '../services/talkBroadcastChannel.js' +import { useCallViewStore } from '../stores/callView.js' import { useGuestNameStore } from '../stores/guestName.js' import CancelableRequest from '../utils/cancelableRequest.js' @@ -842,6 +843,9 @@ const actions = { // "Waiting for others to join the call …" after some seconds. commit('finishedConnecting', { token, sessionId: participantIdentifier.sessionId }) }, 10000) + + const callViewStore = useCallViewStore() + callViewStore.handleJoinCall({ token }) }, async leaveCall({ commit, getters }, { token, participantIdentifier, all = false }) { diff --git a/src/store/participantsStore.spec.js b/src/store/participantsStore.spec.js index a222f0cfa27..36fd7494d88 100644 --- a/src/store/participantsStore.spec.js +++ b/src/store/participantsStore.spec.js @@ -56,6 +56,11 @@ jest.mock('../services/callsService', () => ({ jest.mock('../services/conversationsService', () => ({ fetchConversation: jest.fn(), })) +jest.mock('../stores/callView', () => ({ + useCallViewStore: jest.fn(() => ({ + handleJoinCall: jest.fn(), + })), +})) jest.mock('@nextcloud/event-bus', () => ({ emit: jest.fn(), diff --git a/src/stores/callView.js b/src/stores/callView.js index 431501db63d..2cec27a8ea4 100644 --- a/src/stores/callView.js +++ b/src/stores/callView.js @@ -7,6 +7,7 @@ import { defineStore } from 'pinia' import { CONVERSATION } from '../constants.js' import BrowserStorage from '../services/BrowserStorage.js' +import store from '../store/index.js' export const useCallViewStore = defineStore('callView', { state: () => ({ @@ -27,8 +28,6 @@ export const useCallViewStore = defineStore('callView', { }, actions: { - // Mutations - // Actions setForceCallView(value) { this.forceCallView = value }, @@ -37,35 +36,33 @@ export const useCallViewStore = defineStore('callView', { this.isViewerOverlay = value }, + setIsEmptyCallView(value) { + this.isEmptyCallView = value + }, + setSelectedVideoPeerId(value) { this.selectedVideoPeerId = value }, - joinCall(context, { token }) { - let isGrid = BrowserStorage.getItem('callprefs-' + token + '-isgrid') - if (isGrid === null) { - const conversationType = context.getters.conversations[token].type - // default to grid view for group/public calls, otherwise speaker view - isGrid = (conversationType === CONVERSATION.TYPE.GROUP - || conversationType === CONVERSATION.TYPE.PUBLIC) - } else { - // BrowserStorage.getItem returns a string instead of a boolean - isGrid = (isGrid === 'true') - } - context.dispatch('setCallViewMode', { isGrid, isStripeOpen: true }) + handleJoinCall({ token }) { + const gridPreference = BrowserStorage.getItem(`callprefs-${token}-isgrid`) + const isGrid = gridPreference === null + // not defined yet, default to grid view for group/public calls, otherwise speaker view + ? [CONVERSATION.TYPE.GROUP, CONVERSATION.TYPE.PUBLIC].includes(store.getters.conversations[token].type) + : gridPreference === 'true' + this.setCallViewMode({ isGrid, isStripeOpen: true }) }, /** * Sets the current call view mode and saves it in preferences. * If clearLast is false, also remembers it in separate properties. * - * @param {object} context default store context; * @param {object} data the wrapping object; * @param {boolean|null} [data.isGrid=null] true for enabled grid mode, false for speaker view; * @param {boolean|null} [data.isStripeOpen=null] true for visible striped mode, false for speaker view; * @param {boolean} [data.clearLast=true] set false to not reset last temporary remembered state; */ - setCallViewMode(context, { isGrid = null, isStripeOpen = null, clearLast = true }) { + setCallViewMode({ isGrid = null, isStripeOpen = null, clearLast = true }) { if (clearLast) { this.lastIsGrid = null this.lastIsStripeOpen = null @@ -73,7 +70,7 @@ export const useCallViewStore = defineStore('callView', { if (isGrid !== null) { this.lastIsGrid = this.isGrid - BrowserStorage.setItem('callprefs-' + context.getters.getToken() + '-isgrid', isGrid) + BrowserStorage.setItem(`callprefs-${store.getters.getToken()}-isgrid`, isGrid) this.isGrid = isGrid } @@ -88,53 +85,33 @@ export const useCallViewStore = defineStore('callView', { * * Switches off grid mode and closes the stripe. * Remembers the call view state for after the end of the presentation. - * - * @param {object} context default store context; */ - startPresentation(context) { - // don't start twice, this would prevent multiple - // screen shares to clear the last call view state + startPresentation() { + // don't start twice, this would prevent multiple screen shares to clear the last call view state if (this.presentationStarted) { return } - this.presentationStarted = true - // switch off grid mode during presentation and collapse - // the stripe to focus on the screen share, but continue remembering - // the last state - context.dispatch('setCallViewMode', { - isGrid: false, - isStripeOpen: false, - clearLast: false, - }) + + this.setCallViewMode({ isGrid: false, isStripeOpen: false, clearLast: false }) }, /** * Stops presentation mode. * - * Restores call view state from before starting the presentation, given - * that the last state was not cleared manually. - * - * @param {object} context default store context; + * Restores call view state from before starting the presentation, + * given that the last state was not cleared manually. */ - stopPresentation(context) { + stopPresentation() { if (!this.presentationStarted) { return } - if (!this.isGrid && !this.isStripeOpen) { - // User didn't pick grid view during presentation - // restore previous state - context.dispatch('setCallViewMode', { - isGrid: this.lastIsGrid, - isStripeOpen: this.lastIsStripeOpen, - clearLast: false, - }) - } this.presentationStarted = false - }, - setIsEmptyCallView(value) { - this.isEmptyCallView = value + if (!this.isGrid && !this.isStripeOpen) { + // User didn't pick grid view during presentation, restore previous state + this.setCallViewMode({ isGrid: this.lastIsGrid, isStripeOpen: this.lastIsStripeOpen, clearLast: false }) + } }, /**