diff options
author | marco <marcoambrosini@pm.me> | 2021-09-09 10:32:04 +0300 |
---|---|---|
committer | marco <marcoambrosini@pm.me> | 2021-10-05 19:16:33 +0300 |
commit | 26d0214166498fa6569105d0a1b24b5bece9b390 (patch) | |
tree | cac6765117b1d3043d493795a2b0a380a059c195 /src | |
parent | 7d4e3c6877d697e3d0136fdfedd0462b709070bd (diff) |
Create DeviceChecker component
Signed-off-by: marco <marcoambrosini@pm.me>
Diffstat (limited to 'src')
-rw-r--r-- | src/components/DeviceChecker/DeviceChecker.vue | 330 | ||||
-rw-r--r-- | src/components/TopBar/CallButton.vue | 26 |
2 files changed, 355 insertions, 1 deletions
diff --git a/src/components/DeviceChecker/DeviceChecker.vue b/src/components/DeviceChecker/DeviceChecker.vue new file mode 100644 index 000000000..be6ee6de6 --- /dev/null +++ b/src/components/DeviceChecker/DeviceChecker.vue @@ -0,0 +1,330 @@ +<!-- + - @copyright Copyright (c) 2021 Marco Ambrosini <marcoambrosini@pm.me> + - + - @author Marco Ambrosini <marcoambrosini@pm.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> + <Modal v-if="modal" + class="talk-modal" + size="large" + @close="closeModal"> + <div class="device-checker"> + <h2 class="device-checker__title"> + {{ t('spreed', 'Camera and microphone check') }} + </h2> + <!-- Preview --> + <div class="device-checker__preview"> + <!-- eslint-disable-next-line --> + <video v-show="showVideo" + ref="video" + class="preview__video" + disable-picture-in-picture="true" + tabindex="-1" /> + <div v-show="!showVideo" + class="preview__novideo"> + <VideoBackground + :display-name="displayName" + :user="userId" /> + <Avatar v-if="userId" + :size="128" + :disable-menu="true" + :disable-tooltip="true" + :show-user-status="false" + :user="userId" + :display-name="displayName" /> + <div v-if="!userId" + class="avatar guest"> + {{ firstLetterOfGuestName }} + </div> + </div> + </div> + + <!-- + Toggle audio and video on and off before starting or joining + a call. + --> + <div class="device-checker__call-preferences"> + <!-- Audio toggle --> + <button + class="device-toggle" + :disabled="!audioPreviewAvailable" + @click="toggleAudio"> + <span class="device-toggle__icon"> + <Microphone + v-if="audioOn" + title="" + decorative + :size="20" /> + <MicrophoneOff + v-if="!audioOn" + title="" + decorative + :size="20" /> + </span> + </button> + + <!-- Video toggle --> + <button + class="device-toggle" + :disabled="!videoPreviewAvailable" + @click="toggleVideo"> + <span class="device-toggle__icon"> + <Video + v-if="videoOn" + title="" + decorative + :size="20" /> + <VideoOff + v-if="!videoOn" + title="" + decorative + :size="20" /> + </span> + </button> + </div> + + <!-- Device selection --> + <div class="device-checker__device-selection"> + <button v-if="!showDeviceSelection" + class="select-devices" + @click="showDeviceSelection = true"> + <span class="select-devices__icon"> + <Cog + title="" + decorative + :size="20" /> + </span> + <span> {{ t('spreed', 'Choose devices') }}</span> + </button> + <template v-if="showDeviceSelection"> + <MediaDevicesSelector kind="audioinput" + :devices="devices" + :device-id="audioInputId" + @update:deviceId="audioInputId = $event" /> + <MediaDevicesSelector kind="videoinput" + :devices="devices" + :device-id="videoInputId" + @update:deviceId="videoInputId = $event" /> + </template> + </div> + + <!-- Join call --> + <CallButton + class="call-button" + :force-join-call="true" /> + </div> + </Modal> +</template> + +<script> +import Modal from '@nextcloud/vue/dist/Components/Modal' +import { devices } from '../../mixins/devices' +import MediaDevicesSelector from '../MediaDevicesSelector.vue' +import VideoBackground from '../CallView/shared/VideoBackground.vue' +import Avatar from '@nextcloud/vue/dist/Components/Avatar' +import Cog from 'vue-material-design-icons/Cog.vue' +import Microphone from 'vue-material-design-icons/Microphone' +import MicrophoneOff from 'vue-material-design-icons/MicrophoneOff' +import Video from 'vue-material-design-icons/Video' +import VideoOff from 'vue-material-design-icons/VideoOff' +import { localMediaModel } from '../../utils/webrtc/index' +import CallButton from '../TopBar/CallButton.vue' +import { subscribe, unsubscribe } from '@nextcloud/event-bus' + +export default { + name: 'DeviceChecker', + + components: { + Modal, + MediaDevicesSelector, + VideoBackground, + Avatar, + Cog, + Microphone, + MicrophoneOff, + Video, + VideoOff, + CallButton, + }, + + mixins: [devices], + + data() { + return { + model: localMediaModel, + modal: false, + showDeviceSelection: false, + audioOn: undefined, + videoOn: undefined, + } + }, + + computed: { + displayName() { + return this.$store.getters.getDisplayName() + }, + + userId() { + return this.$store.getters.getUserId() + }, + + token() { + return this.$store.getters.getToken() + }, + + showVideo() { + return this.videoPreviewAvailable && this.videoOn + }, + }, + + mounted() { + subscribe('talk:device-checker:show', this.showModal) + subscribe('talk:device-checker:hide', this.closeModal) + this.audioOn = !localStorage.getItem('audioDisabled_' + this.token) + this.videoOn = !localStorage.getItem('videoDisabled_' + this.token) + }, + + beforeDestroy() { + unsubscribe('talk:device-checker:show', this.showModal) + unsubscribe('talk:device-checker:hide', this.closeModal) + }, + + methods: { + showModal() { + this.modal = true + }, + + closeModal() { + this.modal = false + this.showDeviceSelection = false + }, + + toggleAudio() { + if (!this.audioOn) { + localStorage.removeItem('audioDisabled_' + this.token) + this.audioOn = true + } else { + localStorage.setItem('audioDisabled_' + this.token, 'true') + this.audioOn = false + } + }, + + toggleVideo() { + if (!this.videoOn) { + localStorage.removeItem('videoDisabled_' + this.token) + this.videoOn = true + } else { + localStorage.setItem('videoDisabled_' + this.token, 'true') + this.videoOn = false + } + }, + }, +} +</script> + +<style lang="scss" scoped> +@import '../../assets/variables.scss'; +@import '../../assets/avatar.scss'; +@include avatar-mixin(64px); +@include avatar-mixin(128px); + +.device-checker { + width: 350px; + padding: 20px; + background-color: var(--color-main-background); + &__title { + text-align: center; + } + &__preview { + position: relative; + margin: 0 auto 12px auto; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; + border-radius: 12px; + height: 263px; + background-color: var(--color-loading-dark); + } + + &__device-selection { + width: 100%; + } + + &__call-preferences { + height: $clickable-area; + display: flex; + justify-content: center; + align-items: center; + } +} + +.preview { + &__video { + max-width: 100%; + object-fit: contain; + max-height: 100%; + } + + &__novideo { + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; + width: 100%; + } +} + +.select-devices { + display: flex; + align-items: center; + justify-content: center; + background: none; + border: none; + padding: 0; + margin: auto; + &__icon { + margin-right: 4px; + } + opacity: 0.8; + &:hover, + &:focus { + opacity: 1; + } +} + +.device-toggle { + background: none; + border: none; +} + +.call-button { + display: flex; + justify-content: center; + align-items: center; + min-width: 150px; + margin: auto; +} + +.checkbox { + display: flex; + justify-content: center; + margin: 14px; +} +</style> diff --git a/src/components/TopBar/CallButton.vue b/src/components/TopBar/CallButton.vue index d0ffee55e..35e1ac75d 100644 --- a/src/components/TopBar/CallButton.vue +++ b/src/components/TopBar/CallButton.vue @@ -31,7 +31,7 @@ :disabled="startCallButtonDisabled || loading || blockCalls" class="top-bar__button" :class="startCallButtonClasses" - @click="joinCall"> + @click="handleClick"> <span class="icon" :class="startCallIcon" /> @@ -55,6 +55,7 @@ import isInCall from '../../mixins/isInCall' import participant from '../../mixins/participant' import Tooltip from '@nextcloud/vue/dist/Directives/Tooltip' import { emit } from '@nextcloud/event-bus' +import BrowserStorage from '../../services/BrowserStorage' export default { name: 'CallButton', @@ -69,6 +70,17 @@ export default { participant, ], + props: { + /** + * Skips the device checker dialog and joins or starts the call + * upon clicking the button + */ + forceJoinCall: { + type: Boolean, + default: false, + }, + }, + data() { return { loading: false, @@ -211,6 +223,18 @@ export default { }) this.loading = false }, + + handleClick() { + const shouldShowDeviceCheckerScreen = (BrowserStorage.getItem('showDeviceChecker' + this.token) === null + || BrowserStorage.getItem('showDeviceChecker' + this.token) === 'true') && !this.forceJoinCall + console.debug(shouldShowDeviceCheckerScreen) + if (shouldShowDeviceCheckerScreen) { + emit('talk:device-checker:show') + } else { + emit('talk:device-checker:hide') + this.joinCall() + } + }, }, } </script> |