summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMaksim Sukharev <antreesy.web@gmail.com>2024-06-19 11:54:15 +0200
committerMaksim Sukharev <antreesy.web@gmail.com>2024-06-19 14:31:35 +0200
commitdb3a2edf9757d494b89e9e2a0231b7883ad6269c (patch)
tree5a88196085e5ec6dc1b7d7debc6e8f2eec833230 /src
parent04b1d46a41affedd2065d98674beface67e437a3 (diff)
feat(ban): ban participant from RightSidebar
Signed-off-by: Maksim Sukharev <antreesy.web@gmail.com>
Diffstat (limited to 'src')
-rw-r--r--src/components/RightSidebar/Participants/Participant.spec.js82
-rw-r--r--src/components/RightSidebar/Participants/Participant.vue31
-rw-r--r--src/store/participantsStore.js20
3 files changed, 130 insertions, 3 deletions
diff --git a/src/components/RightSidebar/Participants/Participant.spec.js b/src/components/RightSidebar/Participants/Participant.spec.js
index 6dc3eaf88..b69bad0e1 100644
--- a/src/components/RightSidebar/Participants/Participant.spec.js
+++ b/src/components/RightSidebar/Participants/Participant.spec.js
@@ -14,7 +14,10 @@ import VideoIcon from 'vue-material-design-icons/Video.vue'
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
import NcActionText from '@nextcloud/vue/dist/Components/NcActionText.js'
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
+import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js'
import NcDialog from '@nextcloud/vue/dist/Components/NcDialog.js'
+import NcInputField from '@nextcloud/vue/dist/Components/NcInputField.js'
+import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js'
import Participant from './Participant.vue'
import AvatarWrapper from '../../AvatarWrapper/AvatarWrapper.vue'
@@ -107,7 +110,10 @@ describe('Participant.vue', () => {
stubs: {
NcActionButton,
NcButton,
+ NcCheckboxRadioSwitch,
NcDialog,
+ NcInputField,
+ NcTextField,
},
directives: {
tooltip: tooltipMock,
@@ -639,6 +645,8 @@ describe('Participant.vue', () => {
expect(removeAction).toHaveBeenCalledWith(expect.anything(), {
token: 'current-token',
attendeeId: 'alice-attendee-id',
+ banParticipant: false,
+ internalNote: '',
})
}
@@ -651,6 +659,56 @@ describe('Participant.vue', () => {
expect(actionButton.exists()).toBe(false)
}
+ /**
+ * @param {string} buttonText Label of the remove action to find
+ * @param {string} internalNote text of provided note
+ */
+ async function testCanBan(buttonText = 'Remove participant', internalNote = 'test note') {
+ const wrapper = mountParticipant(participant)
+ const actionButton = findNcActionButton(wrapper, buttonText)
+ expect(actionButton.exists()).toBe(true)
+
+ await actionButton.find('button').trigger('click')
+
+ const dialog = wrapper.findComponent(NcDialog)
+ expect(dialog.exists()).toBeTruthy()
+
+ const checkbox = dialog.findComponent(NcCheckboxRadioSwitch)
+ await checkbox.find('input').trigger('change')
+
+ const input = dialog.findComponent(NcTextField)
+ expect(input.exists()).toBeTruthy()
+ input.find('input').setValue(internalNote)
+ await input.find('input').trigger('change')
+
+ const button = findNcButton(dialog, 'Remove')
+ await button.find('button').trigger('click')
+
+ expect(removeAction).toHaveBeenCalledWith(expect.anything(), {
+ token: 'current-token',
+ attendeeId: 'alice-attendee-id',
+ banParticipant: true,
+ internalNote
+ })
+ }
+
+ /**
+ * @param {string} buttonText Label of the remove action to find
+ */
+ async function testCannotBan(buttonText = 'Remove participant') {
+ const wrapper = mountParticipant(participant)
+ const actionButton = findNcActionButton(wrapper, buttonText)
+ expect(actionButton.exists()).toBe(true)
+
+ await actionButton.find('button').trigger('click')
+
+ const dialog = wrapper.findComponent(NcDialog)
+ expect(dialog.exists()).toBeTruthy()
+
+ const checkbox = dialog.findComponent(NcCheckboxRadioSwitch)
+ expect(checkbox.exists()).toBeFalsy()
+ }
+
test('allows a moderator to remove a moderator', async () => {
conversation.participantType = PARTICIPANT.TYPE.MODERATOR
participant.participantType = PARTICIPANT.TYPE.MODERATOR
@@ -707,6 +765,30 @@ describe('Participant.vue', () => {
conversation.participantType = PARTICIPANT.TYPE.USER
await testCannotRemove()
})
+
+ test('allows a moderator to ban a moderator', async () => {
+ conversation.participantType = PARTICIPANT.TYPE.MODERATOR
+ participant.participantType = PARTICIPANT.TYPE.USER
+ await testCanBan()
+ })
+
+ test('allows a moderator to ban a guest', async () => {
+ conversation.participantType = PARTICIPANT.TYPE.MODERATOR
+ participant.participantType = PARTICIPANT.TYPE.GUEST
+ await testCanBan()
+ })
+
+ test('does not allow a moderator to ban a moderator', async () => {
+ conversation.participantType = PARTICIPANT.TYPE.MODERATOR
+ participant.participantType = PARTICIPANT.TYPE.MODERATOR
+ await testCannotBan()
+ })
+
+ test('does not allow a moderator to ban a group', async () => {
+ conversation.participantType = PARTICIPANT.TYPE.MODERATOR
+ participant.actorType = ATTENDEE.ACTOR_TYPE.GROUPS
+ await testCannotBan('Remove group and members')
+ })
})
describe('dial-in PIN', () => {
/**
diff --git a/src/components/RightSidebar/Participants/Participant.vue b/src/components/RightSidebar/Participants/Participant.vue
index 3d344ee2e..921258c3c 100644
--- a/src/components/RightSidebar/Participants/Participant.vue
+++ b/src/components/RightSidebar/Participants/Participant.vue
@@ -313,6 +313,17 @@
:name="removeParticipantLabel"
:container="container">
<p> {{ removeDialogMessage }} </p>
+ <template v-if="supportBanV1 && showPermissionsOptions">
+ <NcCheckboxRadioSwitch :checked.sync="isBanParticipant">
+ {{ t('spreed', 'Also ban from this conversation') }}
+ </NcCheckboxRadioSwitch>
+ <template v-if="isBanParticipant">
+ <NcTextField v-if="isBanParticipant"
+ class="participant-dialog__input"
+ :label="t('spreed', 'Internal note (reason to ban)')"
+ :value.sync="internalNote" />
+ </template>
+ </template>
<template #actions>
<NcButton type="tertiary" @click="isRemoveDialogOpen = false">
{{ t('spreed', 'Dismiss') }}
@@ -363,7 +374,9 @@ import NcActions from '@nextcloud/vue/dist/Components/NcActions.js'
import NcActionSeparator from '@nextcloud/vue/dist/Components/NcActionSeparator.js'
import NcActionText from '@nextcloud/vue/dist/Components/NcActionText.js'
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
+import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js'
import NcDialog from '@nextcloud/vue/dist/Components/NcDialog.js'
+import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js'
import Tooltip from '@nextcloud/vue/dist/Directives/Tooltip.js'
import ParticipantPermissionsEditor from './ParticipantPermissionsEditor.vue'
@@ -396,7 +409,9 @@ export default {
NcActionText,
NcActionSeparator,
NcButton,
+ NcCheckboxRadioSwitch,
NcDialog,
+ NcTextField,
ParticipantPermissionsEditor,
// Icons
Account,
@@ -470,6 +485,8 @@ export default {
isStatusTooltipVisible: false,
permissionsEditor: false,
isRemoveDialogOpen: false,
+ isBanParticipant: false,
+ internalNote: '',
disabled: false,
}
},
@@ -835,6 +852,10 @@ export default {
|| this.participant.actorType === ATTENDEE.ACTOR_TYPE.EMAILS)
},
+ supportBanV1() {
+ return hasTalkFeature(this.token, 'ban-v1')
+ },
+
isLobbyEnabled() {
return this.conversation.lobbyState === WEBINAR.LOBBY.NON_MODERATORS
},
@@ -977,7 +998,11 @@ export default {
await this.$store.dispatch('removeParticipant', {
token: this.token,
attendeeId: this.attendeeId,
+ banParticipant: this.isBanParticipant,
+ internalNote: this.internalNote,
})
+ this.isBanParticipant = false
+ this.internalNote = ''
this.isRemoveDialogOpen = false
},
@@ -1224,6 +1249,12 @@ export default {
cursor: pointer;
}
+.participant-dialog {
+ &__input {
+ margin-block-end: 6px;
+ }
+}
+
.utils {
&__checkmark {
margin-right: 11px;
diff --git a/src/store/participantsStore.js b/src/store/participantsStore.js
index 240d44af9..aed541c75 100644
--- a/src/store/participantsStore.js
+++ b/src/store/participantsStore.js
@@ -6,17 +6,18 @@ 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 { showError, showSuccess } from '@nextcloud/dialogs'
import { emit } from '@nextcloud/event-bus'
import { t } from '@nextcloud/l10n'
import { generateUrl } from '@nextcloud/router'
import { ATTENDEE, PARTICIPANT } from '../constants.js'
+import { banActor } from '../services/banService.ts'
import {
joinCall,
leaveCall,
} from '../services/callsService.js'
-import { setRemoteCapabilities } from '../services/CapabilitiesManager.ts'
+import { hasTalkFeature, setRemoteCapabilities } from '../services/CapabilitiesManager.ts'
import { EventBus } from '../services/EventBus.js'
import {
promoteToModerator,
@@ -560,12 +561,25 @@ const actions = {
commit('updateParticipant', { token, attendeeId, updatedData })
},
- async removeParticipant({ commit, getters }, { token, attendeeId }) {
+ async removeParticipant({ commit, getters }, { token, attendeeId, banParticipant, internalNote = '' }) {
const attendee = getters.getParticipant(token, attendeeId)
if (!attendee) {
return
}
+ if (hasTalkFeature(token, 'ban-v1') && banParticipant) {
+ try {
+ await banActor(token, {
+ actorId: attendee.actorId,
+ actorType: attendee.actorType,
+ internalNote,
+ })
+ showSuccess(t('spreed', 'Participant is banned successfully'))
+ } catch (error) {
+ showError(t('spreed', 'Error while banning the participant'))
+ console.error('Error while banning the participant: ', error)
+ }
+ }
await removeAttendeeFromConversation(token, attendeeId)
commit('deleteParticipant', { token, attendeeId })
},