Changeset View
Changeset View
Standalone View
Standalone View
src/resources/js/meet/app.js
Show First 20 Lines • Show All 87 Lines • ▼ Show 20 Lines | function Meet(container) | ||||
* @param data Session metadata and event handlers: | * @param data Session metadata and event handlers: | ||||
* token - OpenVidu token for the main connection, | * token - OpenVidu token for the main connection, | ||||
* shareToken - OpenVidu token for screen-sharing connection, | * shareToken - OpenVidu token for screen-sharing connection, | ||||
* nickname - Participant name, | * nickname - Participant name, | ||||
* role - connection (participant) role(s), | * role - connection (participant) role(s), | ||||
* connections - Optional metadata for other users connections (current state), | * connections - Optional metadata for other users connections (current state), | ||||
* chatElement - DOM element for the chat widget, | * chatElement - DOM element for the chat widget, | ||||
* menuElement - DOM element of the room toolbar, | * menuElement - DOM element of the room toolbar, | ||||
* queueElement - DOM element for the Q&A queue (users with a raised hand) | |||||
* onSuccess - Callback for session connection (join) success | * onSuccess - Callback for session connection (join) success | ||||
* onError - Callback for session connection (join) error | * onError - Callback for session connection (join) error | ||||
* onDestroy - Callback for session disconnection event, | * onDestroy - Callback for session disconnection event, | ||||
* onDismiss - Callback for Dismiss action, | * onDismiss - Callback for Dismiss action, | ||||
* onJoinRequest - Callback for join request, | * onJoinRequest - Callback for join request, | ||||
* onConnectionChange - Callback for participant changes, e.g. role update, | * onConnectionChange - Callback for participant changes, e.g. role update, | ||||
* onSessionDataUpdate - Callback for current user connection update, | * onSessionDataUpdate - Callback for current user connection update, | ||||
* onMediaSetup - Called when user clicks the Media setup button | * onMediaSetup - Called when user clicks the Media setup button | ||||
Show All 32 Lines | function joinRoom(data) { | ||||
const connId = metadata.connectionId | const connId = metadata.connectionId | ||||
// The connection metadata here is the initial metadata set on | // The connection metadata here is the initial metadata set on | ||||
// connection initialization. There's no way to update it via OpenVidu API. | // connection initialization. There's no way to update it via OpenVidu API. | ||||
// So, we merge the initial connection metadata with up-to-dated one that | // So, we merge the initial connection metadata with up-to-dated one that | ||||
// we got from our database. | // we got from our database. | ||||
if (sessionData.connections && connId in sessionData.connections) { | if (sessionData.connections && connId in sessionData.connections) { | ||||
Object.assign(metadata, sessionData.connections[connId]) | Object.assign(metadata, sessionData.connections[connId]) | ||||
delete sessionData.connections[connId] | |||||
} | } | ||||
metadata.element = participantCreate(metadata) | metadata.element = participantCreate(metadata) | ||||
connections[connId] = metadata | connections[connId] = metadata | ||||
// Send the current user status to the connecting user | // Send the current user status to the connecting user | ||||
// otherwise e.g. nickname might be not up to date | // otherwise e.g. nickname might be not up to date | ||||
signalUserUpdate(event.connection) | signalUserUpdate(event.connection) | ||||
}) | }) | ||||
session.on('connectionDestroyed', event => { | session.on('connectionDestroyed', event => { | ||||
let connectionId = event.connection.connectionId | let connectionId = event.connection.connectionId | ||||
let conn = connections[connectionId] | let conn = connections[connectionId] | ||||
if (conn) { | if (conn) { | ||||
// Remove elements related to the participant | |||||
connectionHandDown(connectionId) | |||||
$(conn.element).remove() | $(conn.element).remove() | ||||
delete connections[connectionId] | delete connections[connectionId] | ||||
} | } | ||||
resize() | resize() | ||||
}) | }) | ||||
// On every new Stream received... | // On every new Stream received... | ||||
▲ Show 20 Lines • Show All 78 Lines • ▼ Show 20 Lines | function joinRoom(data) { | ||||
let wrapper = participantCreate(params) | let wrapper = participantCreate(params) | ||||
if (data.role & Roles.PUBLISHER) { | if (data.role & Roles.PUBLISHER) { | ||||
publisher.createVideoElement(wrapper, 'PREPEND') | publisher.createVideoElement(wrapper, 'PREPEND') | ||||
session.publish(publisher) | session.publish(publisher) | ||||
} | } | ||||
sessionData.element = wrapper | sessionData.element = wrapper | ||||
// Create Q&A queue from the existing connections with rised hand. | |||||
// Here we expect connections in a proper queue order | |||||
Object.keys(data.connections || {}).forEach(key => { | |||||
let conn = data.connections[key] | |||||
if (conn.hand) { | |||||
conn.connectionId = key | |||||
connectionHandUp(conn) | |||||
} | |||||
}) | |||||
}) | }) | ||||
.catch(error => { | .catch(error => { | ||||
console.error('There was an error connecting to the session: ', error.message); | console.error('There was an error connecting to the session: ', error.message); | ||||
if (data.onError) { | if (data.onError) { | ||||
data.onError(error) | data.onError(error) | ||||
} | } | ||||
}) | }) | ||||
▲ Show 20 Lines • Show All 506 Lines • ▼ Show 20 Lines | function Meet(container) | ||||
} | } | ||||
/** | /** | ||||
* Update participant connection state | * Update participant connection state | ||||
*/ | */ | ||||
function connectionUpdate(data) { | function connectionUpdate(data) { | ||||
let conn = connections[data.connectionId] | let conn = connections[data.connectionId] | ||||
let handUpdate = conn => { | |||||
if ('hand' in data && data.hand != conn.hand) { | |||||
if (data.hand) { | |||||
connectionHandUp(conn) | |||||
} else { | |||||
connectionHandDown(data.connectionId) | |||||
} | |||||
} | |||||
} | |||||
// It's me | // It's me | ||||
if (session.connection.connectionId == data.connectionId) { | if (session.connection.connectionId == data.connectionId) { | ||||
const rolePublisher = data.role && data.role & Roles.PUBLISHER | const rolePublisher = data.role && data.role & Roles.PUBLISHER | ||||
const roleModerator = data.role && data.role & Roles.MODERATOR | const roleModerator = data.role && data.role & Roles.MODERATOR | ||||
const isPublisher = sessionData.role & Roles.PUBLISHER | const isPublisher = sessionData.role & Roles.PUBLISHER | ||||
const isModerator = sessionData.role & Roles.MODERATOR | const isModerator = sessionData.role & Roles.MODERATOR | ||||
// Inform the vue component, so it can update some UI controls | // Inform the vue component, so it can update some UI controls | ||||
let update = () => { | let update = () => { | ||||
if (sessionData.onSessionDataUpdate) { | if (sessionData.onSessionDataUpdate) { | ||||
sessionData.onSessionDataUpdate(data) | sessionData.onSessionDataUpdate(data) | ||||
} | } | ||||
} | } | ||||
// demoted to a subscriber | // demoted to a subscriber | ||||
if ('role' in data && isPublisher && !rolePublisher) { | if ('role' in data && isPublisher && !rolePublisher) { | ||||
session.unpublish(publisher) | session.unpublish(publisher) | ||||
// FIXME: There's a reference in OpenVidu to a video element that should not | // FIXME: There's a reference in OpenVidu to a video element that should not | ||||
// exist anymore. It causes issues when we try to do publish/unpublish | // exist anymore. It causes issues when we try to do publish/unpublish | ||||
// sequence multiple times in a row. So, we're clearing the reference here. | // sequence multiple times in a row. So, we're clearing the reference here. | ||||
let videos = publisher.stream.streamManager.videos | let videos = publisher.stream.streamManager.videos | ||||
publisher.stream.streamManager.videos = videos.filter(video => video.video.parentNode != null) | publisher.stream.streamManager.videos = videos.filter(video => video.video.parentNode != null) | ||||
} | } | ||||
handUpdate(sessionData) | |||||
// merge the changed data into internal session metadata object | // merge the changed data into internal session metadata object | ||||
Object.keys(data).forEach(key => { sessionData[key] = data[key] }) | Object.keys(data).forEach(key => { sessionData[key] = data[key] }) | ||||
// update the participant element | // update the participant element | ||||
sessionData.element = participantUpdate(sessionData.element, sessionData) | sessionData.element = participantUpdate(sessionData.element, sessionData) | ||||
// promoted/demoted to/from a moderator | // promoted/demoted to/from a moderator | ||||
if ('role' in data) { | if ('role' in data) { | ||||
if ((!isModerator && roleModerator) || (isModerator && !roleModerator)) { | if ((!isModerator && roleModerator) || (isModerator && !roleModerator)) { | ||||
// Update all participants, to enable/disable the popup menu | // Update all participants, to enable/disable the popup menu | ||||
Object.keys(connections).forEach(key => { | Object.keys(connections).forEach(key => { | ||||
const conn = connections[key] | const conn = connections[key] | ||||
participantUpdate(conn.element, conn) | participantUpdate(conn.element, conn) | ||||
}) | }) | ||||
} | } | ||||
} | } | ||||
// Inform the vue component, so it can update some UI controls | |||||
update() | |||||
// promoted to a publisher | // promoted to a publisher | ||||
if ('role' in data && !isPublisher && rolePublisher) { | if ('role' in data && !isPublisher && rolePublisher) { | ||||
publisher.createVideoElement(sessionData.element, 'PREPEND') | publisher.createVideoElement(sessionData.element, 'PREPEND') | ||||
session.publish(publisher).then(() => { | session.publish(publisher).then(() => { | ||||
data.audioActive = publisher.stream.audioActive | data.audioActive = publisher.stream.audioActive | ||||
data.videoActive = publisher.stream.videoActive | data.videoActive = publisher.stream.videoActive | ||||
update() | update() | ||||
}) | }) | ||||
// Open the media setup dialog | // Open the media setup dialog | ||||
// Note: If user didn't give permission to media before joining the room | // Note: If user didn't give permission to media before joining the room | ||||
// he will not be able to use them now. Changing permissions requires | // he will not be able to use them now. Changing permissions requires | ||||
// a page refresh. | // a page refresh. | ||||
// Note: In Firefox I'm always being asked again for media permissions. | // Note: In Firefox I'm always being asked again for media permissions. | ||||
// It does not happen in Chrome. In Chrome the cam/mic will be just re-used. | // It does not happen in Chrome. In Chrome the cam/mic will be just re-used. | ||||
// I.e. streaming starts automatically. | // I.e. streaming starts automatically. | ||||
// It might make sense to not start streaming automatically in any cirmustances, | // It might make sense to not start streaming automatically in any cirmustances, | ||||
// display the dialog and wait until user closes it, but this would be | // display the dialog and wait until user closes it, but this would be | ||||
// a bigger refactoring. | // a bigger refactoring. | ||||
if (sessionData.onMediaSetup) { | if (sessionData.onMediaSetup) { | ||||
sessionData.onMediaSetup() | sessionData.onMediaSetup() | ||||
} | } | ||||
} else { | |||||
// Inform the vue component, so it can update some UI controls | |||||
update() | |||||
} | } | ||||
} else if (conn) { | } else if (conn) { | ||||
handUpdate(conn) | |||||
// merge the changed data into internal session metadata object | // merge the changed data into internal session metadata object | ||||
Object.keys(data).forEach(key => { conn[key] = data[key] }) | Object.keys(data).forEach(key => { conn[key] = data[key] }) | ||||
conn.element = participantUpdate(conn.element, conn) | conn.element = participantUpdate(conn.element, conn) | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Update nickname in chat | * Handler for Hand-Up "signal" | ||||
*/ | |||||
function connectionHandUp(connection) { | |||||
connection.isSelf = session.connection.connectionId == connection.connectionId | |||||
let element = $(nicknameWidget(connection)) | |||||
participantUpdate(element, connection) | |||||
element.attr('id', 'qa' + connection.connectionId) | |||||
.appendTo($(sessionData.queueElement).show()) | |||||
setTimeout(() => element.addClass('widdle'), 50) | |||||
} | |||||
/** | |||||
* Handler for Hand-Down "signal" | |||||
*/ | |||||
function connectionHandDown(connectionId) { | |||||
let list = $(sessionData.queueElement) | |||||
list.find('#qa' + connectionId).remove(); | |||||
if (!list.find('.meet-nickname').length) { | |||||
list.hide(); | |||||
} | |||||
} | |||||
/** | |||||
* Update participant nickname in the UI | |||||
* | * | ||||
* @param nickname Nickname | * @param nickname Nickname | ||||
* @param connectionId Connection identifier of the user | * @param connectionId Connection identifier of the user | ||||
*/ | */ | ||||
function nicknameUpdate(nickname, connectionId) { | function nicknameUpdate(nickname, connectionId) { | ||||
if (connectionId) { | if (connectionId) { | ||||
$(sessionData.chatElement).find('.chat').find('.message').each(function() { | $(sessionData.chatElement).find('.chat').find('.message').each(function() { | ||||
let elem = $(this) | let elem = $(this) | ||||
if (elem.data('id') == connectionId) { | if (elem.data('id') == connectionId) { | ||||
elem.find('.nickname').text(nickname || '') | elem.find('.nickname').text(nickname || '') | ||||
} | } | ||||
}) | }) | ||||
$(sessionData.queueElement).find('#qa' + connectionId + ' .content').text(nickname || '') | |||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Create a participant element in the matrix. Depending on the connection role | * Create a participant element in the matrix. Depending on the connection role | ||||
* parameter it will be a video element wrapper inside the matrix or a simple | * parameter it will be a video element wrapper inside the matrix or a simple | ||||
* tag-like element on the subscribers list. | * tag-like element on the subscribers list. | ||||
* | * | ||||
▲ Show 20 Lines • Show All 542 Lines • Show Last 20 Lines |