From 003eb68e28e3ee664c20c208031e74cbe14bc369 Mon Sep 17 00:00:00 2001 From: Lyubo Marinov Date: Fri, 13 Oct 2017 14:31:05 -0500 Subject: [PATCH] Flow, coding style --- conference.js | 14 +- modules/UI/UI.js | 20 +- modules/UI/side_pannels/chat/Chat.js | 8 +- react/features/base/conference/actions.js | 4 +- react/features/base/conference/functions.js | 40 ++-- react/features/base/jwt/actions.js | 14 +- react/features/base/jwt/middleware.js | 224 +++++++++--------- react/features/base/participants/constants.js | 10 +- react/features/base/participants/functions.js | 66 +++--- react/features/base/participants/reducer.js | 26 +- react/features/conference/route.js | 8 +- react/features/feedback/actions.js | 16 +- react/features/filmstrip/functions.js | 45 ++-- .../speaker-stats/components/SpeakerStats.js | 61 +++-- .../welcome/components/WelcomePage.web.js | 2 +- 15 files changed, 298 insertions(+), 260 deletions(-) diff --git a/conference.js b/conference.js index bca7f92fa4..109d3d913e 100644 --- a/conference.js +++ b/conference.js @@ -27,11 +27,11 @@ import { conferenceJoined, conferenceLeft, dataChannelOpened, - toggleAudioOnly, EMAIL_COMMAND, lockStateChanged, p2pStatusChanged, - setLocalParticipantData + sendLocalParticipant, + toggleAudioOnly } from './react/features/base/conference'; import { updateDeviceList } from './react/features/base/devices'; import { @@ -1138,13 +1138,15 @@ export default { } }, - _createRoom (localTracks) { - room = connection.initJitsiConference(APP.conference.roomName, - this._getConferenceOptions()); + _createRoom(localTracks) { + room + = connection.initJitsiConference( + APP.conference.roomName, + this._getConferenceOptions()); this._setLocalAudioVideoStreams(localTracks); this._room = room; // FIXME do not use this - setLocalParticipantData(room, APP.store.getState()); + sendLocalParticipant(APP.store, room); this._setupListeners(); }, diff --git a/modules/UI/UI.js b/modules/UI/UI.js index e9c5de3f96..9934a26d17 100644 --- a/modules/UI/UI.js +++ b/modules/UI/UI.js @@ -20,12 +20,17 @@ import Filmstrip from "./videolayout/Filmstrip"; import SettingsMenu from "./side_pannels/settings/SettingsMenu"; import Profile from "./side_pannels/profile/Profile"; -import { updateDeviceList } from '../../react/features/base/devices'; -import { JitsiTrackErrors } from '../../react/features/base/lib-jitsi-meet'; import { openDeviceSelectionDialog } from '../../react/features/device-selection'; +import { updateDeviceList } from '../../react/features/base/devices'; +import { JitsiTrackErrors } from '../../react/features/base/lib-jitsi-meet'; +import { getLocalParticipant } from '../../react/features/base/participants'; import { openDisplayNamePrompt } from '../../react/features/display-name'; +import { + maybeShowNotificationWithDoNotDisplay, + setNotificationsEnabled +} from '../../react/features/notifications'; import { checkAutoEnableDesktopSharing, clearButtonPopup, @@ -37,11 +42,6 @@ import { showSharedVideoButton, showToolbox } from '../../react/features/toolbox'; -import { - maybeShowNotificationWithDoNotDisplay, - setNotificationsEnabled -} from '../../react/features/notifications'; -import { getLocalParticipant } from '../../react/features/base/participants'; var EventEmitter = require("events"); UI.messageHandler = messageHandler; @@ -199,8 +199,8 @@ UI.setLocalRaisedHandStatus * Initialize conference UI. */ UI.initConference = function () { - const { id, avatarID, email, name } - = getLocalParticipant(APP.store.getState()); + const { dispatch, getState } = APP.store; + const { avatarID, email, id, name } = getLocalParticipant(getState); // Update default button states before showing the toolbar // if local role changes buttons state will be again updated. @@ -221,7 +221,7 @@ UI.initConference = function () { UI.setUserAvatarID(id, avatarID); } - APP.store.dispatch(checkAutoEnableDesktopSharing()); + dispatch(checkAutoEnableDesktopSharing()); // FollowMe attempts to copy certain aspects of the moderator's UI into the // other participants' UI. Consequently, it needs (1) read and write access diff --git a/modules/UI/side_pannels/chat/Chat.js b/modules/UI/side_pannels/chat/Chat.js index abf99633cc..d4b44c0bf6 100644 --- a/modules/UI/side_pannels/chat/Chat.js +++ b/modules/UI/side_pannels/chat/Chat.js @@ -244,10 +244,10 @@ var Chat = { // if we are in conversation mode focus on the text input // if we are not, focus on the display name input - if (APP.conference.getLocalDisplayName()) - deferredFocus('usermsg'); - else - deferredFocus('nickinput'); + deferredFocus( + APP.conference.getLocalDisplayName() + ? 'usermsg' + : 'nickinput'); }); addSmileys(); diff --git a/react/features/base/conference/actions.js b/react/features/base/conference/actions.js index ad986f2491..f1ea715ac4 100644 --- a/react/features/base/conference/actions.js +++ b/react/features/base/conference/actions.js @@ -37,7 +37,7 @@ import { } from './constants'; import { _addLocalTracksToConference, - setLocalParticipantData + sendLocalParticipant } from './functions'; import type { Dispatch } from 'redux'; @@ -288,7 +288,7 @@ export function createConference() { _addConferenceListeners(conference, dispatch); - setLocalParticipantData(conference, state); + sendLocalParticipant(state, conference); conference.join(password); }; diff --git a/react/features/base/conference/functions.js b/react/features/base/conference/functions.js index 7e23b1daa2..573b479547 100644 --- a/react/features/base/conference/functions.js +++ b/react/features/base/conference/functions.js @@ -1,11 +1,14 @@ +// @flow + +import { JitsiTrackErrors } from '../lib-jitsi-meet'; +import { getLocalParticipant } from '../participants'; +import { toState } from '../redux'; + import { AVATAR_ID_COMMAND, AVATAR_URL_COMMAND, EMAIL_COMMAND } from './constants'; -import { JitsiTrackErrors } from '../lib-jitsi-meet'; -import { getLocalParticipant } from '../participants'; -import { toState } from '../redux'; /** * Attach a set of local tracks to a conference. @@ -15,7 +18,9 @@ import { toState } from '../redux'; * @protected * @returns {Promise} */ -export function _addLocalTracksToConference(conference, localTracks) { +export function _addLocalTracksToConference( + conference: { addTrack: Function, getLocalTracks: Function }, + localTracks: Array) { const conferenceLocalTracks = conference.getLocalTracks(); const promises = []; @@ -45,7 +50,7 @@ export function _addLocalTracksToConference(conference, localTracks) { * {@code getState} function. * @returns {JitsiConference|undefined} */ -export function getCurrentConference(stateful) { +export function getCurrentConference(stateful: Function | Object) { const { conference, joining, leaving } = toState(stateful)['features/base/conference']; @@ -64,7 +69,7 @@ export function getCurrentConference(stateful) { * @protected * @returns {void} */ -export function _handleParticipantError(err) { +export function _handleParticipantError(err: { message: ?string }) { // XXX DataChannels are initialized at some later point when the conference // has multiple participants, but code that pins or selects a participant // might be executed before. So here we're swallowing a particular error. @@ -83,7 +88,7 @@ export function _handleParticipantError(err) { * @returns {boolean} If the specified room name is valid, then true; otherwise, * false. */ -export function isRoomValid(room) { +export function isRoomValid(room: ?string) { return typeof room === 'string' && room !== ''; } @@ -95,7 +100,9 @@ export function isRoomValid(room) { * @protected * @returns {Promise} */ -export function _removeLocalTracksFromConference(conference, localTracks) { +export function _removeLocalTracksFromConference( + conference: { removeTrack: Function }, + localTracks: Array) { return Promise.all(localTracks.map(track => conference.removeTrack(track) .catch(err => { @@ -129,15 +136,20 @@ function _reportError(msg, err) { } /** - * Sets the data like avatar URL, email and display name for the local - * participant to the conference. + * Sends a representation of the local participant such as her avatar (URL), + * e-mail address, and display name to (the remote participants of) a specific + * conference. * - * @param {JitsiConference} conference - The JitsiConference instance. - * @param {Object} state - The whole Redux state. + * @param {Function|Object} stateful - The redux store, state, or + * {@code getState} function. + * @param {JitsiConference} conference - The {@code JitsiConference} to which + * the representation of the local participant is to be sent. * @returns {void} */ -export function setLocalParticipantData(conference, state) { - const { avatarID, avatarURL, email, name } = getLocalParticipant(state); +export function sendLocalParticipant( + stateful: Function | Object, + conference: { sendCommand: Function, setDisplayName: Function }) { + const { avatarID, avatarURL, email, name } = getLocalParticipant(stateful); avatarID && conference.sendCommand(AVATAR_ID_COMMAND, { value: avatarID diff --git a/react/features/base/jwt/actions.js b/react/features/base/jwt/actions.js index de2ba7707e..d22a278bb8 100644 --- a/react/features/base/jwt/actions.js +++ b/react/features/base/jwt/actions.js @@ -1,19 +1,19 @@ -/* @flow */ +// @flow import { SET_CALL_OVERLAY_VISIBLE, SET_JWT } from './actionTypes'; /** * Sets the visibility of {@code CallOverlay}. * - * @param {boolean|undefined} callOverlayVisible - If {@code CallOverlay} is to - * be displayed/visible, then {@code true}; otherwise, {@code false} or + * @param {boolean|undefined} [callOverlayVisible] - If {@code CallOverlay} is + * to be displayed/visible, then {@code true}; otherwise, {@code false} or * {@code undefined}. * @returns {{ * type: SET_CALL_OVERLAY_VISIBLE, * callOverlayVisible: (boolean|undefined) * }} */ -export function setCallOverlayVisible(callOverlayVisible: boolean) { +export function setCallOverlayVisible(callOverlayVisible: ?boolean) { return (dispatch: Dispatch<*>, getState: Function) => { getState()['features/base/jwt'] .callOverlayVisible === callOverlayVisible @@ -27,13 +27,13 @@ export function setCallOverlayVisible(callOverlayVisible: boolean) { /** * Stores a specific JSON Web Token (JWT) into the redux store. * - * @param {string} jwt - The JSON Web Token (JWT) to store. + * @param {string} [jwt] - The JSON Web Token (JWT) to store. * @returns {{ * type: SET_TOKEN_DATA, - * jwt: string + * jwt: (string|undefined) * }} */ -export function setJWT(jwt: string) { +export function setJWT(jwt: ?string) { return { type: SET_JWT, jwt diff --git a/react/features/base/jwt/middleware.js b/react/features/base/jwt/middleware.js index 4ad5d54148..7f6a1fefe3 100644 --- a/react/features/base/jwt/middleware.js +++ b/react/features/base/jwt/middleware.js @@ -1,3 +1,5 @@ +// @flow + import jwtDecode from 'jwt-decode'; import { @@ -22,6 +24,8 @@ import { setCallOverlayVisible, setJWT } from './actions'; import { SET_JWT } from './actionTypes'; import { parseJWTFromURLParams } from './functions'; +declare var APP: Object; + /** * Middleware to parse token data upon setting a new room URL. * @@ -80,8 +84,8 @@ function _maybeSetCallOverlayVisible({ dispatch, getState }, next, action) { if (stateFeaturesJWT.callee) { const { conference, leaving, room } = state['features/base/conference']; - // XXX The CallOverlay is to be displayed/visible as soon as - // possible including even before the conference is joined. + // XXX The CallOverlay is to be displayed/visible as soon as possible + // including even before the conference is joined. if (room && (!conference || conference !== leaving)) { switch (action.type) { case CONFERENCE_FAILED: @@ -100,10 +104,11 @@ function _maybeSetCallOverlayVisible({ dispatch, getState }, next, action) { break; default: { - // The CallOverlay it to no longer be displayed/visible as soon + // The CallOverlay is to no longer be displayed/visible as soon // as another participant joins. - callOverlayVisible = getParticipantCount(state) === 1 - && Boolean(getLocalParticipant(state)); + callOverlayVisible + = getParticipantCount(state) === 1 + && Boolean(getLocalParticipant(state)); // However, the CallDialog is not to be displayed/visible again // after all remote participants leave. @@ -122,53 +127,24 @@ function _maybeSetCallOverlayVisible({ dispatch, getState }, next, action) { } /** - * Converts 'context.user' JWT token structure to the format compatible with the - * corresponding fields overridden in base/participants. - * - * @param {Object} user - The 'jwt.context.user' structure parsed from the JWT - * token. - * @returns {({ - * avatarURL: string?, - * email: string?, - * name: string? - * })} - * @private - */ -function _normalizeCallerFields(user) { - const { avatar, avatarUrl, email, name } = user; - const caller = { }; - - if (typeof (avatarUrl || avatar) === 'string') { - caller.avatarURL = (avatarUrl || avatar).trim(); - } - if (typeof email === 'string') { - caller.email = email.trim(); - } - if (typeof name === 'string') { - caller.name = name.trim(); - } - - return Object.keys(caller).length ? caller : undefined; -} - -/** - * Eventually overwrites 'avatarURL', 'email' and 'name' fields with the values - * from JWT token for the local participant stored in the 'base/participants' - * Redux store by dispatching the participant updated action. + * Overwrites the properties {@code avatarURL}, {@code email}, and {@code name} + * of the local participant stored in the redux state base/participants. * * @param {Store} store - The redux store. - * @param {Object} caller - The "caller" structure parsed from 'context.user' - * part of the JWT token and then normalized using - * {@link _normalizeCallerFields}. - * @returns {void} + * @param {Object} localParticipant - The {@code Participant} structure to + * overwrite the local participant stored in the redux store base/participants + * with. * @private + * @returns {void} */ -function _overwriteLocalParticipant({ dispatch, getState }, caller) { - const { avatarURL, email, name } = caller; - const localParticipant = getLocalParticipant(getState()); +function _overwriteLocalParticipant( + { dispatch, getState }, + { avatarURL, email, name }) { + let localParticipant; - if (localParticipant && (avatarURL || email || name)) { - const newProperties = { id: localParticipant.id }; + if ((avatarURL || email || name) + && (localParticipant = getLocalParticipant(getState))) { + const newProperties: Object = { id: localParticipant.id }; if (avatarURL) { newProperties.avatarURL = avatarURL; @@ -183,41 +159,6 @@ function _overwriteLocalParticipant({ dispatch, getState }, caller) { } } -/** - * Will reset the values overridden by {@link _overwriteLocalParticipant} - * by either clearing them or setting to default values. Only the values that - * have not changed since the override happened will be restored. - * - * NOTE Once there is the possibility to edit and save participant properties, - * this method should restore values from the storage instead. - * - * @param {Store} store - The Redux store. - * @param {Object} caller - The 'caller' part of the JWT Redux state which tells - * which local participant's fields's been overridden when the JWT token was - * set. - * @returns {void} - * @private - */ -function _resetLocalParticipantOverrides({ dispatch, getState }, caller) { - const { avatarURL, name, email } = caller; - const localParticipant = getLocalParticipant(getState()); - - if (localParticipant && (avatarURL || name || email)) { - const newProperties = { id: localParticipant.id }; - - if (avatarURL === localParticipant.avatarURL) { - newProperties.avatarURL = undefined; - } - if (name === localParticipant.name) { - newProperties.name = LOCAL_PARTICIPANT_DEFAULT_NAME; - } - if (email === localParticipant.email) { - newProperties.email = undefined; - } - dispatch(participantUpdated(newProperties)); - } -} - /** * Notifies the feature jwt that the action {@link SET_CONFIG} or * {@link SET_LOCATION_URL} is being dispatched within a specific redux @@ -266,41 +207,108 @@ function _setJWT(store, next, action) { // eslint-disable-next-line no-unused-vars const { jwt, type, ...actionPayload } = action; - if (jwt && !Object.keys(actionPayload).length) { - const { - enableUserRolesBasedOnToken - } = store.getState()['features/base/config']; + if (!Object.keys(actionPayload).length) { + if (jwt) { + const { + enableUserRolesBasedOnToken + } = store.getState()['features/base/config']; + + action.isGuest = !enableUserRolesBasedOnToken; - action.isGuest = !enableUserRolesBasedOnToken; + const jwtPayload = jwtDecode(jwt); - const jwtPayload = jwtDecode(jwt); + if (jwtPayload) { + const { context, iss } = jwtPayload; - if (jwtPayload) { - const { context, iss } = jwtPayload; + action.jwt = jwt; + action.issuer = iss; + if (context) { + const user = _user2participant(context.user); - action.jwt = jwt; - action.issuer = iss; - if (context) { - action.callee = context.callee; - action.caller = _normalizeCallerFields(context.user); - action.group = context.group; - action.server = context.server; + action.callee = context.callee; + action.group = context.group; + action.server = context.server; + action.user = user; - if (action.caller) { - _overwriteLocalParticipant(store, action.caller); + user && _overwriteLocalParticipant(store, user); } } - } - } else if (!jwt && !Object.keys(actionPayload).length) { - const jwtState = store.getState()['features/base/jwt']; - - // The logic of restoring JWT overrides make sense only on mobile. On - // web it should eventually be restored from storage, but there's no - // such use case yet. - if (jwtState.caller && typeof APP === 'undefined') { - _resetLocalParticipantOverrides(store, jwtState.caller); + } else if (typeof APP === 'undefined') { + // The logic of restoring JWT overrides make sense only on mobile. + // On Web it should eventually be restored from storage, but there's + // no such use case yet. + + const { user } = store.getState()['features/base/jwt']; + + user && _undoOverwriteLocalParticipant(store, user); } } return _maybeSetCallOverlayVisible(store, next, action); } + +/** + * Undoes/resets the values overwritten by {@link _overwriteLocalParticipant} + * by either clearing them or setting to default values. Only the values that + * have not changed since the overwrite happened will be restored. + * + * NOTE Once it is possible to edit and save participant properties, this + * function should restore values from the storage instead. + * + * @param {Store} store - The redux store. + * @param {Object} localParticipant - The {@code Participant} structure used + * previously to {@link _overwriteLocalParticipant}. + * @private + * @returns {void} + */ +function _undoOverwriteLocalParticipant( + { dispatch, getState }, + { avatarURL, name, email }) { + let localParticipant; + + if ((avatarURL || name || email) + && (localParticipant = getLocalParticipant(getState))) { + const newProperties: Object = { id: localParticipant.id }; + + if (avatarURL === localParticipant.avatarURL) { + newProperties.avatarURL = undefined; + } + if (email === localParticipant.email) { + newProperties.email = undefined; + } + if (name === localParticipant.name) { + newProperties.name = LOCAL_PARTICIPANT_DEFAULT_NAME; + } + dispatch(participantUpdated(newProperties)); + } +} + +/** + * Converts the JWT {@code context.user} structure to the {@code Participant} + * structure stored in the redux state base/participants. + * + * @param {Object} user - The JWT {@code context.user} structure to convert. + * @private + * @returns {{ + * avatarURL: ?string, + * email: ?string, + * name: ?string + * }} + */ +function _user2participant({ avatar, avatarUrl, email, name }) { + const participant = {}; + + if (typeof avatarUrl === 'string') { + participant.avatarURL = avatarUrl.trim(); + } else if (typeof avatar === 'string') { + participant.avatarURL = avatar.trim(); + } + if (typeof email === 'string') { + participant.email = email.trim(); + } + if (typeof name === 'string') { + participant.name = name.trim(); + } + + return Object.keys(participant).length ? participant : undefined; +} diff --git a/react/features/base/participants/constants.js b/react/features/base/participants/constants.js index 81d46f540d..66230c7b86 100644 --- a/react/features/base/participants/constants.js +++ b/react/features/base/participants/constants.js @@ -14,16 +14,18 @@ export const DEFAULT_AVATAR_RELATIVE_PATH = 'images/avatar.png'; /** - * Local participant might not have real ID until he joins a conference, - * so use 'local' as its default ID. + * The local participant might not have real ID until she joins a conference, + * so use 'local' as her default ID. * * @type {string} */ export const LOCAL_PARTICIPANT_DEFAULT_ID = 'local'; /** - * The default display name for the local participant. - * TODO Get the from config and/or localized. + * The default display name of the local participant. + * + * TODO Get the display name from config and/or localized. + * * @type {string} */ export const LOCAL_PARTICIPANT_DEFAULT_NAME = 'me'; diff --git a/react/features/base/participants/functions.js b/react/features/base/participants/functions.js index 967f114216..7c3118ad31 100644 --- a/react/features/base/participants/functions.js +++ b/react/features/base/participants/functions.js @@ -1,4 +1,4 @@ -/* @flow */ +// @flow import { toState } from '../redux'; @@ -77,14 +77,14 @@ export function getAvatarURL({ avatarID, avatarURL, email, id }: { /** * Returns local participant from Redux state. * - * @param {(Function|Object|Participant[])} stateOrGetState - The redux state + * @param {(Function|Object|Participant[])} stateful - The redux state * features/base/participants, the (whole) redux state, or redux's - * {@code getState} function to be used to retrieve the - * features/base/participants state. + * {@code getState} function to be used to retrieve the state + * features/base/participants. * @returns {(Participant|undefined)} */ -export function getLocalParticipant(stateOrGetState: Object | Function) { - const participants = _getAllParticipants(stateOrGetState); +export function getLocalParticipant(stateful: Object | Function) { + const participants = _getAllParticipants(stateful); return participants.find(p => p.local); } @@ -92,18 +92,18 @@ export function getLocalParticipant(stateOrGetState: Object | Function) { /** * Returns participant by ID from Redux state. * - * @param {(Function|Object|Participant[])} stateOrGetState - The redux state + * @param {(Function|Object|Participant[])} stateful - The redux state * features/base/participants, the (whole) redux state, or redux's - * {@code getState} function to be used to retrieve the - * features/base/participants state. + * {@code getState} function to be used to retrieve the state + * features/base/participants. * @param {string} id - The ID of the participant to retrieve. * @private * @returns {(Participant|undefined)} */ export function getParticipantById( - stateOrGetState: Object | Function, + stateful: Object | Function, id: string) { - const participants = _getAllParticipants(stateOrGetState); + const participants = _getAllParticipants(stateful); return participants.find(p => p.id === id); } @@ -112,14 +112,14 @@ export function getParticipantById( * Returns a count of the known participants in the passed in redux state, * excluding any fake participants. * - * @param {(Function|Object|Participant[])} stateOrGetState - The redux state + * @param {(Function|Object|Participant[])} stateful - The redux state * features/base/participants, the (whole) redux state, or redux's - * {@code getState} function to be used to retrieve the - * features/base/participants state. + * {@code getState} function to be used to retrieve the state + * features/base/participants. * @returns {number} */ -export function getParticipantCount(stateOrGetState: Object | Function) { - return getParticipants(stateOrGetState).length; +export function getParticipantCount(stateful: Object | Function) { + return getParticipants(stateful).length; } @@ -127,42 +127,42 @@ export function getParticipantCount(stateOrGetState: Object | Function) { * Selectors for getting all known participants with fake participants filtered * out. * - * @param {(Function|Object|Participant[])} stateOrGetState - The redux state + * @param {(Function|Object|Participant[])} stateful - The redux state * features/base/participants, the (whole) redux state, or redux's - * {@code getState} function to be used to retrieve the - * features/base/participants state. + * {@code getState} function to be used to retrieve the state + * features/base/participants. * @returns {Participant[]} */ -export function getParticipants(stateOrGetState: Object | Function) { - return _getAllParticipants(stateOrGetState).filter(p => !p.isBot); +export function getParticipants(stateful: Object | Function) { + return _getAllParticipants(stateful).filter(p => !p.isBot); } /** * Returns the participant which has its pinned state set to truthy. * - * @param {(Function|Object|Participant[])} stateOrGetState - The redux state + * @param {(Function|Object|Participant[])} stateful - The redux state * features/base/participants, the (whole) redux state, or redux's - * {@code getState} function to be used to retrieve the - * features/base/participants state. + * {@code getState} function to be used to retrieve the state + * features/base/participants. * @returns {(Participant|undefined)} */ -export function getPinnedParticipant(stateOrGetState: Object | Function) { - return _getAllParticipants(stateOrGetState).find(p => p.pinned); +export function getPinnedParticipant(stateful: Object | Function) { + return _getAllParticipants(stateful).find(p => p.pinned); } /** * Returns array of participants from Redux state. * - * @param {(Function|Object|Participant[])} stateOrGetState - The redux state + * @param {(Function|Object|Participant[])} stateful - The redux state * features/base/participants, the (whole) redux state, or redux's - * {@code getState} function to be used to retrieve the - * features/base/participants state. + * {@code getState} function to be used to retrieve the state + * features/base/participants. * @private * @returns {Participant[]} */ -function _getAllParticipants(stateOrGetState) { +function _getAllParticipants(stateful) { return ( - Array.isArray(stateOrGetState) - ? stateOrGetState - : toState(stateOrGetState)['features/base/participants'] || []); + Array.isArray(stateful) + ? stateful + : toState(stateful)['features/base/participants'] || []); } diff --git a/react/features/base/participants/reducer.js b/react/features/base/participants/reducer.js index ed692d67ab..800dfc7db5 100644 --- a/react/features/base/participants/reducer.js +++ b/react/features/base/participants/reducer.js @@ -1,3 +1,5 @@ +// @flow + import { ReducerRegistry, set } from '../redux'; import { randomHexString } from '../util'; @@ -31,6 +33,8 @@ import { * @property {string} email - Participant email. */ +declare var APP: Object; + /** * These properties should not be bulk assigned when updating a particular * @see Participant. @@ -49,9 +53,9 @@ const PARTICIPANT_PROPS_TO_OMIT_WHEN_UPDATE * added/modified. * @param {JitsiConference} action.conference - Conference instance. * @private - * @returns {Participant|undefined} + * @returns {Participant} */ -function _participant(state, action) { +function _participant(state: Object = {}, action) { switch (action.type) { case DOMINANT_SPEAKER_CHANGED: // Only one dominant speaker is allowed. @@ -99,9 +103,9 @@ function _participant(state, action) { // name if (!name) { - // TODO Get the from config and/or localized. - // On web default value is handled in: - // conference.js getParticipantDisplayName + // TODO Get the display name from config and/or localized. + // XXX On Web the default value is handled in conference.js by + // getParticipantDisplayName. if (typeof APP === 'undefined') { name = local ? LOCAL_PARTICIPANT_DEFAULT_NAME : 'Fellow Jitster'; @@ -169,18 +173,18 @@ function _participant(state, action) { */ ReducerRegistry.register('features/base/participants', (state = [], action) => { switch (action.type) { - case PARTICIPANT_JOINED: - return [ ...state, _participant(undefined, action) ]; - - case PARTICIPANT_LEFT: - return state.filter(p => p.id !== action.participant.id); - case DOMINANT_SPEAKER_CHANGED: case PARTICIPANT_ID_CHANGED: case PARTICIPANT_UPDATED: case PIN_PARTICIPANT: return state.map(p => _participant(p, action)); + case PARTICIPANT_JOINED: + return [ ...state, _participant(undefined, action) ]; + + case PARTICIPANT_LEFT: + return state.filter(p => p.id !== action.participant.id); + default: return state; } diff --git a/react/features/conference/route.js b/react/features/conference/route.js index b8918f742f..3e208f25ec 100644 --- a/react/features/conference/route.js +++ b/react/features/conference/route.js @@ -1,4 +1,4 @@ -/* global APP, config */ +// @flow import ConferenceUrl from '../../../modules/URL/ConferenceUrl'; @@ -7,6 +7,9 @@ import { RouteRegistry } from '../base/react'; import { Conference } from './components'; +declare var APP: Object; +declare var config: Object; + const logger = require('jitsi-meet-logger').getLogger(__filename); /** @@ -29,7 +32,6 @@ RouteRegistry.register({ * @returns {void} */ function _initConference() { - // Initialize the conference URL handler APP.ConferenceUrl = new ConferenceUrl(window.location); } @@ -43,7 +45,7 @@ function _initConference() { * @private * @returns {Promise} */ -function _obtainConfig(location, room) { +function _obtainConfig(location: string, room: string) { return new Promise((resolve, reject) => obtainConfig(location, room, (success, error) => { success ? resolve() : reject(error); diff --git a/react/features/feedback/actions.js b/react/features/feedback/actions.js index 57164a3d58..59575211d6 100644 --- a/react/features/feedback/actions.js +++ b/react/features/feedback/actions.js @@ -1,6 +1,6 @@ // @flow -import { openDialog } from '../../features/base/dialog'; +import { openDialog } from '../base/dialog'; import { FEEDBACK_REQUEST_IN_PROGRESS } from '../../../modules/UI/UIErrors'; import { CANCEL_FEEDBACK, SUBMIT_FEEDBACK } from './actionTypes'; @@ -55,8 +55,8 @@ export function maybeOpenFeedbackDialog(conference: Object) { // Feedback has been submitted already. return Promise.resolve({ - thankYouDialogVisible: true, - feedbackSubmitted: true + feedbackSubmitted: true, + thankYouDialogVisible: true }); } else if (conference.isCallstatsEnabled()) { return new Promise(resolve => { @@ -71,12 +71,12 @@ export function maybeOpenFeedbackDialog(conference: Object) { }); } - // If the feedback functionality isn't enabled we show a thank - // you dialog. Signaling it (true), so the caller - // of requestFeedback can act on it + // If the feedback functionality isn't enabled we show a "thank you" + // dialog. Signaling it (true), so the caller of requestFeedback can act + // on it. return Promise.resolve({ - thankYouDialogVisible: true, - feedbackSubmitted: false + feedbackSubmitted: false, + thankYouDialogVisible: true }); }; } diff --git a/react/features/filmstrip/functions.js b/react/features/filmstrip/functions.js index ad7cfd72b6..8656e4d69e 100644 --- a/react/features/filmstrip/functions.js +++ b/react/features/filmstrip/functions.js @@ -1,37 +1,36 @@ -/* @flow */ +// @flow -declare var interfaceConfig: Object; +import { getPinnedParticipant } from '../base/participants'; -import { - getPinnedParticipant -} from '../base/participants'; +declare var interfaceConfig: Object; /** - * A selector for determining whether or not remote video thumbnails should be - * displayed in the filmstrip. + * Determines whether the remote video thumbnails should be displayed/visible in + * the filmstrip. * * @param {Object} state - The full redux state. - * @returns {boolean} - True if remote video thumbnails should be displayed. + * @returns {boolean} - If remote video thumbnails should be displayed/visible + * in the filmstrip, then {@code true}; otherwise, {@code false}. */ export function shouldRemoteVideosBeVisible(state: Object) { const participants = state['features/base/participants']; - const participantsCount = participants.length; - const pinnedParticipant = getPinnedParticipant(state); - - const shouldShowVideos - = participantsCount > 2 + const participantCount = participants.length; + let pinnedParticipant; - // Always show the filmstrip when there is another participant to show - // and the filmstrip is hovered, or local video is pinned, or the - // toolbar is displayed. - || (participantsCount > 1 - && (state['features/filmstrip'].hovered - || state['features/toolbox'].visible - || (pinnedParticipant && pinnedParticipant.local))) + return Boolean( + participantCount > 2 - || interfaceConfig.filmStripOnly + // Always show the filmstrip when there is another participant to + // show and the filmstrip is hovered, or local video is pinned, or + // the toolbar is displayed. + || (participantCount > 1 + && (state['features/filmstrip'].hovered + || state['features/toolbox'].visible + || ((pinnedParticipant = getPinnedParticipant(participants)) + && pinnedParticipant.local))) - || state['features/base/config'].disable1On1Mode; + || (typeof interfaceConfig === 'object' + && interfaceConfig.filmStripOnly) - return Boolean(shouldShowVideos); + || state['features/base/config'].disable1On1Mode); } diff --git a/react/features/speaker-stats/components/SpeakerStats.js b/react/features/speaker-stats/components/SpeakerStats.js index cec28fbdba..0471cc4484 100644 --- a/react/features/speaker-stats/components/SpeakerStats.js +++ b/react/features/speaker-stats/components/SpeakerStats.js @@ -1,4 +1,4 @@ -/* global interfaceConfig */ +// @flow import PropTypes from 'prop-types'; import React, { Component } from 'react'; @@ -10,6 +10,8 @@ import { getLocalParticipant } from '../../base/participants'; import SpeakerStatsItem from './SpeakerStatsItem'; import SpeakerStatsLabels from './SpeakerStatsLabels'; +declare var interfaceConfig: Object; + /** * React component for displaying a list of speaker stats. * @@ -23,7 +25,7 @@ class SpeakerStats extends Component { */ static propTypes = { /** - * The display name for the local participant obtained from the Redux + * The display name for the local participant obtained from the redux * store. */ _localDisplayName: PropTypes.string, @@ -39,6 +41,12 @@ class SpeakerStats extends Component { t: PropTypes.func }; + state = { + stats: {} + }; + + _updateInterval: number; + /** * Initializes a new SpeakerStats instance. * @@ -48,10 +56,7 @@ class SpeakerStats extends Component { constructor(props) { super(props); - this.state = { - stats: {} - }; - this._updateInterval = null; + // Bind event handlers so they are only bound once per instance. this._updateStats = this._updateStats.bind(this); } @@ -100,18 +105,6 @@ class SpeakerStats extends Component { ); } - /** - * Update the internal state with the latest speaker stats. - * - * @returns {void} - * @private - */ - _updateStats() { - const stats = this.props.conference.getSpeakerStats(); - - this.setState({ stats }); - } - /** * Create a SpeakerStatsItem instance for the passed in user id. * @@ -131,18 +124,19 @@ class SpeakerStats extends Component { const dominantSpeakerTime = statsModel.getTotalDominantSpeakerTime(); const hasLeft = statsModel.hasLeft(); - let displayName = ''; + let displayName; if (statsModel.isLocalStats()) { const { t } = this.props; const meString = t('me'); displayName = this.props._localDisplayName; - displayName = displayName ? `${displayName} (${meString})` - : meString; + displayName + = displayName ? `${displayName} (${meString})` : meString; } else { - displayName = this.state.stats[userId].getDisplayName() - || interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME; + displayName + = this.state.stats[userId].getDisplayName() + || interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME; } return ( @@ -154,15 +148,29 @@ class SpeakerStats extends Component { key = { userId } /> ); } + + _updateStats: () => void; + + /** + * Update the internal state with the latest speaker stats. + * + * @returns {void} + * @private + */ + _updateStats() { + const stats = this.props.conference.getSpeakerStats(); + + this.setState({ stats }); + } } /** - * Maps (parts of) the Redux state to the associated SpeakerStats's props. + * Maps (parts of) the redux state to the associated SpeakerStats's props. * - * @param {Object} state - The Redux state. + * @param {Object} state - The redux state. * @private * @returns {{ - * _localDisplayName: string? + * _localDisplayName: ?string * }} */ function _mapStateToProps(state) { @@ -171,6 +179,7 @@ function _mapStateToProps(state) { return { /** * The local display name. + * * @private * @type {string|undefined} */ diff --git a/react/features/welcome/components/WelcomePage.web.js b/react/features/welcome/components/WelcomePage.web.js index 057bb8d9e8..2189e650e1 100644 --- a/react/features/welcome/components/WelcomePage.web.js +++ b/react/features/welcome/components/WelcomePage.web.js @@ -31,7 +31,7 @@ class WelcomePage extends AbstractWelcomePage { interfaceConfig.GENERATE_ROOMNAMES_ON_WELCOME_PAGE }; - // Bind event handlers so they are only bound once for every instance. + // Bind event handlers so they are only bound once per instance. this._onDisableWelcomeChange = this._onDisableWelcomeChange.bind(this); this._onKeyDown = this._onKeyDown.bind(this); this._onRoomChange = this._onRoomChange.bind(this);