Fix toolbar's mute buttons when starting muted

The toolbar's mute buttons depict respective features/base/media states.
However, (un)muting is practically carried out by features/base/tracks.
When the mobile app enters a conference configured to invite the joining
participant to mute themselves, the tracks would be muted but the
toolbar's mute buttons would not reflect that.
This commit is contained in:
Lyubomir Marinov 2016-10-25 11:43:15 -05:00
parent f271eb4610
commit 4997ae79e3
7 changed files with 107 additions and 31 deletions

View File

@ -34,7 +34,7 @@ export function disposeLib() {
*/
export function initLib() {
return (dispatch, getState) => {
const config = getState()['features/base/lib'].config;
const config = getState()['features/base/lib-jitsi-meet'].config;
if (!config) {
throw new Error('Cannot initialize lib-jitsi-meet without config');

View File

@ -19,26 +19,23 @@ import { SET_CONFIG } from './actionTypes';
MiddlewareRegistry.register(store => next => action => {
switch (action.type) {
case PARTICIPANT_LEFT:
if (action.participant.local) {
store.dispatch(disposeLib());
}
action.participant.local && store.dispatch(disposeLib());
break;
case SET_CONFIG: {
const { dispatch, getState } = store;
const libInitialized = getState()['features/base/lib'].initialized;
const initialized
= getState()['features/base/lib-jitsi-meet'].initialized;
// XXX If we already have config, that means new config is coming, which
// means that we should dispose instance of lib initialized with
// previous config first.
// TODO Currently 'disposeLib' actually does not dispose lib-jitsi-meet.
// This functionality should be implemented.
const promise = libInitialized
? dispatch(disposeLib())
: Promise.resolve();
const promise
= initialized ? dispatch(disposeLib()) : Promise.resolve();
promise
.then(dispatch(initLib()));
promise.then(dispatch(initLib()));
break;
}

View File

@ -8,7 +8,7 @@ import {
} from './actionTypes';
/**
* Initial state of 'features/base/lib'.
* Initial state of 'features/base/lib-jitsi-meet'.
*
* @type {{
* initializationError: null,
@ -31,7 +31,7 @@ const INITIAL_STATE = {
};
ReducerRegistry.register(
'features/base/lib',
'features/base/lib-jitsi-meet',
(state = INITIAL_STATE, action) => {
switch (action.type) {
case LIB_DISPOSED:

View File

@ -42,15 +42,10 @@ function resetInitialMediaState(store) {
const { dispatch, getState } = store;
const state = getState()['features/base/media'];
if (state.audio.muted) {
dispatch(audioMutedChanged(false));
}
if (state.video.facingMode !== CAMERA_FACING_MODE.USER) {
dispatch(cameraFacingModeChanged(CAMERA_FACING_MODE.USER));
}
if (state.video.muted) {
dispatch(videoMutedChanged(false));
}
state.audio.muted && dispatch(audioMutedChanged(false));
(state.video.facingMode !== CAMERA_FACING_MODE.USER)
&& dispatch(cameraFacingModeChanged(CAMERA_FACING_MODE.USER));
state.video.muted && dispatch(videoMutedChanged(false));
}
/**

View File

@ -72,14 +72,18 @@ export function getTracksByMediaType(tracks, mediaType) {
}
/**
* Mute or unmute local track if any.
* Mutes or unmutes a specific <tt>JitsiLocalTrack</tt>. If the muted state of
* the specified <tt>track</tt> is already in accord with the specified
* <tt>muted</tt> value, then does nothing.
*
* @param {JitsiLocalTrack} track - Track instance.
* @param {boolean} muted - If audio stream should be muted or unmuted.
* @param {JitsiLocalTrack} track - The <tt>JitsiLocalTrack</tt> to mute or
* unmute.
* @param {boolean} muted - If the specified <tt>track</tt> is to be muted, then
* <tt>true</tt>; otherwise, <tt>false</tt>.
* @returns {Promise}
*/
export function setTrackMuted(track, muted) {
if (!track) {
if (track.isMuted() === muted) {
return Promise.resolve();
}

View File

@ -4,9 +4,11 @@ import {
} from '../lib-jitsi-meet';
import {
AUDIO_MUTED_CHANGED,
audioMutedChanged,
CAMERA_FACING_MODE_CHANGED,
MEDIA_TYPE,
VIDEO_MUTED_CHANGED
VIDEO_MUTED_CHANGED,
videoMutedChanged
} from '../media';
import { MiddlewareRegistry } from '../redux';
@ -14,6 +16,7 @@ import {
createLocalTracks,
destroyLocalTracks
} from './actions';
import { TRACK_UPDATED } from './actionTypes';
import {
getLocalTrack,
setTrackMuted
@ -50,6 +53,9 @@ MiddlewareRegistry.register(store => next => action => {
store.dispatch(destroyLocalTracks());
break;
case TRACK_UPDATED:
return _trackUpdated(store, next, action);
case VIDEO_MUTED_CHANGED:
_mutedChanged(store, action, MEDIA_TYPE.VIDEO);
break;
@ -58,6 +64,22 @@ MiddlewareRegistry.register(store => next => action => {
return next(action);
});
/**
* Gets the local track associated with a specific <tt>MEDIA_TYPE</tt> in a
* specific Redux store.
*
* @param {Store} store - The Redux store from which the local track associated
* with the specified <tt>mediaType</tt> is to be retrieved.
* @param {MEDIA_TYPE} mediaType - The <tt>MEDIA_TYPE</tt> of the local track to
* be retrieved from the specified <tt>store</tt>.
* @private
* @returns {Track} The local <tt>Track</tt> associated with the specified
* <tt>mediaType</tt> in the specified <tt>store</tt>.
*/
function _getLocalTrack(store, mediaType) {
return getLocalTrack(store.getState()['features/base/tracks'], mediaType);
}
/**
* Mutes or unmutes a local track with a specific media type.
*
@ -70,8 +92,67 @@ MiddlewareRegistry.register(store => next => action => {
* @returns {void}
*/
function _mutedChanged(store, action, mediaType) {
const tracks = store.getState()['features/base/tracks'];
const localTrack = getLocalTrack(tracks, mediaType);
const localTrack = _getLocalTrack(store, mediaType);
localTrack && setTrackMuted(localTrack.jitsiTrack, action.muted);
}
/**
* Intercepts the action <tt>TRACK_UPDATED</tt> in order to synchronize the
* muted states of the local tracks of features/base/tracks with the muted
* states of features/base/media.
*
* @param {Store} store - The Redux store in which the specified <tt>action</tt>
* is being dispatched.
* @param {Dispatch} next - The Redux dispatch function to dispatch the
* specified <tt>action</tt> to the specified <tt>store</tt>.
* @param {Action} action - The Redux action <tt>TRACK_UPDATED</tt> which is
* being dispatched in the specified <tt>store</tt>.
* @private
* @returns {void}
*/
function _trackUpdated(store, next, action) {
// Determine the muted state of the local track before the update.
const track = action.track;
let mediaType;
let oldMuted;
if ('muted' in track) {
// XXX The return value of JitsiTrack.getType() is of type MEDIA_TYPE
// that happens to be compatible with the type MEDIA_TYPE defined by
// jitsi-meet-react.
mediaType = track.jitsiTrack.getType();
const localTrack = _getLocalTrack(store, mediaType);
if (localTrack) {
oldMuted = localTrack.muted;
}
}
const result = next(action);
if (typeof oldMuted !== 'undefined') {
// Determine the muted state of the local track after the update. If the
// muted states before and after the update differ, then the respective
// media state should by synchronized.
const localTrack = _getLocalTrack(store, mediaType);
if (localTrack) {
const newMuted = localTrack.muted;
if (oldMuted !== newMuted) {
switch (mediaType) {
case MEDIA_TYPE.AUDIO:
store.dispatch(audioMutedChanged(newMuted));
break;
case MEDIA_TYPE.VIDEO:
store.dispatch(videoMutedChanged(newMuted));
break;
}
}
}
}
return result;
}

View File

@ -49,9 +49,8 @@ MiddlewareRegistry.register(store => next => action => {
action.track.jitsiTrack);
const participantId = state['features/largeVideo'].participantId;
if (track.participantId === participantId) {
store.dispatch(selectParticipant());
}
(track.participantId === participantId)
&& store.dispatch(selectParticipant());
}
break;
}