[RN] base/media is intent, base/tracks is reality
This commit is contained in:
parent
d600504d85
commit
85a168d51b
|
@ -62,13 +62,17 @@ function _addConferenceListeners(conference, dispatch) {
|
|||
|
||||
// Dispatches into features/base/media follow:
|
||||
|
||||
// FIXME: This is needed because when Jicofo tells us to start muted
|
||||
// lib-jitsi-meet does the actual muting. Perhaps this should be refactored
|
||||
// so applications are hinted to start muted, but lib-jitsi-meet doesn't
|
||||
// take action.
|
||||
conference.on(
|
||||
JitsiConferenceEvents.STARTED_MUTED,
|
||||
() => {
|
||||
// XXX Jicofo tells lib-jitsi-meet to start with audio and/or video
|
||||
// muted i.e. Jicofo expresses an intent. Lib-jitsi-meet has turned
|
||||
// Jicofo's intent into reality by actually muting the respective
|
||||
// tracks. The reality is expressed in base/tracks already so what
|
||||
// is left is to express Jicofo's intent in base/media.
|
||||
// TODO Maybe the app needs to learn about Jicofo's intent and
|
||||
// transfer that intent to lib-jitsi-meet instead of lib-jitsi-meet
|
||||
// acting on Jicofo's intent without the app's knowledge.
|
||||
dispatch(setAudioMuted(Boolean(conference.startAudioMuted)));
|
||||
dispatch(setVideoMuted(Boolean(conference.startVideoMuted)));
|
||||
});
|
||||
|
|
|
@ -32,7 +32,7 @@ import {
|
|||
/**
|
||||
* Implements the middleware of the feature base/conference.
|
||||
*
|
||||
* @param {Store} store - Redux store.
|
||||
* @param {Store} store - The redux store.
|
||||
* @returns {Function}
|
||||
*/
|
||||
MiddlewareRegistry.register(store => next => action => {
|
||||
|
@ -40,13 +40,13 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
case CONNECTION_ESTABLISHED:
|
||||
return _connectionEstablished(store, next, action);
|
||||
|
||||
case CONFERENCE_JOINED:
|
||||
return _conferenceJoined(store, next, action);
|
||||
|
||||
case CONFERENCE_FAILED:
|
||||
case CONFERENCE_LEFT:
|
||||
return _conferenceFailedOrLeft(store, next, action);
|
||||
|
||||
case CONFERENCE_JOINED:
|
||||
return _conferenceJoined(store, next, action);
|
||||
|
||||
case PIN_PARTICIPANT:
|
||||
return _pinParticipant(store, next, action);
|
||||
|
||||
|
@ -66,13 +66,13 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
|
||||
/**
|
||||
* Notifies the feature base/conference that the action CONNECTION_ESTABLISHED
|
||||
* is being dispatched within a specific Redux store.
|
||||
* is being dispatched within a specific redux store.
|
||||
*
|
||||
* @param {Store} store - The Redux store in which the specified action is being
|
||||
* @param {Store} store - The redux store in which the specified action is being
|
||||
* dispatched.
|
||||
* @param {Dispatch} next - The Redux dispatch function to dispatch the
|
||||
* @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
|
||||
* @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
|
||||
|
@ -91,37 +91,37 @@ function _connectionEstablished(store, next, action) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Does extra sync up on properties that may need to be updated, after
|
||||
* the conference failed or was left.
|
||||
* Does extra sync up on properties that may need to be updated after the
|
||||
* conference failed or was left.
|
||||
*
|
||||
* @param {Store} store - The Redux store in which the specified action is being
|
||||
* @param {Store} store - The redux store in which the specified action is being
|
||||
* dispatched.
|
||||
* @param {Dispatch} next - The Redux dispatch function to dispatch the
|
||||
* @param {Dispatch} next - The redux dispatch function to dispatch the
|
||||
* specified action to the specified store.
|
||||
* @param {Action} action - The Redux action {@link CONFERENCE_FAILED} or
|
||||
* @param {Action} action - The redux action {@link CONFERENCE_FAILED} or
|
||||
* {@link CONFERENCE_LEFT} 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 _conferenceFailedOrLeft(store, next, action) {
|
||||
function _conferenceFailedOrLeft({ dispatch, getState }, next, action) {
|
||||
const result = next(action);
|
||||
const { audioOnly } = store.getState()['features/base/conference'];
|
||||
|
||||
audioOnly && store.dispatch(setAudioOnly(false));
|
||||
getState()['features/base/conference'].audioOnly
|
||||
&& dispatch(setAudioOnly(false));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does extra sync up on properties that may need to be updated, after
|
||||
* the conference was joined.
|
||||
* Does extra sync up on properties that may need to be updated after the
|
||||
* conference was joined.
|
||||
*
|
||||
* @param {Store} store - The Redux store in which the specified action is being
|
||||
* @param {Store} store - The redux store in which the specified action is being
|
||||
* dispatched.
|
||||
* @param {Dispatch} next - The Redux dispatch function to dispatch the
|
||||
* @param {Dispatch} next - The redux dispatch function to dispatch the
|
||||
* specified action to the specified store.
|
||||
* @param {Action} action - The Redux action CONFERENCE_JOINED which is being
|
||||
* @param {Action} action - The redux action CONFERENCE_JOINED which is being
|
||||
* dispatched in the specified store.
|
||||
* @private
|
||||
* @returns {Object} The new state that is the result of the reduction of the
|
||||
|
@ -144,14 +144,14 @@ function _conferenceJoined(store, next, action) {
|
|||
|
||||
/**
|
||||
* Notifies the feature base/conference that the action PIN_PARTICIPANT is being
|
||||
* dispatched within a specific Redux store. Pins the specified remote
|
||||
* 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
|
||||
* @param {Store} store - The redux store in which the specified action is being
|
||||
* dispatched.
|
||||
* @param {Dispatch} next - The Redux dispatch function to dispatch the
|
||||
* @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
|
||||
* @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
|
||||
|
@ -195,26 +195,26 @@ function _pinParticipant(store, next, action) {
|
|||
* Sets the audio-only flag for the current conference. When audio-only is set,
|
||||
* local video is muted and last N is set to 0 to avoid receiving remote video.
|
||||
*
|
||||
* @param {Store} store - The Redux store in which the specified action is being
|
||||
* @param {Store} store - The redux store in which the specified action is being
|
||||
* dispatched.
|
||||
* @param {Dispatch} next - The Redux dispatch function to dispatch the
|
||||
* @param {Dispatch} next - The redux dispatch function to dispatch the
|
||||
* specified action to the specified store.
|
||||
* @param {Action} action - The Redux action SET_AUDIO_ONLY which is being
|
||||
* @param {Action} action - The redux action SET_AUDIO_ONLY 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 _setAudioOnly({ dispatch }, next, action) {
|
||||
function _setAudioOnly({ dispatch, getState }, next, action) {
|
||||
const result = next(action);
|
||||
|
||||
const { audioOnly } = action;
|
||||
const { audioOnly } = getState()['features/base/conference'];
|
||||
|
||||
// Set lastN to 0 in case audio-only is desired; leave it as undefined,
|
||||
// otherwise, and the default lastN value will be chosen automatically.
|
||||
dispatch(setLastN(audioOnly ? 0 : undefined));
|
||||
|
||||
// Mute the local video.
|
||||
// Mute/unmute the local video.
|
||||
dispatch(setVideoMuted(audioOnly, VIDEO_MUTISM_AUTHORITY.AUDIO_ONLY));
|
||||
|
||||
if (typeof APP !== 'undefined') {
|
||||
|
@ -229,11 +229,11 @@ function _setAudioOnly({ dispatch }, next, action) {
|
|||
/**
|
||||
* Sets the last N (value) of the video channel in the conference.
|
||||
*
|
||||
* @param {Store} store - The Redux store in which the specified action is being
|
||||
* @param {Store} store - The redux store in which the specified action is being
|
||||
* dispatched.
|
||||
* @param {Dispatch} next - The Redux dispatch function to dispatch the
|
||||
* @param {Dispatch} next - The redux dispatch function to dispatch the
|
||||
* specified action to the specified store.
|
||||
* @param {Action} action - The Redux action SET_LASTN which is being dispatched
|
||||
* @param {Action} action - The redux action SET_LASTN which is being dispatched
|
||||
* in the specified store.
|
||||
* @private
|
||||
* @returns {Object} The new state that is the result of the reduction of the
|
||||
|
@ -257,7 +257,7 @@ function _setLastN(store, next, action) {
|
|||
* Synchronizes local tracks from state with local tracks in JitsiConference
|
||||
* instance.
|
||||
*
|
||||
* @param {Store} store - Redux store.
|
||||
* @param {Store} store - The redux store.
|
||||
* @param {Object} action - Action object.
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
|
@ -284,13 +284,13 @@ function _syncConferenceLocalTracksWithState(store, action) {
|
|||
|
||||
/**
|
||||
* Notifies the feature base/conference that the action TRACK_ADDED
|
||||
* or TRACK_REMOVED is being dispatched within a specific Redux store.
|
||||
* or TRACK_REMOVED is being dispatched within a specific redux store.
|
||||
*
|
||||
* @param {Store} store - The Redux store in which the specified action is being
|
||||
* @param {Store} store - The redux store in which the specified action is being
|
||||
* dispatched.
|
||||
* @param {Dispatch} next - The Redux dispatch function to dispatch the
|
||||
* @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
|
||||
* @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
|
||||
|
|
|
@ -72,16 +72,15 @@ function _setRoom({ dispatch, getState }, next, action) {
|
|||
&& (videoMuted = config.startWithVideoMuted);
|
||||
|
||||
// Apply startWithAudioMuted and startWithVideoMuted.
|
||||
const { audio, video } = state['features/base/media'];
|
||||
|
||||
audioMuted = Boolean(audioMuted);
|
||||
videoMuted = Boolean(videoMuted);
|
||||
|
||||
(audio.muted !== audioMuted) && dispatch(setAudioMuted(audioMuted));
|
||||
(video.facingMode !== CAMERA_FACING_MODE.USER)
|
||||
&& dispatch(setCameraFacingMode(CAMERA_FACING_MODE.USER));
|
||||
(Boolean(video.muted) !== videoMuted)
|
||||
&& dispatch(setVideoMuted(videoMuted));
|
||||
// Unconditionally express the desires/expectations/intents of the app and
|
||||
// the user i.e. the state of base/media. Eventually, practice/reality i.e.
|
||||
// the state of base/tracks will or will not agree with the desires.
|
||||
dispatch(setAudioMuted(audioMuted));
|
||||
dispatch(setCameraFacingMode(CAMERA_FACING_MODE.USER));
|
||||
dispatch(setVideoMuted(videoMuted));
|
||||
|
||||
return next(action);
|
||||
}
|
||||
|
|
|
@ -87,19 +87,21 @@ export function destroyLocalTracks() {
|
|||
*/
|
||||
export function replaceLocalTrack(oldTrack, newTrack, conference) {
|
||||
return (dispatch, getState) => {
|
||||
const currentConference = conference
|
||||
|| getState()['features/base/conference'].conference;
|
||||
conference
|
||||
|
||||
return currentConference.replaceTrack(oldTrack, newTrack)
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
|| (conference = getState()['features/base/conference'].conference);
|
||||
|
||||
return conference.replaceTrack(oldTrack, newTrack)
|
||||
.then(() => {
|
||||
// We call dispose after doing the replace because
|
||||
// dispose will try and do a new o/a after the
|
||||
// track removes itself. Doing it after means
|
||||
// the JitsiLocalTrack::conference member is already
|
||||
// cleared, so it won't try and do the o/a
|
||||
const disposePromise = oldTrack
|
||||
? dispatch(_disposeAndRemoveTracks([ oldTrack ]))
|
||||
: Promise.resolve();
|
||||
// We call dispose after doing the replace because dispose will
|
||||
// try and do a new o/a after the track removes itself. Doing it
|
||||
// after means the JitsiLocalTrack.conference is already
|
||||
// cleared, so it won't try and do the o/a.
|
||||
const disposePromise
|
||||
= oldTrack
|
||||
? dispatch(_disposeAndRemoveTracks([ oldTrack ]))
|
||||
: Promise.resolve();
|
||||
|
||||
return disposePromise
|
||||
.then(() => {
|
||||
|
@ -113,10 +115,12 @@ export function replaceLocalTrack(oldTrack, newTrack, conference) {
|
|||
// track's mute state. If this is not done, the
|
||||
// current mute state of the app will be reflected
|
||||
// on the track, not vice-versa.
|
||||
const muteAction = newTrack.isVideoTrack()
|
||||
? setVideoMuted : setAudioMuted;
|
||||
const setMuted
|
||||
= newTrack.isVideoTrack()
|
||||
? setVideoMuted
|
||||
: setAudioMuted;
|
||||
|
||||
return dispatch(muteAction(newTrack.isMuted()));
|
||||
return dispatch(setMuted(newTrack.isMuted()));
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
|
@ -366,17 +370,26 @@ export function setTrackMuted(track, muted) {
|
|||
|
||||
const f = muted ? 'mute' : 'unmute';
|
||||
|
||||
// FIXME: This operation disregards the authority. It is not a problem
|
||||
// (on mobile) at the moment, but it will be once we start not creating
|
||||
// tracks early. Refactor this then.
|
||||
return track[f]().catch(error => {
|
||||
console.error(`set track ${f} failed`, error);
|
||||
|
||||
if (navigator.product === 'ReactNative') {
|
||||
// Synchronizing the state of base/tracks into the state of
|
||||
// base/media is not required in React (and, respectively, React
|
||||
// Native) because base/media expresses the app's and the user's
|
||||
// desires/expectations/intents and base/tracks expresses
|
||||
// practice/reality. Unfortunately, the old Web does not comply
|
||||
// and/or does the opposite.
|
||||
return;
|
||||
}
|
||||
|
||||
const setMuted
|
||||
= track.mediaType === MEDIA_TYPE.AUDIO
|
||||
? setAudioMuted
|
||||
: setVideoMuted;
|
||||
|
||||
// FIXME The following disregards VIDEO_MUTISM_AUTHORITY (in the
|
||||
// case of setVideoMuted, of course).
|
||||
dispatch(setMuted(!muted));
|
||||
});
|
||||
};
|
||||
|
|
|
@ -100,35 +100,37 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
break;
|
||||
|
||||
case TRACK_UPDATED:
|
||||
// TODO Remove the below calls to APP.UI once components interested in
|
||||
// track mute changes are moved into react.
|
||||
// TODO Remove the following calls to APP.UI once components interested
|
||||
// in track mute changes are moved into React and/or redux.
|
||||
if (typeof APP !== 'undefined') {
|
||||
const { jitsiTrack } = action.track;
|
||||
const isMuted = jitsiTrack.isMuted();
|
||||
const muted = jitsiTrack.isMuted();
|
||||
const participantID = jitsiTrack.getParticipantId();
|
||||
const isVideoTrack = jitsiTrack.isVideoTrack();
|
||||
|
||||
if (jitsiTrack.isLocal()) {
|
||||
if (isVideoTrack) {
|
||||
APP.conference.videoMuted = isMuted;
|
||||
APP.conference.videoMuted = muted;
|
||||
} else {
|
||||
APP.conference.audioMuted = isMuted;
|
||||
APP.conference.audioMuted = muted;
|
||||
}
|
||||
}
|
||||
|
||||
if (isVideoTrack) {
|
||||
APP.UI.setVideoMuted(participantID, isMuted);
|
||||
APP.UI.setVideoMuted(participantID, muted);
|
||||
APP.UI.onPeerVideoTypeChanged(
|
||||
participantID, jitsiTrack.videoType);
|
||||
participantID,
|
||||
jitsiTrack.videoType);
|
||||
} else {
|
||||
APP.UI.setAudioMuted(participantID, isMuted);
|
||||
APP.UI.setAudioMuted(participantID, muted);
|
||||
}
|
||||
|
||||
// XXX This function synchronizes track states with media states.
|
||||
// This is not required in React, because media is the source of
|
||||
// truth, synchronization should always happen in the media -> track
|
||||
// direction. The old web, however, does the opposite, hence the
|
||||
// need for this.
|
||||
// XXX The following synchronizes the state of base/tracks into the
|
||||
// state of base/media. Which is not required in React (and,
|
||||
// respectively, React Native) because base/media expresses the
|
||||
// app's and the user's desires/expectations/intents and base/tracks
|
||||
// expresses practice/reality. Unfortunately, the old Web does not
|
||||
// comply and/or does the opposite. Hence, the following:
|
||||
return _trackUpdated(store, next, action);
|
||||
}
|
||||
|
||||
|
@ -149,8 +151,8 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
* @returns {Track} The local <tt>Track</tt> associated with the specified
|
||||
* <tt>mediaType</tt> in the specified <tt>store</tt>.
|
||||
*/
|
||||
function _getLocalTrack(store, mediaType: MEDIA_TYPE) {
|
||||
return getLocalTrack(store.getState()['features/base/tracks'], mediaType);
|
||||
function _getLocalTrack({ getState }, mediaType: MEDIA_TYPE) {
|
||||
return getLocalTrack(getState()['features/base/tracks'], mediaType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,10 +16,10 @@ import { MiddlewareRegistry } from '../../base/redux';
|
|||
* based on the type of conference. Audio-only conferences don't use the speaker
|
||||
* by default, and video conferences do.
|
||||
*
|
||||
* @param {Store} store - Redux store.
|
||||
* @param {Store} store - The redux store.
|
||||
* @returns {Function}
|
||||
*/
|
||||
MiddlewareRegistry.register(store => next => action => {
|
||||
MiddlewareRegistry.register(({ getState }) => next => action => {
|
||||
const AudioMode = NativeModules.AudioMode;
|
||||
|
||||
if (AudioMode) {
|
||||
|
@ -32,34 +32,20 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
mode = AudioMode.DEFAULT;
|
||||
break;
|
||||
|
||||
case CONFERENCE_WILL_JOIN: {
|
||||
const { audioOnly } = store.getState()['features/base/conference'];
|
||||
|
||||
mode = audioOnly ? AudioMode.AUDIO_CALL : AudioMode.VIDEO_CALL;
|
||||
break;
|
||||
}
|
||||
|
||||
case CONFERENCE_WILL_JOIN:
|
||||
case SET_AUDIO_ONLY: {
|
||||
const { conference } = store.getState()['features/base/conference'];
|
||||
|
||||
if (conference) {
|
||||
if (getState()['features/base/conference'].conference
|
||||
|| action.conference) {
|
||||
mode
|
||||
= action.audioOnly
|
||||
? AudioMode.AUDIO_CALL
|
||||
: AudioMode.VIDEO_CALL;
|
||||
} else {
|
||||
mode = null;
|
||||
? AudioMode.AUDIO_CALL
|
||||
: AudioMode.VIDEO_CALL;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
mode = null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (mode !== null) {
|
||||
if (typeof mode !== 'undefined') {
|
||||
AudioMode.setMode(mode)
|
||||
.catch(err =>
|
||||
console.error(
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
import { setLastN } from '../../base/conference';
|
||||
import { setVideoMuted, VIDEO_MUTISM_AUTHORITY } from '../../base/media';
|
||||
|
||||
import {
|
||||
_SET_APP_STATE_LISTENER,
|
||||
APP_STATE_CHANGED
|
||||
} from './actionTypes';
|
||||
import { _SET_APP_STATE_LISTENER, APP_STATE_CHANGED } from './actionTypes';
|
||||
|
||||
/**
|
||||
* Sets the listener to be used with React Native's AppState API.
|
||||
|
@ -41,7 +38,7 @@ export function _setBackgroundVideoMuted(muted: boolean) {
|
|||
// for last N will be chosen automatically.
|
||||
const { audioOnly } = getState()['features/base/conference'];
|
||||
|
||||
!audioOnly && dispatch(setLastN(muted ? 0 : undefined));
|
||||
audioOnly || dispatch(setLastN(muted ? 0 : undefined));
|
||||
dispatch(setVideoMuted(muted, VIDEO_MUTISM_AUTHORITY.BACKGROUND));
|
||||
};
|
||||
}
|
||||
|
|
|
@ -22,29 +22,31 @@ import { MiddlewareRegistry } from '../../base/redux';
|
|||
* In immersive mode the status and navigation bars are hidden and thus the
|
||||
* entire screen will be covered by our application.
|
||||
*
|
||||
* @param {Store} store - Redux store.
|
||||
* @param {Store} store - The redux store.
|
||||
* @returns {Function}
|
||||
*/
|
||||
MiddlewareRegistry.register(store => next => action => {
|
||||
MiddlewareRegistry.register(({ getState }) => next => action => {
|
||||
const result = next(action);
|
||||
|
||||
let fullScreen = null;
|
||||
|
||||
switch (action.type) {
|
||||
case APP_STATE_CHANGED: {
|
||||
// Check if we just came back from the background and reenable full
|
||||
case APP_STATE_CHANGED:
|
||||
case CONFERENCE_WILL_JOIN:
|
||||
case HIDE_DIALOG:
|
||||
case SET_AUDIO_ONLY: {
|
||||
// Check if we just came back from the background and re-enable full
|
||||
// screen mode if necessary.
|
||||
if (action.appState === 'active') {
|
||||
const { audioOnly, conference }
|
||||
= store.getState()['features/base/conference'];
|
||||
const { appState } = action;
|
||||
|
||||
fullScreen = conference ? !audioOnly : false;
|
||||
if (typeof appState !== 'undefined' && appState !== 'active') {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CONFERENCE_WILL_JOIN: {
|
||||
const { audioOnly } = store.getState()['features/base/conference'];
|
||||
const { audioOnly, conference }
|
||||
= getState()['features/base/conference'];
|
||||
|
||||
fullScreen = !audioOnly;
|
||||
fullScreen = conference || action.conference ? !audioOnly : false;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -52,21 +54,6 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
case CONFERENCE_LEFT:
|
||||
fullScreen = false;
|
||||
break;
|
||||
|
||||
case HIDE_DIALOG: {
|
||||
const { audioOnly, conference }
|
||||
= store.getState()['features/base/conference'];
|
||||
|
||||
fullScreen = conference ? !audioOnly : false;
|
||||
break;
|
||||
}
|
||||
|
||||
case SET_AUDIO_ONLY: {
|
||||
const { conference } = store.getState()['features/base/conference'];
|
||||
|
||||
fullScreen = conference ? !action.audioOnly : false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fullScreen !== null) {
|
||||
|
@ -75,7 +62,7 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
console.warn(`Failed to set full screen mode: ${err}`));
|
||||
}
|
||||
|
||||
return next(action);
|
||||
return result;
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
|
@ -14,32 +14,29 @@ import { MiddlewareRegistry } from '../../base/redux';
|
|||
* the screen and disable touch controls when an object is nearby. The
|
||||
* functionality is enabled when a conference is in audio-only mode.
|
||||
*
|
||||
* @param {Store} store - Redux store.
|
||||
* @param {Store} store - The redux store.
|
||||
* @returns {Function}
|
||||
*/
|
||||
MiddlewareRegistry.register(store => next => action => {
|
||||
MiddlewareRegistry.register(({ getState }) => next => action => {
|
||||
const result = next(action);
|
||||
|
||||
switch (action.type) {
|
||||
case CONFERENCE_JOINED: {
|
||||
const { audioOnly } = store.getState()['features/base/conference'];
|
||||
|
||||
_setProximityEnabled(audioOnly);
|
||||
break;
|
||||
}
|
||||
|
||||
case CONFERENCE_FAILED:
|
||||
case CONFERENCE_LEFT:
|
||||
_setProximityEnabled(false);
|
||||
break;
|
||||
|
||||
case CONFERENCE_JOINED:
|
||||
case SET_AUDIO_ONLY: {
|
||||
const { conference } = store.getState()['features/base/conference'];
|
||||
const { audioOnly, conference }
|
||||
= getState()['features/base/conference'];
|
||||
|
||||
conference && _setProximityEnabled(action.audioOnly);
|
||||
conference && _setProximityEnabled(audioOnly);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return next(action);
|
||||
return result;
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,7 +3,12 @@ import { View } from 'react-native';
|
|||
import { connect } from 'react-redux';
|
||||
|
||||
import { toggleAudioOnly } from '../../base/conference';
|
||||
import { MEDIA_TYPE, toggleCameraFacingMode } from '../../base/media';
|
||||
import {
|
||||
MEDIA_TYPE,
|
||||
setAudioMuted,
|
||||
setVideoMuted,
|
||||
toggleCameraFacingMode
|
||||
} from '../../base/media';
|
||||
import { Container } from '../../base/react';
|
||||
import { ColorPalette } from '../../base/styles';
|
||||
import { beginRoomLockRequest } from '../../room-lock';
|
||||
|
@ -56,11 +61,6 @@ class Toolbox extends Component {
|
|||
*/
|
||||
_onShareRoom: React.PropTypes.func,
|
||||
|
||||
/**
|
||||
* Handler for toggle audio.
|
||||
*/
|
||||
_onToggleAudio: React.PropTypes.func,
|
||||
|
||||
/**
|
||||
* Toggles the audio-only flag of the conference.
|
||||
*/
|
||||
|
@ -72,11 +72,6 @@ class Toolbox extends Component {
|
|||
*/
|
||||
_onToggleCameraFacingMode: React.PropTypes.func,
|
||||
|
||||
/**
|
||||
* Handler for toggling video.
|
||||
*/
|
||||
_onToggleVideo: React.PropTypes.func,
|
||||
|
||||
/**
|
||||
* Flag showing whether video is muted.
|
||||
*/
|
||||
|
@ -85,9 +80,25 @@ class Toolbox extends Component {
|
|||
/**
|
||||
* Flag showing whether toolbar is visible.
|
||||
*/
|
||||
_visible: React.PropTypes.bool
|
||||
_visible: React.PropTypes.bool,
|
||||
|
||||
dispatch: React.PropTypes.func
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes a new {@code Toolbox} instance.
|
||||
*
|
||||
* @param {Object} props - The read-only React {@code Component} props with
|
||||
* which the new instance is to be initialized.
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
// Bind event handlers so they are only bound once per instance.
|
||||
this._onToggleAudio = this._onToggleAudio.bind(this);
|
||||
this._onToggleVideo = this._onToggleVideo.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
|
@ -144,6 +155,36 @@ class Toolbox extends Component {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an action to toggle the mute state of the audio/microphone.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onToggleAudio() {
|
||||
// The user sees the reality i.e. the state of base/tracks and intends
|
||||
// to change reality by tapping on the respective button i.e. the user
|
||||
// sets the state of base/media. Whether the user's intention will turn
|
||||
// into reality is a whole different story which is of no concern to the
|
||||
// tapping.
|
||||
this.props.dispatch(setAudioMuted(!this.props._audioMuted));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an action to toggle the mute state of the video/camera.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onToggleVideo() {
|
||||
// The user sees the reality i.e. the state of base/tracks and intends
|
||||
// to change reality by tapping on the respective button i.e. the user
|
||||
// sets the state of base/media. Whether the user's intention will turn
|
||||
// into reality is a whole different story which is of no concern to the
|
||||
// tapping.
|
||||
this.props.dispatch(setVideoMuted(!this.props._videoMuted));
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the toolbar which contains the primary buttons such as hangup,
|
||||
* audio and video mute.
|
||||
|
@ -162,7 +203,7 @@ class Toolbox extends Component {
|
|||
<ToolbarButton
|
||||
iconName = { audioButtonStyles.iconName }
|
||||
iconStyle = { audioButtonStyles.iconStyle }
|
||||
onClick = { this.props._onToggleAudio }
|
||||
onClick = { this._onToggleAudio }
|
||||
style = { audioButtonStyles.style } />
|
||||
<ToolbarButton
|
||||
iconName = 'hangup'
|
||||
|
@ -174,7 +215,7 @@ class Toolbox extends Component {
|
|||
disabled = { this.props._audioOnly }
|
||||
iconName = { videoButtonStyles.iconName }
|
||||
iconStyle = { videoButtonStyles.iconStyle }
|
||||
onClick = { this.props._onToggleVideo }
|
||||
onClick = { this._onToggleVideo }
|
||||
style = { videoButtonStyles.style } />
|
||||
</View>
|
||||
);
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { appNavigate } from '../app';
|
||||
import { toggleAudioMuted, toggleVideoMuted } from '../base/media';
|
||||
import { getLocalAudioTrack, getLocalVideoTrack } from '../base/tracks';
|
||||
|
||||
/**
|
||||
|
@ -19,6 +18,11 @@ import { getLocalAudioTrack, getLocalVideoTrack } from '../base/tracks';
|
|||
*/
|
||||
export function abstractMapDispatchToProps(dispatch: Dispatch<*>): Object {
|
||||
return {
|
||||
// Inject {@code dispatch} into the React Component's props in case it
|
||||
// needs to dispatch an action in the redux store without
|
||||
// {@code mapDispatchToProps}.
|
||||
dispatch,
|
||||
|
||||
/**
|
||||
* Dispatches action to leave the current conference.
|
||||
*
|
||||
|
@ -33,29 +37,6 @@ export function abstractMapDispatchToProps(dispatch: Dispatch<*>): Object {
|
|||
// expression of (1) the lack of knowledge & (2) the desire to no
|
||||
// longer have a valid room name to join.
|
||||
dispatch(appNavigate(undefined));
|
||||
},
|
||||
|
||||
/**
|
||||
* Dispatches an action to toggle the mute state of the
|
||||
* audio/microphone.
|
||||
*
|
||||
* @private
|
||||
* @returns {Object} - Dispatched action.
|
||||
* @type {Function}
|
||||
*/
|
||||
_onToggleAudio() {
|
||||
dispatch(toggleAudioMuted());
|
||||
},
|
||||
|
||||
/**
|
||||
* Dispatches an action to toggle the mute state of the video/camera.
|
||||
*
|
||||
* @private
|
||||
* @returns {Object} - Dispatched action.
|
||||
* @type {Function}
|
||||
*/
|
||||
_onToggleVideo() {
|
||||
dispatch(toggleVideoMuted());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue