Skip to content

Commit

Permalink
Merge pull request #11903 from nextcloud/enh/11869/add-grabber-pointer
Browse files Browse the repository at this point in the history
Fix(PresenterOverlay): Add grabbing cursor
  • Loading branch information
Antreesy authored Mar 26, 2024
2 parents 68af2c6 + 2a06419 commit f28c5c4
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 80 deletions.
95 changes: 15 additions & 80 deletions src/components/CallView/CallView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -75,36 +75,13 @@
:shared-data="sharedDatas[shownRemoteScreenPeerId]"
is-big />
<!-- presenter overlay -->
<VueDraggableResizable v-if="shouldShowPresenterOverlay"
:key="presenterOverlaySize"
parent
:resizable="false"
:h="presenterOverlaySize"
:w="presenterOverlaySize"
:x="10"
:y="10">
<VideoVue class="presenter-overlay__video"
:token="token"
:model="shownRemoteScreenCallParticipantModel"
:shared-data="sharedDatas[shownRemoteScreenPeerId]"
is-presenter-overlay
un-selectable
hide-bottom-bar
@click-presenter="toggleShowPresenterOverlay" />
</VueDraggableResizable>
<!-- presenter button when presenter overlay is collapsed -->
<NcButton v-else-if="isPresenterCollapsed"
:aria-label="t('spreed', 'Show presenter')"
:title="t('spreed', 'Show presenter')"
class="presenter-overlay--collapse"
type="tertiary-no-background"
@click="toggleShowPresenterOverlay">
<template #icon>
<AccountBox fill-color="#ffffff" :size="20" />
</template>
</NcButton>
<PresenterOverlay v-if="shouldShowPresenterOverlay"
:token="token"
:model="shownRemoteScreenCallParticipantModel"
:shared-data="sharedDatas[shownRemoteScreenPeerId]"
:is-collapsed="!showPresenterOverlay"
@click="toggleShowPresenterOverlay" />
</template>

<!-- Promoted "autopilot" mode -->
<VideoVue v-else-if="promotedParticipantModel"
:key="promotedParticipantModel.attributes.peerId"
Expand Down Expand Up @@ -159,19 +136,15 @@

<script>
import debounce from 'debounce'
import VueDraggableResizable from 'vue-draggable-resizable'

import AccountBox from 'vue-material-design-icons/AccountBoxOutline.vue'

import { getCapabilities } from '@nextcloud/capabilities'
import { showMessage } from '@nextcloud/dialogs'
import { subscribe, unsubscribe } from '@nextcloud/event-bus'

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

import Grid from './Grid/Grid.vue'
import EmptyCallView from './shared/EmptyCallView.vue'
import LocalVideo from './shared/LocalVideo.vue'
import PresenterOverlay from './shared/PresenterOverlay.vue'
import ReactionToaster from './shared/ReactionToaster.vue'
import Screen from './shared/Screen.vue'
import VideoVue from './shared/VideoVue.vue'
Expand All @@ -190,13 +163,11 @@ export default {
name: 'CallView',

components: {
AccountBox,
EmptyCallView,
ViewerOverlayCallView,
VueDraggableResizable,
Grid,
LocalVideo,
NcButton,
PresenterOverlay,
ReactionToaster,
Screen,
VideoVue,
Expand Down Expand Up @@ -242,9 +213,9 @@ export default {
isBackgroundBlurred: true,
showPresenterOverlay: true,
debounceFetchPeers: () => {},
presenterOverlaySize: 128,
}
},

computed: {
promotedParticipantModel() {
return this.callParticipantModels.find((callParticipantModel) => this.sharedDatas[callParticipantModel.attributes.peerId].promoted)
Expand Down Expand Up @@ -367,19 +338,16 @@ export default {
})
},

isPresenterCollapsed() {
return !this.showPresenterOverlay && this.shownRemoteScreenCallParticipantModel.attributes.videoAvailable
},

shouldShowPresenterOverlay() {
return this.showPresenterOverlay && this.isModelWithVideo(this.shownRemoteScreenCallParticipantModel)
return this.shownRemoteScreenCallParticipantModel.attributes.videoAvailable || this.isModelWithVideo(this.shownRemoteScreenCallParticipantModel)

},

presenterVideoBlockerEnabled() {
return this.sharedDatas[this.shownRemoteScreenPeerId]?.remoteVideoBlocker?.isVideoEnabled()
},
},

watch: {
'localCallParticipantModel.attributes.peerId'(newValue, previousValue) {
const index = this.screens.indexOf(previousValue)
Expand Down Expand Up @@ -462,12 +430,14 @@ export default {
this.showPresenterOverlay = value
},
},

created() {
// Ensure that data is properly initialized before mounting the
// subviews.
this.updateDataFromCallParticipantModels(this.callParticipantModels)
this.isBackgroundBlurred = BrowserStorage.getItem('background-blurred') !== 'false'
},

mounted() {
this.debounceFetchPeers = debounce(this.fetchPeers, 1500)
EventBus.$on('refresh-peer-list', this.debounceFetchPeers)
Expand All @@ -476,9 +446,8 @@ export default {

subscribe('switch-screen-to-id', this._switchScreenToId)
subscribe('set-background-blurred', this.setBackgroundBlurred)

window.addEventListener('resize', this.updateSize)
},

beforeDestroy() {
this.debounceFetchPeers.clear?.()
EventBus.$off('refresh-peer-list', this.debounceFetchPeers)
Expand All @@ -487,9 +456,8 @@ export default {

unsubscribe('switch-screen-to-id', this._switchScreenToId)
unsubscribe('set-background-blurred', this.setBackgroundBlurred)

window.removeEventListener('resize', this.updateSize)
},

methods: {
/**
* Updates data properties that depend on the CallParticipantModels.
Expand Down Expand Up @@ -769,9 +737,6 @@ export default {
}
},

updateSize() {
this.presenterOverlaySize = Math.min(Math.max(window.innerWidth * 0.1, 100), 242)
},
},
}
</script>
Expand All @@ -796,36 +761,6 @@ export default {
}
}

.presenter-overlay__video {
position: relative;
--max-size: 242px;
--min-size: 100px;
width: 10vw;
height: 10vw;
max-width: var(--max-size);
max-height: var(--max-size);
min-width: var(--min-size);
min-height: var(--min-size);
z-index: 10;
}

.presenter-overlay--collapse {
position: absolute !important;
opacity: .7;
bottom: 48px;
right: 0;

#call-container:hover & {
background-color: rgba(0, 0, 0, 0.1) !important;

&:hover,
&:focus {
opacity: 1;
background-color: rgba(0, 0, 0, 0.2) !important;
}
}
}

#videos {
position: absolute;
width: 100%;
Expand Down
164 changes: 164 additions & 0 deletions src/components/CallView/shared/PresenterOverlay.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
<!--
- @copyright Copyright (c) 2024 Dorra Jaouad <dorra.jaoued1@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>
<VueDraggableResizable v-if="!isCollapsed"
:key="presenterOverlaySize"
parent
:resizable="false"
:h="presenterOverlaySize"
:w="presenterOverlaySize"
:x="10"
:y="10"
@dragging="isDragging = true"
@dragstop="isDragging = false">
<VideoVue :token="token"
:class="{ 'dragging': isDragging }"
class="presenter-overlay__video"
:model="model"
:shared-data="sharedData"
is-presenter-overlay
un-selectable
hide-bottom-bar
@click-presenter="$emit('click')" />
</VueDraggableResizable>

<!-- presenter button when presenter overlay is collapsed -->
<NcButton v-else
:aria-label="t('spreed', 'Show presenter')"
:title="t('spreed', 'Show presenter')"
class="presenter-overlay--collapsed"
type="tertiary-no-background"
@click="$emit('click')">
<template #icon>
<AccountBox fill-color="#ffffff" :size="20" />
</template>
</NcButton>
</template>

<script>

import VueDraggableResizable from 'vue-draggable-resizable'

import AccountBox from 'vue-material-design-icons/AccountBoxOutline.vue'

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

import VideoVue from './VideoVue.vue'

export default {
name: 'PresenterOverlay',

components: {
AccountBox,
VueDraggableResizable,
NcButton,
VideoVue,
},

props: {
token: {
type: String,
required: true,
},

model: {
type: Object,
required: true,
},

sharedData: {
type: Object,
required: true,
},

isCollapsed: {
type: Boolean,
required: true,
},
},

emits: ['click'],

data() {
return {
presenterOverlaySize: 128,
isDragging: false,
}
},

mounted() {
window.addEventListener('resize', this.updateSize)
},

beforeDestroy() {
window.removeEventListener('resize', this.updateSize)
},

methods: {
updateSize() {
this.presenterOverlaySize = Math.min(Math.max(window.innerWidth * 0.1, 100), 242)
},
},
}
</script>

<style lang="scss" scoped>
.presenter-overlay__video {
position: relative;
--max-size: 242px;
--min-size: 100px;
width: 10vw;
height: 10vw;
max-width: var(--max-size);
max-height: var(--max-size);
min-width: var(--min-size);
min-height: var(--min-size);
z-index: 10;

&:hover {
cursor: grab;
}

&.dragging {
cursor: grabbing;
}
}

.presenter-overlay--collapsed {
position: absolute !important;
opacity: .7;
bottom: 48px;
right: 0;

#call-container:hover & {
background-color: rgba(0, 0, 0, 0.1) !important;

&:hover,
&:focus {
opacity: 1;
background-color: rgba(0, 0, 0, 0.2) !important;
}
}
}

:deep(div) {
// prevent default cursor
cursor: inherit;
}
</style>
2 changes: 2 additions & 0 deletions src/components/CallView/shared/VideoVue.vue
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,7 @@ export default {
content: '';
box-shadow: inset 0 0 0 3px white;
cursor: pointer;
z-index: 1;
}

.presenter-icon__hide {
Expand All @@ -739,6 +740,7 @@ export default {
border-radius: 50%;
padding: 6px;
width: 44px;
z-index: 2; // Above video and its border

&:hover {
cursor: pointer;
Expand Down

0 comments on commit f28c5c4

Please sign in to comment.