From 0ed39dad630cbe102fa717c69311aa61e69a87a5 Mon Sep 17 00:00:00 2001 From: hristoterezov Date: Thu, 23 Mar 2017 12:45:51 -0500 Subject: [PATCH 1/2] fix(iframe_api): Display name command race condition If executeCommand('displayName') is executed before Jitsi Meet is fully initialized some listeners were not added and the display name was not changed. --- conference.js | 37 +++++++++++++++++++------------------ modules/API/API.js | 3 ++- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/conference.js b/conference.js index 8c7b4ac11..8dc5c2afc 100644 --- a/conference.js +++ b/conference.js @@ -348,23 +348,6 @@ function createLocalTracks (options, checkForPermissionPrompt) { }); } -/** - * Changes the display name for the local user - * @param nickname {string} the new display name - */ -function changeLocalDisplayName(nickname = '') { - const formattedNickname - = nickname.trim().substr(0, MAX_DISPLAY_NAME_LENGTH); - - if (formattedNickname === APP.settings.getDisplayName()) { - return; - } - - APP.settings.setDisplayName(formattedNickname); - room.setDisplayName(formattedNickname); - APP.UI.changeDisplayName(APP.conference.getMyUserId(), formattedNickname); -} - class ConferenceConnector { constructor(resolve, reject, invite) { this._resolve = resolve; @@ -1505,7 +1488,8 @@ export default { APP.UI.setUserAvatarID(from, data.value); }); - APP.UI.addListener(UIEvents.NICKNAME_CHANGED, changeLocalDisplayName); + APP.UI.addListener(UIEvents.NICKNAME_CHANGED, + this.changeLocalDisplayName.bind(this)); APP.UI.addListener(UIEvents.START_MUTED_CHANGED, (startAudioMuted, startVideoMuted) => { @@ -1978,5 +1962,22 @@ export default { */ isInLastN (participantId) { return room.isInLastN(participantId); + }, + /** + * Changes the display name for the local user + * @param nickname {string} the new display name + */ + changeLocalDisplayName(nickname = '') { + const formattedNickname + = nickname.trim().substr(0, MAX_DISPLAY_NAME_LENGTH); + + if (formattedNickname === APP.settings.getDisplayName()) { + return; + } + + APP.settings.setDisplayName(formattedNickname); + room.setDisplayName(formattedNickname); + APP.UI.changeDisplayName(this.getMyUserId(), + formattedNickname); } }; diff --git a/modules/API/API.js b/modules/API/API.js index 8417e7db2..3bb979e31 100644 --- a/modules/API/API.js +++ b/modules/API/API.js @@ -45,7 +45,8 @@ let enabled = false; function initCommands() { commands = { - "display-name": APP.UI.inputDisplayNameHandler, + "display-name": + APP.conference.changeLocalDisplayName.bind(APP.conference), "toggle-audio": APP.conference.toggleAudioMuted.bind(APP.conference), "toggle-video": APP.conference.toggleVideoMuted.bind(APP.conference), "toggle-film-strip": APP.UI.toggleFilmStrip, From 4ab4aa04da4844f0cf69f6d191bde075a5337504 Mon Sep 17 00:00:00 2001 From: hristoterezov Date: Thu, 23 Mar 2017 13:01:33 -0500 Subject: [PATCH 2/2] fix(avatar): Avatar properties not updated before local user join Replaces changeAvatarID, changeAvatarURL and changeEmail with participantUpdated action. participantUpdated can be fired for local user without id. This fixes the problem with updating the local user before the user join the conference which results in fix for failing to execute commands for avatarID, avatarURL and email right after the iframe api creates the iframe with Jitsi Meet. --- conference.js | 47 ++++++++--- modules/UI/UI.js | 3 + modules/UI/avatar/Avatar.js | 4 +- modules/UI/side_pannels/profile/Profile.js | 12 ++- react/features/base/conference/actions.js | 21 +++-- react/features/base/participants/actions.js | 93 +++++---------------- react/features/base/participants/reducer.js | 7 +- 7 files changed, 91 insertions(+), 96 deletions(-) diff --git a/conference.js b/conference.js index 8dc5c2afc..12470b209 100644 --- a/conference.js +++ b/conference.js @@ -32,12 +32,10 @@ import { isFatalJitsiConnectionError } from './react/features/base/lib-jitsi-meet'; import { - changeParticipantAvatarID, - changeParticipantAvatarURL, - changeParticipantEmail, participantJoined, participantLeft, - participantRoleChanged + participantRoleChanged, + participantUpdated } from './react/features/base/participants'; import { mediaPermissionPromptVisibilityChanged, @@ -161,6 +159,10 @@ function createInitialLocalTracksAndConnect(roomName) { * @param {string} value new value */ function sendData (command, value) { + if(!room) { + return; + } + room.removeCommand(command); room.sendCommand(command, {value: value}); } @@ -1469,7 +1471,10 @@ export default { APP.UI.addListener(UIEvents.EMAIL_CHANGED, this.changeLocalEmail); room.addCommandListener(this.commands.defaults.EMAIL, (data, from) => { - APP.store.dispatch(changeParticipantEmail(from, data.value)); + APP.store.dispatch(participantUpdated({ + id: from, + email: data.value + })); APP.UI.setUserEmail(from, data.value); }); @@ -1477,14 +1482,20 @@ export default { this.commands.defaults.AVATAR_URL, (data, from) => { APP.store.dispatch( - changeParticipantAvatarURL(from, data.value)); + participantUpdated({ + id: from, + avatarURL: data.value + })); APP.UI.setUserAvatarUrl(from, data.value); }); room.addCommandListener(this.commands.defaults.AVATAR_ID, (data, from) => { APP.store.dispatch( - changeParticipantAvatarID(from, data.value)); + participantUpdated({ + id: from, + avatarID: data.value + })); APP.UI.setUserAvatarID(from, data.value); }); @@ -1896,10 +1907,17 @@ export default { if (email === APP.settings.getEmail()) { return; } - APP.store.dispatch(changeParticipantEmail(room.myUserId(), email)); + + const localId = room ? room.myUserId() : undefined; + + APP.store.dispatch(participantUpdated({ + id: localId, + local: true, + email + })); APP.settings.setEmail(email); - APP.UI.setUserEmail(room.myUserId(), email); + APP.UI.setUserEmail(localId, email); sendData(commands.EMAIL, email); }, @@ -1913,10 +1931,17 @@ export default { if (url === APP.settings.getAvatarUrl()) { return; } - APP.store.dispatch(changeParticipantAvatarURL(room.myUserId(), url)); + + const localId = room ? room.myUserId() : undefined; + + APP.store.dispatch(participantUpdated({ + id: localId, + local: true, + avatarURL: url + })); APP.settings.setAvatarUrl(url); - APP.UI.setUserAvatarUrl(room.myUserId(), url); + APP.UI.setUserAvatarUrl(localId, url); sendData(commands.AVATAR_URL, url); }, diff --git a/modules/UI/UI.js b/modules/UI/UI.js index 75a05522d..56bd4c6c9 100644 --- a/modules/UI/UI.js +++ b/modules/UI/UI.js @@ -762,6 +762,9 @@ UI.setUserEmail = function (id, email) { Avatar.setUserEmail(id, email); changeAvatar(id, Avatar.getAvatarUrl(id)); + if (APP.conference.isLocalId(id)) { + Profile.changeEmail(email); + } }; /** diff --git a/modules/UI/avatar/Avatar.js b/modules/UI/avatar/Avatar.js index e29619dee..198dac073 100644 --- a/modules/UI/avatar/Avatar.js +++ b/modules/UI/avatar/Avatar.js @@ -30,7 +30,7 @@ let users = {}; export default { /** * Sets prop in users object. - * @param id {string} user id + * @param id {string} user id or undefined for the local user. * @param prop {string} name of the prop * @param val {string} value to be set */ @@ -38,7 +38,7 @@ export default { // FIXME: Fixes the issue with not be able to return avatar for the // local user when the conference has been left. Maybe there is beter // way to solve it. - if(APP.conference.isLocalId(id)) { + if(!id || APP.conference.isLocalId(id)) { id = "local"; } if(!val || (users[id] && users[id][prop] === val)) diff --git a/modules/UI/side_pannels/profile/Profile.js b/modules/UI/side_pannels/profile/Profile.js index 45c1a8caa..c08d2b9a9 100644 --- a/modules/UI/side_pannels/profile/Profile.js +++ b/modules/UI/side_pannels/profile/Profile.js @@ -15,10 +15,10 @@ const htmlStr = `
-
-

    @@ -122,6 +122,14 @@ export default { $('#avatar').attr('src', avatarUrl); }, + /** + * Change the value of the field for the user email. + * @param {string} email the new value that will be displayed in the field. + */ + changeEmail (email) { + $('#setEmail').val(email); + }, + /** * Shows or hides authentication related buttons * @param {boolean} show true to show or false to hide diff --git a/react/features/base/conference/actions.js b/react/features/base/conference/actions.js index 1b7b7f1f6..e7258ab3c 100644 --- a/react/features/base/conference/actions.js +++ b/react/features/base/conference/actions.js @@ -1,13 +1,11 @@ import { JitsiConferenceEvents } from '../lib-jitsi-meet'; import { - changeParticipantAvatarID, - changeParticipantAvatarURL, - changeParticipantEmail, dominantSpeakerChanged, getLocalParticipant, participantJoined, participantLeft, - participantRoleChanged + participantRoleChanged, + participantUpdated } from '../participants'; import { trackAdded, trackRemoved } from '../tracks'; @@ -78,13 +76,22 @@ function _addConferenceListeners(conference, dispatch) { conference.addCommandListener( AVATAR_ID_COMMAND, - (data, id) => dispatch(changeParticipantAvatarID(id, data.value))); + (data, id) => dispatch(participantUpdated({ + id, + avatarID: data.value + }))); conference.addCommandListener( AVATAR_URL_COMMAND, - (data, id) => dispatch(changeParticipantAvatarURL(id, data.value))); + (data, id) => dispatch(participantUpdated({ + id, + avatarURL: data.value + }))); conference.addCommandListener( EMAIL_COMMAND, - (data, id) => dispatch(changeParticipantEmail(id, data.value))); + (data, id) => dispatch(participantUpdated({ + id, + email: data.value + }))); } /** diff --git a/react/features/base/participants/actions.js b/react/features/base/participants/actions.js index 02f8f2c65..c84fbdc77 100644 --- a/react/features/base/participants/actions.js +++ b/react/features/base/participants/actions.js @@ -8,75 +8,6 @@ import { } from './actionTypes'; import { getLocalParticipant } from './functions'; -/** - * Action to update a participant's avatar ID. - * - * @param {string} id - Participant's ID. - * @param {string} avatarID - Participant's avatar ID. - * @returns {{ - * type: PARTICIPANT_UPDATED, - * participant: { - * id: string, - * avatarID: string, - * } - * }} - */ -export function changeParticipantAvatarID(id, avatarID) { - return { - type: PARTICIPANT_UPDATED, - participant: { - id, - avatarID - } - }; -} - -/** - * Action to update a participant's avatar URL. - * - * @param {string} id - Participant's ID. - * @param {string} avatarURL - Participant's avatar URL. - * @returns {{ - * type: PARTICIPANT_UPDATED, - * participant: { - * id: string, - * avatarURL: string, - * } - * }} - */ -export function changeParticipantAvatarURL(id, avatarURL) { - return { - type: PARTICIPANT_UPDATED, - participant: { - id, - avatarURL - } - }; -} - -/** - * Action to update a participant's email. - * - * @param {string} id - Participant's ID. - * @param {string} email - Participant's email. - * @returns {{ - * type: PARTICIPANT_UPDATED, - * participant: { - * id: string, - * email: string - * } - * }} - */ -export function changeParticipantEmail(id, email) { - return { - type: PARTICIPANT_UPDATED, - participant: { - id, - email - } - }; -} - /** * Create an action for when dominant speaker changes. * @@ -203,12 +134,28 @@ export function participantLeft(id) { * }} */ export function participantRoleChanged(id, role) { + return participantUpdated({ + id, + role + }); +} + +/** + * Action to signal that some of participant properties has been changed. + * + * @param {Participant} participant={} - Information about participant. To + * identify the participant the object should contain either property id with + * value the id of the participant or property local with value true (if the + * local participant hasn't joined the conference yet). + * @returns {{ + * type: PARTICIPANT_UPDATED, + * participant: Participant + * }} + */ +export function participantUpdated(participant = {}) { return { type: PARTICIPANT_UPDATED, - participant: { - id, - role - } + participant }; } diff --git a/react/features/base/participants/reducer.js b/react/features/base/participants/reducer.js index 946db688c..67cc91861 100644 --- a/react/features/base/participants/reducer.js +++ b/react/features/base/participants/reducer.js @@ -112,7 +112,12 @@ function _participant(state, action) { case PARTICIPANT_UPDATED: { const participant = action.participant; // eslint-disable-line no-shadow - const { id } = participant; + const { local } = participant; + let { id } = participant; + + if (!id && local) { + id = LOCAL_PARTICIPANT_DEFAULT_ID; + } if (state.id === id) { const newState = { ...state };