Changeset View
Changeset View
Standalone View
Standalone View
src/resources/vue/Meet/Room.vue
<template> | <template> | ||||
<div id="meet-component"> | <div id="meet-component"> | ||||
<div id="meet-session-toolbar" class="hidden"> | <div id="meet-session-toolbar" class="hidden"> | ||||
<span id="meet-counter" title="Number of participants"><svg-icon icon="users"></svg-icon> <span></span></span> | |||||
<span id="meet-session-logo" v-html="$root.logo()"></span> | <span id="meet-session-logo" v-html="$root.logo()"></span> | ||||
<div id="meet-session-menu"> | <div id="meet-session-menu"> | ||||
<button class="btn link-audio" :class="{ 'text-primary' : audioActive }" @click="switchSound" :disabled="!isPublisher()" :title="audioActive ? 'Mute audio' : 'Unmute audio'"> | <button :class="'btn link-audio' + (audioActive ? '' : ' on')" @click="switchSound" :disabled="!isPublisher()" :title="audioActive ? 'Mute audio' : 'Unmute audio'"> | ||||
<svg-icon :icon="audioActive ? 'microphone' : 'microphone-slash'"></svg-icon> | <svg-icon :icon="audioActive ? 'microphone' : 'microphone-slash'"></svg-icon> | ||||
</button> | </button> | ||||
<button class="btn link-video" :class="{ 'text-primary' : videoActive }" @click="switchVideo" :disabled="!isPublisher()" :title="videoActive ? 'Mute video' : 'Unmute video'"> | <button :class="'btn link-video' + (videoActive ? '' : ' on')" @click="switchVideo" :disabled="!isPublisher()" :title="videoActive ? 'Mute video' : 'Unmute video'"> | ||||
<svg-icon :icon="videoActive ? 'video' : 'video-slash'"></svg-icon> | <svg-icon :icon="videoActive ? 'video' : 'video-slash'"></svg-icon> | ||||
</button> | </button> | ||||
<button class="btn link-screen" :class="{ 'text-danger' : screenShareActive }" @click="switchScreen" :disabled="!canShareScreen || !isPublisher()" title="Share screen"> | <button :class="'btn link-screen' + (screenShareActive ? ' on' : '')" @click="switchScreen" :disabled="!canShareScreen || !isPublisher()" title="Share screen"> | ||||
<svg-icon icon="desktop"></svg-icon> | <svg-icon icon="desktop"></svg-icon> | ||||
</button> | </button> | ||||
<button class="btn link-hand" :class="{ 'text-primary' : handRaised }" v-if="!isPublisher()" @click="switchHand" :title="handRaised ? 'Lower hand' : 'Raise hand'"> | <button :class="'btn link-hand' + (handRaised ? ' on' : '')" v-if="!isPublisher()" @click="switchHand" :title="handRaised ? 'Lower hand' : 'Raise hand'"> | ||||
<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" title="Interpreted language channel" aria-haspopup="true" aria-expanded="false" data-toggle="dropdown"> | <button :class="'btn link-channel' + (session.channel ? ' on' : '')" data-toggle="dropdown" | ||||
title="Interpreted language 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">- none -</a> | <a :class="'dropdown-item' + (!session.channel ? ' active' : '')" href="#" data-code="" @click="switchChannel">- 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" | ||||
:class="'dropdown-item' + (session.channel == code ? ' active' : '')" | :class="'dropdown-item' + (session.channel == code ? ' active' : '')" | ||||
>{{ languages[code] }}</a> | >{{ languages[code] }}</a> | ||||
</div> | </div> | ||||
</span> | </span> | ||||
<button class="btn link-chat" @click="switchChat" title="Chat"> | <button :class="'btn link-chat' + (chatActive ? ' on' : '')" @click="switchChat" title="Chat"> | ||||
<svg-icon icon="comment"></svg-icon> | <svg-icon icon="comment"></svg-icon> | ||||
</button> | </button> | ||||
<button class="btn link-fullscreen closed hidden" @click="switchFullscreen" title="Full screen"> | <button class="btn link-fullscreen closed hidden" @click="switchFullscreen" title="Full screen"> | ||||
<svg-icon icon="expand"></svg-icon> | <svg-icon icon="expand"></svg-icon> | ||||
</button> | </button> | ||||
<button class="btn link-fullscreen open hidden" @click="switchFullscreen" title="Exit full screen"> | <button class="btn link-fullscreen open hidden" @click="switchFullscreen" title="Exit full screen"> | ||||
<svg-icon icon="compress"></svg-icon> | <svg-icon icon="compress"></svg-icon> | ||||
</button> | </button> | ||||
▲ Show 20 Lines • Show All 106 Lines • ▼ Show 20 Lines | <div id="meet-component"> | ||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> | <button type="button" class="close" data-dismiss="modal" aria-label="Close"> | ||||
<span aria-hidden="true">×</span> | <span aria-hidden="true">×</span> | ||||
</button> | </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-microphone" class="input-group-prepend mb-0"> | <label for="setup-mic" class="input-group-prepend mb-0"> | ||||
<span class="input-group-text" title="Microphone"><svg-icon icon="microphone"></svg-icon></span> | <span class="input-group-text" title="Microphone"><svg-icon icon="microphone"></svg-icon></span> | ||||
</label> | </label> | ||||
<select class="custom-select" id="setup-microphone" v-model="microphone" @change="setupMicrophoneChange"> | <select class="custom-select" id="setup-mic" v-model="microphone" @change="setupMicrophoneChange"> | ||||
<option value="">None</option> | <option value="">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-camera" class="input-group-prepend mb-0"> | <label for="setup-cam" class="input-group-prepend mb-0"> | ||||
<span class="input-group-text" title="Camera"><svg-icon icon="video"></svg-icon></span> | <span class="input-group-text" title="Camera"><svg-icon icon="video"></svg-icon></span> | ||||
</label> | </label> | ||||
<select class="custom-select" id="setup-camera" v-model="camera" @change="setupCameraChange"> | <select class="custom-select" id="setup-cam" v-model="camera" @change="setupCameraChange"> | ||||
<option value="">None</option> | <option value="">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">Close</button> | <button type="button" class="btn btn-secondary modal-action" data-dismiss="modal">Close</button> | ||||
Show All 24 Lines | import { | ||||
faExpand, | faExpand, | ||||
faHandPaper, | faHandPaper, | ||||
faHeadphones, | faHeadphones, | ||||
faMicrophone, | faMicrophone, | ||||
faMicrophoneSlash, | faMicrophoneSlash, | ||||
faMicrophoneAlt, | faMicrophoneAlt, | ||||
faPowerOff, | faPowerOff, | ||||
faUser, | faUser, | ||||
faUsers, | |||||
faVideo, | faVideo, | ||||
faVideoSlash, | faVideoSlash, | ||||
faVolumeMute | faVolumeMute | ||||
} from '@fortawesome/free-solid-svg-icons' | } from '@fortawesome/free-solid-svg-icons' | ||||
// Register only these icons we need | // Register only these icons we need | ||||
library.add( | library.add( | ||||
faComment, | faComment, | ||||
faCog, | faCog, | ||||
faCompress, | faCompress, | ||||
faCrown, | faCrown, | ||||
faDesktop, | faDesktop, | ||||
faExpand, | faExpand, | ||||
faHandPaper, | faHandPaper, | ||||
faHeadphones, | faHeadphones, | ||||
faMicrophone, | faMicrophone, | ||||
faMicrophoneSlash, | faMicrophoneSlash, | ||||
faMicrophoneAlt, | faMicrophoneAlt, | ||||
faPowerOff, | faPowerOff, | ||||
faUser, | faUser, | ||||
faUsers, | |||||
faVideo, | faVideo, | ||||
faVideoSlash, | faVideoSlash, | ||||
faVolumeMute | faVolumeMute | ||||
) | ) | ||||
let roomRequest | let roomRequest | ||||
const authHeader = 'X-Meet-Auth-Token' | const authHeader = 'X-Meet-Auth-Token' | ||||
Show All 33 Lines | export default { | ||||
327: 'Waiting for permission to join the room.', | 327: 'Waiting for permission to join the room.', | ||||
404: 'The room does not exist.', | 404: 'The room does not exist.', | ||||
429: 'Too many requests. Please, wait.', | 429: 'Too many requests. Please, wait.', | ||||
500: 'Failed to connect to the room. Server error.' | 500: 'Failed to connect to the room. Server error.' | ||||
}, | }, | ||||
session: {}, | session: {}, | ||||
audioActive: false, | audioActive: false, | ||||
videoActive: false, | videoActive: false, | ||||
chatActive: false, | |||||
handRaised: false, | handRaised: false, | ||||
screenShareActive: false | screenShareActive: false | ||||
} | } | ||||
}, | }, | ||||
mounted() { | mounted() { | ||||
this.room = this.$route.params.room | this.room = this.$route.params.room | ||||
// Initialize OpenVidu and do some basic checks | // Initialize OpenVidu and do some basic checks | ||||
▲ Show 20 Lines • Show All 215 Lines • ▼ Show 20 Lines | export default { | ||||
clearTimeout(roomRequest) | clearTimeout(roomRequest) | ||||
this.session.nickname = this.nickname | this.session.nickname = this.nickname | ||||
this.session.languages = this.languages | this.session.languages = this.languages | ||||
this.session.menuElement = $('#meet-session-menu')[0] | this.session.menuElement = $('#meet-session-menu')[0] | ||||
this.session.chatElement = $('#meet-chat')[0] | this.session.chatElement = $('#meet-chat')[0] | ||||
this.session.queueElement = $('#meet-queue')[0] | this.session.queueElement = $('#meet-queue')[0] | ||||
this.session.counterElement = $('#meet-counter span')[0] | |||||
this.session.onSuccess = () => { | this.session.onSuccess = () => { | ||||
$('#app').addClass('meet') | $('#app').addClass('meet') | ||||
$('#meet-setup').addClass('hidden') | $('#meet-setup').addClass('hidden') | ||||
$('#meet-session-toolbar,#meet-session-layout').removeClass('hidden') | $('#meet-session-toolbar,#meet-session-layout').removeClass('hidden') | ||||
} | } | ||||
this.session.onError = () => { | this.session.onError = () => { | ||||
this.roomState = 500 | this.roomState = 500 | ||||
} | } | ||||
▲ Show 20 Lines • Show All 144 Lines • ▼ Show 20 Lines | export default { | ||||
let channel = $(e.target).data('code') | let channel = $(e.target).data('code') | ||||
this.$set(this.session, 'channel', channel) | this.$set(this.session, 'channel', channel) | ||||
this.meet.switchChannel(channel) | this.meet.switchChannel(channel) | ||||
}, | }, | ||||
switchChat() { | switchChat() { | ||||
let chat = $('#meet-chat') | let chat = $('#meet-chat') | ||||
let enabled = chat.is('.open') | let enabled = chat.is('.open') | ||||
chat.toggleClass('open') | chat.toggleClass('open') | ||||
if (!enabled) { | if (!enabled) { | ||||
chat.find('textarea').focus() | chat.find('textarea').focus() | ||||
} | } | ||||
this.chatActive = !enabled | |||||
// Trigger resize, so participant matrix can update its layout | // Trigger resize, so participant matrix can update its layout | ||||
window.dispatchEvent(new Event('resize')); | window.dispatchEvent(new Event('resize')); | ||||
}, | }, | ||||
switchFullscreen() { | switchFullscreen() { | ||||
const element = this.$el | const element = this.$el | ||||
$(element).off('fullscreenchange').on('fullscreenchange', (e) => { | $(element).off('fullscreenchange').on('fullscreenchange', (e) => { | ||||
let enabled = document.fullscreenElement == element | let enabled = document.fullscreenElement == element | ||||
let buttons = $('#meet-session-menu').find('.link-fullscreen') | let buttons = $('#meet-session-menu').find('.link-fullscreen') | ||||
buttons.first()[enabled ? 'addClass' : 'removeClass']('hidden') | buttons.first()[enabled ? 'addClass' : 'removeClass']('hidden') | ||||
buttons.last()[!enabled ? 'addClass' : 'removeClass']('hidden') | buttons.last()[!enabled ? 'addClass' : 'removeClass']('hidden') | ||||
}) | }) | ||||
if (document.fullscreenElement) { | if (document.fullscreenElement) { | ||||
document.exitFullscreen() | document.exitFullscreen() | ||||
} else { | } else { | ||||
element.requestFullscreen() | element.requestFullscreen() | ||||
} | } | ||||
}, | }, | ||||
switchHand() { | switchHand() { | ||||
this.updateSelf({ hand: !this.handRaised }) | this.updateSelf({ hand: !this.handRaised }) | ||||
}, | }, | ||||
switchSound() { | switchSound() { | ||||
const enabled = this.meet.switchAudio() | this.audioActive = this.meet.switchAudio() | ||||
this.audioActive = enabled | |||||
}, | }, | ||||
switchVideo() { | switchVideo() { | ||||
const enabled = this.meet.switchVideo() | this.videoActive = this.meet.switchVideo() | ||||
this.videoActive = enabled | |||||
}, | }, | ||||
switchScreen() { | switchScreen() { | ||||
const switchScreenAction = () => { | const switchScreenAction = () => { | ||||
this.meet.switchScreen((enabled, error) => { | this.meet.switchScreen((enabled, error) => { | ||||
this.screenShareActive = enabled | this.screenShareActive = enabled | ||||
if (!enabled && !error) { | if (!enabled && !error) { | ||||
// Closing a screen sharing connection invalidates the token | // Closing a screen sharing connection invalidates the token | ||||
delete this.session.shareToken | delete this.session.shareToken | ||||
Show All 28 Lines | export default { | ||||
updateSession(data) { | updateSession(data) { | ||||
this.session = data | this.session = data | ||||
this.channels = data.channels || [] | this.channels = data.channels || [] | ||||
const isPublisher = this.isPublisher() | const isPublisher = this.isPublisher() | ||||
this.videoActive = isPublisher ? data.videoActive : false | this.videoActive = isPublisher ? data.videoActive : false | ||||
this.audioActive = isPublisher ? data.audioActive : false | this.audioActive = isPublisher ? data.audioActive : false | ||||
this.handRaised = data.hand | this.handRaised = data.hand | ||||
} | } | ||||
} | } | ||||
} | } | ||||
</script> | </script> |