Changeset View
Changeset View
Standalone View
Standalone View
src/resources/vue/Meet/Room.vue
Show All 11 Lines | <div id="meet-component"> | ||||
</button> | </button> | ||||
<button :class="'btn link-screen' + (screenShareActive ? ' on' : '')" @click="switchScreen" :disabled="!canShareScreen || !isPublisher()" :title="$t('meet.menu-screen')"> | <button :class="'btn link-screen' + (screenShareActive ? ' on' : '')" @click="switchScreen" :disabled="!canShareScreen || !isPublisher()" :title="$t('meet.menu-screen')"> | ||||
<svg-icon icon="desktop"></svg-icon> | <svg-icon icon="desktop"></svg-icon> | ||||
</button> | </button> | ||||
<button :class="'btn link-hand' + (handRaised ? ' on' : '')" v-if="!isPublisher()" @click="switchHand" :title="$t('meet.menu-hand-' + (handRaised ? 'lower' : 'raise'))"> | <button :class="'btn link-hand' + (handRaised ? ' on' : '')" v-if="!isPublisher()" @click="switchHand" :title="$t('meet.menu-hand-' + (handRaised ? 'lower' : 'raise'))"> | ||||
<svg-icon icon="hand-paper"></svg-icon> | <svg-icon icon="hand-paper"></svg-icon> | ||||
</button> | </button> | ||||
<span id="channel-select" :style="'display:' + (channels.length ? '' : 'none')" class="dropdown"> | <span id="channel-select" :style="'display:' + (channels.length ? '' : 'none')" class="dropdown"> | ||||
<button :class="'btn link-channel' + (session.channel ? ' on' : '')" data-toggle="dropdown" | <button :class="'btn link-channel' + (session.channel ? ' on' : '')" data-bs-toggle="dropdown" | ||||
:title="$t('meet.menu-channel')" aria-haspopup="true" aria-expanded="false" | :title="$t('meet.menu-channel')" aria-haspopup="true" aria-expanded="false" | ||||
> | > | ||||
<svg-icon icon="headphones"></svg-icon> | <svg-icon icon="headphones"></svg-icon> | ||||
<span class="badge badge-danger" v-if="session.channel">{{ session.channel.toUpperCase() }}</span> | <span class="badge badge-danger" v-if="session.channel">{{ session.channel.toUpperCase() }}</span> | ||||
</button> | </button> | ||||
<div class="dropdown-menu"> | <div class="dropdown-menu"> | ||||
<a :class="'dropdown-item' + (!session.channel ? ' active' : '')" href="#" data-code="" @click="switchChannel">- {{ $t('form.none') }} -</a> | <a :class="'dropdown-item' + (!session.channel ? ' active' : '')" href="#" data-code="" @click="switchChannel">- {{ $t('form.none') }} -</a> | ||||
<a v-for="code in channels" :key="code" href="#" @click="switchChannel" :data-code="code" | <a v-for="code in channels" :key="code" href="#" @click="switchChannel" :data-code="code" | ||||
Show All 24 Lines | <div id="meet-component"> | ||||
<div class="card-title">{{ $t('meet.setup-title') }}</div> | <div class="card-title">{{ $t('meet.setup-title') }}</div> | ||||
<div class="card-text"> | <div class="card-text"> | ||||
<form class="media-setup-form row" @submit.prevent="joinSession"> | <form class="media-setup-form row" @submit.prevent="joinSession"> | ||||
<div class="media-setup-preview col-sm-6 mb-3 mb-sm-0"> | <div class="media-setup-preview col-sm-6 mb-3 mb-sm-0"> | ||||
<video class="rounded"></video> | <video class="rounded"></video> | ||||
<div class="volume"><div class="bar"></div></div> | <div class="volume"><div class="bar"></div></div> | ||||
</div> | </div> | ||||
<div class="col-sm-6 align-self-center"> | <div class="col-sm-6 align-self-center"> | ||||
<div class="input-group"> | <div class="input-group mb-2"> | ||||
<label for="setup-microphone" class="input-group-prepend mb-0"> | <label for="setup-microphone" class="input-group-text mb-0" :title="$t('meet.mic')"> | ||||
<span class="input-group-text" :title="$t('meet.mic')"><svg-icon icon="microphone"></svg-icon></span> | <svg-icon icon="microphone"></svg-icon> | ||||
</label> | </label> | ||||
<select class="custom-select" id="setup-microphone" v-model="microphone" @change="setupMicrophoneChange"> | <select class="form-select" id="setup-microphone" v-model="microphone" @change="setupMicrophoneChange"> | ||||
<option value="">{{ $t('form.none') }}</option> | <option value="">{{ $t('form.none') }}</option> | ||||
<option v-for="mic in setup.microphones" :value="mic.deviceId" :key="mic.deviceId">{{ mic.label }}</option> | <option v-for="mic in setup.microphones" :value="mic.deviceId" :key="mic.deviceId">{{ mic.label }}</option> | ||||
</select> | </select> | ||||
</div> | </div> | ||||
<div class="input-group mt-2"> | <div class="input-group mb-2"> | ||||
<label for="setup-camera" class="input-group-prepend mb-0"> | <label for="setup-camera" class="input-group-text mb-0" :title="$t('meet.cam')"> | ||||
<span class="input-group-text" :title="$t('meet.cam')"><svg-icon icon="video"></svg-icon></span> | <svg-icon icon="video"></svg-icon> | ||||
</label> | </label> | ||||
<select class="custom-select" id="setup-camera" v-model="camera" @change="setupCameraChange"> | <select class="form-select" id="setup-camera" v-model="camera" @change="setupCameraChange"> | ||||
<option value="">{{ $t('form.none') }}</option> | <option value="">{{ $t('form.none') }}</option> | ||||
<option v-for="cam in setup.cameras" :value="cam.deviceId" :key="cam.deviceId">{{ cam.label }}</option> | <option v-for="cam in setup.cameras" :value="cam.deviceId" :key="cam.deviceId">{{ cam.label }}</option> | ||||
</select> | </select> | ||||
</div> | </div> | ||||
<div class="input-group mt-2"> | <div class="input-group mb-2"> | ||||
<label for="setup-nickname" class="input-group-prepend mb-0"> | <label for="setup-nickname" class="input-group-text mb-0" :title="$t('meet.nick')"> | ||||
<span class="input-group-text" :title="$t('meet.nick')"><svg-icon icon="user"></svg-icon></span> | <svg-icon icon="user"></svg-icon> | ||||
</label> | </label> | ||||
<input class="form-control" type="text" id="setup-nickname" v-model="nickname" :placeholder="$t('meet.nick-placeholder')"> | <input class="form-control" type="text" id="setup-nickname" v-model="nickname" :placeholder="$t('meet.nick-placeholder')"> | ||||
</div> | </div> | ||||
<div class="input-group mt-2" v-if="session.config && session.config.requires_password"> | <div class="input-group mt-2" v-if="session.config && session.config.requires_password"> | ||||
<label for="setup-password" class="input-group-prepend mb-0"> | <label for="setup-password" class="input-group-text mb-0" :title="$t('form.password')"> | ||||
<span class="input-group-text" :title="$t('form.password')"><svg-icon icon="key"></svg-icon></span> | <svg-icon icon="key"></svg-icon> | ||||
</label> | </label> | ||||
<input type="password" class="form-control" id="setup-password" v-model="password" :placeholder="$t('form.password')"> | <input type="password" class="form-control" id="setup-password" v-model="password" :placeholder="$t('form.password')"> | ||||
</div> | </div> | ||||
<div class="mt-3"> | <div class="mt-3"> | ||||
<button type="submit" id="join-button" | <button type="submit" id="join-button" | ||||
:class="'btn w-100 btn-' + (isRoomReady() ? 'success' : 'primary')" | :class="'btn w-100 btn-' + (isRoomReady() ? 'success' : 'primary')" | ||||
> | > | ||||
<span v-if="isRoomReady()">{{ $t('meet.joinnow') }}</span> | <span v-if="isRoomReady()">{{ $t('meet.joinnow') }}</span> | ||||
Show All 25 Lines | <div id="meet-component"> | ||||
<logon-form id="meet-auth" class="hidden" :dashboard="false" @success="authSuccess"></logon-form> | <logon-form id="meet-auth" class="hidden" :dashboard="false" @success="authSuccess"></logon-form> | ||||
<div id="leave-dialog" class="modal" tabindex="-1" role="dialog"> | <div id="leave-dialog" class="modal" tabindex="-1" role="dialog"> | ||||
<div class="modal-dialog" role="document"> | <div class="modal-dialog" role="document"> | ||||
<div class="modal-content"> | <div class="modal-content"> | ||||
<div class="modal-header"> | <div class="modal-header"> | ||||
<h5 class="modal-title">{{ $t('meet.leave-title') }}</h5> | <h5 class="modal-title">{{ $t('meet.leave-title') }}</h5> | ||||
<button type="button" class="close" data-dismiss="modal" :aria-label="$t('btn.close')"> | <button type="button" class="btn-close" data-bs-dismiss="modal" :aria-label="$t('btn.close')"></button> | ||||
<span aria-hidden="true">×</span> | |||||
</button> | |||||
</div> | </div> | ||||
<div class="modal-body"> | <div class="modal-body"> | ||||
<p>{{ $t('meet.leave-body') }}</p> | <p>{{ $t('meet.leave-body') }}</p> | ||||
</div> | </div> | ||||
<div class="modal-footer"> | <div class="modal-footer"> | ||||
<button type="button" class="btn btn-danger modal-action" data-dismiss="modal">{{ $t('btn.close') }}</button> | <button type="button" class="btn btn-danger modal-action" data-bs-dismiss="modal">{{ $t('btn.close') }}</button> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<div id="media-setup-dialog" class="modal" tabindex="-1" role="dialog"> | <div id="media-setup-dialog" class="modal" tabindex="-1" role="dialog"> | ||||
<div class="modal-dialog" role="document"> | <div class="modal-dialog" role="document"> | ||||
<div class="modal-content"> | <div class="modal-content"> | ||||
<div class="modal-header"> | <div class="modal-header"> | ||||
<h5 class="modal-title">{{ $t('meet.media-title') }}</h5> | <h5 class="modal-title">{{ $t('meet.media-title') }}</h5> | ||||
<button type="button" class="close" data-dismiss="modal" :aria-label="$t('btn.close')"> | <button type="button" class="btn-close" data-bs-dismiss="modal" :aria-label="$t('btn.close')"></button> | ||||
<span aria-hidden="true">×</span> | |||||
</button> | |||||
</div> | </div> | ||||
<div class="modal-body"> | <div class="modal-body"> | ||||
<form class="media-setup-form"> | <form class="media-setup-form"> | ||||
<div class="media-setup-preview"></div> | <div class="media-setup-preview"></div> | ||||
<div class="input-group mt-2"> | <div class="input-group mt-2"> | ||||
<label for="setup-mic" class="input-group-prepend mb-0"> | <label for="setup-mic" class="input-group-text mb-0" :title="$t('meet.mic')"> | ||||
<span class="input-group-text" :title="$t('meet.mic')"><svg-icon icon="microphone"></svg-icon></span> | <svg-icon icon="microphone"></svg-icon> | ||||
</label> | </label> | ||||
<select class="custom-select" id="setup-mic" v-model="microphone" @change="setupMicrophoneChange"> | <select class="form-select" id="setup-mic" v-model="microphone" @change="setupMicrophoneChange"> | ||||
<option value="">{{ $t('form.none') }}</option> | <option value="">{{ $t('form.none') }}</option> | ||||
<option v-for="mic in setup.microphones" :value="mic.deviceId" :key="mic.deviceId">{{ mic.label }}</option> | <option v-for="mic in setup.microphones" :value="mic.deviceId" :key="mic.deviceId">{{ mic.label }}</option> | ||||
</select> | </select> | ||||
</div> | </div> | ||||
<div class="input-group mt-2"> | <div class="input-group mt-2"> | ||||
<label for="setup-cam" class="input-group-prepend mb-0"> | <label for="setup-cam" class="input-group-text mb-0" :title="$t('meet.cam')"> | ||||
<span class="input-group-text" :title="$t('meet.cam')"><svg-icon icon="video"></svg-icon></span> | <svg-icon icon="video"></svg-icon> | ||||
</label> | </label> | ||||
<select class="custom-select" id="setup-cam" v-model="camera" @change="setupCameraChange"> | <select class="form-select" id="setup-cam" v-model="camera" @change="setupCameraChange"> | ||||
<option value="">{{ $t('form.none') }}</option> | <option value="">{{ $t('form.none') }}</option> | ||||
<option v-for="cam in setup.cameras" :value="cam.deviceId" :key="cam.deviceId">{{ cam.label }}</option> | <option v-for="cam in setup.cameras" :value="cam.deviceId" :key="cam.deviceId">{{ cam.label }}</option> | ||||
</select> | </select> | ||||
</div> | </div> | ||||
</form> | </form> | ||||
</div> | </div> | ||||
<div class="modal-footer"> | <div class="modal-footer"> | ||||
<button type="button" class="btn btn-secondary modal-action" data-dismiss="modal">{{ $t('btn.close') }}</button> | <button type="button" class="btn btn-secondary modal-action" data-bs-dismiss="modal">{{ $t('btn.close') }}</button> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<room-options v-if="session.config" :config="session.config" :room="room" @config-update="configUpdate"></room-options> | <room-options v-if="session.config" :config="session.config" :room="room" @config-update="configUpdate"></room-options> | ||||
</div> | </div> | ||||
</template> | </template> | ||||
<script> | <script> | ||||
import { Modal } from 'bootstrap' | |||||
import { Meet, Roles } from '../../js/meet/app.js' | import { Meet, Roles } from '../../js/meet/app.js' | ||||
import StatusMessage from '../Widgets/StatusMessage' | import StatusMessage from '../Widgets/StatusMessage' | ||||
import LogonForm from '../Login' | import LogonForm from '../Login' | ||||
import RoomOptions from './RoomOptions' | import RoomOptions from './RoomOptions' | ||||
// Register additional icons | // Register additional icons | ||||
import { library } from '@fortawesome/fontawesome-svg-core' | import { library } from '@fortawesome/fontawesome-svg-core' | ||||
▲ Show 20 Lines • Show All 94 Lines • ▼ Show 20 Lines | export default { | ||||
this.meet = new Meet($('#meet-session')[0]); | this.meet = new Meet($('#meet-session')[0]); | ||||
this.canShareScreen = this.meet.isScreenSharingSupported() | this.canShareScreen = this.meet.isScreenSharingSupported() | ||||
// Check the room and init the session | // Check the room and init the session | ||||
this.initSession() | this.initSession() | ||||
// Setup the room UI | // Setup the room UI | ||||
this.setupSession() | this.setupSession() | ||||
// Configure dialog events | |||||
$('#leave-dialog')[0].addEventListener('hide.bs.modal', () => { | |||||
// FIXME: Where exactly the user should land? Currently he'll land | |||||
// on dashboard (if he's logged in) or login form (if he's not). | |||||
this.$router.push({ name: 'dashboard' }) | |||||
}) | |||||
const dialog = $('#media-setup-dialog')[0] | |||||
dialog.addEventListener('show.bs.modal', () => { this.meet.setupStart() }) | |||||
dialog.addEventListener('hide.bs.modal', () => { this.meet.setupStop() }) | |||||
}, | }, | ||||
beforeDestroy() { | beforeDestroy() { | ||||
clearTimeout(roomRequest) | clearTimeout(roomRequest) | ||||
$('#app').removeClass('meet') | $('#app').removeClass('meet') | ||||
if (this.meet) { | if (this.meet) { | ||||
this.meet.leaveRoom() | this.meet.leaveRoom() | ||||
▲ Show 20 Lines • Show All 143 Lines • ▼ Show 20 Lines | export default { | ||||
// FIXME: Should the message close button act as the Deny button? Do we need the Deny button? | // FIXME: Should the message close button act as the Deny button? Do we need the Deny button? | ||||
let body = $( | let body = $( | ||||
`<div>` | `<div>` | ||||
+ `<div class="picture"><img src="${data.picture}"></div>` | + `<div class="picture"><img src="${data.picture}"></div>` | ||||
+ `<div class="content">` | + `<div class="content">` | ||||
+ `<p class="mb-2"></p>` | + `<p class="mb-2"></p>` | ||||
+ `<div class="text-right">` | + `<div class="text-end">` | ||||
+ `<button type="button" class="btn btn-sm btn-success accept">${this.$t('btn.accept')}</button>` | + `<button type="button" class="btn btn-sm btn-success accept">${this.$t('btn.accept')}</button>` | ||||
+ `<button type="button" class="btn btn-sm btn-danger deny ml-2">${this.$t('btn.deny')}</button>` | + `<button type="button" class="btn btn-sm btn-danger deny ms-2">${this.$t('btn.deny')}</button>` | ||||
) | ) | ||||
this.$toast.message({ | this.$toast.message({ | ||||
className: 'join-request', | className: 'join-request', | ||||
icon: 'user', | icon: 'user', | ||||
timeout: 0, | timeout: 0, | ||||
title: this.$t('meet.join-request'), | title: this.$t('meet.join-request'), | ||||
// titleClassName: '', | // titleClassName: '', | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | export default { | ||||
} | } | ||||
this.session.onError = () => { | this.session.onError = () => { | ||||
this.roomState = 500 | this.roomState = 500 | ||||
} | } | ||||
this.session.onDestroy = event => { | this.session.onDestroy = event => { | ||||
// TODO: Display different message for each reason: forceDisconnectByUser, | // TODO: Display different message for each reason: forceDisconnectByUser, | ||||
// forceDisconnectByServer, sessionClosedByServer? | // forceDisconnectByServer, sessionClosedByServer? | ||||
if (event.reason != 'disconnect' && event.reason != 'networkDisconnect' && !this.isRoomOwner()) { | if (event.reason != 'disconnect' && event.reason != 'networkDisconnect' && !this.isRoomOwner()) { | ||||
$('#leave-dialog').on('hide.bs.modal', () => { | new Modal('#leave-dialog').show() | ||||
// FIXME: Where exactly the user should land? Currently he'll land | |||||
// on dashboard (if he's logged in) or login form (if he's not). | |||||
this.$router.push({ name: 'dashboard' }) | |||||
}).modal() | |||||
} | } | ||||
} | } | ||||
this.session.onDismiss = connId => { this.dismissParticipant(connId) } | this.session.onDismiss = connId => { this.dismissParticipant(connId) } | ||||
this.session.onSessionDataUpdate = data => { this.updateSession(data) } | this.session.onSessionDataUpdate = data => { this.updateSession(data) } | ||||
this.session.onConnectionChange = (connId, data) => { this.updateParticipant(connId, data) } | this.session.onConnectionChange = (connId, data) => { this.updateParticipant(connId, data) } | ||||
this.session.onJoinRequest = data => { this.joinRequest(data) } | this.session.onJoinRequest = data => { this.joinRequest(data) } | ||||
this.session.onMediaSetup = () => { this.setupMedia() } | this.session.onMediaSetup = () => { this.setupMedia() } | ||||
▲ Show 20 Lines • Show All 76 Lines • ▼ Show 20 Lines | export default { | ||||
// This will create max. 24-char numeric string | // This will create max. 24-char numeric string | ||||
this.reqId = (String(Date.now()) + String(Math.random()).substring(2)).substring(0, 24) | this.reqId = (String(Date.now()) + String(Math.random()).substring(2)).substring(0, 24) | ||||
localStorage.setItem(key, this.reqId) | localStorage.setItem(key, this.reqId) | ||||
} | } | ||||
return this.reqId | return this.reqId | ||||
}, | }, | ||||
roomOptions() { | roomOptions() { | ||||
$('#room-options-dialog').modal() | new Modal('#room-options-dialog').show() | ||||
}, | }, | ||||
setupMedia() { | setupMedia() { | ||||
let dialog = $('#media-setup-dialog') | const dialog = $('#media-setup-dialog')[0] | ||||
if (!dialog.find('video').length) { | if (!$('video', dialog).length) { | ||||
$('#meet-setup').find('video,div.volume').appendTo(dialog.find('.media-setup-preview')) | $('#meet-setup').find('video,div.volume').appendTo($('.media-setup-preview', dialog)) | ||||
} | } | ||||
dialog.on('show.bs.modal', () => { this.meet.setupStart() }) | new Modal(dialog).show() | ||||
.on('hide.bs.modal', () => { this.meet.setupStop() }) | |||||
.modal() | |||||
}, | }, | ||||
setupSession() { | setupSession() { | ||||
this.meet.setupStart({ | this.meet.setupStart({ | ||||
videoElement: $('#meet-setup video')[0], | videoElement: $('#meet-setup video')[0], | ||||
volumeElement: $('#meet-setup .volume')[0], | volumeElement: $('#meet-setup .volume')[0], | ||||
onSuccess: setup => { | onSuccess: setup => { | ||||
this.setup = setup | this.setup = setup | ||||
this.microphone = setup.audioSource | this.microphone = setup.audioSource | ||||
▲ Show 20 Lines • Show All 116 Lines • Show Last 20 Lines |