Changeset View
Changeset View
Standalone View
Standalone View
src/resources/vue/Meet/Room.vue
Show All 27 Lines | <div id="meet-component"> | ||||
</button> | </button> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<div id="meet-setup" class="card container mt-2 mt-md-5 mb-5"> | <div id="meet-setup" class="card container mt-2 mt-md-5 mb-5"> | ||||
<div class="card-body"> | <div class="card-body"> | ||||
<div class="card-title">Set up your session</div> | <div class="card-title">Set up your session</div> | ||||
<div class="card-text"> | <div class="card-text"> | ||||
<form class="setup-form row" @submit.prevent="joinSession"> | <form class="media-setup-form row" @submit.prevent="joinSession"> | ||||
<div id="setup-preview" class="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"> | ||||
<label for="setup-microphone" class="input-group-prepend mb-0"> | <label for="setup-microphone" 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> | ||||
▲ Show 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | <div id="meet-component"> | ||||
</div> | </div> | ||||
<div class="modal-footer"> | <div class="modal-footer"> | ||||
<button type="button" class="btn btn-danger modal-action" data-dismiss="modal">Close</button> | <button type="button" class="btn btn-danger modal-action" data-dismiss="modal">Close</button> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<div id="media-setup-dialog" class="modal" tabindex="-1" role="dialog"> | |||||
<div class="modal-dialog" role="document"> | |||||
<div class="modal-content"> | |||||
<div class="modal-header"> | |||||
<h5 class="modal-title">Media setup</h5> | |||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> | |||||
<span aria-hidden="true">×</span> | |||||
</button> | |||||
</div> | |||||
<div class="modal-body"> | |||||
<form class="media-setup-form"> | |||||
<div class="media-setup-preview"></div> | |||||
<div class="input-group mt-2"> | |||||
<label for="setup-microphone" class="input-group-prepend mb-0"> | |||||
<span class="input-group-text" title="Microphone"><svg-icon icon="microphone"></svg-icon></span> | |||||
</label> | |||||
<select class="custom-select" id="setup-microphone" v-model="microphone" @change="setupMicrophoneChange"> | |||||
<option value="">None</option> | |||||
<option v-for="mic in setup.microphones" :value="mic.deviceId" :key="mic.deviceId">{{ mic.label }}</option> | |||||
</select> | |||||
</div> | |||||
<div class="input-group mt-2"> | |||||
<label for="setup-camera" class="input-group-prepend mb-0"> | |||||
<span class="input-group-text" title="Camera"><svg-icon icon="video"></svg-icon></span> | |||||
</label> | |||||
<select class="custom-select" id="setup-camera" v-model="camera" @change="setupCameraChange"> | |||||
<option value="">None</option> | |||||
<option v-for="cam in setup.cameras" :value="cam.deviceId" :key="cam.deviceId">{{ cam.label }}</option> | |||||
</select> | |||||
</div> | |||||
</form> | |||||
</div> | |||||
<div class="modal-footer"> | |||||
<button type="button" class="btn btn-secondary modal-action" data-dismiss="modal">Close</button> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<session-security-options v-if="session.config" :config="session.config" :room="room" @config-update="configUpdate"></session-security-options> | <session-security-options v-if="session.config" :config="session.config" :room="room" @config-update="configUpdate"></session-security-options> | ||||
</div> | </div> | ||||
</template> | </template> | ||||
<script> | <script> | ||||
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 SessionSecurityOptions from './SessionSecurityOptions' | import SessionSecurityOptions from './SessionSecurityOptions' | ||||
// Register additional icons | // Register additional icons | ||||
import { library } from '@fortawesome/fontawesome-svg-core' | import { library } from '@fortawesome/fontawesome-svg-core' | ||||
import { | import { | ||||
faAlignLeft, | faAlignLeft, | ||||
faCog, | |||||
faCompress, | faCompress, | ||||
faDesktop, | faDesktop, | ||||
faExpand, | faExpand, | ||||
faMicrophone, | faMicrophone, | ||||
faPowerOff, | faPowerOff, | ||||
faUser, | faUser, | ||||
faShieldAlt, | faShieldAlt, | ||||
faVideo, | faVideo, | ||||
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( | ||||
faAlignLeft, | faAlignLeft, | ||||
faCog, | |||||
faCompress, | faCompress, | ||||
faDesktop, | faDesktop, | ||||
faExpand, | faExpand, | ||||
faMicrophone, | faMicrophone, | ||||
faPowerOff, | faPowerOff, | ||||
faUser, | faUser, | ||||
faShieldAlt, | faShieldAlt, | ||||
faVideo, | faVideo, | ||||
faVolumeMute | faVolumeMute | ||||
) | ) | ||||
let roomRequest | let roomRequest | ||||
export default { | export default { | ||||
components: { | components: { | ||||
LogonForm, | LogonForm, | ||||
SessionSecurityOptions, | SessionSecurityOptions, | ||||
StatusMessage | StatusMessage | ||||
▲ Show 20 Lines • Show All 143 Lines • ▼ Show 20 Lines | export default { | ||||
break; | break; | ||||
} | } | ||||
}) | }) | ||||
if (document.fullscreenEnabled) { | if (document.fullscreenEnabled) { | ||||
$('#meet-session-menu').find('.link-fullscreen.closed').removeClass('hidden') | $('#meet-session-menu').find('.link-fullscreen.closed').removeClass('hidden') | ||||
} | } | ||||
}, | }, | ||||
isModerator() { | |||||
return this.isRoomOwner() || (!!this.session.role && (this.session.role & Roles.MODERATOR) > 0) | |||||
}, | |||||
isPublisher() { | isPublisher() { | ||||
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) | ||||
▲ Show 20 Lines • Show All 82 Lines • ▼ Show 20 Lines | export default { | ||||
$('#leave-dialog').on('hide.bs.modal', () => { | $('#leave-dialog').on('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' }) | ||||
}).modal() | }).modal() | ||||
} | } | ||||
} | } | ||||
this.session.onDismiss = connId => { this.dismissParticipant(connId) } | this.session.onDismiss = connId => { this.dismissParticipant(connId) } | ||||
this.session.onSessionDataUpdate = data => { this.updateSession(data) } | |||||
if (this.isRoomOwner()) { | 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.meet.joinRoom(this.session) | this.meet.joinRoom(this.session) | ||||
}, | }, | ||||
logout() { | logout() { | ||||
const logout = () => { | const logout = () => { | ||||
this.meet.leaveRoom() | this.meet.leaveRoom() | ||||
this.meet = null | this.meet = null | ||||
this.$router.push({ name: 'dashboard' }) | this.$router.push({ name: 'dashboard' }) | ||||
} | } | ||||
if (this.isRoomOwner()) { | if (this.isRoomOwner()) { | ||||
axios.post('/api/v4/openvidu/rooms/' + this.room + '/close').then(logout) | axios.post('/api/v4/openvidu/rooms/' + this.room + '/close').then(logout) | ||||
} else { | } else { | ||||
logout() | logout() | ||||
} | } | ||||
}, | }, | ||||
makePicture() { | makePicture() { | ||||
const video = $("#setup-preview video")[0]; | const video = $("#meet-setup video")[0]; | ||||
// Skip if video is not "playing" | // Skip if video is not "playing" | ||||
if (!video.videoWidth || !this.camera) { | if (!video.videoWidth || !this.camera) { | ||||
return '' | return '' | ||||
} | } | ||||
// we're going to crop a square from the video and resize it | // we're going to crop a square from the video and resize it | ||||
const maxSize = 64 | const maxSize = 64 | ||||
Show All 40 Lines | export default { | ||||
let button = $('#meet-session-menu').find('.link-' + type) | let button = $('#meet-session-menu').find('.link-' + type) | ||||
button[state ? 'removeClass' : 'addClass']('text-danger') | button[state ? 'removeClass' : 'addClass']('text-danger') | ||||
if (disabled !== undefined) { | if (disabled !== undefined) { | ||||
button.prop('disabled', disabled) | button.prop('disabled', disabled) | ||||
} | } | ||||
}, | }, | ||||
setupMedia() { | |||||
let dialog = $('#media-setup-dialog') | |||||
if (!dialog.find('video').length) { | |||||
$('#meet-setup').find('video,div.volume').appendTo(dialog.find('.media-setup-preview')) | |||||
} | |||||
dialog.on('show.bs.modal', () => { this.meet.setupStart() }) | |||||
.on('hide.bs.modal', () => { this.meet.setupStop() }) | |||||
.modal() | |||||
}, | |||||
setupSession() { | setupSession() { | ||||
this.meet.setup({ | this.meet.setupStart({ | ||||
videoElement: $('#setup-preview video')[0], | videoElement: $('#meet-setup video')[0], | ||||
volumeElement: $('#setup-preview .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 | ||||
this.camera = setup.videoSource | this.camera = setup.videoSource | ||||
this.setMenuItem('audio', setup.audioActive) | this.setMenuItem('audio', setup.audioActive) | ||||
this.setMenuItem('video', setup.videoActive) | this.setMenuItem('video', setup.videoActive) | ||||
}, | }, | ||||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | export default { | ||||
this.meet.switchScreen(enabled => { | this.meet.switchScreen(enabled => { | ||||
this.setMenuItem('screen', enabled) | this.setMenuItem('screen', enabled) | ||||
// After one screen sharing session ended request a new token | // After one screen sharing session ended request a new token | ||||
// for the next screen sharing session | // for the next screen sharing session | ||||
if (!enabled) { | if (!enabled) { | ||||
// TODO: This might need to be a different route. E.g. the room password might have | // TODO: This might need to be a different route. E.g. the room password might have | ||||
// changed since user joined the session | // changed since user joined the session | ||||
// Also because it creates a redundant connection (token) | |||||
axios.post('/api/v4/openvidu/rooms/' + this.room, this.post, { ignoreErrors: true }) | axios.post('/api/v4/openvidu/rooms/' + this.room, this.post, { ignoreErrors: true }) | ||||
.then(response => { | .then(response => { | ||||
// Response data contains: session, token and shareToken | // Response data contains: session, token and shareToken | ||||
this.session.shareToken = response.data.token | this.session.shareToken = response.data.shareToken | ||||
this.meet.updateSession(this.session) | this.meet.updateSession(this.session) | ||||
}) | }) | ||||
} | } | ||||
}) | }) | ||||
}, | |||||
updateParticipant(connId, params) { | |||||
if (this.isModerator()) { | |||||
axios.put('/api/v4/openvidu/rooms/' + this.room + '/connections/' + connId, params) | |||||
} | |||||
}, | |||||
updateSession(data) { | |||||
let params = {} | |||||
if ('role' in data) { | |||||
params.role = data.role | |||||
} | |||||
// merge new params into the object | |||||
this.session = Object.assign({}, this.session, params) | |||||
// update some buttons state e.g. when switching from publisher to subscriber | |||||
if (!this.isPublisher()) { | |||||
this.setMenuItem('audio', false) | |||||
this.setMenuItem('video', false) | |||||
} else { | |||||
if ('videoActive' in data) { | |||||
this.setMenuItem('video', data.videoActive) | |||||
} | |||||
if ('audioActive' in data) { | |||||
this.setMenuItem('audio', data.audioActive) | |||||
} | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } | ||||
</script> | </script> |