[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:
|
// 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(
|
conference.on(
|
||||||
JitsiConferenceEvents.STARTED_MUTED,
|
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(setAudioMuted(Boolean(conference.startAudioMuted)));
|
||||||
dispatch(setVideoMuted(Boolean(conference.startVideoMuted)));
|
dispatch(setVideoMuted(Boolean(conference.startVideoMuted)));
|
||||||
});
|
});
|
||||||
|
|
|
@ -32,7 +32,7 @@ import {
|
||||||
/**
|
/**
|
||||||
* Implements the middleware of the feature base/conference.
|
* Implements the middleware of the feature base/conference.
|
||||||
*
|
*
|
||||||
* @param {Store} store - Redux store.
|
* @param {Store} store - The redux store.
|
||||||
* @returns {Function}
|
* @returns {Function}
|
||||||
*/
|
*/
|
||||||
MiddlewareRegistry.register(store => next => action => {
|
MiddlewareRegistry.register(store => next => action => {
|
||||||
|
@ -40,13 +40,13 @@ MiddlewareRegistry.register(store => next => action => {
|
||||||
case CONNECTION_ESTABLISHED:
|
case CONNECTION_ESTABLISHED:
|
||||||
return _connectionEstablished(store, next, action);
|
return _connectionEstablished(store, next, action);
|
||||||
|
|
||||||
case CONFERENCE_JOINED:
|
|
||||||
return _conferenceJoined(store, next, action);
|
|
||||||
|
|
||||||
case CONFERENCE_FAILED:
|
case CONFERENCE_FAILED:
|
||||||
case CONFERENCE_LEFT:
|
case CONFERENCE_LEFT:
|
||||||
return _conferenceFailedOrLeft(store, next, action);
|
return _conferenceFailedOrLeft(store, next, action);
|
||||||
|
|
||||||
|
case CONFERENCE_JOINED:
|
||||||
|
return _conferenceJoined(store, next, action);
|
||||||
|
|
||||||
case PIN_PARTICIPANT:
|
case PIN_PARTICIPANT:
|
||||||
return _pinParticipant(store, next, action);
|
return _pinParticipant(store, next, action);
|
||||||
|
|
||||||
|
@ -66,13 +66,13 @@ MiddlewareRegistry.register(store => next => action => {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies the feature base/conference that the action CONNECTION_ESTABLISHED
|
* 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.
|
* 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.
|
* 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.
|
* being dispatched in the specified store.
|
||||||
* @private
|
* @private
|
||||||
* @returns {Object} The new state that is the result of the reduction of the
|
* @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
|
* Does extra sync up on properties that may need to be updated after the
|
||||||
* the conference failed or was left.
|
* 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.
|
* 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.
|
* 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.
|
* {@link CONFERENCE_LEFT} which is being dispatched in the specified store.
|
||||||
* @private
|
* @private
|
||||||
* @returns {Object} The new state that is the result of the reduction of the
|
* @returns {Object} The new state that is the result of the reduction of the
|
||||||
* specified action.
|
* specified action.
|
||||||
*/
|
*/
|
||||||
function _conferenceFailedOrLeft(store, next, action) {
|
function _conferenceFailedOrLeft({ dispatch, getState }, next, action) {
|
||||||
const result = 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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does extra sync up on properties that may need to be updated, after
|
* Does extra sync up on properties that may need to be updated after the
|
||||||
* the conference was joined.
|
* 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.
|
* 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.
|
* 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.
|
* dispatched in the specified store.
|
||||||
* @private
|
* @private
|
||||||
* @returns {Object} The new state that is the result of the reduction of the
|
* @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
|
* 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.
|
* 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.
|
* 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.
|
* 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.
|
* dispatched in the specified store.
|
||||||
* @private
|
* @private
|
||||||
* @returns {Object} The new state that is the result of the reduction of the
|
* @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,
|
* 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.
|
* 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.
|
* 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.
|
* 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.
|
* dispatched in the specified store.
|
||||||
* @private
|
* @private
|
||||||
* @returns {Object} The new state that is the result of the reduction of the
|
* @returns {Object} The new state that is the result of the reduction of the
|
||||||
* specified action.
|
* specified action.
|
||||||
*/
|
*/
|
||||||
function _setAudioOnly({ dispatch }, next, action) {
|
function _setAudioOnly({ dispatch, getState }, next, action) {
|
||||||
const result = 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,
|
// Set lastN to 0 in case audio-only is desired; leave it as undefined,
|
||||||
// otherwise, and the default lastN value will be chosen automatically.
|
// otherwise, and the default lastN value will be chosen automatically.
|
||||||
dispatch(setLastN(audioOnly ? 0 : undefined));
|
dispatch(setLastN(audioOnly ? 0 : undefined));
|
||||||
|
|
||||||
// Mute the local video.
|
// Mute/unmute the local video.
|
||||||
dispatch(setVideoMuted(audioOnly, VIDEO_MUTISM_AUTHORITY.AUDIO_ONLY));
|
dispatch(setVideoMuted(audioOnly, VIDEO_MUTISM_AUTHORITY.AUDIO_ONLY));
|
||||||
|
|
||||||
if (typeof APP !== 'undefined') {
|
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.
|
* 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.
|
* 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.
|
* 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.
|
* in the specified store.
|
||||||
* @private
|
* @private
|
||||||
* @returns {Object} The new state that is the result of the reduction of the
|
* @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
|
* Synchronizes local tracks from state with local tracks in JitsiConference
|
||||||
* instance.
|
* instance.
|
||||||
*
|
*
|
||||||
* @param {Store} store - Redux store.
|
* @param {Store} store - The redux store.
|
||||||
* @param {Object} action - Action object.
|
* @param {Object} action - Action object.
|
||||||
* @private
|
* @private
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
|
@ -284,13 +284,13 @@ function _syncConferenceLocalTracksWithState(store, action) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies the feature base/conference that the action TRACK_ADDED
|
* 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.
|
* 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.
|
* 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.
|
* is being dispatched in the specified store.
|
||||||
* @private
|
* @private
|
||||||
* @returns {Object} The new state that is the result of the reduction of the
|
* @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);
|
&& (videoMuted = config.startWithVideoMuted);
|
||||||
|
|
||||||
// Apply startWithAudioMuted and startWithVideoMuted.
|
// Apply startWithAudioMuted and startWithVideoMuted.
|
||||||
const { audio, video } = state['features/base/media'];
|
|
||||||
|
|
||||||
audioMuted = Boolean(audioMuted);
|
audioMuted = Boolean(audioMuted);
|
||||||
videoMuted = Boolean(videoMuted);
|
videoMuted = Boolean(videoMuted);
|
||||||
|
|
||||||
(audio.muted !== audioMuted) && dispatch(setAudioMuted(audioMuted));
|
// Unconditionally express the desires/expectations/intents of the app and
|
||||||
(video.facingMode !== CAMERA_FACING_MODE.USER)
|
// the user i.e. the state of base/media. Eventually, practice/reality i.e.
|
||||||
&& dispatch(setCameraFacingMode(CAMERA_FACING_MODE.USER));
|
// the state of base/tracks will or will not agree with the desires.
|
||||||
(Boolean(video.muted) !== videoMuted)
|
dispatch(setAudioMuted(audioMuted));
|
||||||
&& dispatch(setVideoMuted(videoMuted));
|
dispatch(setCameraFacingMode(CAMERA_FACING_MODE.USER));
|
||||||
|
dispatch(setVideoMuted(videoMuted));
|
||||||
|
|
||||||
return next(action);
|
return next(action);
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,17 +87,19 @@ export function destroyLocalTracks() {
|
||||||
*/
|
*/
|
||||||
export function replaceLocalTrack(oldTrack, newTrack, conference) {
|
export function replaceLocalTrack(oldTrack, newTrack, conference) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
const currentConference = conference
|
conference
|
||||||
|| getState()['features/base/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(() => {
|
.then(() => {
|
||||||
// We call dispose after doing the replace because
|
// We call dispose after doing the replace because dispose will
|
||||||
// dispose will try and do a new o/a after the
|
// try and do a new o/a after the track removes itself. Doing it
|
||||||
// track removes itself. Doing it after means
|
// after means the JitsiLocalTrack.conference is already
|
||||||
// the JitsiLocalTrack::conference member is already
|
// cleared, so it won't try and do the o/a.
|
||||||
// cleared, so it won't try and do the o/a
|
const disposePromise
|
||||||
const disposePromise = oldTrack
|
= oldTrack
|
||||||
? dispatch(_disposeAndRemoveTracks([ oldTrack ]))
|
? dispatch(_disposeAndRemoveTracks([ oldTrack ]))
|
||||||
: Promise.resolve();
|
: Promise.resolve();
|
||||||
|
|
||||||
|
@ -113,10 +115,12 @@ export function replaceLocalTrack(oldTrack, newTrack, conference) {
|
||||||
// track's mute state. If this is not done, the
|
// track's mute state. If this is not done, the
|
||||||
// current mute state of the app will be reflected
|
// current mute state of the app will be reflected
|
||||||
// on the track, not vice-versa.
|
// on the track, not vice-versa.
|
||||||
const muteAction = newTrack.isVideoTrack()
|
const setMuted
|
||||||
? setVideoMuted : setAudioMuted;
|
= newTrack.isVideoTrack()
|
||||||
|
? setVideoMuted
|
||||||
|
: setAudioMuted;
|
||||||
|
|
||||||
return dispatch(muteAction(newTrack.isMuted()));
|
return dispatch(setMuted(newTrack.isMuted()));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
@ -366,17 +370,26 @@ export function setTrackMuted(track, muted) {
|
||||||
|
|
||||||
const f = muted ? 'mute' : 'unmute';
|
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 => {
|
return track[f]().catch(error => {
|
||||||
console.error(`set track ${f} failed`, 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
|
const setMuted
|
||||||
= track.mediaType === MEDIA_TYPE.AUDIO
|
= track.mediaType === MEDIA_TYPE.AUDIO
|
||||||
? setAudioMuted
|
? setAudioMuted
|
||||||
: setVideoMuted;
|
: setVideoMuted;
|
||||||
|
|
||||||
|
// FIXME The following disregards VIDEO_MUTISM_AUTHORITY (in the
|
||||||
|
// case of setVideoMuted, of course).
|
||||||
dispatch(setMuted(!muted));
|
dispatch(setMuted(!muted));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -100,35 +100,37 @@ MiddlewareRegistry.register(store => next => action => {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TRACK_UPDATED:
|
case TRACK_UPDATED:
|
||||||
// TODO Remove the below calls to APP.UI once components interested in
|
// TODO Remove the following calls to APP.UI once components interested
|
||||||
// track mute changes are moved into react.
|
// in track mute changes are moved into React and/or redux.
|
||||||
if (typeof APP !== 'undefined') {
|
if (typeof APP !== 'undefined') {
|
||||||
const { jitsiTrack } = action.track;
|
const { jitsiTrack } = action.track;
|
||||||
const isMuted = jitsiTrack.isMuted();
|
const muted = jitsiTrack.isMuted();
|
||||||
const participantID = jitsiTrack.getParticipantId();
|
const participantID = jitsiTrack.getParticipantId();
|
||||||
const isVideoTrack = jitsiTrack.isVideoTrack();
|
const isVideoTrack = jitsiTrack.isVideoTrack();
|
||||||
|
|
||||||
if (jitsiTrack.isLocal()) {
|
if (jitsiTrack.isLocal()) {
|
||||||
if (isVideoTrack) {
|
if (isVideoTrack) {
|
||||||
APP.conference.videoMuted = isMuted;
|
APP.conference.videoMuted = muted;
|
||||||
} else {
|
} else {
|
||||||
APP.conference.audioMuted = isMuted;
|
APP.conference.audioMuted = muted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isVideoTrack) {
|
if (isVideoTrack) {
|
||||||
APP.UI.setVideoMuted(participantID, isMuted);
|
APP.UI.setVideoMuted(participantID, muted);
|
||||||
APP.UI.onPeerVideoTypeChanged(
|
APP.UI.onPeerVideoTypeChanged(
|
||||||
participantID, jitsiTrack.videoType);
|
participantID,
|
||||||
|
jitsiTrack.videoType);
|
||||||
} else {
|
} else {
|
||||||
APP.UI.setAudioMuted(participantID, isMuted);
|
APP.UI.setAudioMuted(participantID, muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX This function synchronizes track states with media states.
|
// XXX The following synchronizes the state of base/tracks into the
|
||||||
// This is not required in React, because media is the source of
|
// state of base/media. Which is not required in React (and,
|
||||||
// truth, synchronization should always happen in the media -> track
|
// respectively, React Native) because base/media expresses the
|
||||||
// direction. The old web, however, does the opposite, hence the
|
// app's and the user's desires/expectations/intents and base/tracks
|
||||||
// need for this.
|
// expresses practice/reality. Unfortunately, the old Web does not
|
||||||
|
// comply and/or does the opposite. Hence, the following:
|
||||||
return _trackUpdated(store, next, action);
|
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
|
* @returns {Track} The local <tt>Track</tt> associated with the specified
|
||||||
* <tt>mediaType</tt> in the specified <tt>store</tt>.
|
* <tt>mediaType</tt> in the specified <tt>store</tt>.
|
||||||
*/
|
*/
|
||||||
function _getLocalTrack(store, mediaType: MEDIA_TYPE) {
|
function _getLocalTrack({ getState }, mediaType: MEDIA_TYPE) {
|
||||||
return getLocalTrack(store.getState()['features/base/tracks'], mediaType);
|
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
|
* based on the type of conference. Audio-only conferences don't use the speaker
|
||||||
* by default, and video conferences do.
|
* by default, and video conferences do.
|
||||||
*
|
*
|
||||||
* @param {Store} store - Redux store.
|
* @param {Store} store - The redux store.
|
||||||
* @returns {Function}
|
* @returns {Function}
|
||||||
*/
|
*/
|
||||||
MiddlewareRegistry.register(store => next => action => {
|
MiddlewareRegistry.register(({ getState }) => next => action => {
|
||||||
const AudioMode = NativeModules.AudioMode;
|
const AudioMode = NativeModules.AudioMode;
|
||||||
|
|
||||||
if (AudioMode) {
|
if (AudioMode) {
|
||||||
|
@ -32,34 +32,20 @@ MiddlewareRegistry.register(store => next => action => {
|
||||||
mode = AudioMode.DEFAULT;
|
mode = AudioMode.DEFAULT;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CONFERENCE_WILL_JOIN: {
|
case CONFERENCE_WILL_JOIN:
|
||||||
const { audioOnly } = store.getState()['features/base/conference'];
|
|
||||||
|
|
||||||
mode = audioOnly ? AudioMode.AUDIO_CALL : AudioMode.VIDEO_CALL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case SET_AUDIO_ONLY: {
|
case SET_AUDIO_ONLY: {
|
||||||
const { conference } = store.getState()['features/base/conference'];
|
if (getState()['features/base/conference'].conference
|
||||||
|
|| action.conference) {
|
||||||
if (conference) {
|
|
||||||
mode
|
mode
|
||||||
= action.audioOnly
|
= action.audioOnly
|
||||||
? AudioMode.AUDIO_CALL
|
? AudioMode.AUDIO_CALL
|
||||||
: AudioMode.VIDEO_CALL;
|
: AudioMode.VIDEO_CALL;
|
||||||
} else {
|
|
||||||
mode = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
|
||||||
mode = null;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode !== null) {
|
if (typeof mode !== 'undefined') {
|
||||||
AudioMode.setMode(mode)
|
AudioMode.setMode(mode)
|
||||||
.catch(err =>
|
.catch(err =>
|
||||||
console.error(
|
console.error(
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
import { setLastN } from '../../base/conference';
|
import { setLastN } from '../../base/conference';
|
||||||
import { setVideoMuted, VIDEO_MUTISM_AUTHORITY } from '../../base/media';
|
import { setVideoMuted, VIDEO_MUTISM_AUTHORITY } from '../../base/media';
|
||||||
|
|
||||||
import {
|
import { _SET_APP_STATE_LISTENER, APP_STATE_CHANGED } from './actionTypes';
|
||||||
_SET_APP_STATE_LISTENER,
|
|
||||||
APP_STATE_CHANGED
|
|
||||||
} from './actionTypes';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the listener to be used with React Native's AppState API.
|
* 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.
|
// for last N will be chosen automatically.
|
||||||
const { audioOnly } = getState()['features/base/conference'];
|
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));
|
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
|
* In immersive mode the status and navigation bars are hidden and thus the
|
||||||
* entire screen will be covered by our application.
|
* entire screen will be covered by our application.
|
||||||
*
|
*
|
||||||
* @param {Store} store - Redux store.
|
* @param {Store} store - The redux store.
|
||||||
* @returns {Function}
|
* @returns {Function}
|
||||||
*/
|
*/
|
||||||
MiddlewareRegistry.register(store => next => action => {
|
MiddlewareRegistry.register(({ getState }) => next => action => {
|
||||||
|
const result = next(action);
|
||||||
|
|
||||||
let fullScreen = null;
|
let fullScreen = null;
|
||||||
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case APP_STATE_CHANGED: {
|
case APP_STATE_CHANGED:
|
||||||
// Check if we just came back from the background and reenable full
|
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.
|
// screen mode if necessary.
|
||||||
if (action.appState === 'active') {
|
const { appState } = action;
|
||||||
const { audioOnly, conference }
|
|
||||||
= store.getState()['features/base/conference'];
|
|
||||||
|
|
||||||
fullScreen = conference ? !audioOnly : false;
|
if (typeof appState !== 'undefined' && appState !== 'active') {
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case CONFERENCE_WILL_JOIN: {
|
const { audioOnly, conference }
|
||||||
const { audioOnly } = store.getState()['features/base/conference'];
|
= getState()['features/base/conference'];
|
||||||
|
|
||||||
fullScreen = !audioOnly;
|
fullScreen = conference || action.conference ? !audioOnly : false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,21 +54,6 @@ MiddlewareRegistry.register(store => next => action => {
|
||||||
case CONFERENCE_LEFT:
|
case CONFERENCE_LEFT:
|
||||||
fullScreen = false;
|
fullScreen = false;
|
||||||
break;
|
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) {
|
if (fullScreen !== null) {
|
||||||
|
@ -75,7 +62,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||||
console.warn(`Failed to set full screen mode: ${err}`));
|
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
|
* the screen and disable touch controls when an object is nearby. The
|
||||||
* functionality is enabled when a conference is in audio-only mode.
|
* functionality is enabled when a conference is in audio-only mode.
|
||||||
*
|
*
|
||||||
* @param {Store} store - Redux store.
|
* @param {Store} store - The redux store.
|
||||||
* @returns {Function}
|
* @returns {Function}
|
||||||
*/
|
*/
|
||||||
MiddlewareRegistry.register(store => next => action => {
|
MiddlewareRegistry.register(({ getState }) => next => action => {
|
||||||
|
const result = next(action);
|
||||||
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case CONFERENCE_JOINED: {
|
|
||||||
const { audioOnly } = store.getState()['features/base/conference'];
|
|
||||||
|
|
||||||
_setProximityEnabled(audioOnly);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case CONFERENCE_FAILED:
|
case CONFERENCE_FAILED:
|
||||||
case CONFERENCE_LEFT:
|
case CONFERENCE_LEFT:
|
||||||
_setProximityEnabled(false);
|
_setProximityEnabled(false);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CONFERENCE_JOINED:
|
||||||
case SET_AUDIO_ONLY: {
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return next(action);
|
return result;
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -3,7 +3,12 @@ import { View } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { toggleAudioOnly } from '../../base/conference';
|
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 { Container } from '../../base/react';
|
||||||
import { ColorPalette } from '../../base/styles';
|
import { ColorPalette } from '../../base/styles';
|
||||||
import { beginRoomLockRequest } from '../../room-lock';
|
import { beginRoomLockRequest } from '../../room-lock';
|
||||||
|
@ -56,11 +61,6 @@ class Toolbox extends Component {
|
||||||
*/
|
*/
|
||||||
_onShareRoom: React.PropTypes.func,
|
_onShareRoom: React.PropTypes.func,
|
||||||
|
|
||||||
/**
|
|
||||||
* Handler for toggle audio.
|
|
||||||
*/
|
|
||||||
_onToggleAudio: React.PropTypes.func,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggles the audio-only flag of the conference.
|
* Toggles the audio-only flag of the conference.
|
||||||
*/
|
*/
|
||||||
|
@ -72,11 +72,6 @@ class Toolbox extends Component {
|
||||||
*/
|
*/
|
||||||
_onToggleCameraFacingMode: React.PropTypes.func,
|
_onToggleCameraFacingMode: React.PropTypes.func,
|
||||||
|
|
||||||
/**
|
|
||||||
* Handler for toggling video.
|
|
||||||
*/
|
|
||||||
_onToggleVideo: React.PropTypes.func,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag showing whether video is muted.
|
* Flag showing whether video is muted.
|
||||||
*/
|
*/
|
||||||
|
@ -85,9 +80,25 @@ class Toolbox extends Component {
|
||||||
/**
|
/**
|
||||||
* Flag showing whether toolbar is visible.
|
* 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()}.
|
* 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,
|
* Renders the toolbar which contains the primary buttons such as hangup,
|
||||||
* audio and video mute.
|
* audio and video mute.
|
||||||
|
@ -162,7 +203,7 @@ class Toolbox extends Component {
|
||||||
<ToolbarButton
|
<ToolbarButton
|
||||||
iconName = { audioButtonStyles.iconName }
|
iconName = { audioButtonStyles.iconName }
|
||||||
iconStyle = { audioButtonStyles.iconStyle }
|
iconStyle = { audioButtonStyles.iconStyle }
|
||||||
onClick = { this.props._onToggleAudio }
|
onClick = { this._onToggleAudio }
|
||||||
style = { audioButtonStyles.style } />
|
style = { audioButtonStyles.style } />
|
||||||
<ToolbarButton
|
<ToolbarButton
|
||||||
iconName = 'hangup'
|
iconName = 'hangup'
|
||||||
|
@ -174,7 +215,7 @@ class Toolbox extends Component {
|
||||||
disabled = { this.props._audioOnly }
|
disabled = { this.props._audioOnly }
|
||||||
iconName = { videoButtonStyles.iconName }
|
iconName = { videoButtonStyles.iconName }
|
||||||
iconStyle = { videoButtonStyles.iconStyle }
|
iconStyle = { videoButtonStyles.iconStyle }
|
||||||
onClick = { this.props._onToggleVideo }
|
onClick = { this._onToggleVideo }
|
||||||
style = { videoButtonStyles.style } />
|
style = { videoButtonStyles.style } />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
import type { Dispatch } from 'redux';
|
import type { Dispatch } from 'redux';
|
||||||
|
|
||||||
import { appNavigate } from '../app';
|
import { appNavigate } from '../app';
|
||||||
import { toggleAudioMuted, toggleVideoMuted } from '../base/media';
|
|
||||||
import { getLocalAudioTrack, getLocalVideoTrack } from '../base/tracks';
|
import { getLocalAudioTrack, getLocalVideoTrack } from '../base/tracks';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -19,6 +18,11 @@ import { getLocalAudioTrack, getLocalVideoTrack } from '../base/tracks';
|
||||||
*/
|
*/
|
||||||
export function abstractMapDispatchToProps(dispatch: Dispatch<*>): Object {
|
export function abstractMapDispatchToProps(dispatch: Dispatch<*>): Object {
|
||||||
return {
|
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.
|
* 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
|
// expression of (1) the lack of knowledge & (2) the desire to no
|
||||||
// longer have a valid room name to join.
|
// longer have a valid room name to join.
|
||||||
dispatch(appNavigate(undefined));
|
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