Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(participantsStore) - handle participants fetch through the store #10523

Merged
merged 5 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions src/components/RightSidebar/Participants/ParticipantsTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -99,11 +98,8 @@ export default {
setup() {
const { sortParticipants } = useSortParticipants()
// FIXME move to getParticipants when replace with composable
const guestNameStore = useGuestNameStore()
return {
guestNameStore,
sortParticipants,
}
},
Expand Down
9 changes: 7 additions & 2 deletions src/components/RightSidebar/RightSidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
},
Expand Down
28 changes: 2 additions & 26 deletions src/components/TopBar/TopBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -196,7 +195,7 @@ export default {
mixins: [
richEditor,
getParticipants,
isInLobby,
],
props: {
Expand All @@ -214,15 +213,6 @@ export default {
},
},
setup() {
// FIXME move to getParticipants when replace with composable
const guestNameStore = useGuestNameStore()
return {
guestNameStore,
}
},
data: () => {
return {
unreadNotificationHandle: null,
Expand Down Expand Up @@ -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() {
Expand All @@ -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: {
Expand Down
67 changes: 10 additions & 57 deletions src/mixins/getParticipants.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
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 = {
Expand All @@ -41,10 +34,6 @@ const getParticipants = {
data() {
return {
participantsInitialised: false,
/**
* Stores the cancel function for cancelableGetParticipants
*/
cancelGetParticipants: () => {},
fetchingParticipants: false,
}
},
Expand All @@ -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() {
Expand Down Expand Up @@ -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
},
},
}
Expand Down
91 changes: 91 additions & 0 deletions src/store/participantsStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
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'
Expand All @@ -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: {
Expand All @@ -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 = {
Expand Down Expand Up @@ -414,6 +426,10 @@ const mutations = {
Vue.delete(state.peers, token)
}
},

setCancelFetchParticipants(state, cancelFunction) {
state.cancelFetchParticipants = cancelFunction
},
}

const actions = {
Expand Down Expand Up @@ -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')
Expand Down
Loading
Loading