Skip to content

Commit

Permalink
feat(ConversationSettings): allow moderators to enable consent per co…
Browse files Browse the repository at this point in the history
…nversation (V2)

Signed-off-by: Maksim Sukharev <antreesy.web@gmail.com>
  • Loading branch information
Antreesy committed Oct 6, 2023
1 parent be45c52 commit 83adcdc
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
:title="t('spreed', 'Moderation')">
<ListableSettings v-if="!isNoteToSelf" :token="token" />
<LinkShareSettings v-if="!isNoteToSelf" ref="linkShareSettings" />
<RecordingConsentSettings v-if="!isNoteToSelf && recordingConsentAvailable" :token="token" :can-full-moderate="canFullModerate" />
<ExpirationSettings :token="token" can-full-moderate />
</NcAppSettingsSection>
<NcAppSettingsSection v-else
Expand Down Expand Up @@ -130,11 +131,16 @@ import LobbySettings from './LobbySettings.vue'
import LockingSettings from './LockingSettings.vue'
import MatterbridgeSettings from './Matterbridge/MatterbridgeSettings.vue'
import NotificationsSettings from './NotificationsSettings.vue'
import RecordingConsentSettings from './RecordingConsentSettings.vue'
import SipSettings from './SipSettings.vue'

import { PARTICIPANT, CONVERSATION } from '../../constants.js'
import { CALL, PARTICIPANT, CONVERSATION } from '../../constants.js'
import BrowserStorage from '../../services/BrowserStorage.js'

const recordingEnabled = getCapabilities()?.spreed?.config?.call?.recording || false
const recordingConsentCapability = getCapabilities()?.spreed?.features?.includes('recording-consent')
const recordingConsent = getCapabilities()?.spreed?.config?.call?.['recording-consent'] !== CALL.RECORDING_CONSENT.OFF

export default {
name: 'ConversationSettingsDialog',

Expand All @@ -154,6 +160,7 @@ export default {
NcAppSettingsSection,
NcCheckboxRadioSwitch,
NotificationsSettings,
RecordingConsentSettings,
SipSettings,
},

Expand Down Expand Up @@ -232,6 +239,10 @@ export default {
&& breakoutRoomsEnabled
&& this.conversation.type === CONVERSATION.TYPE.GROUP
},

recordingConsentAvailable() {
return recordingEnabled && recordingConsentCapability && recordingConsent
}
},

watch: {
Expand Down
152 changes: 152 additions & 0 deletions src/components/ConversationSettings/RecordingConsentSettings.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
<!--
- @copyright Copyright (c) 2023 Maksim Sukharev <antreesy.web@gmail.com>
-
- @author Maksim Sukharev <antreesy.web@gmail.com>
-
- @license AGPL-3.0-or-later
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-->

<template>
<div class="app-settings-subsection">
<h4 class="app-settings-section__subtitle">
{{ t('spreed', 'Recording Consent') }}
</h4>

<template v-if="canFullModerate && !isGlobalConsent">
<label for="recording-consent_input" class="recording-consent__label">
{{ t('spreed', 'Require recording consent before joining call in this conversation') }}
</label>
<NcSelect v-model="recordingConsentSelected"
input-id="recording-consent_input"
class="recording-consent__select"
name="recording-consent"
:options="recordingConsentOptions"
:clearable="false"
:placeholder="t('spreed', 'Recording consent')"
label="label"
no-wrap
:disabled="isSelectDisabled"
@input="setRecordingConsent" />
</template>
<p v-else-if="isGlobalConsent">
{{ t('spreed', 'Recording consent is required for all calls') }}
</p>
<p v-else>
{{ summaryLabel }}
</p>
</div>
</template>

<script>
import { showError } from '@nextcloud/dialogs'

import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js'

import { CALL } from '../../constants.js'
import { getCapabilities } from '@nextcloud/capabilities'

const recordingConsent = getCapabilities()?.spreed?.config?.call?.['recording-consent']
const recordingConsentOptions = [
{ id: CALL.RECORDING_CONSENT.OFF, label: t('spreed', 'Off') },
{ id: CALL.RECORDING_CONSENT.REQUIRED, label: t('spreed', 'Enabled') },
]

export default {
name: 'RecordingConsentSettings',

components: {
NcSelect,
},

props: {
token: {
type: String,
default: null,
},

canFullModerate: {
type: Boolean,
default: true,
},
},

setup() {
return {
recordingConsentOptions
}
},

data() {
return {
loading: false,
recordingConsentSelected: recordingConsentOptions[0],
}
},

computed: {
conversation() {
return this.$store.getters.conversation(this.token) || this.$store.getters.dummyConversation
},

isGlobalConsent() {
return recordingConsent === CALL.RECORDING_CONSENT.REQUIRED
},

isSelectDisabled() {
return this.isGlobalConsent || this.loading || this.conversation.hasCall
},

summaryLabel() {
return this.recordingConsentSelected.id === CALL.RECORDING_CONSENT.REQUIRED
? t('spreed', 'Recording consent is required for calls in this conversation')
: t('spreed', 'Recording consent is not required for calls in this conversation')
},
},

mounted() {
this.recordingConsentSelected = recordingConsentOptions.find(option => option.id === this.conversation.recordingConsent)
},

methods: {
async setRecordingConsent(option) {
this.loading = true
try {
await this.$store.dispatch('setRecordingConsent', {
token: this.token,
state: option.id,
})
} catch (error) {
showError(t('spreed', 'Error occurred while updating recording consent'))
console.error(error)
}
this.loading = false
},
},
}
</script>

<style lang="scss" scoped>
.recording-consent {
&__select {
width: 300px;
}

&__label {
display: block;
padding: 4px 0;
}
}
</style>
13 changes: 13 additions & 0 deletions src/services/conversationsService.js
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,18 @@ const setSIPEnabled = async function(token, newState) {
})
}

/**
* Change the recording consent per conversation
*
* @param {string} token The token of the conversation to be modified
* @param {number} newState The new recording consent state to set
*/
const setRecordingConsent = async function(token, newState) {
return axios.put(generateOcsUrl('apps/spreed/api/v4/room/{token}/recording-consent', { token }), {
recordingConsent: newState,
})
}

/**
* Change the lobby state
*
Expand Down Expand Up @@ -461,6 +473,7 @@ export {
makePublic,
makePrivate,
setSIPEnabled,
setRecordingConsent,
changeLobbyState,
changeReadOnlyState,
changeListable,
Expand Down
13 changes: 13 additions & 0 deletions src/store/conversationsStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
makePublic,
makePrivate,
setSIPEnabled,
setRecordingConsent,
changeLobbyState,
changeReadOnlyState,
changeListable,
Expand Down Expand Up @@ -565,6 +566,18 @@ const actions = {
commit('addConversation', conversation)
},

async setRecordingConsent({ commit, getters }, { token, state }) {
if (!getters.conversations[token]) {
return
}

await setRecordingConsent(token, state)

const conversation = Object.assign({}, getters.conversations[token], { recordingConsent: state })

commit('addConversation', conversation)
},

async setConversationProperties({ commit, getters }, { token, properties }) {
if (!getters.conversations[token]) {
return
Expand Down
3 changes: 2 additions & 1 deletion src/store/participantsStore.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -521,12 +521,13 @@ describe('participantsStore', () => {
},
flags,
silent: false,
recordingConsent: false,
})
})

test('joins call', async () => {
// Assert
expect(joinCall).toHaveBeenCalledWith(TOKEN, flags, false)
expect(joinCall).toHaveBeenCalledWith(TOKEN, flags, false, false)
expect(store.getters.isInCall(TOKEN)).toBe(true)
expect(store.getters.isConnecting(TOKEN)).toBe(true)
expect(store.getters.participantsList(TOKEN)).toStrictEqual([
Expand Down

0 comments on commit 83adcdc

Please sign in to comment.