Reduce the complexity of the source code

This commit is contained in:
Lyubomir Marinov 2016-12-05 09:14:50 -06:00
parent 727df551e6
commit 02e3f6b3a2
10 changed files with 307 additions and 239 deletions

View File

@ -6,10 +6,7 @@ import {
participantLeft,
participantRoleChanged
} from '../participants';
import {
trackAdded,
trackRemoved
} from '../tracks';
import { trackAdded, trackRemoved } from '../tracks';
import {
CONFERENCE_JOINED,
@ -46,7 +43,7 @@ export function createConference() {
const conference
= connection.initJitsiConference(room, { openSctp: true });
dispatch(_setupConferenceListeners(conference));
_setupConferenceListeners(conference, dispatch);
conference.join();
};
@ -60,11 +57,12 @@ export function createConference() {
* joined by the local participant.
* @returns {Function}
*/
export function conferenceJoined(conference) {
function _conferenceJoined(conference) {
return (dispatch, getState) => {
const localTracks = getState()['features/base/tracks']
.filter(t => t.local)
.map(t => t.jitsiTrack);
const localTracks
= getState()['features/base/tracks']
.filter(t => t.local)
.map(t => t.jitsiTrack);
if (localTracks.length) {
_addLocalTracksToConference(conference, localTracks);
@ -91,7 +89,7 @@ export function conferenceJoined(conference) {
* }
* }}
*/
export function conferenceLeft(conference) {
function _conferenceLeft(conference) {
return {
type: CONFERENCE_LEFT,
conference: {
@ -144,48 +142,45 @@ export function setRoom(room) {
/**
* Setup various conference event handlers.
*
* @param {JitsiConference} conference - Conference instance.
* @param {JitsiConference} conference - The JitsiConference instance.
* @param {Dispatch} dispatch - The Redux dispatch function.
* @private
* @returns {Function}
* @returns {void}
*/
function _setupConferenceListeners(conference) {
return dispatch => {
conference.on(
function _setupConferenceListeners(conference, dispatch) {
conference.on(
JitsiConferenceEvents.CONFERENCE_JOINED,
() => dispatch(conferenceJoined(conference)));
conference.on(
() => dispatch(_conferenceJoined(conference)));
conference.on(
JitsiConferenceEvents.CONFERENCE_LEFT,
() => dispatch(conferenceLeft(conference)));
() => dispatch(_conferenceLeft(conference)));
conference.on(
conference.on(
JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED,
id => dispatch(dominantSpeakerChanged(id)));
conference.on(
conference.on(
JitsiConferenceEvents.TRACK_ADDED,
track =>
track && !track.isLocal() && dispatch(trackAdded(track)));
conference.on(
t => t && !t.isLocal() && dispatch(trackAdded(t)));
conference.on(
JitsiConferenceEvents.TRACK_REMOVED,
track =>
track && !track.isLocal() && dispatch(trackRemoved(track)));
t => t && !t.isLocal() && dispatch(trackRemoved(t)));
conference.on(
conference.on(
JitsiConferenceEvents.USER_JOINED,
(id, user) => dispatch(participantJoined({
id,
name: user.getDisplayName(),
role: user.getRole()
})));
conference.on(
conference.on(
JitsiConferenceEvents.USER_LEFT,
id => dispatch(participantLeft(id)));
conference.on(
conference.on(
JitsiConferenceEvents.USER_ROLE_CHANGED,
(id, role) => dispatch(participantRoleChanged(id, role)));
conference.addCommandListener(
conference.addCommandListener(
EMAIL_COMMAND,
(data, id) => dispatch(changeParticipantEmail(id, data.value)));
};
}

View File

@ -1,14 +1,13 @@
import { CONNECTION_ESTABLISHED } from '../connection';
import {
getLocalParticipant,
getParticipantById,
PIN_PARTICIPANT
} from '../participants';
import { MiddlewareRegistry } from '../redux';
import {
TRACK_ADDED,
TRACK_REMOVED
} from '../tracks';
import { TRACK_ADDED, TRACK_REMOVED } from '../tracks';
import { createConference } from './actions';
import {
_addLocalTracksToConference,
_handleParticipantError,
@ -16,45 +15,68 @@ import {
} from './functions';
/**
* This middleware intercepts TRACK_ADDED and TRACK_REMOVED actions to sync
* conference's local tracks with local tracks in state. Also captures
* PIN_PARTICIPANT action to pin participant in conference.
* Implements the middleware of the feature base/conference.
*
* @param {Store} store - Redux store.
* @returns {Function}
*/
MiddlewareRegistry.register(store => next => action => {
switch (action.type) {
case CONNECTION_ESTABLISHED:
return _connectionEstablished(store, next, action);
case PIN_PARTICIPANT:
pinParticipant(store, action.participant.id);
break;
return _pinParticipant(store, next, action);
case TRACK_ADDED:
case TRACK_REMOVED: {
const track = action.track;
if (track && track.local) {
return syncConferenceLocalTracksWithState(store, action)
.then(() => next(action));
}
break;
}
case TRACK_REMOVED:
return _trackAddedOrRemoved(store, next, action);
}
return next(action);
});
/**
* Pins remote participant in conference, ignores local participant.
* Notifies the feature base/conference that the action CONNECTION_ESTABLISHED
* is being dispatched within a specific Redux store.
*
* @param {Store} store - Redux store.
* @param {string|null} id - Participant id or null if no one is currently
* pinned.
* @returns {void}
* @param {Store} store - The Redux store in which the specified action is being
* dispatched.
* @param {Dispatch} next - The Redux dispatch function to dispatch the
* specified action to the specified store.
* @param {Action} action - The Redux action CONNECTION_ESTABLISHED which is
* being dispatched in the specified store.
* @private
* @returns {Object} The new state that is the result of the reduction of the
* specified action.
*/
function pinParticipant(store, id) {
function _connectionEstablished(store, next, action) {
const result = next(action);
store.dispatch(createConference());
return result;
}
/**
* Notifies the feature base/conference that the action PIN_PARTICIPANT is being
* dispatched within a specific Redux store. Pins the specified remote
* participant in the associated conference, ignores the local participant.
*
* @param {Store} store - The Redux store in which the specified action is being
* dispatched.
* @param {Dispatch} next - The Redux dispatch function to dispatch the
* specified action to the specified store.
* @param {Action} action - The Redux action PIN_PARTICIPANT which is being
* dispatched in the specified store.
* @private
* @returns {Object} The new state that is the result of the reduction of the
* specified action.
*/
function _pinParticipant(store, next, action) {
const state = store.getState();
const participants = state['features/base/participants'];
const id = action.participant.id;
const participantById = getParticipantById(participants, id);
let pin;
@ -81,16 +103,46 @@ function pinParticipant(store, id) {
_handleParticipantError(err);
}
}
return next(action);
}
/**
* Syncs local tracks from state with local tracks in JitsiConference instance.
* Notifies the feature base/conference that the action TRACK_ADDED
* or TRACK_REMOVED is being dispatched within a specific Redux store.
*
* @param {Store} store - The Redux store in which the specified action is being
* dispatched.
* @param {Dispatch} next - The Redux dispatch function to dispatch the
* specified action to the specified store.
* @param {Action} action - The Redux action TRACK_ADDED or TRACK_REMOVED which
* is being dispatched in the specified store.
* @private
* @returns {Object} The new state that is the result of the reduction of the
* specified action.
*/
function _trackAddedOrRemoved(store, next, action) {
const track = action.track;
if (track && track.local) {
return (
_syncConferenceLocalTracksWithState(store, action)
.then(() => 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) {
function _syncConferenceLocalTracksWithState(store, action) {
const conferenceState = store.getState()['features/base/conference'];
const conference = conferenceState.jitsiConference;
const leavingConference = conferenceState.leavingJitsiConference;

View File

@ -1,4 +1,4 @@
import { ReducerRegistry } from '../redux';
import { ReducerRegistry, setStateProperty } from '../redux';
import {
CONFERENCE_JOINED,
@ -33,10 +33,11 @@ ReducerRegistry.register('features/base/conference',
(state = INITIAL_STATE, action) => {
switch (action.type) {
case CONFERENCE_JOINED:
return {
...state,
jitsiConference: action.conference.jitsiConference
};
return (
setStateProperty(
state,
'jitsiConference',
action.conference.jitsiConference));
case CONFERENCE_LEFT:
if (state.jitsiConference === action.conference.jitsiConference) {
@ -52,10 +53,11 @@ ReducerRegistry.register('features/base/conference',
break;
case CONFERENCE_WILL_LEAVE:
return {
...state,
leavingJitsiConference: action.conference.jitsiConference
};
return (
setStateProperty(
state,
'leavingJitsiConference',
action.conference.jitsiConference));
case SET_ROOM: {
let room = action.room;
@ -63,16 +65,9 @@ ReducerRegistry.register('features/base/conference',
// Technically, there're 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.
if (!isRoomValid(room)) {
room = INITIAL_STATE.room;
}
if (state.room !== room) {
return {
...state,
room
};
}
break;
isRoomValid(room) || (room = INITIAL_STATE.room);
return setStateProperty(state, 'room', room);
}
}

View File

@ -1,8 +1,6 @@
import {
conferenceWillLeave,
createConference
} from '../conference';
import { conferenceWillLeave } from '../conference';
import JitsiMeetJS from '../lib-jitsi-meet';
import {
CONNECTION_DISCONNECTED,
CONNECTION_ESTABLISHED,
@ -21,90 +19,81 @@ const JitsiConnectionEvents = JitsiMeetJS.events.connection;
export function connect() {
return (dispatch, getState) => {
const state = getState();
const connectionOpts
const connectionOptions
= state['features/base/connection'].connectionOptions;
const room = state['features/base/conference'].room;
const connection = new JitsiMeetJS.JitsiConnection(
connectionOpts.appId,
connectionOpts.token,
{
...connectionOpts,
bosh: connectionOpts.bosh + (
room ? `?room=${room}` : ''
)
}
);
const connection
= new JitsiMeetJS.JitsiConnection(
connectionOptions.appId,
connectionOptions.token,
{
...connectionOptions,
bosh: connectionOptions.bosh + (room ? `?room=${room}` : '')
});
return new Promise((resolve, reject) => {
connection.addEventListener(
connection.addEventListener(
JitsiConnectionEvents.CONNECTION_DISCONNECTED,
handleConnectionDisconnected);
connection.addEventListener(
connectionDisconnected);
connection.addEventListener(
JitsiConnectionEvents.CONNECTION_ESTABLISHED,
handleConnectionEstablished);
connection.addEventListener(
connectionEstablished);
connection.addEventListener(
JitsiConnectionEvents.CONNECTION_FAILED,
handleConnectionFailed);
connectionFailed);
connection.connect();
connection.connect();
/**
* Dispatches CONNECTION_DISCONNECTED action when connection is
* disconnected.
*
* @param {string} message - Disconnect reason.
* @returns {void}
*/
function handleConnectionDisconnected(message) {
connection.removeEventListener(
/**
* Dispatches CONNECTION_DISCONNECTED action when connection is
* disconnected.
*
* @param {string} message - Disconnect reason.
* @returns {void}
*/
function connectionDisconnected(message) {
connection.removeEventListener(
JitsiConnectionEvents.CONNECTION_DISCONNECTED,
handleConnectionDisconnected);
connectionDisconnected);
dispatch(_connectionDisconnected(connection, message));
}
dispatch(_connectionDisconnected(connection, message));
}
/**
* Resolves external promise when connection is established.
*
* @returns {void}
*/
function handleConnectionEstablished() {
unsubscribe();
resolve(connection);
}
/**
* Resolves external promise when connection is established.
*
* @returns {void}
*/
function connectionEstablished() {
unsubscribe();
dispatch(_connectionEstablished(connection));
}
/**
* Rejects external promise when connection fails.
*
* @param {JitsiConnectionErrors} err - Connection error.
* @returns {void}
*/
function handleConnectionFailed(err) {
unsubscribe();
console.error('CONNECTION FAILED:', err);
reject(err);
}
/**
* Rejects external promise when connection fails.
*
* @param {JitsiConnectionErrors} err - Connection error.
* @returns {void}
*/
function connectionFailed(err) {
unsubscribe();
console.error('CONNECTION FAILED:', err);
dispatch(_connectionFailed(connection, err));
}
/**
* Unsubscribes connection instance from CONNECTION_ESTABLISHED
* and CONNECTION_FAILED events.
*
* @returns {void}
*/
function unsubscribe() {
connection.removeEventListener(
/**
* Unsubscribes connection instance from CONNECTION_ESTABLISHED
* and CONNECTION_FAILED events.
*
* @returns {void}
*/
function unsubscribe() {
connection.removeEventListener(
JitsiConnectionEvents.CONNECTION_ESTABLISHED,
handleConnectionEstablished
);
connection.removeEventListener(
connectionEstablished);
connection.removeEventListener(
JitsiConnectionEvents.CONNECTION_FAILED,
handleConnectionFailed
);
}
})
.catch(err => dispatch(_connectionFailed(err)))
.then(con => dispatch(_connectionEstablished(con)))
.then(() => dispatch(createConference()));
connectionFailed);
}
};
}
@ -162,8 +151,7 @@ export function setDomain(domain) {
/**
* Create an action for when the signaling connection has been lost.
*
* @param {JitsiConnection} connection - The JitsiConnection which was
* disconnected.
* @param {JitsiConnection} connection - The JitsiConnection which disconnected.
* @param {string} message - Error message.
* @private
* @returns {{
@ -183,9 +171,13 @@ function _connectionDisconnected(connection, message) {
/**
* Create an action for when the signaling connection has been established.
*
* @param {JitsiConnection} connection - JitsiConnection instance.
* @param {JitsiConnection} connection - The JitsiConnection which was
* established.
* @private
* @returns {{type: CONNECTION_ESTABLISHED, connection: JitsiConnection}}
* @returns {{
* type: CONNECTION_ESTABLISHED,
* connection: JitsiConnection
* }}
*/
function _connectionEstablished(connection) {
return {
@ -197,13 +189,19 @@ function _connectionEstablished(connection) {
/**
* Create an action for when the signaling connection could not be created.
*
* @param {JitsiConnection} connection - The JitsiConnection which failed.
* @param {string} error - Error message.
* @private
* @returns {{type: CONNECTION_FAILED, error: string}}
* @returns {{
* type: CONNECTION_FAILED,
* connection: JitsiConnection,
* error: string
* }}
*/
function _connectionFailed(error) {
function _connectionFailed(connection, error) {
return {
type: CONNECTION_FAILED,
connection,
error
};
}

View File

@ -1,4 +1,4 @@
import { ReducerRegistry } from '../redux';
import { ReducerRegistry, setStateProperty } from '../redux';
import {
CONNECTION_DISCONNECTED,
@ -7,62 +7,64 @@ import {
} from './actionTypes';
/**
* Initial Redux state.
*
* @type {{
* jitsiConnection: (JitsiConnection|null),
* connectionOptions: Object
* }}
* Reduces the Redux actions of the feature base/connection.
*/
const INITIAL_STATE = {
jitsiConnection: null,
connectionOptions: null
};
ReducerRegistry.register('features/base/connection', (state = {}, action) => {
switch (action.type) {
case CONNECTION_DISCONNECTED:
return _connectionDisconnected(state, action);
case CONNECTION_ESTABLISHED:
return _connectionEstablished(state, action);
case SET_DOMAIN:
return _setDomain(state, action);
}
return state;
});
/**
* Listen for actions that contain the connection object, so that
* it can be stored for use by other action creators.
* Reduces a specific Redux action CONNECTION_DISCONNECTED of the feature
* base/connection.
*
* @param {Object} state - The Redux state of the feature base/connection.
* @param {Action} action - The Redux action CONNECTION_DISCONNECTED to reduce.
* @private
* @returns {Object} The new state of the feature base/connection after the
* reduction of the specified action.
*/
ReducerRegistry.register('features/base/connection',
(state = INITIAL_STATE, action) => {
switch (action.type) {
case CONNECTION_DISCONNECTED:
if (state.jitsiConnection === action.connection) {
return {
...state,
jitsiConnection: null
};
}
function _connectionDisconnected(state, action) {
if (state.jitsiConnection === action.connection) {
return setStateProperty(state, 'jitsiConnection', undefined);
}
return state;
case CONNECTION_ESTABLISHED:
return {
...state,
jitsiConnection: action.connection
};
case SET_DOMAIN:
return {
...state,
connectionOptions: {
...state.connectionOptions,
...buildConnectionOptions(action.domain)
}
};
default:
return state;
}
});
return state;
}
/**
* Builds connection options based on domain.
* Reduces a specific Redux action CONNECTION_ESTABLISHED of the feature
* base/connection.
*
* @param {string} domain - Domain name.
* @param {Object} state - The Redux state of the feature base/connection.
* @param {Action} action - The Redux action CONNECTION_ESTABLISHED to reduce.
* @private
* @returns {Object} The new state of the feature base/connection after the
* reduction of the specified action.
*/
function _connectionEstablished(state, action) {
return setStateProperty(state, 'jitsiConnection', action.connection);
}
/**
* Constructs options to be passed to the constructor of JitsiConnection based
* on a specific domain.
*
* @param {string} domain - The domain with which the returned options are to be
* populated.
* @returns {Object}
*/
function buildConnectionOptions(domain) {
function _constructConnectionOptions(domain) {
// FIXME The HTTPS scheme for the BOSH URL works with meet.jit.si on both
// mobile & Web. It also works with beta.meet.jit.si on Web. Unfortunately,
// it doesn't work with beta.meet.jit.si on mobile. Temporarily, use the
@ -79,15 +81,11 @@ function buildConnectionOptions(domain) {
boshProtocol = windowLocation.protocol;
}
}
if (!boshProtocol) {
boshProtocol = 'http:';
}
boshProtocol || (boshProtocol = 'http:');
}
// Default to the HTTPS scheme for the BOSH URL.
if (!boshProtocol) {
boshProtocol = 'https:';
}
boshProtocol || (boshProtocol = 'https:');
return {
bosh: `${boshProtocol}//${domain}/http-bind`,
@ -98,3 +96,22 @@ function buildConnectionOptions(domain) {
}
};
}
/**
* Reduces a specific Redux action SET_DOMAIN of the feature base/connection.
*
* @param {Object} state - The Redux state of the feature base/connection.
* @param {Action} action - The Redux action SET_DOMAIN to reduce.
* @private
* @returns {Object} The new state of the feature base/connection after the
* reduction of the specified action.
*/
function _setDomain(state, action) {
return {
...state,
connectionOptions: {
...state.connectionOptions,
..._constructConnectionOptions(action.domain)
}
};
}

View File

@ -1,6 +1,6 @@
/* global MD5 */
import { ReducerRegistry } from '../redux';
import { ReducerRegistry, setStateProperty } from '../redux';
import {
DOMINANT_SPEAKER_CHANGED,
@ -55,7 +55,7 @@ function participant(state, action) {
case DOMINANT_SPEAKER_CHANGED:
// Only one dominant speaker is allowed.
return (
_setStateProperty(
setStateProperty(
state,
'dominantSpeaker',
state.id === action.participant.id));
@ -123,7 +123,7 @@ function participant(state, action) {
case PIN_PARTICIPANT:
// Currently, only one pinned participant is allowed.
return (
_setStateProperty(
setStateProperty(
state,
'pinned',
state.id === action.participant.id));
@ -201,30 +201,3 @@ function _getAvatarURL(participantId, email) {
return urlPref + avatarId + urlSuf;
}
/**
* Sets a specific property of a specific state to a specific value. Prevents
* unnecessary state changes (when the specified <tt>value</tt> is equal to the
* value of the specified <tt>property</tt> of the specified <tt>state</tt>).
*
* @param {Object} state - The (Redux) state from which a new state is to be
* constructed by setting the specified <tt>property</tt> to the specified
* <tt>value</tt>.
* @param {string} property - The property of <tt>state</tt> which is to be
* assigned the specified <tt>value</tt> (in the new state).
* @param {*} value - The value to assign to the specified <tt>property</tt>.
* @returns {Object} The specified <tt>state</tt> if the value of the specified
* <tt>property</tt> equals the specified <tt>value/tt>; otherwise, a new state
* constructed from the specified <tt>state</tt> by setting the specified
* <tt>property</tt> to the specified <tt>value</tt>.
*/
function _setStateProperty(state, property, value) {
if (state[property] !== value) {
return {
...state,
[property]: value
};
}
return state;
}

View File

@ -0,0 +1,37 @@
/**
* Sets a specific property of a specific state to a specific value. Prevents
* unnecessary state changes (when the specified <tt>value</tt> is equal to the
* value of the specified <tt>property</tt> of the specified <tt>state</tt>).
*
* @param {Object} state - The (Redux) state from which a new state is to be
* constructed by setting the specified <tt>property</tt> to the specified
* <tt>value</tt>.
* @param {string} property - The property of <tt>state</tt> which is to be
* assigned the specified <tt>value</tt> (in the new state).
* @param {*} value - The value to assign to the specified <tt>property</tt>.
* @returns {Object} The specified <tt>state</tt> if the value of the specified
* <tt>property</tt> equals the specified <tt>value/tt>; otherwise, a new state
* constructed from the specified <tt>state</tt> by setting the specified
* <tt>property</tt> to the specified <tt>value</tt>.
*/
export function setStateProperty(state, property, value) {
// 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 };
if (delete newState[property]) {
return newState;
}
}
if (state[property] !== value) {
return {
...state,
[property]: value
};
}
return state;
}

View File

@ -1,2 +1,3 @@
export * from './functions';
export { default as MiddlewareRegistry } from './MiddlewareRegistry';
export { default as ReducerRegistry } from './ReducerRegistry';

View File

@ -109,7 +109,8 @@ function _mutedChanged(store, action, mediaType) {
* @param {Action} action - The Redux action <tt>TRACK_UPDATED</tt> which is
* being dispatched in the specified <tt>store</tt>.
* @private
* @returns {void}
* @returns {Object} The new state that is the result of the reduction of the
* specified <tt>action</tt>.
*/
function _trackUpdated(store, next, action) {
// Determine the muted state of the local track before the update.

View File

@ -95,7 +95,6 @@ class FilmStrip extends Component {
* @param {Object} state - Redux state.
* @returns {{
* participants: Participant[],
* tracks: (JitsiLocalTrack|JitsiRemoteTrack)[]
* }}
*/
function mapStateToProps(state) {