mirror of
https://github.com/nextcloud/spreed.git
synced 2025-12-18 05:20:50 +01:00
289 lines
7.7 KiB
Vue
289 lines
7.7 KiB
Vue
<!--
|
|
- SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
|
|
- SPDX-License-Identifier: AGPL-3.0-or-later
|
|
-->
|
|
|
|
<template>
|
|
<TransitionWrapper name="slide-right">
|
|
<aside v-if="isOpen" id="talk-sidebar">
|
|
<div v-if="!token" class="emptycontent">
|
|
<div class="icon icon-talk" />
|
|
<h2>{{ t('spreed', 'This conversation has ended') }}</h2>
|
|
</div>
|
|
<template v-else>
|
|
<TopBar is-in-call is-sidebar />
|
|
<CallView :token="token" is-sidebar />
|
|
<InternalSignalingHint />
|
|
<RouterView />
|
|
<PollManager />
|
|
<PollViewer />
|
|
<MediaSettings v-model:recording-consent-given="recordingConsentGiven" />
|
|
</template>
|
|
</aside>
|
|
</TransitionWrapper>
|
|
</template>
|
|
|
|
<script>
|
|
import { getCurrentUser, getGuestNickname } from '@nextcloud/auth'
|
|
import { emit } from '@nextcloud/event-bus'
|
|
import { loadState } from '@nextcloud/initial-state'
|
|
import { t } from '@nextcloud/l10n'
|
|
import CallView from './components/CallView/CallView.vue'
|
|
import MediaSettings from './components/MediaSettings/MediaSettings.vue'
|
|
import PollManager from './components/PollViewer/PollManager.vue'
|
|
import PollViewer from './components/PollViewer/PollViewer.vue'
|
|
import InternalSignalingHint from './components/RightSidebar/InternalSignalingHint.vue'
|
|
import TopBar from './components/TopBar/TopBar.vue'
|
|
import TransitionWrapper from './components/UIShared/TransitionWrapper.vue'
|
|
import { useGetMessagesProvider } from './composables/useGetMessages.ts'
|
|
import { useHashCheck } from './composables/useHashCheck.js'
|
|
import { useSessionIssueHandler } from './composables/useSessionIssueHandler.ts'
|
|
import { EventBus } from './services/EventBus.ts'
|
|
import {
|
|
leaveConversationSync,
|
|
setGuestUserName,
|
|
} from './services/participantsService.js'
|
|
import { useActorStore } from './stores/actor.ts'
|
|
import { useTokenStore } from './stores/token.ts'
|
|
import { signalingKill } from './utils/webrtc/index.js'
|
|
|
|
export default {
|
|
|
|
name: 'PublicShareAuthSidebar',
|
|
|
|
components: {
|
|
InternalSignalingHint,
|
|
CallView,
|
|
MediaSettings,
|
|
PollManager,
|
|
PollViewer,
|
|
TopBar,
|
|
TransitionWrapper,
|
|
},
|
|
|
|
setup() {
|
|
useHashCheck()
|
|
useGetMessagesProvider()
|
|
|
|
return {
|
|
isLeavingAfterSessionIssue: useSessionIssueHandler(),
|
|
actorStore: useActorStore(),
|
|
tokenStore: useTokenStore(),
|
|
}
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
fetchCurrentConversationIntervalId: null,
|
|
isWaitingToClose: false,
|
|
recordingConsentGiven: false,
|
|
}
|
|
},
|
|
|
|
computed: {
|
|
token() {
|
|
return this.tokenStore.token
|
|
},
|
|
|
|
conversation() {
|
|
return this.$store.getters.conversation(this.token)
|
|
},
|
|
|
|
isOpen() {
|
|
return this.conversation || this.isWaitingToClose
|
|
},
|
|
},
|
|
|
|
watch: {
|
|
token(token) {
|
|
if (token) {
|
|
this.joinConversation()
|
|
}
|
|
},
|
|
|
|
conversation(conversation) {
|
|
if (!conversation) {
|
|
this.isWaitingToClose = true
|
|
window.setTimeout(() => {
|
|
this.isWaitingToClose = false
|
|
}, 5000)
|
|
}
|
|
},
|
|
},
|
|
|
|
beforeMount() {
|
|
window.addEventListener('unload', () => {
|
|
console.info('Navigating away, leaving conversation')
|
|
if (this.token) {
|
|
// We have to do this synchronously, because in unload and beforeunload
|
|
// Promises, async and await are prohibited.
|
|
signalingKill()
|
|
if (!this.isLeavingAfterSessionIssue) {
|
|
leaveConversationSync(this.token)
|
|
}
|
|
}
|
|
})
|
|
},
|
|
|
|
methods: {
|
|
t,
|
|
|
|
async joinConversation() {
|
|
const currentUser = getCurrentUser()
|
|
const guestNickname = getGuestNickname()
|
|
|
|
if (currentUser) {
|
|
this.actorStore.setCurrentUser(currentUser)
|
|
} else if (guestNickname) {
|
|
this.actorStore.setDisplayName(guestNickname)
|
|
}
|
|
|
|
await this.$router.push({ name: 'conversation', params: { token: this.token } })
|
|
await this.$store.dispatch('joinConversation', { token: this.token })
|
|
|
|
// Add guest name to the store, only possible after joining the conversation
|
|
if (guestNickname) {
|
|
await setGuestUserName(this.token, guestNickname)
|
|
}
|
|
|
|
// Fetching the conversation needs to be done once the user has
|
|
// joined the conversation (otherwise only limited data would be
|
|
// received if the user was not a participant of the conversation
|
|
// yet).
|
|
await this.fetchCurrentConversation()
|
|
|
|
// FIXME The participant will not be updated with the server data
|
|
// when the conversation is got again (as "addParticipantOnce" is
|
|
// used), although that should not be a problem given that only the
|
|
// "inCall" flag (which is locally updated when joining and leaving
|
|
// a call) is currently used.
|
|
if (loadState('spreed', 'signaling_mode') !== 'internal') {
|
|
EventBus.on('should-refresh-conversations', this.fetchCurrentConversation)
|
|
EventBus.on('signaling-participant-list-changed', this.fetchCurrentConversation)
|
|
} else {
|
|
// The "should-refresh-conversations" event is triggered only when
|
|
// the external signaling server is used; when the internal
|
|
// signaling server is used periodic polling has to be used
|
|
// instead.
|
|
this.fetchCurrentConversationIntervalId = window.setInterval(this.fetchCurrentConversation, 30000)
|
|
}
|
|
|
|
emit('talk:media-settings:show', 'video-verification')
|
|
},
|
|
|
|
async fetchCurrentConversation() {
|
|
if (!this.token) {
|
|
return
|
|
}
|
|
|
|
try {
|
|
await this.$store.dispatch('fetchConversation', { token: this.token })
|
|
|
|
// Although the current participant is automatically added to
|
|
// the participants store it must be explicitly set in the
|
|
// actors store.
|
|
if (!this.actorStore.userId) {
|
|
// Set the current actor/participant for guests
|
|
const conversation = this.$store.getters.conversation(this.token)
|
|
|
|
// Setting a guest only uses "sessionId" and "participantType".
|
|
this.actorStore.setCurrentParticipant(conversation)
|
|
}
|
|
} catch (exception) {
|
|
window.clearInterval(this.fetchCurrentConversationIntervalId)
|
|
|
|
this.$store.dispatch('deleteConversation', this.token)
|
|
this.tokenStore.updateToken('')
|
|
}
|
|
},
|
|
},
|
|
}
|
|
</script>
|
|
|
|
<style lang="css">
|
|
#talk-sidebar,
|
|
#talk-sidebar *,
|
|
#talk-sidebar *::before,
|
|
#talk-sidebar *::after {
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
</style>
|
|
|
|
<style lang="scss" scoped>
|
|
@use './assets/variables' as *;
|
|
|
|
/* Styles based on the NcAppSidebar */
|
|
#talk-sidebar {
|
|
position: relative;
|
|
flex-shrink: 0;
|
|
width: clamp(300px, 27vw, 500px);
|
|
height: 100%;
|
|
|
|
background: var(--color-main-background);
|
|
border-inline-start: 1px solid var(--color-border);
|
|
|
|
overflow-x: hidden;
|
|
overflow-y: auto;
|
|
z-index: 1500;
|
|
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
|
|
/* Unset conflicting rules from guest.css for the sidebar. */
|
|
text-align: start;
|
|
|
|
& > .emptycontent {
|
|
/* Remove default margin-top as it is unneeded when showing only the empty
|
|
* content in a flex sidebar. */
|
|
margin-top: 0;
|
|
}
|
|
|
|
& #call-container {
|
|
position: relative;
|
|
|
|
flex-grow: 1;
|
|
|
|
/* Prevent shadows of videos from leaking on other elements. */
|
|
overflow: hidden;
|
|
|
|
/* Distribute available height between call container and chat view. */
|
|
height: 40%;
|
|
|
|
/* Ensure that the background will be black also in voice only calls. */
|
|
background-color: $color-call-background;
|
|
|
|
:deep(.videoContainer.promoted video) {
|
|
/* Base the size of the video on its width instead of on its height;
|
|
* otherwise the video could appear in full height but cropped on the sides
|
|
* due to the space available in the sidebar being typically larger in
|
|
* vertical than in horizontal. */
|
|
width: 100%;
|
|
height: auto;
|
|
}
|
|
}
|
|
|
|
& .chatView {
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
position: relative;
|
|
|
|
flex-grow: 1;
|
|
|
|
/* Distribute available height between call container and chat view. */
|
|
height: 60%;
|
|
}
|
|
|
|
& :deep(.wrapper) {
|
|
margin-top: 0;
|
|
}
|
|
|
|
/* Restore rules from style.scss overwritten by guest.css for the sidebar. */
|
|
& :deep(a) {
|
|
color: var(--color-main-text);
|
|
font-weight: inherit;
|
|
}
|
|
}
|
|
</style>
|