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="$t('meet.partcnt')"><svg-icon icon="users"></svg-icon> <span></span></span> | <span id="meet-counter" :title="$t('meet.partcnt')"><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' + (audioActive ? '' : ' on')" @click="switchSound" :disabled="!isPublisher()" :title="$t('meet.menu-audio-' + (audioActive ? 'mute' : 'unmute'))"> | <button :class="'btn link-audio' + (audioActive ? '' : ' on')" @click="switchSound" :disabled="!isPublisher()" :title="$t('meet.menu-audio-' + (audioActive ? 'mute' : 'unmute'))"> | ||||
<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' + (videoActive ? '' : ' on')" @click="switchVideo" :disabled="!isPublisher()" :title="$t('meet.menu-video-' + (videoActive ? 'mute' : 'unmute'))"> | <button :class="'btn link-video' + (videoActive ? '' : ' on')" @click="switchVideo" :disabled="!isPublisher()" :title="$t('meet.menu-video-' + (videoActive ? 'mute' : 'unmute'))"> | ||||
<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' + (screenShareActive ? ' on' : '')" @click="switchScreen" :disabled="!canShareScreen || !isPublisher()" :title="$t('meet.menu-screen')"> | <button :class="'btn link-screen' + (screenActive ? ' 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-bs-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" | ||||
▲ Show 20 Lines • Show All 149 Lines • ▼ Show 20 Lines | <div id="meet-component"> | ||||
<div class="modal-footer"> | <div class="modal-footer"> | ||||
<button type="button" class="btn btn-secondary modal-action" data-bs-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> | ||||
<room-stats ref="roomStatsDialog" :stats="stats" :room="room"></room-stats> | |||||
</div> | </div> | ||||
</template> | </template> | ||||
<script> | <script> | ||||
import { Modal } from 'bootstrap' | import { Modal, Dropdown } from 'bootstrap' | ||||
import { Meet, Roles } from '../../js/meet/app.js' | import { Media } from '../../js/meet/media.js' | ||||
import { Room as Meet } from '../../js/meet/room.js' | |||||
import { Roles } from '../../js/meet/constants.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' | ||||
import RoomStats from './RoomStats' | |||||
// Register additional icons | // Register additional icons | ||||
import { library } from '@fortawesome/fontawesome-svg-core' | import { library } from '@fortawesome/fontawesome-svg-core' | ||||
import { | import { | ||||
faComment, | faComment, | ||||
faCog, | faCog, | ||||
faCompress, | faCompress, | ||||
Show All 30 Lines | library.add( | ||||
faUser, | faUser, | ||||
faUsers, | faUsers, | ||||
faVideo, | faVideo, | ||||
faVideoSlash, | faVideoSlash, | ||||
faVolumeMute | faVolumeMute | ||||
) | ) | ||||
let roomRequest | let roomRequest | ||||
let statsRequest | |||||
const authHeader = 'X-Meet-Auth-Token' | const authHeader = 'X-Meet-Auth-Token' | ||||
export default { | export default { | ||||
components: { | components: { | ||||
LogonForm, | LogonForm, | ||||
RoomOptions, | RoomOptions, | ||||
RoomStats, | |||||
StatusMessage | StatusMessage | ||||
}, | }, | ||||
data() { | data() { | ||||
return { | return { | ||||
setup: { | setup: { | ||||
cameras: [], | cameras: [], | ||||
microphones: [], | microphones: [], | ||||
}, | }, | ||||
Show All 19 Lines | export default { | ||||
325: 'meet.status-325', | 325: 'meet.status-325', | ||||
326: 'meet.status-326', | 326: 'meet.status-326', | ||||
327: 'meet.status-327', | 327: 'meet.status-327', | ||||
404: 'meet.status-404', | 404: 'meet.status-404', | ||||
429: 'meet.status-429', | 429: 'meet.status-429', | ||||
500: 'meet.status-500' | 500: 'meet.status-500' | ||||
}, | }, | ||||
session: {}, | session: {}, | ||||
stats: {}, | |||||
audioActive: false, | audioActive: false, | ||||
videoActive: false, | videoActive: false, | ||||
chatActive: false, | chatActive: false, | ||||
handRaised: false, | handRaised: false, | ||||
screenShareActive: false | screenActive: false | ||||
} | } | ||||
}, | }, | ||||
mounted() { | mounted() { | ||||
this.room = this.$route.params.room | this.room = this.$route.params.room | ||||
// Initialize OpenVidu and do some basic checks | // Initialize Meet client and do some basic checks | ||||
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 | // Configure dialog events | ||||
$('#leave-dialog')[0].addEventListener('hide.bs.modal', () => { | $('#leave-dialog')[0].addEventListener('hide.bs.modal', () => { | ||||
// FIXME: Where exactly the user should land? Currently he'll land | // 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). | // on dashboard (if he's logged in) or login form (if he's not). | ||||
this.$router.push({ name: 'dashboard' }) | this.$router.push({ name: 'dashboard' }) | ||||
}) | }) | ||||
const dialog = $('#media-setup-dialog')[0] | const dialog = $('#media-setup-dialog')[0] | ||||
dialog.addEventListener('show.bs.modal', () => { this.meet.setupStart() }) | dialog.addEventListener('show.bs.modal', () => { this.setupSession() }) | ||||
dialog.addEventListener('hide.bs.modal', () => { this.meet.setupStop() }) | dialog.addEventListener('hide.bs.modal', () => { this.meet.setupStop() }) | ||||
this.roomStatsDialog = new Modal('#room-stats-dialog') | |||||
}, | }, | ||||
beforeDestroy() { | beforeDestroy() { | ||||
clearTimeout(roomRequest) | clearTimeout(roomRequest) | ||||
clearInterval(statsRequest) | |||||
$('#app').removeClass('meet') | $('#app').removeClass('meet') | ||||
if (this.meet) { | if (this.meet) { | ||||
this.meet.leaveRoom() | this.meet.leaveRoom() | ||||
} | } | ||||
delete axios.defaults.headers.common[authHeader] | delete axios.defaults.headers.common[authHeader] | ||||
$(document.body).off('keydown.meet') | $(document.body).off('keydown.meet') | ||||
}, | }, | ||||
methods: { | methods: { | ||||
authSuccess() { | authSuccess() { | ||||
// The user authentication succeeded, we still don't know it's really the room owner | // The user authentication succeeded, we still don't know it's really the room owner | ||||
this.initSession() | this.initSession() | ||||
$('#meet-setup').removeClass('hidden') | $('#meet-setup').removeClass('hidden') | ||||
$('#meet-auth').addClass('hidden') | $('#meet-auth').addClass('hidden') | ||||
}, | }, | ||||
configUpdate(config) { | configUpdate(config) { | ||||
this.session.config = Object.assign({}, this.session.config, config) | this.session.config = Object.assign({}, this.session.config, config) | ||||
}, | }, | ||||
dismissParticipant(id) { | async refreshStats() { | ||||
axios.post('/api/v4/openvidu/rooms/' + this.room + '/connections/' + id + '/dismiss') | let stats = await this.meet.getStats() | ||||
this.stats = stats | |||||
}, | }, | ||||
initSession(init) { | initSession(init) { | ||||
const button = $('#join-button').prop('disabled', true) | const button = $('#join-button').prop('disabled', true) | ||||
this.post = { | this.post = { | ||||
password: this.password, | password: this.password, | ||||
nickname: this.nickname, | nickname: this.nickname, | ||||
screenShare: this.canShareScreen ? 1 : 0, | screenShare: this.canShareScreen ? 1 : 0, | ||||
init: init ? 1 : 0, | init: init ? 1 : 0, | ||||
picture: init ? this.makePicture() : '', | picture: init ? this.makePicture() : '', | ||||
requestId: this.requestId(), | requestId: this.requestId(), | ||||
canPublish: !!this.camera || !!this.microphone | canPublish: !!this.camera || !!this.microphone | ||||
} | } | ||||
$('#setup-password,#setup-nickname').removeClass('is-invalid') | $('#setup-password,#setup-nickname').removeClass('is-invalid') | ||||
axios.post('/api/v4/openvidu/rooms/' + this.room, this.post, { ignoreErrors: true }) | axios.post('/api/v4/meet/rooms/' + this.room, this.post, { ignoreErrors: true }) | ||||
.then(response => { | .then(response => { | ||||
button.prop('disabled', false) | button.prop('disabled', false) | ||||
// We already have token, the response is redundant | // We already have token, the response is redundant | ||||
if (this.roomState == 'ready' && this.session.token) { | if (this.roomState == 'ready' && this.session.token) { | ||||
return | return | ||||
} | } | ||||
this.roomState = 'ready' | this.roomState = 'ready' | ||||
this.session = response.data | this.session = response.data | ||||
if (init) { | if (init) { | ||||
this.joinSession() | this.joinSession() | ||||
} | } | ||||
if (this.session.authToken) { | |||||
axios.defaults.headers.common[authHeader] = this.session.authToken | |||||
} | |||||
}) | }) | ||||
.catch(error => { | .catch(error => { | ||||
if (!error.response) { | if (!error.response) { | ||||
console.error(error) | console.error(error) | ||||
return | return | ||||
} | } | ||||
const data = error.response.data || {} | const data = error.response.data || {} | ||||
▲ Show 20 Lines • Show All 66 Lines • ▼ Show 20 Lines | export default { | ||||
return !!this.session.role && (this.session.role & Roles.PUBLISHER) > 0 | return !!this.session.role && (this.session.role & Roles.PUBLISHER) > 0 | ||||
}, | }, | ||||
isRoomOwner() { | isRoomOwner() { | ||||
return !!this.session.role && (this.session.role & Roles.OWNER) > 0 | return !!this.session.role && (this.session.role & Roles.OWNER) > 0 | ||||
}, | }, | ||||
isRoomReady() { | isRoomReady() { | ||||
return ['ready', 322, 324, 325, 326, 327].includes(this.roomState) | return ['ready', 322, 324, 325, 326, 327].includes(this.roomState) | ||||
}, | }, | ||||
// An event received by the room owner when a participant is asking for a permission to join the room | |||||
joinRequest(data) { | |||||
// The toast for this user request already exists, ignore | |||||
// It's not really needed as we do this on server-side already | |||||
if ($('#i' + data.requestId).length) { | |||||
return | |||||
} | |||||
// FIXME: Should the message close button act as the Deny button? Do we need the Deny button? | |||||
let body = $( | |||||
`<div>` | |||||
+ `<div class="picture"><img src="${data.picture}"></div>` | |||||
+ `<div class="content">` | |||||
+ `<p class="mb-2"></p>` | |||||
+ `<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-danger deny ms-2">${this.$t('btn.deny')}</button>` | |||||
) | |||||
this.$toast.message({ | |||||
className: 'join-request', | |||||
icon: 'user', | |||||
timeout: 0, | |||||
title: this.$t('meet.join-request'), | |||||
// titleClassName: '', | |||||
body: body.html(), | |||||
onShow: element => { | |||||
const id = data.requestId | |||||
$(element).find('p').text(this.$t('meet.join-requested', { user: data.nickname || '' })) | |||||
// add id attribute, so we can identify it | |||||
$(element).attr('id', 'i' + id) | |||||
// add action to the buttons | |||||
.find('button.accept,button.deny').on('click', e => { | |||||
const action = $(e.target).is('.accept') ? 'accept' : 'deny' | |||||
axios.post('/api/v4/openvidu/rooms/' + this.room + '/request/' + id + '/' + action) | |||||
.then(response => { | |||||
$('#i' + id).remove() | |||||
}) | |||||
}) | |||||
} | |||||
}) | |||||
}, | |||||
// Entering the room | // Entering the room | ||||
joinSession() { | joinSession() { | ||||
// The form can be submitted not only via the submit button, | // The form can be submitted not only via the submit button, | ||||
// make sure the submit is allowed | // make sure the submit is allowed | ||||
if ($('#meet-setup [type=submit]').prop('disabled')) { | if ($('#meet-setup [type=submit]').prop('disabled')) { | ||||
return; | return; | ||||
} | } | ||||
if (this.roomState == 323) { | if (this.roomState == 323) { | ||||
$('#meet-setup').addClass('hidden') | $('#meet-setup').addClass('hidden') | ||||
$('#meet-auth').removeClass('hidden') | $('#meet-auth').removeClass('hidden') | ||||
return | return | ||||
} | } | ||||
if (this.roomState != 'ready' && !this.session.token) { | if (this.roomState != 'ready' && !this.session.token) { | ||||
this.initSession(true) | this.initSession(true) | ||||
return | return | ||||
} | } | ||||
clearTimeout(roomRequest) | clearTimeout(roomRequest) | ||||
clearInterval(statsRequest) | |||||
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.counterElement = $('#meet-counter span')[0] | ||||
this.session.translate = (label, args) => this.$t(label, args) | this.session.translate = (label, args) => this.$t(label, args) | ||||
this.session.toast = this.$toast | |||||
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 | ||||
} | } | ||||
this.session.onDestroy = event => { | this.session.onDestroy = event => { | ||||
// TODO: Display different message for each reason: forceDisconnectByUser, | // TODO: Display different message for every other reason | ||||
// forceDisconnectByServer, sessionClosedByServer? | if (event.reason == 'session-closed' && !this.isRoomOwner()) { | ||||
if (event.reason != 'disconnect' && event.reason != 'networkDisconnect' && !this.isRoomOwner()) { | |||||
new Modal('#leave-dialog').show() | new Modal('#leave-dialog').show() | ||||
} | } | ||||
} | } | ||||
this.session.onDismiss = connId => { this.dismissParticipant(connId) } | this.session.onUpdate = data => { this.updateSession(data) } | ||||
this.session.onSessionDataUpdate = data => { this.updateSession(data) } | |||||
this.session.onConnectionChange = (connId, data) => { this.updateParticipant(connId, data) } | |||||
this.session.onJoinRequest = data => { this.joinRequest(data) } | |||||
this.session.onMediaSetup = () => { this.setupMedia() } | this.session.onMediaSetup = () => { this.setupMedia() } | ||||
this.meet.joinRoom(this.session) | this.meet.joinRoom(this.session) | ||||
this.refreshStats() | |||||
this.keyboardShortcuts() | this.keyboardShortcuts() | ||||
}, | }, | ||||
keyboardShortcuts() { | keyboardShortcuts() { | ||||
$(document.body).on('keydown.meet', e => { | $(document.body).on('keydown.meet', e => { | ||||
if ($(e.target).is('select,input,textarea')) { | if ($(e.target).is('select,input,textarea')) { | ||||
return | return | ||||
} | } | ||||
// Self-Mute with 'm' key | // Self-Mute with 'm' key | ||||
if (e.key == 'm' || e.key == 'M') { | if (e.key == 'm' || e.key == 'M') { | ||||
if ($('#meet-session-menu').find('.link-audio:not(:disabled)').length) { | if ($('#meet-session-menu').find('.link-audio:not(:disabled)').length) { | ||||
this.switchSound() | this.switchSound() | ||||
} | } | ||||
} | } | ||||
//Show stats with '?' key | |||||
if (e.key == '?' || e.key == '?') { | |||||
this.roomStats() | |||||
} | |||||
}) | }) | ||||
}, | }, | ||||
logout() { | logout() { | ||||
const logout = () => { | this.meet.leaveRoom(true) | ||||
this.meet.leaveRoom() | |||||
this.meet = null | this.meet = null | ||||
this.$router.push({ name: 'dashboard' }) | this.$router.push({ name: 'dashboard' }) | ||||
} | |||||
if (this.isRoomOwner()) { | |||||
axios.post('/api/v4/openvidu/rooms/' + this.room + '/close').then(logout) | |||||
} else { | |||||
logout() | |||||
} | |||||
}, | }, | ||||
makePicture() { | makePicture() { | ||||
const video = $("#meet-setup video")[0]; | return (new Media()).makePicture($("#meet-setup video")[0]) || ''; | ||||
// Skip if video is not "playing" | |||||
if (!video.videoWidth || !this.camera) { | |||||
return '' | |||||
} | |||||
// we're going to crop a square from the video and resize it | |||||
const maxSize = 64 | |||||
// Calculate sizing | |||||
let sh = Math.floor(video.videoHeight / 1.5) | |||||
let sw = sh | |||||
let sx = (video.videoWidth - sw) / 2 | |||||
let sy = (video.videoHeight - sh) / 2 | |||||
let dh = Math.min(sh, maxSize) | |||||
let dw = sh < maxSize ? sw : Math.floor(sw * dh/sh) | |||||
const canvas = $("<canvas>")[0]; | |||||
canvas.width = dw; | |||||
canvas.height = dh; | |||||
// draw the image on the canvas (square cropped and resized) | |||||
canvas.getContext('2d').drawImage(video, sx, sy, sw, sh, 0, 0, dw, dh); | |||||
// convert it to a usable data URL (png format) | |||||
return canvas.toDataURL(); | |||||
}, | }, | ||||
requestId() { | requestId() { | ||||
const key = 'kolab-meet-uid' | const key = 'kolab-meet-uid' | ||||
if (!this.reqId) { | if (!this.reqId) { | ||||
this.reqId = localStorage.getItem(key) | this.reqId = localStorage.getItem(key) | ||||
} | } | ||||
Show All 10 Lines | export default { | ||||
localStorage.setItem(key, this.reqId) | localStorage.setItem(key, this.reqId) | ||||
} | } | ||||
return this.reqId | return this.reqId | ||||
}, | }, | ||||
roomOptions() { | roomOptions() { | ||||
new Modal('#room-options-dialog').show() | new Modal('#room-options-dialog').show() | ||||
}, | }, | ||||
roomStats() { | |||||
clearInterval(statsRequest) | |||||
if (this.roomStatsDialog.visible) { | |||||
this.roomStatsDialog.hide() | |||||
} else { | |||||
this.refreshStats() | |||||
statsRequest = setInterval(() => { | |||||
this.refreshStats() | |||||
}, 3000) | |||||
this.roomStatsDialog.show() | |||||
} | |||||
}, | |||||
setupMedia() { | setupMedia() { | ||||
const dialog = $('#media-setup-dialog')[0] | const dialog = $('#media-setup-dialog')[0] | ||||
if (!$('video', dialog).length) { | if (!$('video', dialog).length) { | ||||
$('#meet-setup').find('video,div.volume').appendTo($('.media-setup-preview', dialog)) | $('#meet-setup').find('video,div.volume').appendTo($('.media-setup-preview', dialog)) | ||||
} | } | ||||
new Modal(dialog).show() | new Modal(dialog).show() | ||||
}, | }, | ||||
setupSession() { | setupSession() { | ||||
this.meet.setupStart({ | this.meet.setupStart({ | ||||
videoElement: $('#meet-setup video')[0], | videoElement: $('#meet-setup video')[0] || $('#media-setup-dialog video')[0], | ||||
volumeElement: $('#meet-setup .volume')[0], | volumeElement: $('#meet-setup .volume')[0] || $('#media-setup-dialog .volume')[0], | ||||
onSuccess: setup => { | onSuccess: setup => { | ||||
this.setup = setup | this.setup = setup | ||||
this.microphone = setup.audioSource | this.microphone = setup.audioSource | ||||
this.camera = setup.videoSource | this.camera = setup.videoSource | ||||
this.audioActive = setup.audioActive | this.audioActive = setup.audioActive | ||||
this.videoActive = setup.videoActive | this.videoActive = setup.videoActive | ||||
}, | }, | ||||
onError: error => { | onError: error => { | ||||
console.warn("Media setup failed: ", error); | |||||
this.audioActive = false | this.audioActive = false | ||||
this.videoActive = false | this.videoActive = false | ||||
} | } | ||||
}) | }) | ||||
}, | }, | ||||
setupCameraChange() { | async setupCameraChange() { | ||||
this.meet.setupSetVideoDevice(this.camera).then(enabled => { | this.videoActive = await this.meet.setupSetVideoDevice(this.camera) | ||||
this.videoActive = enabled | |||||
}) | |||||
}, | }, | ||||
setupMicrophoneChange() { | async setupMicrophoneChange() { | ||||
this.meet.setupSetAudioDevice(this.microphone).then(enabled => { | this.audioActive = await this.meet.setupSetAudioDevice(this.microphone) | ||||
this.audioActive = enabled | |||||
}) | |||||
}, | }, | ||||
switchChannel(e) { | switchChannel(e) { | ||||
let channel = $(e.target).data('code') | this.meet.switchChannel($(e.target).data('code')) | ||||
// FIXME: Why is the menu not closing by itself? | |||||
this.$set(this.session, 'channel', channel) | new Dropdown('#meet-session-menu .link-channel').hide() | ||||
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) { | ||||
Show All 17 Lines | export default { | ||||
}) | }) | ||||
if (document.fullscreenElement) { | if (document.fullscreenElement) { | ||||
document.exitFullscreen() | document.exitFullscreen() | ||||
} else { | } else { | ||||
element.requestFullscreen() | element.requestFullscreen() | ||||
} | } | ||||
}, | }, | ||||
switchHand() { | async switchHand() { | ||||
this.updateSelf({ hand: !this.handRaised }) | this.handRaised = await this.meet.raiseHand(!this.handRaised) | ||||
}, | }, | ||||
switchSound() { | async switchSound() { | ||||
this.audioActive = this.meet.switchAudio() | this.audioActive = await this.meet.switchAudio() | ||||
}, | }, | ||||
switchVideo() { | async switchVideo() { | ||||
this.videoActive = this.meet.switchVideo() | this.videoActive = await this.meet.switchVideo() | ||||
}, | }, | ||||
switchScreen() { | async switchScreen() { | ||||
const switchScreenAction = () => { | this.screenActive = await this.meet.switchScreen() | ||||
this.meet.switchScreen((enabled, error) => { | |||||
this.screenShareActive = enabled | |||||
if (!enabled && !error) { | |||||
// Closing a screen sharing connection invalidates the token | |||||
delete this.session.shareToken | |||||
} | |||||
}) | |||||
} | |||||
if (this.session.shareToken || this.screenShareActive) { | |||||
switchScreenAction() | |||||
} else { | |||||
axios.post('/api/v4/openvidu/rooms/' + this.room + '/connections') | |||||
.then(response => { | |||||
this.session.shareToken = response.data.token | |||||
this.meet.updateSession(this.session) | |||||
switchScreenAction() | |||||
}) | |||||
} | |||||
}, | |||||
updateParticipant(connId, params) { | |||||
if (this.isModerator()) { | |||||
axios.put('/api/v4/openvidu/rooms/' + this.room + '/connections/' + connId, params) | |||||
} | |||||
}, | |||||
updateSelf(params, onSuccess) { | |||||
axios.put('/api/v4/openvidu/rooms/' + this.room + '/connections/' + this.session.connectionId, params) | |||||
.then(response => { | |||||
if (onSuccess) { | |||||
onSuccess(response) | |||||
} | |||||
}) | |||||
}, | }, | ||||
updateSession(data) { | updateSession(data) { | ||||
this.session = data | this.session = Object.assign({}, 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.raisedHand | ||||
} | } | ||||
} | } | ||||
} | } | ||||
</script> | </script> |