diff options
author | Grigorii Shartsev <grigorii.shartsev@nextcloud.com> | 2023-05-02 15:06:09 +0200 |
---|---|---|
committer | Grigorii Shartsev <grigorii.shartsev@nextcloud.com> | 2023-05-02 17:06:17 +0200 |
commit | 37d0350c083929060c478ee6ac3334a2d98e3706 (patch) | |
tree | 2d14132bf61030bf7759747f557a5bbe7ce48be6 /src/components/CallView | |
parent | 45f2bf27e96a642c10cec3cd353bc247ed2fbc8f (diff) |
Refactor: add Local Audio and Video Control Buttons
Signed-off-by: Grigorii Shartsev <grigorii.shartsev@nextcloud.com>
Diffstat (limited to 'src/components/CallView')
4 files changed, 395 insertions, 412 deletions
diff --git a/src/components/CallView/shared/LocalAudioControlButton.vue b/src/components/CallView/shared/LocalAudioControlButton.vue new file mode 100644 index 000000000..004499fae --- /dev/null +++ b/src/components/CallView/shared/LocalAudioControlButton.vue @@ -0,0 +1,162 @@ +<!-- + - @copyright Copyright (c) 2023 Grigorii Shartsev <me@shgk.me> + - + - @author Grigorii Shartsev <me@shgk.me> + - + - @license GNU AGPL version 3 or any later version + - + - 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> + <NcButton v-shortkey.once="disableKeyboardShortcuts ? null : ['m']" + v-tooltip="audioButtonTooltip" + :type="ncButtonType" + :aria-label="audioButtonAriaLabel" + :class="{ 'no-audio-available': !isAudioAllowed || !model.attributes.audioAvailable }" + @shortkey="toggleAudio" + @click.stop="toggleAudio"> + <template #icon> + <VolumeIndicator :audio-preview-available="model.attributes.audioAvailable" + :audio-enabled="showMicrophoneOn" + :current-volume="model.attributes.currentVolume" + :volume-threshold="model.attributes.volumeThreshold" + :primary-color="color" + overlay-muted-color="#888888" /> + </template> + </NcButton> +</template> + +<script> +import { emit } from '@nextcloud/event-bus' + +import { NcButton } from '@nextcloud/vue' + +import VolumeIndicator from '../../VolumeIndicator/VolumeIndicator.vue' + +import { PARTICIPANT } from '../../../constants.js' + +export default { + name: 'LocalAudioControlButton', + + components: { + NcButton, + VolumeIndicator, + }, + + props: { + conversation: { + type: Object, + required: true, + }, + + model: { + type: Object, + required: true, + }, + + disableKeyboardShortcuts: { + type: Boolean, + default: OCP.Accessibility.disableKeyboardShortcuts(), + }, + + ncButtonType: { + type: String, + default: 'tertiary-no-background', + }, + + color: { + type: String, + default: 'currentColor', + }, + }, + + computed: { + isAudioAllowed() { + return this.conversation.permissions & PARTICIPANT.PERMISSIONS.PUBLISH_AUDIO + }, + + showMicrophoneOn() { + return this.model.attributes.audioAvailable && this.model.attributes.audioEnabled + }, + + audioButtonTooltip() { + if (!this.isAudioAllowed) { + return t('spreed', 'You are not allowed to enable audio') + } + + if (!this.model.attributes.audioAvailable) { + return { + content: t('spreed', 'No audio. Click to select device'), + show: false, + } + } + + if (this.speakingWhileMutedNotification && !this.screenSharingMenuOpen) { + return { + content: this.speakingWhileMutedNotification, + show: true, + } + } + + let content = '' + if (this.model.attributes.audioEnabled) { + content = this.disableKeyboardShortcuts + ? t('spreed', 'Mute audio') + : t('spreed', 'Mute audio (M)') + } else { + content = this.disableKeyboardShortcuts + ? t('spreed', 'Unmute audio') + : t('spreed', 'Unmute audio (M)') + } + + return { + content, + show: false, + } + }, + + audioButtonAriaLabel() { + if (!this.model.attributes.audioAvailable) { + return t('spreed', 'No audio. Click to select device') + } + + return this.model.attributes.audioEnabled + ? t('spreed', 'Mute audio') + : t('spreed', 'Unmute audio') + }, + }, + + methods: { + toggleAudio() { + if (!this.model.attributes.audioAvailable) { + emit('show-settings', {}) + return + } + + if (this.model.attributes.audioEnabled) { + this.model.disableAudio() + } else { + this.model.enableAudio() + } + }, + }, +} +</script> + +<style scoped> +.no-audio-available { + opacity: .7; +} +</style> diff --git a/src/components/CallView/shared/LocalMediaControls.vue b/src/components/CallView/shared/LocalMediaControls.vue index 1b1bfaeba..3cd0f30e1 100644 --- a/src/components/CallView/shared/LocalMediaControls.vue +++ b/src/components/CallView/shared/LocalMediaControls.vue @@ -35,8 +35,7 @@ :aria-label="qualityWarningAriaLabel" @click="mouseover = !mouseover"> <template #icon> - <NetworkStrength2Alert fill-color="#e9322d" - :size="20" /> + <NetworkStrength2Alert fill-color="#e9322d" :size="20" /> </template> </NcButton> </template> @@ -59,38 +58,11 @@ </div> </NcPopover> </div> - <NcButton v-shortkey.once="disableKeyboardShortcuts ? null : ['m']" - v-tooltip="audioButtonTooltip" - type="tertiary-no-background" - :aria-label="audioButtonAriaLabel" - :class="audioButtonClass" - @shortkey="toggleAudio" - @click.stop="toggleAudio"> - <template #icon> - <VolumeIndicator :audio-preview-available="model.attributes.audioAvailable" - :audio-enabled="showMicrophoneOn" - :current-volume="model.attributes.currentVolume" - :volume-threshold="model.attributes.volumeThreshold" - primary-color="#ffffff" - overlay-muted-color="#888888" /> - </template> - </NcButton> - <NcButton v-shortkey.once="disableKeyboardShortcuts ? null : ['v']" - v-tooltip="videoButtonTooltip" - type="tertiary-no-background" - :aria-label="videoButtonAriaLabel" - :class="videoButtonClass" - @shortkey="toggleVideo" - @click.stop="toggleVideo"> - <template #icon> - <VideoIcon v-if="showVideoOn" - :size="20" - fill-color="#ffffff" /> - <VideoOff v-else - :size="20" - fill-color="#ffffff" /> - </template> - </NcButton> + + <LocalAudioControlButton :conversation="conversation" :model="model" color="#ffffff" /> + + <LocalVideoControlButton :conversation="conversation" :model="model" color="#ffffff" /> + <NcButton v-if="isVirtualBackgroundAvailable && !showActions" v-tooltip="toggleVirtualBackgroundButtonLabel" type="tertiary-no-background" @@ -98,14 +70,11 @@ :class="blurButtonClass" @click.stop="toggleVirtualBackground"> <template #icon> - <Blur v-if="isVirtualBackgroundEnabled" - :size="20" - fill-color="#ffffff" /> - <BlurOff v-else - :size="20" - fill-color="#ffffff" /> + <Blur v-if="isVirtualBackgroundEnabled" :size="20" fill-color="#ffffff" /> + <BlurOff v-else :size="20" fill-color="#ffffff" /> </template> </NcButton> + <NcActions v-if="!screenSharingButtonHidden" id="screensharing-button" v-tooltip="screenSharingButtonTooltip" @@ -119,20 +88,15 @@ @update:close="screenSharingMenuOpen = false"> <!-- Actions button icon --> <template #icon> - <CancelPresentation v-if="model.attributes.localScreen" - :size="20" - fill-color="#ffffff" /> - <PresentToAll v-else - :size="20" - fill-color="#ffffff" /> + <CancelPresentation v-if="model.attributes.localScreen" :size="20" fill-color="#ffffff" /> + <PresentToAll v-else :size="20" fill-color="#ffffff" /> </template> <!-- /Actions button icon --> <!-- Actions --> <NcActionButton v-if="!screenSharingMenuOpen" @click.stop="toggleScreenSharingMenu"> <template #icon> - <PresentToAll :size="20" - fill-color="#ffffff" /> + <PresentToAll :size="20" fill-color="#ffffff" /> </template> {{ screenSharingButtonTooltip }} </NcActionButton> @@ -163,8 +127,7 @@ <template #icon> <!-- The following icon is much bigger than all the others so we reduce its size --> - <HandBackLeft :size="18" - fill-color="#ffffff" /> + <HandBackLeft :size="18" fill-color="#ffffff" /> </template> </NcButton> </div> @@ -179,8 +142,6 @@ import BlurOff from 'vue-material-design-icons/BlurOff.vue' import HandBackLeft from 'vue-material-design-icons/HandBackLeft.vue' import Monitor from 'vue-material-design-icons/Monitor.vue' import NetworkStrength2Alert from 'vue-material-design-icons/NetworkStrength2Alert.vue' -import VideoIcon from 'vue-material-design-icons/Video.vue' -import VideoOff from 'vue-material-design-icons/VideoOff.vue' import { showMessage } from '@nextcloud/dialogs' import { emit } from '@nextcloud/event-bus' @@ -193,7 +154,8 @@ import Tooltip from '@nextcloud/vue/dist/Directives/Tooltip.js' import CancelPresentation from '../../missingMaterialDesignIcons/CancelPresentation.vue' import PresentToAll from '../../missingMaterialDesignIcons/PresentToAll.vue' -import VolumeIndicator from '../../VolumeIndicator/VolumeIndicator.vue' +import LocalAudioControlButton from './LocalAudioControlButton.vue' +import LocalVideoControlButton from './LocalVideoControlButton.vue' import { PARTICIPANT } from '../../../constants.js' import isInCall from '../../../mixins/isInCall.js' @@ -209,6 +171,8 @@ export default { tooltip: Tooltip, }, components: { + LocalAudioControlButton, + LocalVideoControlButton, Blur, BlurOff, CancelPresentation, @@ -220,9 +184,6 @@ export default { NcPopover, NetworkStrength2Alert, PresentToAll, - VideoIcon, - VideoOff, - VolumeIndicator, }, mixins: [ @@ -292,140 +253,22 @@ export default { return this.$store.getters.conversation(this.token) || this.$store.getters.dummyConversation }, - isAudioAllowed() { - return this.conversation.permissions & PARTICIPANT.PERMISSIONS.PUBLISH_AUDIO - }, - - isVideoAllowed() { - return this.conversation.permissions & PARTICIPANT.PERMISSIONS.PUBLISH_VIDEO - }, - isScreensharingAllowed() { return this.conversation.permissions & PARTICIPANT.PERMISSIONS.PUBLISH_SCREEN }, - audioButtonClass() { - return { - 'audio-enabled': this.isAudioAllowed && this.model.attributes.audioAvailable && this.model.attributes.audioEnabled, - 'no-audio-available': !this.isAudioAllowed || !this.model.attributes.audioAvailable, - } - }, - - showMicrophoneOn() { - return this.model.attributes.audioAvailable && this.model.attributes.audioEnabled - }, - - audioButtonTooltip() { - if (!this.isAudioAllowed) { - return t('spreed', 'You are not allowed to enable audio') - } - - if (!this.model.attributes.audioAvailable) { - return { - content: t('spreed', 'No audio. Click to select device'), - show: false, - } - } - - if (this.speakingWhileMutedNotification && !this.screenSharingMenuOpen) { - return { - content: this.speakingWhileMutedNotification, - show: true, - } - } - - let content = '' - if (this.model.attributes.audioEnabled) { - content = this.disableKeyboardShortcuts - ? t('spreed', 'Mute audio') - : t('spreed', 'Mute audio (M)') - } else { - content = this.disableKeyboardShortcuts - ? t('spreed', 'Unmute audio') - : t('spreed', 'Unmute audio (M)') - } - - return { - content, - show: false, - } - }, - - audioButtonAriaLabel() { - if (!this.model.attributes.audioAvailable) { - return t('spreed', 'No audio. Click to select device') - } - - return this.model.attributes.audioEnabled - ? t('spreed', 'Mute audio') - : t('spreed', 'Unmute audio') - }, - lowerHandAriaLabel() { return this.disableKeyboardShortcuts ? t('spreed', 'Lower hand') : t('spreed', 'Lower hand (R)') }, - videoButtonClass() { - return { - 'video-enabled': this.isVideoAllowed && this.model.attributes.videoAvailable && this.model.attributes.videoEnabled, - 'no-video-available': !this.isVideoAllowed || !this.model.attributes.videoAvailable, - } - }, - blurButtonClass() { return { 'blur-disabled': this.isVirtualBackgroundEnabled, } }, - showVideoOn() { - return this.model.attributes.videoAvailable && this.model.attributes.videoEnabled - }, - - videoButtonTooltip() { - if (!this.isVideoAllowed) { - return t('spreed', 'You are not allowed to enable video') - } - - if (!this.model.attributes.videoAvailable) { - return t('spreed', 'No video. Click to select device') - } - - if (this.model.attributes.videoEnabled) { - return this.disableKeyboardShortcuts - ? t('spreed', 'Disable video') - : t('spreed', 'Disable video (V)') - } - - if (!this.model.getWebRtc() || !this.model.getWebRtc().connection || this.model.getWebRtc().connection.getSendVideoIfAvailable()) { - return this.disableKeyboardShortcuts - ? t('spreed', 'Enable video') - : t('spreed', 'Enable video (V)') - } - - return this.disableKeyboardShortcuts - ? t('spreed', 'Enable video - Your connection will be briefly interrupted when enabling the video for the first time') - : t('spreed', 'Enable video (V) - Your connection will be briefly interrupted when enabling the video for the first time') - }, - - videoButtonAriaLabel() { - if (!this.model.attributes.videoAvailable) { - return t('spreed', 'No video. Click to select device') - } - - if (this.model.attributes.videoEnabled) { - return t('spreed', 'Disable video') - } - - if (!this.model.getWebRtc() || !this.model.getWebRtc().connection || this.model.getWebRtc().connection.getSendVideoIfAvailable()) { - return t('spreed', 'Enable video') - } - - return t('spreed', 'Enable video. Your connection will be briefly interrupted when enabling the video for the first time') - }, - screenSharingButtonClass() { return { 'screensharing-enabled': this.isScreensharingAllowed && this.model.attributes.localScreen, @@ -646,27 +489,6 @@ export default { this.speakingWhileMutedNotification = message }, - toggleVideo() { - /** - * Abort toggling the video if the 'v' key is lifted when pasting an - * image in the new message form. - */ - if (document.getElementsByClassName('upload-editor').length !== 0) { - return - } - - if (!this.model.attributes.videoAvailable) { - emit('show-settings', {}) - return - } - - if (this.model.attributes.videoEnabled) { - this.model.disableVideo() - } else { - this.model.enableVideo() - } - }, - toggleVirtualBackground() { if (this.model.attributes.virtualBackgroundEnabled) { this.model.disableVirtualBackground() @@ -813,14 +635,10 @@ export default { } /* Highlight the media buttons when enabled */ -.buttons-bar button.audio-enabled, -.buttons-bar button.video-enabled, .buttons-bar button.screensharing-enabled { opacity: 1; } -.buttons-bar button.no-audio-available, -.buttons-bar button.no-video-available, .buttons-bar button.no-screensharing-available { &, & * { opacity: .7; diff --git a/src/components/CallView/shared/LocalVideoControlButton.vue b/src/components/CallView/shared/LocalVideoControlButton.vue new file mode 100644 index 000000000..ae5be0eb1 --- /dev/null +++ b/src/components/CallView/shared/LocalVideoControlButton.vue @@ -0,0 +1,164 @@ +<!-- + - @copyright Copyright (c) 2023 Grigorii Shartsev <me@shgk.me> + - + - @author Grigorii Shartsev <me@shgk.me> + - + - @license GNU AGPL version 3 or any later version + - + - 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> + <NcButton v-shortkey.once="disableKeyboardShortcuts ? null : ['v']" + v-tooltip="videoButtonTooltip" + :type="ncButtonType" + :aria-label="videoButtonAriaLabel" + :class="{ 'no-video-available': !isVideoAllowed || !model.attributes.videoAvailable }" + @shortkey="toggleVideo" + @click.stop="toggleVideo"> + <template #icon> + <VideoIcon v-if="showVideoOn" :size="20" :fill-color="color" /> + <VideoOff v-else :size="20" :fill-color="color" /> + </template> + </NcButton> +</template> + +<script> +import VideoIcon from 'vue-material-design-icons/Video.vue' +import VideoOff from 'vue-material-design-icons/VideoOff.vue' + +import { emit } from '@nextcloud/event-bus' + +import { NcButton } from '@nextcloud/vue' + +import { PARTICIPANT } from '../../../constants.js' + +export default { + name: 'LocalVideoControlButton', + + components: { + NcButton, + VideoIcon, + VideoOff, + }, + + props: { + conversation: { + type: Object, + required: true, + }, + + model: { + type: Object, + required: true, + }, + + disableKeyboardShortcuts: { + type: Boolean, + default: OCP.Accessibility.disableKeyboardShortcuts(), + }, + + ncButtonType: { + type: String, + default: 'tertiary-no-background', + }, + + color: { + type: String, + default: 'currentColor', + }, + }, + + computed: { + isVideoAllowed() { + return this.conversation.permissions & PARTICIPANT.PERMISSIONS.PUBLISH_VIDEO + }, + + showVideoOn() { + return this.model.attributes.videoAvailable && this.model.attributes.videoEnabled + }, + + videoButtonTooltip() { + if (!this.isVideoAllowed) { + return t('spreed', 'You are not allowed to enable video') + } + + if (!this.model.attributes.videoAvailable) { + return t('spreed', 'No video. Click to select device') + } + + if (this.model.attributes.videoEnabled) { + return this.disableKeyboardShortcuts + ? t('spreed', 'Disable video') + : t('spreed', 'Disable video (V)') + } + + if (!this.model.getWebRtc() || !this.model.getWebRtc().connection || this.model.getWebRtc().connection.getSendVideoIfAvailable()) { + return this.disableKeyboardShortcuts + ? t('spreed', 'Enable video') + : t('spreed', 'Enable video (V)') + } + + return this.disableKeyboardShortcuts + ? t('spreed', 'Enable video - Your connection will be briefly interrupted when enabling the video for the first time') + : t('spreed', 'Enable video (V) - Your connection will be briefly interrupted when enabling the video for the first time') + }, + + videoButtonAriaLabel() { + if (!this.model.attributes.videoAvailable) { + return t('spreed', 'No video. Click to select device') + } + + if (this.model.attributes.videoEnabled) { + return t('spreed', 'Disable video') + } + + if (!this.model.getWebRtc() || !this.model.getWebRtc().connection || this.model.getWebRtc().connection.getSendVideoIfAvailable()) { + return t('spreed', 'Enable video') + } + + return t('spreed', 'Enable video. Your connection will be briefly interrupted when enabling the video for the first time') + }, + }, + + methods: { + toggleVideo() { + /** + * Abort toggling the video if the 'v' key is lifted when pasting an + * image in the new message form. + */ + if (document.getElementsByClassName('upload-editor').length !== 0) { + return + } + + if (!this.model.attributes.videoAvailable) { + emit('show-settings', {}) + return + } + + if (this.model.attributes.videoEnabled) { + this.model.disableVideo() + } else { + this.model.enableVideo() + } + }, + }, +} +</script> + +<style scoped lang="scss"> +.no-video-available { + opacity: .7; +} +</style> diff --git a/src/components/CallView/shared/ViewerOverlayCallView.vue b/src/components/CallView/shared/ViewerOverlayCallView.vue index ee50227e6..965222101 100644 --- a/src/components/CallView/shared/ViewerOverlayCallView.vue +++ b/src/components/CallView/shared/ViewerOverlayCallView.vue @@ -22,11 +22,18 @@ <template> <div ref="ghost" class="viewer-overlay-ghost"> <Portal> - <div class="viewer-overlay" :style="{ right: position.right + 'px', bottom: position.bottom + 'px' }"> - <div class="viewer-overlay__collapse" :class="{ collapsed: isCollapsed }"> + <div class="viewer-overlay" + :style="{ + right: position.right + 'px', + bottom: position.bottom + 'px' + }"> + <div class="viewer-overlay__collapse" + :class="{ collapsed: isCollapsed }"> <NcButton type="tertiary" class="viewer-overlay__button" - :aria-label="isCollapsed ? t('spreed', 'Collapse') : t('spreed', 'Expand')" + :aria-label=" + isCollapsed ? t('spreed', 'Collapse') : t('spreed', 'Expand') + " @click.stop="isCollapsed = !isCollapsed"> <template #icon> <ChevronDown v-if="!isCollapsed" :size="20" /> @@ -65,38 +72,16 @@ @click-video="maximize"> <template #bottom-bar> <div class="viewer-overlay__bottom-bar"> - <NcButton v-tooltip="audioButtonTooltip" - type="tertiary" - class="viewer-overlay__button" - :aria-label="audioButtonAriaLabel" - :class="{ - 'audio-enabled': isAudioAllowed && localModel.attributes.audioAvailable && localModel.attributes.audioEnabled, - 'no-audio-available': !isAudioAllowed || !localModel.attributes.audioAvailable, - }" - @click.stop="toggleAudio"> - <template #icon> - <VolumeIndicator :audio-preview-available="localModel.attributes.audioAvailable" - :audio-enabled="showMicrophoneOn" - :current-volume="localModel.attributes.currentVolume" - :volume-threshold="localModel.attributes.volumeThreshold" - primary-color="currentColor" - overlay-muted-color="#888888" /> - </template> - </NcButton> - <NcButton v-tooltip="videoButtonTooltip" - type="tertiary" - class="viewer-overlay__button" - :aria-label="videoButtonAriaLabel" - :class="{ - 'video-enabled': isVideoAllowed && localModel.attributes.videoAvailable && localModel.attributes.videoEnabled, - 'no-video-available': !isVideoAllowed || !localModel.attributes.videoAvailable, - }" - @click.stop="toggleVideo"> - <template #icon> - <VideoIcon v-if="showVideoOn" :size="20" /> - <VideoOff v-else :size="20" /> - </template> - |