diff options
author | Vincent Petry <vincent@nextcloud.com> | 2021-07-08 22:58:16 +0300 |
---|---|---|
committer | Joas Schilling <coding@schilljs.com> | 2021-11-03 15:10:33 +0300 |
commit | cb2f6e2c18eee8263b3d939d776ea63a9f3504a8 (patch) | |
tree | cfa1b17f8c52deb50fb7b9da8e87a87ed2d54e47 | |
parent | 2819892f317609a4a5fee86692e7ffa007a11730 (diff) |
Add button to end meeting for all
Signed-off-by: Vincent Petry <vincent@nextcloud.com>
-rw-r--r-- | lib/Controller/CallController.php | 14 | ||||
-rw-r--r-- | src/components/TopBar/CallButton.vue | 121 | ||||
-rw-r--r-- | src/services/callsService.js | 5 | ||||
-rw-r--r-- | src/store/participantsStore.js | 4 | ||||
-rw-r--r-- | src/store/participantsStore.spec.js | 2 | ||||
-rw-r--r-- | src/utils/signaling.js | 8 | ||||
-rw-r--r-- | src/utils/webrtc/index.js | 5 |
7 files changed, 121 insertions, 38 deletions
diff --git a/lib/Controller/CallController.php b/lib/Controller/CallController.php index 17f89d416..0cca8f991 100644 --- a/lib/Controller/CallController.php +++ b/lib/Controller/CallController.php @@ -157,15 +157,25 @@ class CallController extends AEnvironmentAwareController { * @PublicPage * @RequireParticipant * + * @param bool $all whether to also terminate the call for all participants * @return DataResponse */ - public function leaveCall(): DataResponse { + public function leaveCall(bool $all = false): DataResponse { $session = $this->participant->getSession(); if (!$session instanceof Session) { return new DataResponse([], Http::STATUS_NOT_FOUND); } - $this->participantService->changeInCall($this->room, $this->participant, Participant::FLAG_DISCONNECTED); + if ($all && $this->participant->hasModeratorPermissions()) { + $participants = $this->participantService->getParticipantsInCall($this->room); + + // kick out all participants out of the call + foreach ($participants as $participant) { + $this->participantService->changeInCall($this->room, $participant, Participant::FLAG_DISCONNECTED); + } + } else { + $this->participantService->changeInCall($this->room, $this->participant, Participant::FLAG_DISCONNECTED); + } return new DataResponse(); } diff --git a/src/components/TopBar/CallButton.vue b/src/components/TopBar/CallButton.vue index 1a02569a6..ae272efad 100644 --- a/src/components/TopBar/CallButton.vue +++ b/src/components/TopBar/CallButton.vue @@ -20,32 +20,61 @@ --> <template> - <button v-if="showStartCallButton" - v-tooltip="{ - placement: 'auto', - trigger: 'hover', - content: startCallToolTip, - autoHide: false, - html: true - }" - :disabled="startCallButtonDisabled || loading || blockCalls" - class="top-bar__button" - :class="startCallButtonClasses" - @click="handleClick"> - <span - class="icon" - :class="startCallIcon" /> - {{ startCallLabel }} - </button> - <button v-else-if="showLeaveCallButton" - class="top-bar__button error" - :disabled="loading" - @click="leaveCall"> - <span - class="icon" - :class="leaveCallIcon" /> - {{ leaveCallLabel }} - </button> + <div> + <button v-if="showStartCallButton" + v-tooltip="{ + placement: 'auto', + trigger: 'hover', + content: startCallToolTip, + autoHide: false, + html: true + }" + :disabled="startCallButtonDisabled || loading || blockCalls" + class="top-bar__button" + :class="startCallButtonClasses" + @click="handleClick"> + <span + class="icon" + :class="startCallIcon" /> + {{ startCallLabel }} + </button> + <button v-else-if="showLeaveCallButton && !canEndForAll" + class="top-bar__button error" + :disabled="loading" + @click="leaveCall"> + <span + class="icon" + :class="leaveCallIcon" /> + {{ leaveCallLabel }} + </button> + <Actions + v-else-if="showLeaveCallButton && canEndForAll" + :disabled="loading"> + <template slot="icon"> + <VideoOff + :size="16" + decorative /> + <span class="label">{{ leaveCallLabel }}</span> + <MenuDown + :size="16" + decorative /> + </template> + <ActionButton @click="leaveCall(false)"> + <VideoOff + slot="icon" + :size="24" + decorative /> + {{ leaveCallLabel }} + </ActionButton> + <ActionButton @click="leaveCall(true)"> + <VideoOff + slot="icon" + :size="24" + decorative /> + {{ t('spreed', 'End meeting for all') }} + </ActionButton> + </Actions> + </div> </template> <script> @@ -57,6 +86,10 @@ import participant from '../../mixins/participant' import Tooltip from '@nextcloud/vue/dist/Directives/Tooltip' import { emit } from '@nextcloud/event-bus' import BrowserStorage from '../../services/BrowserStorage' +import Actions from '@nextcloud/vue/dist/Components/Actions' +import ActionButton from '@nextcloud/vue/dist/Components/ActionButton' +import VideoOff from 'vue-material-design-icons/VideoOff' +import MenuDown from 'vue-material-design-icons/MenuDown' export default { name: 'CallButton', @@ -65,6 +98,13 @@ export default { Tooltip, }, + components: { + Actions, + ActionButton, + VideoOff, + MenuDown, + }, + mixins: [ browserCheck, isInCall, @@ -101,6 +141,14 @@ export default { return this.$store.getters.conversation(this.token) || this.$store.getters.dummyConversation }, + participantType() { + return this.conversation.participantType + }, + + canEndForAll() { + return (this.participantType === PARTICIPANT.TYPE.OWNER || this.participantType === PARTICIPANT.TYPE.MODERATOR || this.participantType === PARTICIPANT.TYPE.GUEST_MODERATOR) + }, + startCallButtonDisabled() { return (!this.conversation.canStartCall && !this.conversation.hasCall) @@ -208,7 +256,7 @@ export default { this.loading = false }, - async leaveCall() { + async leaveCall(all = false) { console.info('Leaving call') // Remove selected participant this.$store.dispatch('selectedVideoPeerId', null) @@ -220,6 +268,7 @@ export default { await this.$store.dispatch('leaveCall', { token: this.token, participantIdentifier: this.$store.getters.getParticipantIdentifier(), + all, }) this.loading = false }, @@ -261,4 +310,22 @@ export default { border: 1px solid var(--color-success) !important; } } + +/* HACK: to override the default action button styles to make it look like a regular button */ +::v-deep .trigger > button { + &, + &:hover, + &:focus, + &:active { + color: white; + background-color: var(--color-error) !important; + border: 1px solid var(--color-error) !important; + padding: 0 16px; + opacity: 1; + } + + & > .label { + margin: 0 8px; + } +} </style> diff --git a/src/services/callsService.js b/src/services/callsService.js index 0a6361510..1c841a467 100644 --- a/src/services/callsService.js +++ b/src/services/callsService.js @@ -54,10 +54,11 @@ const joinCall = async function(token, flags) { * Leave a call as participant * * @param {string} token The token of the call to be left + * @param {boolean} all Whether to end the meeting for all */ -const leaveCall = async function(token) { +const leaveCall = async function(token, all = false) { try { - await signalingLeaveCall(token) + await signalingLeaveCall(token, all) } catch (error) { console.debug('Error while leaving call: ', error) } diff --git a/src/store/participantsStore.js b/src/store/participantsStore.js index e1bafc5dc..56c6e11ee 100644 --- a/src/store/participantsStore.js +++ b/src/store/participantsStore.js @@ -398,7 +398,7 @@ const actions = { }, 10000) }, - async leaveCall({ commit, getters }, { token, participantIdentifier }) { + async leaveCall({ commit, getters }, { token, participantIdentifier, all = false }) { if (!participantIdentifier?.sessionId) { console.error('Trying to leave call without sessionId') } @@ -409,7 +409,7 @@ const actions = { return } - await leaveCall(token) + await leaveCall(token, all) const updatedData = { inCall: PARTICIPANT.CALL_FLAG.DISCONNECTED, diff --git a/src/store/participantsStore.spec.js b/src/store/participantsStore.spec.js index 1bc6a8ab5..3fb567db6 100644 --- a/src/store/participantsStore.spec.js +++ b/src/store/participantsStore.spec.js @@ -469,7 +469,7 @@ describe('participantsStore', () => { }, }) - expect(leaveCall).toHaveBeenCalledWith(TOKEN) + expect(leaveCall).toHaveBeenCalledWith(TOKEN, false) expect(store.getters.isInCall(TOKEN)).toBe(false) expect(store.getters.isConnecting(TOKEN)).toBe(false) expect(store.getters.participantsList(TOKEN)).toStrictEqual([ diff --git a/src/utils/signaling.js b/src/utils/signaling.js index 4db8c2a23..28b699c10 100644 --- a/src/utils/signaling.js +++ b/src/utils/signaling.js @@ -311,14 +311,18 @@ Signaling.Base.prototype.updateCallFlags = function(token, flags) { }) } -Signaling.Base.prototype.leaveCall = function(token, keepToken) { +Signaling.Base.prototype.leaveCall = function(token, keepToken, all = false) { return new Promise((resolve, reject) => { if (!token) { reject(new Error()) return } - axios.delete(generateOcsUrl('apps/spreed/api/v4/call/{token}', { token })) + axios.delete(generateOcsUrl('apps/spreed/api/v4/call/{token}', { token }), { + data: { + all, + }, + }) .then(function() { this._trigger('leaveCall', [token, keepToken]) this._leaveCallSuccess(token) diff --git a/src/utils/webrtc/index.js b/src/utils/webrtc/index.js index ac8fc12ef..91a44b9c5 100644 --- a/src/utils/webrtc/index.js +++ b/src/utils/webrtc/index.js @@ -236,9 +236,10 @@ async function signalingJoinCall(token, flags) { * Leave the call of the given conversation * * @param {string} token Conversation to leave the call + * @param {boolean} all Whether to end the meeting for all * @return {Promise<void>} */ -async function signalingLeaveCall(token) { +async function signalingLeaveCall(token, all = false) { sentVideoQualityThrottler.destroy() sentVideoQualityThrottler = null @@ -246,7 +247,7 @@ async function signalingLeaveCall(token) { callAnalyzer = null if (tokensInSignaling[token]) { - await signaling.leaveCall(token) + await signaling.leaveCall(token, false, all) } } |