Browse Source

Merge pull request #8738 from nextcloud/feature/8339/allow-moderators-to-move-participants

Add participant editor to breakoutroomstab
pull/8742/head
Marco 3 years ago
committed by GitHub
parent
commit
cf83f5ee4b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 99
      src/components/BreakoutRoomsEditor/BreakoutRoomsParticipantsEditor.vue
  2. 56
      src/components/RightSidebar/BreakoutRooms/BreakoutRoomsTab.vue
  3. 17
      src/services/breakoutRoomsService.js
  4. 26
      src/store/breakoutRoomsStore.js

99
src/components/BreakoutRoomsEditor/BreakoutRoomsParticipantsEditor.vue

@ -55,7 +55,9 @@
</template>
</div>
<div class="participants-editor__buttons">
<NcButton type="tertiary" @click="goBack">
<NcButton v-if="!isReorganizingAttendees"
type="tertiary"
@click="goBack">
<template #icon>
<!-- TODO: choose final icon -->
<ArrowLeft :size="20" />
@ -66,10 +68,10 @@
<template #icon>
<Reload :size="20" />
</template>
{{ t('spreed', 'Reset') }}
{{ resetButtonLabel }}
</NcButton>
<NcActions v-if="hasSelected"
:menu-title="t('spreed', 'Assign participants')">
:menu-title="t('spreed', 'Assign')">
<NcActionButton v-for="(item, index) in assignments"
:key="index"
:close-after-click="true"
@ -81,8 +83,10 @@
{{ roomName(index) }}
</NcActionButton>
</NcActions>
<NcButton type="primary" @click="handleCreateRooms">
{{ t('spreed', 'Create breakout rooms') }}
<NcButton :disabled="!hasAssigned"
type="primary"
@click="handleSubmit">
{{ confirmButtonLabel }}
</NcButton>
</div>
</div>
@ -121,7 +125,12 @@ export default {
roomNumber: {
type: Number,
required: true,
default: undefined,
},
breakoutRooms: {
type: Array,
default: undefined,
},
},
@ -168,6 +177,20 @@ export default {
hasUnassigned() {
return this.unassignedParticipants.length > 0
},
// If the breakoutRooms prop is populated it means that this component is
// being used to reorganize the attendees of an existing breakout room.
isReorganizingAttendees() {
return this.breakoutRooms?.length
},
confirmButtonLabel() {
return this.isReorganizingAttendees ? t('spreed', 'Confirm') : t('spreed', 'Create breakout rooms')
},
resetButtonLabel() {
return t('spreed', 'Reset')
},
},
created() {
@ -175,12 +198,45 @@ export default {
},
methods: {
initialiseAssignments() {
this.assignments = Array.from(Array(this.roomNumber), () => [])
/**
* Initialise the assignments array.
*
* @param forceReset {boolean} If true, the assignments array will be reset if the breakoutRooms prop is populated.
*/
initialiseAssignments(forceReset) {
if (this.isReorganizingAttendees && !forceReset) {
this.assignments = this.breakoutRooms.map(room => {
const participantInBreakoutRoomActorIdList = this.$store.getters.participantsList(room.token)
.map(participant => participant.actorId)
return this.participants.filter(participant => {
return participantInBreakoutRoomActorIdList.includes(participant.actorId)
}).map(participant => participant.attendeeId)
})
} else {
this.assignments = Array.from(Array(this.isReorganizingAttendees
? this.breakoutRooms.length
: this.roomNumber), () => [])
}
},
assignAttendees(roomIndex) {
this.assignments[roomIndex].push(...this.selectedParticipants)
this.selectedParticipants.forEach(attendeeId => {
if (this.unassignedParticipants.find(participant => participant.attendeeId === attendeeId)) {
this.assignments[roomIndex].push(attendeeId)
return
}
const assignedRoomIndex = this.assignments.findIndex(room => room.includes(attendeeId))
if (assignedRoomIndex === roomIndex) {
return
}
this.assignments[assignedRoomIndex].splice(this.assignments[assignedRoomIndex].findIndex(id => id === attendeeId), 1)
this.assignments[roomIndex].push(attendeeId)
})
this.selectedParticipants = []
},
@ -192,26 +248,41 @@ export default {
resetAssignments() {
this.selectedParticipants = []
this.assignments = []
this.initialiseAssignments()
this.initialiseAssignments(true)
},
goBack() {
this.$emit('back')
},
handleCreateRooms() {
let attendeeMap = {}
handleSubmit() {
this.isReorganizingAttendees ? this.reorganizeAttendees() : this.createRooms()
},
createAttendeeMap() {
const attendeeMap = {}
this.assignments.forEach((room, index) => {
room.forEach(attendeeId => {
attendeeMap[attendeeId] = index
})
})
attendeeMap = JSON.stringify(attendeeMap)
return JSON.stringify(attendeeMap)
},
createRooms() {
this.$store.dispatch('configureBreakoutRoomsAction', {
token: this.token,
mode: 2,
amount: this.roomNumber,
attendeeMap,
attendeeMap: this.createAttendeeMap(),
})
this.$emit('close')
},
reorganizeAttendees() {
this.$store.dispatch('reorganizeAttendeesAction', {
token: this.token,
attendeeMap: this.createAttendeeMap(),
})
this.$emit('close')
},

56
src/components/RightSidebar/BreakoutRooms/BreakoutRoomsTab.vue

@ -56,13 +56,13 @@
</template>
</div>
<div class="breakout-rooms__actions-group">
<!-- Configuration button -->
<NcButton :type="breakoutRoomsConfigured ? 'tertiary' : 'secondary'"
:title="configurationButtonTitle"
:aria-label="configurationButtonTitle"
@click="openBreakoutRoomsEditor">
<!-- Re-arrange participants button -->
<NcButton type="tertiary"
:title="moveParticipantsButtonTitle"
:aria-label="moveParticipantsButtonTitle"
@click="openParticipantsEditor">
<template #icon>
<Reload :size="20" />
<AccountMultiple :size="20" />
</template>
</NcButton>
<NcButton v-if="breakoutRoomsConfigured"
@ -86,10 +86,18 @@
</template>
</ul>
<!-- Breakout rooms editor -->
<BreakoutRoomsEditor v-if="showBreakoutRoomsEditor"
:token="token"
@close="showBreakoutRoomsEditor = false" />
<!-- Participants editor -->
<NcModal v-if="showParticipantsEditor"
@close="closeParticipantsEditor">
<div class="breakout-rooms__editor">
<h2> {{ moveParticipantsButtonTitle }} </h2>
<BreakoutRoomsParticipantsEditor :token="token"
:breakout-rooms="breakoutRooms"
:is-creating-rooms="false"
@close="closeParticipantsEditor"
v-on="$listeners" />
</div>
</NcModal>
<!-- Send message dialog -->
<SendMessageDialog v-if="sendMessageDialogOpened"
@ -103,7 +111,8 @@
<script>
// Components
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
import BreakoutRoomsEditor from '../../BreakoutRoomsEditor/BreakoutRoomsEditor.vue'
import NcModal from '@nextcloud/vue/dist/Components/NcModal.js'
import BreakoutRoomsParticipantsEditor from '../../BreakoutRoomsEditor/BreakoutRoomsParticipantsEditor.vue'
import SendMessageDialog from '../../BreakoutRoomsEditor/SendMessageDialog.vue'
import BreakoutRoomItem from './BreakoutRoomItem.vue'
@ -111,7 +120,7 @@ import BreakoutRoomItem from './BreakoutRoomItem.vue'
import Delete from 'vue-material-design-icons/Delete.vue'
import Play from 'vue-material-design-icons/Play.vue'
import StopIcon from 'vue-material-design-icons/Stop.vue'
import Reload from 'vue-material-design-icons/Reload.vue'
import AccountMultiple from 'vue-material-design-icons/AccountMultiple.vue'
import Message from 'vue-material-design-icons/Message.vue'
// Constants
@ -126,14 +135,15 @@ export default {
components: {
// Components
NcButton,
BreakoutRoomsEditor,
BreakoutRoomsParticipantsEditor,
SendMessageDialog,
BreakoutRoomItem,
NcModal,
// Icons
Delete,
Play,
Reload,
AccountMultiple,
StopIcon,
Message,
},
@ -159,7 +169,7 @@ export default {
data() {
return {
showBreakoutRoomsEditor: false,
showParticipantsEditor: false,
sendMessageDialogOpened: false,
breakoutRoomsParticipantsInterval: undefined,
}
@ -188,8 +198,8 @@ export default {
return this.conversation.breakoutRoomStatus !== CONVERSATION.BREAKOUT_ROOM_STATUS.STARTED
},
configurationButtonTitle() {
return this.breakoutRoomsConfigured ? t('spreed', 'Re-configure breakout rooms') : t('spreed', 'Configure breakout rooms')
moveParticipantsButtonTitle() {
return t('spreed', 'Reorganize participants')
},
hasBreakoutRooms() {
@ -303,6 +313,14 @@ export default {
closeSendMessageDialog() {
this.sendMessageDialogOpened = false
},
openParticipantsEditor() {
this.showParticipantsEditor = true
},
closeParticipantsEditor() {
this.showParticipantsEditor = false
},
},
}
</script>
@ -325,6 +343,10 @@ export default {
&__room {
margin-top: var(--default-grid-baseline);
}
&__editor {
padding: 20px;
}
}
::v-deep .app-navigation-entry__title {

17
src/services/breakoutRoomsService.js

@ -40,6 +40,22 @@ const configureBreakoutRooms = async function(token, mode, amount, attendeeMap)
})
}
/**
* Resets the request assistance
*
* @param token the breakout room token
* @param attendeeMap A json encoded Map of attendeeId => room number (0 based)
* @return {Promise<AxiosResponse<any>>} The array of conversations
*/
const reorganizeAttendees = async function(token, attendeeMap) {
return await axios.post(generateOcsUrl('/apps/spreed/api/v1/breakout-rooms/{token}/attendees', {
token,
}), {
attendeeMap,
}
)
}
/**
* Deletes all breakout rooms for a given conversation
*
@ -133,6 +149,7 @@ const resetRequestAssistance = async function(token) {
export {
configureBreakoutRooms,
reorganizeAttendees,
deleteBreakoutRooms,
getBreakoutRooms,
startBreakoutRooms,

26
src/store/breakoutRoomsStore.js

@ -29,6 +29,7 @@ import {
getBreakoutRoomsParticipants,
requestAssistance,
resetRequestAssistance,
reorganizeAttendees,
} from '../services/breakoutRoomsService.js'
import { showError } from '@nextcloud/dialogs'
import { set } from 'vue'
@ -99,6 +100,21 @@ const actions = {
}
},
async reorganizeAttendeesAction(context, { token, attendeeMap }) {
try {
const response = await reorganizeAttendees(token, attendeeMap)
// Get the participants of the breakout rooms
context.dispatch('getBreakoutRoomsParticipantsAction', { token })
// Add breakout rooms and conversations to the conversations store
response.data.ocs.data.forEach(conversation => {
context.commit('addConversation', conversation)
})
} catch (error) {
console.error(error)
showError(t('spreed', 'An error occurred while re-ordering the attendees'))
}
},
async deleteBreakoutRoomsAction(context, { token }) {
try {
const response = await deleteBreakoutRooms(token)
@ -163,6 +179,16 @@ const actions = {
async getBreakoutRoomsParticipantsAction(context, { token }) {
try {
const response = await getBreakoutRoomsParticipants(token)
// Purge the participants of the breakout rooms before adding the updated ones
context.state.breakoutRoomsReferences[token].forEach(breakoutRoomToken => {
context.commit('purgeParticipantsStore', breakoutRoomToken)
})
// Purge the participants of the main room
context.commit('purgeParticipantsStore', token)
// Add the participants of the breakout rooms to the participants store
response.data.ocs.data.forEach(participant => {
context.dispatch('addParticipant', {
token: participant.roomToken,

Loading…
Cancel
Save