Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/spreed.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVincent Petry <vincent@nextcloud.com>2021-07-08 22:58:16 +0300
committerJoas Schilling <coding@schilljs.com>2021-11-03 15:10:33 +0300
commitcb2f6e2c18eee8263b3d939d776ea63a9f3504a8 (patch)
treecfa1b17f8c52deb50fb7b9da8e87a87ed2d54e47
parent2819892f317609a4a5fee86692e7ffa007a11730 (diff)
Add button to end meeting for all
Signed-off-by: Vincent Petry <vincent@nextcloud.com>
-rw-r--r--lib/Controller/CallController.php14
-rw-r--r--src/components/TopBar/CallButton.vue121
-rw-r--r--src/services/callsService.js5
-rw-r--r--src/store/participantsStore.js4
-rw-r--r--src/store/participantsStore.spec.js2
-rw-r--r--src/utils/signaling.js8
-rw-r--r--src/utils/webrtc/index.js5
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)
}
}