From 9f93ce86bee2be25728d58a31f7b2d663df4fc53 Mon Sep 17 00:00:00 2001 From: Lyubomir Marinov Date: Sun, 11 Dec 2016 18:29:13 -0600 Subject: [PATCH] [RN] Simplify the source code --- react/features/base/conference/actionTypes.js | 31 ++-- react/features/base/conference/actions.js | 160 +++++++++--------- react/features/base/conference/middleware.js | 61 ++++--- react/features/base/conference/reducer.js | 151 +++++++++++------ react/features/base/connection/actions.js | 4 +- react/features/base/connection/reducer.js | 6 +- .../features/base/participants/middleware.js | 4 +- react/features/base/redux/functions.js | 64 ++++++- .../components/Conference.native.js | 5 +- react/features/largeVideo/actions.js | 2 +- 10 files changed, 288 insertions(+), 200 deletions(-) diff --git a/react/features/base/conference/actionTypes.js b/react/features/base/conference/actionTypes.js index a53bcf804..3d3da4e95 100644 --- a/react/features/base/conference/actionTypes.js +++ b/react/features/base/conference/actionTypes.js @@ -1,37 +1,34 @@ import { Symbol } from '../react'; /** - * Action type to signal that we are joining the conference. + * The type of the Redux action which signals that a specific conference has + * been joined. * * { - * type: CONFERENCE_JOINED, - * conference: { - * jitsiConference: JitsiConference - * } + * type: CONFERENCE_JOINED, + * conference: JitsiConference * } */ export const CONFERENCE_JOINED = Symbol('CONFERENCE_JOINED'); /** - * Action type to signal that we have left the conference. + * The type of the Redux action which signals that a specific conference has + * been left. * * { - * type: CONFERENCE_LEFT, - * conference: { - * jitsiConference: JitsiConference - * } + * type: CONFERENCE_LEFT, + * conference: JitsiConference * } */ export const CONFERENCE_LEFT = Symbol('CONFERENCE_LEFT'); /** - * Action type to signal that we will leave the specified conference. + * The type of the Redux action which signals that a specific conference will be + * left. * * { - * type: CONFERENCE_WILL_LEAVE, - * conference: { - * jitsiConference: JitsiConference - * } + * type: CONFERENCE_WILL_LEAVE, + * conference: JitsiConference * } */ export const CONFERENCE_WILL_LEAVE = Symbol('CONFERENCE_WILL_LEAVE'); @@ -41,8 +38,8 @@ export const CONFERENCE_WILL_LEAVE = Symbol('CONFERENCE_WILL_LEAVE'); * conference to be joined. * * { - * type: SET_ROOM, - * room: string + * type: SET_ROOM, + * room: string * } */ export const SET_ROOM = Symbol('SET_ROOM'); diff --git a/react/features/base/conference/actions.js b/react/features/base/conference/actions.js index 016d6b87b..e992be685 100644 --- a/react/features/base/conference/actions.js +++ b/react/features/base/conference/actions.js @@ -19,34 +19,52 @@ import { _addLocalTracksToConference } from './functions'; import './middleware'; import './reducer'; -const JitsiConferenceEvents = JitsiMeetJS.events.conference; - /** - * Initializes a new conference. + * Adds conference (event) listeners. * - * @returns {Function} + * @param {JitsiConference} conference - The JitsiConference instance. + * @param {Dispatch} dispatch - The Redux dispatch function. + * @private + * @returns {void} */ -export function createConference() { - return (dispatch, getState) => { - const state = getState(); - const connection = state['features/base/connection'].jitsiConnection; - const room = state['features/base/conference'].room; +function _addConferenceListeners(conference, dispatch) { + const JitsiConferenceEvents = JitsiMeetJS.events.conference; - if (!connection) { - throw new Error('Cannot create conference without connection'); - } - if (typeof room === 'undefined' || room === '') { - throw new Error('Cannot join conference without room name'); - } + conference.on( + JitsiConferenceEvents.CONFERENCE_JOINED, + (...args) => dispatch(_conferenceJoined(conference, ...args))); + conference.on( + JitsiConferenceEvents.CONFERENCE_LEFT, + (...args) => dispatch(_conferenceLeft(conference, ...args))); - // TODO Take options from config. - const conference - = connection.initJitsiConference(room, { openSctp: true }); + conference.on( + JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED, + (...args) => dispatch(dominantSpeakerChanged(...args))); - _setupConferenceListeners(conference, dispatch); + conference.on( + JitsiConferenceEvents.TRACK_ADDED, + t => t && !t.isLocal() && dispatch(trackAdded(t))); + conference.on( + JitsiConferenceEvents.TRACK_REMOVED, + t => t && !t.isLocal() && dispatch(trackRemoved(t))); - conference.join(); - }; + conference.on( + JitsiConferenceEvents.USER_JOINED, + (id, user) => dispatch(participantJoined({ + id, + name: user.getDisplayName(), + role: user.getRole() + }))); + conference.on( + JitsiConferenceEvents.USER_LEFT, + (...args) => dispatch(participantLeft(...args))); + conference.on( + JitsiConferenceEvents.USER_ROLE_CHANGED, + (...args) => dispatch(participantRoleChanged(...args))); + + conference.addCommandListener( + EMAIL_COMMAND, + (data, id) => dispatch(changeParticipantEmail(id, data.value))); } /** @@ -70,37 +88,31 @@ function _conferenceJoined(conference) { dispatch({ type: CONFERENCE_JOINED, - conference: { - jitsiConference: conference - } + conference }); }; } /** - * Signal that we have left the conference. + * Signals that a specific conference has been left. * * @param {JitsiConference} conference - The JitsiConference instance which was * left by the local participant. * @returns {{ * type: CONFERENCE_LEFT, - * conference: { - * jitsiConference: JitsiConference - * } + * conference: JitsiConference * }} */ function _conferenceLeft(conference) { return { type: CONFERENCE_LEFT, - conference: { - jitsiConference: conference - } + conference }; } /** - * Signal the intention of the application to have the local participant leave a - * specific conference. Similar in fashion to CONFERENCE_LEFT. Contrary to it + * Signals the intention of the application to have the local participant leave + * a specific conference. Similar in fashion to CONFERENCE_LEFT. Contrary to it * though, it's not guaranteed because CONFERENCE_LEFT may be triggered by * lib-jitsi-meet and not the application. * @@ -108,17 +120,43 @@ function _conferenceLeft(conference) { * be left by the local participant. * @returns {{ * type: CONFERENCE_LEFT, - * conference: { - * jitsiConference: JitsiConference - * } + * conference: JitsiConference * }} */ export function conferenceWillLeave(conference) { return { type: CONFERENCE_WILL_LEAVE, - conference: { - jitsiConference: conference + conference + }; +} + +/** + * Initializes a new conference. + * + * @returns {Function} + */ +export function createConference() { + return (dispatch, getState) => { + const state = getState(); + const connection = state['features/base/connection'].connection; + + if (!connection) { + throw new Error('Cannot create conference without connection'); } + + const room = state['features/base/conference'].room; + + if (typeof room === 'undefined' || room === '') { + throw new Error('Cannot join conference without room name'); + } + + // TODO Take options from config. + const conference + = connection.initJitsiConference(room, { openSctp: true }); + + _addConferenceListeners(conference, dispatch); + + conference.join(); }; } @@ -138,49 +176,3 @@ export function setRoom(room) { room }; } - -/** - * Setup various conference event handlers. - * - * @param {JitsiConference} conference - The JitsiConference instance. - * @param {Dispatch} dispatch - The Redux dispatch function. - * @private - * @returns {void} - */ -function _setupConferenceListeners(conference, dispatch) { - conference.on( - JitsiConferenceEvents.CONFERENCE_JOINED, - () => dispatch(_conferenceJoined(conference))); - conference.on( - JitsiConferenceEvents.CONFERENCE_LEFT, - () => dispatch(_conferenceLeft(conference))); - - conference.on( - JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED, - id => dispatch(dominantSpeakerChanged(id))); - - conference.on( - JitsiConferenceEvents.TRACK_ADDED, - t => t && !t.isLocal() && dispatch(trackAdded(t))); - conference.on( - JitsiConferenceEvents.TRACK_REMOVED, - t => t && !t.isLocal() && dispatch(trackRemoved(t))); - - conference.on( - JitsiConferenceEvents.USER_JOINED, - (id, user) => dispatch(participantJoined({ - id, - name: user.getDisplayName(), - role: user.getRole() - }))); - conference.on( - JitsiConferenceEvents.USER_LEFT, - id => dispatch(participantLeft(id))); - conference.on( - JitsiConferenceEvents.USER_ROLE_CHANGED, - (id, role) => dispatch(participantRoleChanged(id, role))); - - conference.addCommandListener( - EMAIL_COMMAND, - (data, id) => dispatch(changeParticipantEmail(id, data.value))); -} diff --git a/react/features/base/conference/middleware.js b/react/features/base/conference/middleware.js index 6c96f57b2..eb7c538b8 100644 --- a/react/features/base/conference/middleware.js +++ b/react/features/base/conference/middleware.js @@ -95,7 +95,7 @@ function _pinParticipant(store, next, action) { pin = !localParticipant || !localParticipant.pinned; } if (pin) { - const conference = state['features/base/conference'].jitsiConference; + const conference = state['features/base/conference'].conference; try { conference.pinParticipant(id); @@ -107,6 +107,35 @@ function _pinParticipant(store, next, action) { return next(action); } +/** + * Synchronizes local tracks from state with local tracks in JitsiConference + * instance. + * + * @param {Store} store - Redux store. + * @param {Object} action - Action object. + * @private + * @returns {Promise} + */ +function _syncConferenceLocalTracksWithState(store, action) { + const state = store.getState()['features/base/conference']; + const conference = state.conference; + let promise; + + // XXX The conference may already be in the process of being left, that's + // why we should not add/remove local tracks to such conference. + if (conference && conference !== state.leaving) { + const track = action.track.jitsiTrack; + + if (action.type === TRACK_ADDED) { + promise = _addLocalTracksToConference(conference, [ track ]); + } else { + promise = _removeLocalTracksFromConference(conference, [ track ]); + } + } + + return promise || Promise.resolve(); +} + /** * Notifies the feature base/conference that the action TRACK_ADDED * or TRACK_REMOVED is being dispatched within a specific Redux store. @@ -132,33 +161,3 @@ function _trackAddedOrRemoved(store, next, action) { return next(action); } - -/** - * Synchronizes local tracks from state with local tracks in JitsiConference - * instance. - * - * @param {Store} store - Redux store. - * @param {Object} action - Action object. - * @private - * @returns {Promise} - */ -function _syncConferenceLocalTracksWithState(store, action) { - const conferenceState = store.getState()['features/base/conference']; - const conference = conferenceState.jitsiConference; - const leavingConference = conferenceState.leavingJitsiConference; - let promise; - - // XXX The conference in state might be already in 'leaving' state, that's - // why we should not add/remove local tracks to such conference. - if (conference && conference !== leavingConference) { - const track = action.track.jitsiTrack; - - if (action.type === TRACK_ADDED) { - promise = _addLocalTracksToConference(conference, [ track ]); - } else { - promise = _removeLocalTracksFromConference(conference, [ track ]); - } - } - - return promise || Promise.resolve(); -} diff --git a/react/features/base/conference/reducer.js b/react/features/base/conference/reducer.js index 1790ef3f5..43c9032e1 100644 --- a/react/features/base/conference/reducer.js +++ b/react/features/base/conference/reducer.js @@ -1,4 +1,9 @@ -import { ReducerRegistry, setStateProperty } from '../redux'; +import JitsiMeetJS from '../lib-jitsi-meet'; +import { + ReducerRegistry, + setStateProperties, + setStateProperty +} from '../redux'; import { CONFERENCE_JOINED, @@ -8,63 +13,104 @@ import { } from './actionTypes'; import { isRoomValid } from './functions'; -const INITIAL_STATE = { - jitsiConference: null, - - /** - * Instance of JitsiConference that is currently in 'leaving' state. - */ - leavingJitsiConference: null, - - /** - * The name of the room of the conference (to be) joined (i.e. - * {@link #jitsiConference}). - * - * @type {string} - */ - room: null -}; - /** * Listen for actions that contain the conference object, so that it can be * stored for use by other action creators. */ -ReducerRegistry.register('features/base/conference', - (state = INITIAL_STATE, action) => { - switch (action.type) { - case CONFERENCE_JOINED: - return ( - setStateProperty( - state, - 'jitsiConference', - action.conference.jitsiConference)); +ReducerRegistry.register('features/base/conference', (state = {}, action) => { + switch (action.type) { + case CONFERENCE_JOINED: + return _conferenceJoined(state, action); - case CONFERENCE_LEFT: - if (state.jitsiConference === action.conference.jitsiConference) { - return { - ...state, - jitsiConference: null, - leavingJitsiConference: state.leavingJitsiConference - === action.conference.jitsiConference - ? null - : state.leavingJitsiConference - }; - } - break; + case CONFERENCE_LEFT: + return _conferenceLeft(state, action); - case CONFERENCE_WILL_LEAVE: - return ( - setStateProperty( - state, - 'leavingJitsiConference', - action.conference.jitsiConference)); + case CONFERENCE_WILL_LEAVE: + return _conferenceWillLeave(state, action); - case SET_ROOM: - return _setRoom(state, action); - } + case SET_ROOM: + return _setRoom(state, action); + } + return state; +}); + +/** + * Reduces a specific Redux action CONFERENCE_JOINED of the feature + * base/conference. + * + * @param {Object} state - The Redux state of the feature base/conference. + * @param {Action} action - The Redux action CONFERENCE_JOINED to reduce. + * @private + * @returns {Object} The new state of the feature base/conference after the + * reduction of the specified action. + */ +function _conferenceJoined(state, action) { + return ( + setStateProperties(state, { + /** + * The JitsiConference instance represented by the Redux state of + * the feature base/conference. + * + * @type {JitsiConference} + */ + conference: action.conference, + leaving: undefined + })); +} + +/** + * Reduces a specific Redux action CONFERENCE_LEFT of the feature + * base/conference. + * + * @param {Object} state - The Redux state of the feature base/conference. + * @param {Action} action - The Redux action CONFERENCE_LEFT to reduce. + * @private + * @returns {Object} The new state of the feature base/conference after the + * reduction of the specified action. + */ +function _conferenceLeft(state, action) { + const conference = action.conference; + + if (state.conference !== conference) { return state; - }); + } + + return ( + setStateProperties(state, { + conference: undefined, + leaving: undefined + })); +} + +/** + * Reduces a specific Redux action CONFERENCE_WILL_LEAVE of the feature + * base/conference. + * + * @param {Object} state - The Redux state of the feature base/conference. + * @param {Action} action - The Redux action CONFERENCE_WILL_LEAVE to reduce. + * @private + * @returns {Object} The new state of the feature base/conference after the + * reduction of the specified action. + */ +function _conferenceWillLeave(state, action) { + const conference = action.conference; + + if (state.conference !== conference) { + return state; + } + + return ( + setStateProperties(state, { + /** + * The JitsiConference instance which is currently in the process of + * being left. + * + * @type {JitsiConference} + */ + leaving: conference + })); +} /** * Reduces a specific Redux action SET_ROOM of the feature base/conference. @@ -85,8 +131,13 @@ function _setRoom(state, action) { // Technically, there are multiple values which don't represent valid // room names. Practically, each of them is as bad as the rest of them // because we can't use any of them to join a conference. - room = INITIAL_STATE.room; + room = undefined; } + /** + * The name of the room of the conference (to be) joined. + * + * @type {string} + */ return setStateProperty(state, 'room', room); } diff --git a/react/features/base/connection/actions.js b/react/features/base/connection/actions.js index 5cf1fa258..2ad1d6686 100644 --- a/react/features/base/connection/actions.js +++ b/react/features/base/connection/actions.js @@ -105,8 +105,8 @@ export function connect() { export function disconnect() { return (dispatch, getState) => { const state = getState(); - const conference = state['features/base/conference'].jitsiConference; - const connection = state['features/base/connection'].jitsiConnection; + const conference = state['features/base/conference'].conference; + const connection = state['features/base/connection'].connection; let promise; diff --git a/react/features/base/connection/reducer.js b/react/features/base/connection/reducer.js index cedb00f04..a2e08477e 100644 --- a/react/features/base/connection/reducer.js +++ b/react/features/base/connection/reducer.js @@ -35,8 +35,8 @@ ReducerRegistry.register('features/base/connection', (state = {}, action) => { * reduction of the specified action. */ function _connectionDisconnected(state, action) { - if (state.jitsiConnection === action.connection) { - return setStateProperty(state, 'jitsiConnection', undefined); + if (state.connection === action.connection) { + return setStateProperty(state, 'connection', undefined); } return state; @@ -53,7 +53,7 @@ function _connectionDisconnected(state, action) { * reduction of the specified action. */ function _connectionEstablished(state, action) { - return setStateProperty(state, 'jitsiConnection', action.connection); + return setStateProperty(state, 'connection', action.connection); } /** diff --git a/react/features/base/participants/middleware.js b/react/features/base/participants/middleware.js index f22fb6e5c..93018af41 100644 --- a/react/features/base/participants/middleware.js +++ b/react/features/base/participants/middleware.js @@ -17,9 +17,7 @@ import { LOCAL_PARTICIPANT_DEFAULT_ID } from './constants'; MiddlewareRegistry.register(store => next => action => { switch (action.type) { case CONFERENCE_JOINED: - store.dispatch( - localParticipantIdChanged( - action.conference.jitsiConference.myUserId())); + store.dispatch(localParticipantIdChanged(action.conference.myUserId())); break; case CONFERENCE_LEFT: diff --git a/react/features/base/redux/functions.js b/react/features/base/redux/functions.js index a64ee6130..6aecd65f4 100644 --- a/react/features/base/redux/functions.js +++ b/react/features/base/redux/functions.js @@ -1,3 +1,26 @@ +/** + * Sets specific properties of a specific state to specific values and prevents + * unnecessary state changes. + * + * @param {Object} target - The state on which the specified properties are to + * be set. + * @param {Object} source - The map of properties to values which are to be set + * on the specified target. + * @returns {Object} The specified target if the values of the specified + * properties equal the specified values; otherwise, a new state constructed + * from the specified target by setting the specified properties to the + * specified values. + */ +export function setStateProperties(target, source) { + let t = target; + + for (const property in source) { // eslint-disable-line guard-for-in + t = setStateProperty(t, property, source[property], t === target); + } + + return t; +} + /** * Sets a specific property of a specific state to a specific value. Prevents * unnecessary state changes (when the specified value is equal to the @@ -15,11 +38,36 @@ * property to the specified value. */ export function setStateProperty(state, property, value) { + return _setStateProperty(state, property, value, /* copyOnWrite */ false); +} + +/* eslint-disable max-params */ + +/** + * Sets a specific property of a specific state to a specific value. Prevents + * unnecessary state changes (when the specified value is equal to the + * value of the specified property of the specified state). + * + * @param {Object} state - The (Redux) state from which a state is to be + * constructed by setting the specified property to the specified + * value. + * @param {string} property - The property of state which is to be + * assigned the specified value. + * @param {*} value - The value to assign to the specified property. + * @param {boolean} copyOnWrite - If the specified state is to not be + * modified, true; otherwise, false. + * @returns {Object} The specified state if the value of the specified + * property equals the specified value/tt> or copyOnWrite + * is truthy; otherwise, a new state constructed from the specified + * state by setting the specified property to the specified + * value. + */ +function _setStateProperty(state, property, value, copyOnWrite) { // Delete state properties that are to be set to undefined. (It is a matter // of personal preference, mostly.) if (typeof value === 'undefined' && Object.prototype.hasOwnProperty.call(state, property)) { - const newState = { ...state }; + const newState = copyOnWrite ? { ...state } : state; if (delete newState[property]) { return newState; @@ -27,11 +75,17 @@ export function setStateProperty(state, property, value) { } if (state[property] !== value) { - return { - ...state, - [property]: value - }; + if (copyOnWrite) { + return { + ...state, + [property]: value + }; + } + + state[property] = value; } return state; } + +/* eslint-enable max-params */ diff --git a/react/features/conference/components/Conference.native.js b/react/features/conference/components/Conference.native.js index 2353788b2..28bb756ec 100644 --- a/react/features/conference/components/Conference.native.js +++ b/react/features/conference/components/Conference.native.js @@ -1,10 +1,7 @@ import React, { Component } from 'react'; import { connect as reactReduxConnect } from 'react-redux'; -import { - connect, - disconnect -} from '../../base/connection'; +import { connect, disconnect } from '../../base/connection'; import { Container } from '../../base/react'; import { FilmStrip } from '../../filmStrip'; import { LargeVideo } from '../../largeVideo'; diff --git a/react/features/largeVideo/actions.js b/react/features/largeVideo/actions.js index 064a8f1a6..c64f733d8 100644 --- a/react/features/largeVideo/actions.js +++ b/react/features/largeVideo/actions.js @@ -20,7 +20,7 @@ import './reducer'; export function selectParticipant() { return (dispatch, getState) => { const state = getState(); - const conference = state['features/base/conference'].jitsiConference; + const conference = state['features/base/conference'].conference; if (conference) { const largeVideo = state['features/largeVideo'];