audio-only,lastn: move audio-only and last N handling to standalone features
This refactors all handling of audio-only and last N to 2 features in preparation for "low bandwidth mode". The main motivation to do this is that lastN is a "global" setting so it helps to have all processing for it in a single place.
This commit is contained in:
parent
6ddd17c769
commit
467c9d36cf
|
@ -1345,8 +1345,7 @@ export default {
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
isAudioOnly() {
|
isAudioOnly() {
|
||||||
return Boolean(
|
return Boolean(APP.store.getState()['features/base/audio-only'].enabled);
|
||||||
APP.store.getState()['features/base/conference'].audioOnly);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
videoSwitchInProgress: false,
|
videoSwitchInProgress: false,
|
||||||
|
|
|
@ -7,8 +7,8 @@ import { toURLString } from '../../base/util';
|
||||||
import '../../follow-me';
|
import '../../follow-me';
|
||||||
import { OverlayContainer } from '../../overlay';
|
import { OverlayContainer } from '../../overlay';
|
||||||
|
|
||||||
// Enable rejoin analytics
|
import '../../base/lastn'; // Register lastN middleware
|
||||||
import '../../rejoin';
|
import '../../rejoin'; // Enable rejoin analytics
|
||||||
|
|
||||||
import { appNavigate } from '../actions';
|
import { appNavigate } from '../actions';
|
||||||
import { getDefaultURL } from '../functions';
|
import { getDefaultURL } from '../functions';
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of (redux) action which sets the audio-only flag for the current
|
||||||
|
* conference.
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* type: SET_AUDIO_ONLY,
|
||||||
|
* audioOnly: boolean
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
export const SET_AUDIO_ONLY = 'SET_AUDIO_ONLY';
|
|
@ -0,0 +1,64 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import { getLogger } from 'jitsi-meet-logger';
|
||||||
|
|
||||||
|
import UIEvents from '../../../../service/UI/UIEvents';
|
||||||
|
|
||||||
|
import { createAudioOnlyChangedEvent, sendAnalytics } from '../../analytics';
|
||||||
|
|
||||||
|
import { SET_AUDIO_ONLY } from './actionTypes';
|
||||||
|
|
||||||
|
import type { Dispatch } from 'redux';
|
||||||
|
|
||||||
|
declare var APP: Object;
|
||||||
|
const logger = getLogger('features/base/audio-only');
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the audio-only flag for the current JitsiConference.
|
||||||
|
*
|
||||||
|
* @param {boolean} audioOnly - True if the conference should be audio only;
|
||||||
|
* false, otherwise.
|
||||||
|
* @param {boolean} ensureVideoTrack - Define if conference should ensure
|
||||||
|
* to create a video track.
|
||||||
|
* @returns {{
|
||||||
|
* type: SET_AUDIO_ONLY,
|
||||||
|
* audioOnly: boolean,
|
||||||
|
* ensureVideoTrack: boolean
|
||||||
|
* }}
|
||||||
|
*/
|
||||||
|
export function setAudioOnly(audioOnly: boolean, ensureVideoTrack: boolean = false) {
|
||||||
|
return (dispatch: Dispatch<any>, getState: Function) => {
|
||||||
|
const { enabled: oldValue } = getState()['features/base/audio-only'];
|
||||||
|
|
||||||
|
if (oldValue !== audioOnly) {
|
||||||
|
sendAnalytics(createAudioOnlyChangedEvent(audioOnly));
|
||||||
|
logger.log(`Audio-only ${audioOnly ? 'enabled' : 'disabled'}`);
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: SET_AUDIO_ONLY,
|
||||||
|
audioOnly,
|
||||||
|
ensureVideoTrack
|
||||||
|
});
|
||||||
|
|
||||||
|
if (typeof APP !== 'undefined') {
|
||||||
|
// TODO This should be a temporary solution that lasts only until video
|
||||||
|
// tracks and all ui is moved into react/redux on the web.
|
||||||
|
APP.UI.emitEvent(UIEvents.TOGGLE_AUDIO_ONLY, audioOnly);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles the audio-only flag for the current JitsiConference.
|
||||||
|
*
|
||||||
|
* @returns {Function}
|
||||||
|
*/
|
||||||
|
export function toggleAudioOnly() {
|
||||||
|
return (dispatch: Dispatch<any>, getState: Function) => {
|
||||||
|
const { enabled } = getState()['features/base/audio-only'];
|
||||||
|
|
||||||
|
return dispatch(setAudioOnly(!enabled, true));
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
export * from './actions';
|
||||||
|
export * from './actionTypes';
|
||||||
|
|
||||||
|
import './reducer';
|
|
@ -0,0 +1,23 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import { ReducerRegistry } from '../redux';
|
||||||
|
|
||||||
|
import { SET_AUDIO_ONLY } from './actionTypes';
|
||||||
|
|
||||||
|
|
||||||
|
const DEFAULT_STATE = {
|
||||||
|
enabled: false
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
ReducerRegistry.register('features/base/audio-only', (state = DEFAULT_STATE, action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case SET_AUDIO_ONLY:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
enabled: action.audioOnly
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
});
|
|
@ -118,17 +118,6 @@ export const LOCK_STATE_CHANGED = 'LOCK_STATE_CHANGED';
|
||||||
*/
|
*/
|
||||||
export const P2P_STATUS_CHANGED = 'P2P_STATUS_CHANGED';
|
export const P2P_STATUS_CHANGED = 'P2P_STATUS_CHANGED';
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of (redux) action which sets the audio-only flag for the current
|
|
||||||
* conference.
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
* type: SET_AUDIO_ONLY,
|
|
||||||
* audioOnly: boolean
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
export const SET_AUDIO_ONLY = 'SET_AUDIO_ONLY';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of (redux) action which sets the desktop sharing enabled flag for
|
* The type of (redux) action which sets the desktop sharing enabled flag for
|
||||||
* the current conference.
|
* the current conference.
|
||||||
|
@ -152,16 +141,6 @@ export const SET_DESKTOP_SHARING_ENABLED
|
||||||
*/
|
*/
|
||||||
export const SET_FOLLOW_ME = 'SET_FOLLOW_ME';
|
export const SET_FOLLOW_ME = 'SET_FOLLOW_ME';
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of (redux) action which sets the video channel's lastN (value).
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
* type: SET_LASTN,
|
|
||||||
* lastN: number
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
export const SET_LASTN = 'SET_LASTN';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of (redux) action which sets the maximum video height that should be
|
* The type of (redux) action which sets the maximum video height that should be
|
||||||
* received from remote participants, even if the user prefers a larger video
|
* received from remote participants, even if the user prefers a larger video
|
||||||
|
|
|
@ -35,10 +35,8 @@ import {
|
||||||
KICKED_OUT,
|
KICKED_OUT,
|
||||||
LOCK_STATE_CHANGED,
|
LOCK_STATE_CHANGED,
|
||||||
P2P_STATUS_CHANGED,
|
P2P_STATUS_CHANGED,
|
||||||
SET_AUDIO_ONLY,
|
|
||||||
SET_DESKTOP_SHARING_ENABLED,
|
SET_DESKTOP_SHARING_ENABLED,
|
||||||
SET_FOLLOW_ME,
|
SET_FOLLOW_ME,
|
||||||
SET_LASTN,
|
|
||||||
SET_MAX_RECEIVER_VIDEO_QUALITY,
|
SET_MAX_RECEIVER_VIDEO_QUALITY,
|
||||||
SET_PASSWORD,
|
SET_PASSWORD,
|
||||||
SET_PASSWORD_FAILED,
|
SET_PASSWORD_FAILED,
|
||||||
|
@ -522,29 +520,6 @@ export function p2pStatusChanged(p2p: boolean) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the audio-only flag for the current JitsiConference.
|
|
||||||
*
|
|
||||||
* @param {boolean} audioOnly - True if the conference should be audio only;
|
|
||||||
* false, otherwise.
|
|
||||||
* @param {boolean} ensureVideoTrack - Define if conference should ensure
|
|
||||||
* to create a video track.
|
|
||||||
* @returns {{
|
|
||||||
* type: SET_AUDIO_ONLY,
|
|
||||||
* audioOnly: boolean,
|
|
||||||
* ensureVideoTrack: boolean
|
|
||||||
* }}
|
|
||||||
*/
|
|
||||||
export function setAudioOnly(
|
|
||||||
audioOnly: boolean,
|
|
||||||
ensureVideoTrack: boolean = false) {
|
|
||||||
return {
|
|
||||||
type: SET_AUDIO_ONLY,
|
|
||||||
audioOnly,
|
|
||||||
ensureVideoTrack
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the flag for indicating if desktop sharing is enabled.
|
* Sets the flag for indicating if desktop sharing is enabled.
|
||||||
*
|
*
|
||||||
|
@ -577,35 +552,6 @@ export function setFollowMe(enabled: boolean) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the video channel's last N (value) of the current conference. A value of
|
|
||||||
* undefined shall be used to reset it to the default value.
|
|
||||||
*
|
|
||||||
* @param {(number|undefined)} lastN - The last N value to be set.
|
|
||||||
* @returns {Function}
|
|
||||||
*/
|
|
||||||
export function setLastN(lastN: ?number) {
|
|
||||||
return (dispatch: Dispatch<any>, getState: Function) => {
|
|
||||||
if (typeof lastN === 'undefined') {
|
|
||||||
const config = getState()['features/base/config'];
|
|
||||||
|
|
||||||
/* eslint-disable no-param-reassign */
|
|
||||||
|
|
||||||
lastN = config.channelLastN;
|
|
||||||
if (typeof lastN === 'undefined') {
|
|
||||||
lastN = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* eslint-enable no-param-reassign */
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: SET_LASTN,
|
|
||||||
lastN
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the max frame height that should be received from remote videos.
|
* Sets the max frame height that should be received from remote videos.
|
||||||
*
|
*
|
||||||
|
@ -754,19 +700,6 @@ export function setStartMutedPolicy(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggles the audio-only flag for the current JitsiConference.
|
|
||||||
*
|
|
||||||
* @returns {Function}
|
|
||||||
*/
|
|
||||||
export function toggleAudioOnly() {
|
|
||||||
return (dispatch: Dispatch<any>, getState: Function) => {
|
|
||||||
const { audioOnly } = getState()['features/base/conference'];
|
|
||||||
|
|
||||||
return dispatch(setAudioOnly(!audioOnly, true));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changing conference subject.
|
* Changing conference subject.
|
||||||
*
|
*
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { reloadNow } from '../../app';
|
||||||
import {
|
import {
|
||||||
ACTION_PINNED,
|
ACTION_PINNED,
|
||||||
ACTION_UNPINNED,
|
ACTION_UNPINNED,
|
||||||
createAudioOnlyChangedEvent,
|
|
||||||
createConnectionEvent,
|
createConnectionEvent,
|
||||||
createOfferAnswerFailedEvent,
|
createOfferAnswerFailedEvent,
|
||||||
createPinnedEvent,
|
createPinnedEvent,
|
||||||
|
@ -12,7 +11,6 @@ import {
|
||||||
} from '../../analytics';
|
} from '../../analytics';
|
||||||
import { CONNECTION_ESTABLISHED, CONNECTION_FAILED } from '../connection';
|
import { CONNECTION_ESTABLISHED, CONNECTION_FAILED } from '../connection';
|
||||||
import { JitsiConferenceErrors } from '../lib-jitsi-meet';
|
import { JitsiConferenceErrors } from '../lib-jitsi-meet';
|
||||||
import { setVideoMuted, VIDEO_MUTISM_AUTHORITY } from '../media';
|
|
||||||
import {
|
import {
|
||||||
getParticipantById,
|
getParticipantById,
|
||||||
getPinnedParticipant,
|
getPinnedParticipant,
|
||||||
|
@ -20,14 +18,12 @@ import {
|
||||||
PIN_PARTICIPANT
|
PIN_PARTICIPANT
|
||||||
} from '../participants';
|
} from '../participants';
|
||||||
import { MiddlewareRegistry, StateListenerRegistry } from '../redux';
|
import { MiddlewareRegistry, StateListenerRegistry } from '../redux';
|
||||||
import UIEvents from '../../../../service/UI/UIEvents';
|
|
||||||
import { TRACK_ADDED, TRACK_REMOVED } from '../tracks';
|
import { TRACK_ADDED, TRACK_REMOVED } from '../tracks';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
conferenceFailed,
|
conferenceFailed,
|
||||||
conferenceWillLeave,
|
conferenceWillLeave,
|
||||||
createConference,
|
createConference,
|
||||||
setLastN,
|
|
||||||
setSubject
|
setSubject
|
||||||
} from './actions';
|
} from './actions';
|
||||||
import {
|
import {
|
||||||
|
@ -36,16 +32,14 @@ import {
|
||||||
CONFERENCE_SUBJECT_CHANGED,
|
CONFERENCE_SUBJECT_CHANGED,
|
||||||
CONFERENCE_WILL_LEAVE,
|
CONFERENCE_WILL_LEAVE,
|
||||||
DATA_CHANNEL_OPENED,
|
DATA_CHANNEL_OPENED,
|
||||||
SET_AUDIO_ONLY,
|
|
||||||
SET_LASTN,
|
|
||||||
SET_PENDING_SUBJECT_CHANGE,
|
SET_PENDING_SUBJECT_CHANGE,
|
||||||
SET_ROOM
|
SET_ROOM
|
||||||
} from './actionTypes';
|
} from './actionTypes';
|
||||||
import {
|
import {
|
||||||
_addLocalTracksToConference,
|
_addLocalTracksToConference,
|
||||||
|
_removeLocalTracksFromConference,
|
||||||
forEachConference,
|
forEachConference,
|
||||||
getCurrentConference,
|
getCurrentConference
|
||||||
_removeLocalTracksFromConference
|
|
||||||
} from './functions';
|
} from './functions';
|
||||||
|
|
||||||
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||||
|
@ -93,12 +87,6 @@ MiddlewareRegistry.register(store => next => action => {
|
||||||
case PIN_PARTICIPANT:
|
case PIN_PARTICIPANT:
|
||||||
return _pinParticipant(store, next, action);
|
return _pinParticipant(store, next, action);
|
||||||
|
|
||||||
case SET_AUDIO_ONLY:
|
|
||||||
return _setAudioOnly(store, next, action);
|
|
||||||
|
|
||||||
case SET_LASTN:
|
|
||||||
return _setLastN(store, next, action);
|
|
||||||
|
|
||||||
case SET_ROOM:
|
case SET_ROOM:
|
||||||
return _setRoom(store, next, action);
|
return _setRoom(store, next, action);
|
||||||
|
|
||||||
|
@ -197,21 +185,10 @@ function _conferenceFailed(store, next, action) {
|
||||||
*/
|
*/
|
||||||
function _conferenceJoined({ dispatch, getState }, next, action) {
|
function _conferenceJoined({ dispatch, getState }, next, action) {
|
||||||
const result = next(action);
|
const result = next(action);
|
||||||
|
const { conference } = action;
|
||||||
|
const { pendingSubjectChange } = getState()['features/base/conference'];
|
||||||
|
|
||||||
const {
|
pendingSubjectChange && dispatch(setSubject(pendingSubjectChange));
|
||||||
audioOnly,
|
|
||||||
conference,
|
|
||||||
pendingSubjectChange
|
|
||||||
} = getState()['features/base/conference'];
|
|
||||||
|
|
||||||
if (pendingSubjectChange) {
|
|
||||||
dispatch(setSubject(pendingSubjectChange));
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME On Web the audio only mode for "start audio only" is toggled before
|
|
||||||
// conference is added to the redux store ("on conference joined" action)
|
|
||||||
// and the LastN value needs to be synchronized here.
|
|
||||||
audioOnly && conference.getLastN() !== 0 && dispatch(setLastN(0));
|
|
||||||
|
|
||||||
// FIXME: Very dirty solution. This will work on web only.
|
// FIXME: Very dirty solution. This will work on web only.
|
||||||
// When the user closes the window or quits the browser, lib-jitsi-meet
|
// When the user closes the window or quits the browser, lib-jitsi-meet
|
||||||
|
@ -460,79 +437,6 @@ function _pinParticipant({ getState }, next, action) {
|
||||||
return next(action);
|
return 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 {@code action}
|
|
||||||
* is being dispatched.
|
|
||||||
* @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
|
|
||||||
* specified {@code action} to the specified {@code store}.
|
|
||||||
* @param {Action} action - The redux action {@code SET_AUDIO_ONLY} which is
|
|
||||||
* being dispatched in the specified {@code store}.
|
|
||||||
* @private
|
|
||||||
* @returns {Object} The value returned by {@code next(action)}.
|
|
||||||
*/
|
|
||||||
function _setAudioOnly({ dispatch, getState }, next, action) {
|
|
||||||
const { audioOnly: oldValue } = getState()['features/base/conference'];
|
|
||||||
const result = next(action);
|
|
||||||
const { audioOnly: newValue } = getState()['features/base/conference'];
|
|
||||||
|
|
||||||
// Send analytics. We could've done it in the action creator setAudioOnly.
|
|
||||||
// I don't know why it has to happen as early as possible but the analytics
|
|
||||||
// were originally sent before the SET_AUDIO_ONLY action was even dispatched
|
|
||||||
// in the redux store so I'm now sending the analytics as early as possible.
|
|
||||||
if (oldValue !== newValue) {
|
|
||||||
sendAnalytics(createAudioOnlyChangedEvent(newValue));
|
|
||||||
logger.log(`Audio-only ${newValue ? 'enabled' : 'disabled'}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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(newValue ? 0 : undefined));
|
|
||||||
|
|
||||||
// Mute/unmute the local video.
|
|
||||||
dispatch(
|
|
||||||
setVideoMuted(
|
|
||||||
newValue,
|
|
||||||
VIDEO_MUTISM_AUTHORITY.AUDIO_ONLY,
|
|
||||||
action.ensureVideoTrack));
|
|
||||||
|
|
||||||
if (typeof APP !== 'undefined') {
|
|
||||||
// TODO This should be a temporary solution that lasts only until video
|
|
||||||
// tracks and all ui is moved into react/redux on the web.
|
|
||||||
APP.UI.emitEvent(UIEvents.TOGGLE_AUDIO_ONLY, newValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the last N (value) of the video channel in the conference.
|
|
||||||
*
|
|
||||||
* @param {Store} store - The redux store in which the specified {@code action}
|
|
||||||
* is being dispatched.
|
|
||||||
* @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
|
|
||||||
* specified {@code action} to the specified {@code store}.
|
|
||||||
* @param {Action} action - The redux action {@code SET_LASTN} which is being
|
|
||||||
* dispatched in the specified {@code store}.
|
|
||||||
* @private
|
|
||||||
* @returns {Object} The value returned by {@code next(action)}.
|
|
||||||
*/
|
|
||||||
function _setLastN({ getState }, next, action) {
|
|
||||||
const { conference } = getState()['features/base/conference'];
|
|
||||||
|
|
||||||
if (conference) {
|
|
||||||
try {
|
|
||||||
conference.setLastN(action.lastN);
|
|
||||||
} catch (err) {
|
|
||||||
logger.error(`Failed to set lastN: ${err}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return next(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function for updating the preferred receiver video constraint, based
|
* Helper function for updating the preferred receiver video constraint, based
|
||||||
* on the user preference and the internal maximum.
|
* on the user preference and the internal maximum.
|
||||||
|
|
|
@ -15,7 +15,6 @@ import {
|
||||||
CONFERENCE_WILL_LEAVE,
|
CONFERENCE_WILL_LEAVE,
|
||||||
LOCK_STATE_CHANGED,
|
LOCK_STATE_CHANGED,
|
||||||
P2P_STATUS_CHANGED,
|
P2P_STATUS_CHANGED,
|
||||||
SET_AUDIO_ONLY,
|
|
||||||
SET_DESKTOP_SHARING_ENABLED,
|
SET_DESKTOP_SHARING_ENABLED,
|
||||||
SET_FOLLOW_ME,
|
SET_FOLLOW_ME,
|
||||||
SET_MAX_RECEIVER_VIDEO_QUALITY,
|
SET_MAX_RECEIVER_VIDEO_QUALITY,
|
||||||
|
@ -76,9 +75,6 @@ ReducerRegistry.register(
|
||||||
case P2P_STATUS_CHANGED:
|
case P2P_STATUS_CHANGED:
|
||||||
return _p2pStatusChanged(state, action);
|
return _p2pStatusChanged(state, action);
|
||||||
|
|
||||||
case SET_AUDIO_ONLY:
|
|
||||||
return _setAudioOnly(state, action);
|
|
||||||
|
|
||||||
case SET_DESKTOP_SHARING_ENABLED:
|
case SET_DESKTOP_SHARING_ENABLED:
|
||||||
return _setDesktopSharingEnabled(state, action);
|
return _setDesktopSharingEnabled(state, action);
|
||||||
|
|
||||||
|
@ -346,20 +342,6 @@ function _p2pStatusChanged(state, action) {
|
||||||
return set(state, 'p2p', action.p2p);
|
return set(state, 'p2p', action.p2p);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Reduces a specific Redux action SET_AUDIO_ONLY of the feature
|
|
||||||
* base/conference.
|
|
||||||
*
|
|
||||||
* @param {Object} state - The Redux state of the feature base/conference.
|
|
||||||
* @param {Action} action - The Redux action SET_AUDIO_ONLY to reduce.
|
|
||||||
* @private
|
|
||||||
* @returns {Object} The new state of the feature base/conference after the
|
|
||||||
* reduction of the specified action.
|
|
||||||
*/
|
|
||||||
function _setAudioOnly(state, action) {
|
|
||||||
return set(state, 'audioOnly', action.audioOnly);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reduces a specific Redux action SET_DESKTOP_SHARING_ENABLED of the feature
|
* Reduces a specific Redux action SET_DESKTOP_SHARING_ENABLED of the feature
|
||||||
* base/conference.
|
* base/conference.
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of (redux) action which sets the video channel's lastN (value).
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* type: SET_LASTN,
|
||||||
|
* lastN: number
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
export const SET_LASTN = 'SET_LASTN';
|
|
@ -0,0 +1,34 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import { SET_LASTN } from './actionTypes';
|
||||||
|
|
||||||
|
import type { Dispatch } from 'redux';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the video channel's last N (value) of the current conference. A value of
|
||||||
|
* undefined shall be used to reset it to the default value.
|
||||||
|
*
|
||||||
|
* @param {(number|undefined)} lastN - The last N value to be set.
|
||||||
|
* @returns {Function}
|
||||||
|
*/
|
||||||
|
export function setLastN(lastN: ?number) {
|
||||||
|
return (dispatch: Dispatch<any>, getState: Function) => {
|
||||||
|
if (typeof lastN === 'undefined') {
|
||||||
|
const config = getState()['features/base/config'];
|
||||||
|
|
||||||
|
/* eslint-disable no-param-reassign */
|
||||||
|
|
||||||
|
lastN = config.channelLastN;
|
||||||
|
if (typeof lastN === 'undefined') {
|
||||||
|
lastN = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* eslint-enable no-param-reassign */
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: SET_LASTN,
|
||||||
|
lastN
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
export * from './actions';
|
||||||
|
export * from './actionTypes';
|
||||||
|
|
||||||
|
import './middleware';
|
|
@ -0,0 +1,160 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import { getLogger } from 'jitsi-meet-logger';
|
||||||
|
|
||||||
|
import { SET_FILMSTRIP_ENABLED } from '../../filmstrip/actionTypes';
|
||||||
|
import { APP_STATE_CHANGED } from '../../mobile/background/actionTypes';
|
||||||
|
|
||||||
|
import { SET_AUDIO_ONLY } from '../audio-only';
|
||||||
|
import { CONFERENCE_JOINED } from '../conference/actionTypes';
|
||||||
|
import { MiddlewareRegistry } from '../redux';
|
||||||
|
|
||||||
|
import { setLastN } from './actions';
|
||||||
|
import { SET_LASTN } from './actionTypes';
|
||||||
|
|
||||||
|
declare var APP: Object;
|
||||||
|
|
||||||
|
const logger = getLogger('features/base/lastn');
|
||||||
|
|
||||||
|
|
||||||
|
MiddlewareRegistry.register(store => next => action => {
|
||||||
|
switch (action.type) {
|
||||||
|
case APP_STATE_CHANGED:
|
||||||
|
return _appStateChanged(store, next, action);
|
||||||
|
|
||||||
|
case CONFERENCE_JOINED:
|
||||||
|
return _conferenceJoined(store, next, action);
|
||||||
|
|
||||||
|
case SET_AUDIO_ONLY:
|
||||||
|
return _setAudioOnly(store, next, action);
|
||||||
|
|
||||||
|
case SET_FILMSTRIP_ENABLED:
|
||||||
|
return _setFilmstripEnabled(store, next, action);
|
||||||
|
|
||||||
|
case SET_LASTN:
|
||||||
|
return _setLastN(store, next, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
return next(action);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjusts the lasN value based on the app state.
|
||||||
|
*
|
||||||
|
* @param {Store} store - The redux store in which the specified {@code action}
|
||||||
|
* is being dispatched.
|
||||||
|
* @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
|
||||||
|
* specified {@code action} to the specified {@code store}.
|
||||||
|
* @param {Action} action - The redux action {@code APP_STATE_CHANGED} which is
|
||||||
|
* being dispatched in the specified {@code store}.
|
||||||
|
* @private
|
||||||
|
* @returns {Object} The value returned by {@code next(action)}.
|
||||||
|
*/
|
||||||
|
function _appStateChanged({ dispatch, getState }, next, action) {
|
||||||
|
const { enabled: audioOnly } = getState()['features/base/audio-only'];
|
||||||
|
|
||||||
|
if (!audioOnly) {
|
||||||
|
const { appState } = action;
|
||||||
|
const lastN = appState === 'active' ? undefined : 0;
|
||||||
|
|
||||||
|
dispatch(setLastN(lastN));
|
||||||
|
logger.log(`App state changed - updated lastN to ${String(lastN)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return next(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjusts the lasN value upon joining a conference.
|
||||||
|
*
|
||||||
|
* @param {Store} store - The redux store in which the specified {@code action}
|
||||||
|
* is being dispatched.
|
||||||
|
* @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
|
||||||
|
* specified {@code action} to the specified {@code store}.
|
||||||
|
* @param {Action} action - The redux action {@code CONFERENCE_JOINED} which is
|
||||||
|
* being dispatched in the specified {@code store}.
|
||||||
|
* @private
|
||||||
|
* @returns {Object} The value returned by {@code next(action)}.
|
||||||
|
*/
|
||||||
|
function _conferenceJoined({ dispatch, getState }, next, action) {
|
||||||
|
const { conference } = action;
|
||||||
|
const { enabled: audioOnly } = getState()['features/base/audio-only'];
|
||||||
|
|
||||||
|
audioOnly && conference.getLastN() !== 0 && dispatch(setLastN(0));
|
||||||
|
|
||||||
|
return 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 {@code action}
|
||||||
|
* is being dispatched.
|
||||||
|
* @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
|
||||||
|
* specified {@code action} to the specified {@code store}.
|
||||||
|
* @param {Action} action - The redux action {@code SET_AUDIO_ONLY} which is
|
||||||
|
* being dispatched in the specified {@code store}.
|
||||||
|
* @private
|
||||||
|
* @returns {Object} The value returned by {@code next(action)}.
|
||||||
|
*/
|
||||||
|
function _setAudioOnly({ dispatch }, next, action) {
|
||||||
|
const { audioOnly } = action;
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
|
||||||
|
return next(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the feature filmstrip that the action {@link SET_FILMSTRIP_ENABLED}
|
||||||
|
* 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 {@code SET_FILMSTRIP_ENABLED} which
|
||||||
|
* is being dispatched in the specified store.
|
||||||
|
* @private
|
||||||
|
* @returns {Object} The value returned by {@code next(action)}.
|
||||||
|
*/
|
||||||
|
function _setFilmstripEnabled({ dispatch, getState }, next, action) {
|
||||||
|
// FIXME This action is not currently dispatched on web.
|
||||||
|
if (typeof APP === 'undefined') {
|
||||||
|
const { enabled } = action;
|
||||||
|
const { enabled: audioOnly } = getState()['features/base/audio-only'];
|
||||||
|
|
||||||
|
audioOnly || dispatch(setLastN(enabled ? undefined : 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return next(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the last N (value) of the video channel in the conference.
|
||||||
|
*
|
||||||
|
* @param {Store} store - The redux store in which the specified {@code action}
|
||||||
|
* is being dispatched.
|
||||||
|
* @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
|
||||||
|
* specified {@code action} to the specified {@code store}.
|
||||||
|
* @param {Action} action - The redux action {@code SET_LASTN} which is being
|
||||||
|
* dispatched in the specified {@code store}.
|
||||||
|
* @private
|
||||||
|
* @returns {Object} The value returned by {@code next(action)}.
|
||||||
|
*/
|
||||||
|
function _setLastN({ getState }, next, action) {
|
||||||
|
const { conference } = getState()['features/base/conference'];
|
||||||
|
|
||||||
|
if (conference) {
|
||||||
|
try {
|
||||||
|
conference.setLastN(action.lastN);
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(`Failed to set lastN: ${err}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return next(action);
|
||||||
|
}
|
|
@ -4,16 +4,20 @@ import {
|
||||||
createStartAudioOnlyEvent,
|
createStartAudioOnlyEvent,
|
||||||
createStartMutedConfigurationEvent,
|
createStartMutedConfigurationEvent,
|
||||||
createSyncTrackStateEvent,
|
createSyncTrackStateEvent,
|
||||||
|
createTrackMutedEvent,
|
||||||
sendAnalytics
|
sendAnalytics
|
||||||
} from '../../analytics';
|
} from '../../analytics';
|
||||||
import { isRoomValid, SET_ROOM, setAudioOnly } from '../conference';
|
import { APP_STATE_CHANGED } from '../../mobile/background';
|
||||||
|
|
||||||
|
import { SET_AUDIO_ONLY, setAudioOnly } from '../audio-only';
|
||||||
|
import { isRoomValid, SET_ROOM } from '../conference';
|
||||||
import JitsiMeetJS from '../lib-jitsi-meet';
|
import JitsiMeetJS from '../lib-jitsi-meet';
|
||||||
import { MiddlewareRegistry } from '../redux';
|
import { MiddlewareRegistry } from '../redux';
|
||||||
import { getPropertyValue } from '../settings';
|
import { getPropertyValue } from '../settings';
|
||||||
import { setTrackMuted, TRACK_ADDED } from '../tracks';
|
import { setTrackMuted, TRACK_ADDED } from '../tracks';
|
||||||
|
|
||||||
import { setAudioMuted, setCameraFacingMode, setVideoMuted } from './actions';
|
import { setAudioMuted, setCameraFacingMode, setVideoMuted } from './actions';
|
||||||
import { CAMERA_FACING_MODE } from './constants';
|
import { CAMERA_FACING_MODE, VIDEO_MUTISM_AUTHORITY } from './constants';
|
||||||
import {
|
import {
|
||||||
_AUDIO_INITIAL_MEDIA_STATE,
|
_AUDIO_INITIAL_MEDIA_STATE,
|
||||||
_VIDEO_INITIAL_MEDIA_STATE
|
_VIDEO_INITIAL_MEDIA_STATE
|
||||||
|
@ -29,6 +33,12 @@ const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||||
*/
|
*/
|
||||||
MiddlewareRegistry.register(store => next => action => {
|
MiddlewareRegistry.register(store => next => action => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
case APP_STATE_CHANGED:
|
||||||
|
return _appStateChanged(store, next, action);
|
||||||
|
|
||||||
|
case SET_AUDIO_ONLY:
|
||||||
|
return _setAudioOnly(store, next, action);
|
||||||
|
|
||||||
case SET_ROOM:
|
case SET_ROOM:
|
||||||
return _setRoom(store, next, action);
|
return _setRoom(store, next, action);
|
||||||
|
|
||||||
|
@ -45,6 +55,51 @@ MiddlewareRegistry.register(store => next => action => {
|
||||||
return next(action);
|
return next(action);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjusts the video muted state based on the app state.
|
||||||
|
*
|
||||||
|
* @param {Store} store - The redux store in which the specified {@code action}
|
||||||
|
* is being dispatched.
|
||||||
|
* @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
|
||||||
|
* specified {@code action} to the specified {@code store}.
|
||||||
|
* @param {Action} action - The redux action {@code APP_STATE_CHANGED} which is
|
||||||
|
* being dispatched in the specified {@code store}.
|
||||||
|
* @private
|
||||||
|
* @returns {Object} The value returned by {@code next(action)}.
|
||||||
|
*/
|
||||||
|
function _appStateChanged({ dispatch }, next, action) {
|
||||||
|
const { appState } = action;
|
||||||
|
const mute = appState !== 'active'; // Note that 'background' and 'inactive' are treated equal.
|
||||||
|
|
||||||
|
sendAnalytics(createTrackMutedEvent('video', 'background mode', mute));
|
||||||
|
|
||||||
|
dispatch(setVideoMuted(mute, VIDEO_MUTISM_AUTHORITY.BACKGROUND));
|
||||||
|
|
||||||
|
return next(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjusts the video muted state based on the audio-only state.
|
||||||
|
*
|
||||||
|
* @param {Store} store - The redux store in which the specified {@code action}
|
||||||
|
* is being dispatched.
|
||||||
|
* @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
|
||||||
|
* specified {@code action} to the specified {@code store}.
|
||||||
|
* @param {Action} action - The redux action {@code SET_AUDIO_ONLY} which is
|
||||||
|
* being dispatched in the specified {@code store}.
|
||||||
|
* @private
|
||||||
|
* @returns {Object} The value returned by {@code next(action)}.
|
||||||
|
*/
|
||||||
|
function _setAudioOnly({ dispatch }, next, action) {
|
||||||
|
const { audioOnly } = action;
|
||||||
|
|
||||||
|
sendAnalytics(createTrackMutedEvent('video', 'audio-only mode', audioOnly));
|
||||||
|
|
||||||
|
dispatch(setVideoMuted(audioOnly, VIDEO_MUTISM_AUTHORITY.AUDIO_ONLY));
|
||||||
|
|
||||||
|
return next(action);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies the feature base/media that the action {@link SET_ROOM} is being
|
* Notifies the feature base/media that the action {@link SET_ROOM} is being
|
||||||
* dispatched within a specific redux {@code store}.
|
* dispatched within a specific redux {@code store}.
|
||||||
|
|
|
@ -314,7 +314,7 @@ export function shouldRenderParticipantVideo(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const audioOnly = state['features/base/conference'].audioOnly;
|
const audioOnly = state['features/base/audio-only'].enabled;
|
||||||
const connectionStatus = participant.connectionStatus
|
const connectionStatus = participant.connectionStatus
|
||||||
|| JitsiParticipantConnectionStatus.ACTIVE;
|
|| JitsiParticipantConnectionStatus.ACTIVE;
|
||||||
const videoTrack = getTrackByMediaTypeAndParticipant(
|
const videoTrack = getTrackByMediaTypeAndParticipant(
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import { setAudioOnly } from '../conference';
|
import { setAudioOnly } from '../audio-only';
|
||||||
import { getLocalParticipant, participantUpdated } from '../participants';
|
import { getLocalParticipant, participantUpdated } from '../participants';
|
||||||
import { MiddlewareRegistry } from '../redux';
|
import { MiddlewareRegistry } from '../redux';
|
||||||
|
|
||||||
|
|
|
@ -4,5 +4,4 @@ export * from './components';
|
||||||
export * from './constants';
|
export * from './constants';
|
||||||
export * from './functions';
|
export * from './functions';
|
||||||
|
|
||||||
import './middleware';
|
|
||||||
import './reducer';
|
import './reducer';
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
// @flow
|
|
||||||
|
|
||||||
import { setLastN } from '../base/conference';
|
|
||||||
import { MiddlewareRegistry } from '../base/redux';
|
|
||||||
|
|
||||||
import { SET_FILMSTRIP_ENABLED } from './actionTypes';
|
|
||||||
|
|
||||||
declare var APP: Object;
|
|
||||||
|
|
||||||
MiddlewareRegistry.register(store => next => action => {
|
|
||||||
switch (action.type) {
|
|
||||||
case SET_FILMSTRIP_ENABLED:
|
|
||||||
return _setFilmstripEnabled(store, next, action);
|
|
||||||
}
|
|
||||||
|
|
||||||
return next(action);
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notifies the feature filmstrip that the action {@link SET_FILMSTRIP_ENABLED}
|
|
||||||
* 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 {@code SET_FILMSTRIP_ENABLED} which
|
|
||||||
* is being dispatched in the specified store.
|
|
||||||
* @private
|
|
||||||
* @returns {Object} The value returned by {@code next(action)}.
|
|
||||||
*/
|
|
||||||
function _setFilmstripEnabled({ dispatch, getState }, next, action) {
|
|
||||||
const result = next(action);
|
|
||||||
|
|
||||||
// FIXME This action is not currently dispatched on web.
|
|
||||||
if (typeof APP === 'undefined') {
|
|
||||||
const state = getState();
|
|
||||||
const { enabled } = state['features/filmstrip'];
|
|
||||||
const { audioOnly } = state['features/base/conference'];
|
|
||||||
|
|
||||||
// FIXME Audio-only mode fiddles with lastN as well. That's why we don't
|
|
||||||
// touch lastN in audio-only mode. But it's not clear what the value of
|
|
||||||
// lastN should be upon exit from audio-only mode if the filmstrip is
|
|
||||||
// disabled already. Currently, audio-only mode will set undefined
|
|
||||||
// regardless of whether the filmstrip is disabled. But we don't have a
|
|
||||||
// practical use case in which audio-only mode is exited while the
|
|
||||||
// filmstrip is disabled.
|
|
||||||
audioOnly || dispatch(setLastN(enabled ? undefined : 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
|
@ -2,12 +2,12 @@
|
||||||
|
|
||||||
import { NativeModules } from 'react-native';
|
import { NativeModules } from 'react-native';
|
||||||
|
|
||||||
|
import { SET_AUDIO_ONLY } from '../../base/audio-only';
|
||||||
import { APP_WILL_MOUNT } from '../../base/app';
|
import { APP_WILL_MOUNT } from '../../base/app';
|
||||||
import {
|
import {
|
||||||
CONFERENCE_FAILED,
|
CONFERENCE_FAILED,
|
||||||
CONFERENCE_LEFT,
|
CONFERENCE_LEFT,
|
||||||
CONFERENCE_JOINED,
|
CONFERENCE_JOINED,
|
||||||
SET_AUDIO_ONLY,
|
|
||||||
getCurrentConference
|
getCurrentConference
|
||||||
} from '../../base/conference';
|
} from '../../base/conference';
|
||||||
import { MiddlewareRegistry } from '../../base/redux';
|
import { MiddlewareRegistry } from '../../base/redux';
|
||||||
|
@ -53,8 +53,9 @@ MiddlewareRegistry.register(({ getState }) => next => action => {
|
||||||
*/
|
*/
|
||||||
case CONFERENCE_JOINED:
|
case CONFERENCE_JOINED:
|
||||||
case SET_AUDIO_ONLY: {
|
case SET_AUDIO_ONLY: {
|
||||||
const { audioOnly, conference }
|
const state = getState();
|
||||||
= getState()['features/base/conference'];
|
const { conference } = state['features/base/conference'];
|
||||||
|
const { enabled: audioOnly } = state['features/base/audio-only'];
|
||||||
|
|
||||||
conference
|
conference
|
||||||
&& (mode = audioOnly
|
&& (mode = audioOnly
|
||||||
|
|
|
@ -1,14 +1,5 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import type { Dispatch } from 'redux';
|
|
||||||
|
|
||||||
import {
|
|
||||||
createTrackMutedEvent,
|
|
||||||
sendAnalytics
|
|
||||||
} from '../../analytics';
|
|
||||||
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';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,34 +19,6 @@ export function _setAppStateListener(listener: ?Function) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Signals that the app should mute video because it's now running in the
|
|
||||||
* background, or unmute it because it came back from the background. If video
|
|
||||||
* was already muted nothing will happen; otherwise, it will be muted. When
|
|
||||||
* coming back from the background the previous state will be restored.
|
|
||||||
*
|
|
||||||
* @param {boolean} muted - True if video should be muted; false, otherwise.
|
|
||||||
* @protected
|
|
||||||
* @returns {Function}
|
|
||||||
*/
|
|
||||||
export function _setBackgroundVideoMuted(muted: boolean) {
|
|
||||||
return (dispatch: Dispatch<any>, getState: Function) => {
|
|
||||||
// Disable remote video when we mute by setting lastN to 0. Skip it if
|
|
||||||
// the conference is in audio-only mode, as it's already configured to
|
|
||||||
// have no video. Leave it as undefined when unmuting, the default value
|
|
||||||
// for last N will be chosen automatically.
|
|
||||||
const { audioOnly } = getState()['features/base/conference'];
|
|
||||||
|
|
||||||
audioOnly || dispatch(setLastN(muted ? 0 : undefined));
|
|
||||||
|
|
||||||
sendAnalytics(createTrackMutedEvent(
|
|
||||||
'video',
|
|
||||||
'callkit.background.video'));
|
|
||||||
|
|
||||||
dispatch(setVideoMuted(muted, VIDEO_MUTISM_AUTHORITY.BACKGROUND));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signals that the App state has changed (in terms of execution state). The
|
* Signals that the App state has changed (in terms of execution state). The
|
||||||
* application can be in 3 states: 'active', 'inactive' and 'background'.
|
* application can be in 3 states: 'active', 'inactive' and 'background'.
|
||||||
|
|
|
@ -8,13 +8,9 @@ import { MiddlewareRegistry } from '../../base/redux';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
_setAppStateListener as _setAppStateListenerA,
|
_setAppStateListener as _setAppStateListenerA,
|
||||||
_setBackgroundVideoMuted,
|
|
||||||
appStateChanged
|
appStateChanged
|
||||||
} from './actions';
|
} from './actions';
|
||||||
import {
|
import { _SET_APP_STATE_LISTENER } from './actionTypes';
|
||||||
_SET_APP_STATE_LISTENER,
|
|
||||||
APP_STATE_CHANGED
|
|
||||||
} from './actionTypes';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Middleware that captures App lifetime actions and subscribes to application
|
* Middleware that captures App lifetime actions and subscribes to application
|
||||||
|
@ -31,15 +27,10 @@ MiddlewareRegistry.register(store => next => action => {
|
||||||
case _SET_APP_STATE_LISTENER:
|
case _SET_APP_STATE_LISTENER:
|
||||||
return _setAppStateListenerF(store, next, action);
|
return _setAppStateListenerF(store, next, action);
|
||||||
|
|
||||||
case APP_STATE_CHANGED:
|
|
||||||
_appStateChanged(store.dispatch, action.appState);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case APP_WILL_MOUNT: {
|
case APP_WILL_MOUNT: {
|
||||||
const { dispatch } = store;
|
const { dispatch } = store;
|
||||||
|
|
||||||
dispatch(
|
dispatch(_setAppStateListenerA(_onAppStateChange.bind(undefined, dispatch)));
|
||||||
_setAppStateListenerA(_onAppStateChange.bind(undefined, dispatch)));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,37 +42,6 @@ MiddlewareRegistry.register(store => next => action => {
|
||||||
return next(action);
|
return next(action);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles app state changes. Dispatches the necessary redux actions for the
|
|
||||||
* local video to be muted when the app goes to the background, and to be
|
|
||||||
* unmuted when the app comes back.
|
|
||||||
*
|
|
||||||
* @param {Dispatch} dispatch - The redux {@code dispatch} function.
|
|
||||||
* @param {string} appState - The current app state.
|
|
||||||
* @private
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
function _appStateChanged(dispatch: Function, appState: string) {
|
|
||||||
let muted;
|
|
||||||
|
|
||||||
switch (appState) {
|
|
||||||
case 'active':
|
|
||||||
muted = false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'background':
|
|
||||||
muted = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'inactive':
|
|
||||||
default:
|
|
||||||
// XXX: We purposely don't handle the 'inactive' app state.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(_setBackgroundVideoMuted(muted));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by React Native's AppState API to notify that the application state
|
* Called by React Native's AppState API to notify that the application state
|
||||||
* has changed. Dispatches the change within the (associated) redux store.
|
* has changed. Dispatches the change within the (associated) redux store.
|
|
@ -14,22 +14,20 @@ const DEFAULT_STATE = {
|
||||||
appState: 'active'
|
appState: 'active'
|
||||||
};
|
};
|
||||||
|
|
||||||
ReducerRegistry.register(
|
ReducerRegistry.register('features/background', (state = DEFAULT_STATE, action) => {
|
||||||
'features/background',
|
switch (action.type) {
|
||||||
(state = DEFAULT_STATE, action) => {
|
case _SET_APP_STATE_LISTENER:
|
||||||
switch (action.type) {
|
return {
|
||||||
case _SET_APP_STATE_LISTENER:
|
...state,
|
||||||
return {
|
appStateListener: action.listener
|
||||||
...state,
|
};
|
||||||
appStateListener: action.listener
|
|
||||||
};
|
|
||||||
|
|
||||||
case APP_STATE_CHANGED:
|
case APP_STATE_CHANGED:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
appState: action.appState
|
appState: action.appState
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,13 +6,13 @@ import uuid from 'uuid';
|
||||||
import { createTrackMutedEvent, sendAnalytics } from '../../analytics';
|
import { createTrackMutedEvent, sendAnalytics } from '../../analytics';
|
||||||
import { appNavigate } from '../../app';
|
import { appNavigate } from '../../app';
|
||||||
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../../base/app';
|
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../../base/app';
|
||||||
|
import { SET_AUDIO_ONLY } from '../../base/audio-only';
|
||||||
import {
|
import {
|
||||||
CONFERENCE_FAILED,
|
CONFERENCE_FAILED,
|
||||||
CONFERENCE_JOINED,
|
CONFERENCE_JOINED,
|
||||||
CONFERENCE_LEFT,
|
CONFERENCE_LEFT,
|
||||||
CONFERENCE_WILL_JOIN,
|
CONFERENCE_WILL_JOIN,
|
||||||
CONFERENCE_WILL_LEAVE,
|
CONFERENCE_WILL_LEAVE,
|
||||||
SET_AUDIO_ONLY,
|
|
||||||
getConferenceName,
|
getConferenceName,
|
||||||
getCurrentConference
|
getCurrentConference
|
||||||
} from '../../base/conference';
|
} from '../../base/conference';
|
||||||
|
|
|
@ -46,7 +46,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||||
|
|
||||||
StateListenerRegistry.register(
|
StateListenerRegistry.register(
|
||||||
/* selector */ state => {
|
/* selector */ state => {
|
||||||
const { audioOnly } = state['features/base/conference'];
|
const { enabled: audioOnly } = state['features/base/audio-only'];
|
||||||
const conference = getCurrentConference(state);
|
const conference = getCurrentConference(state);
|
||||||
|
|
||||||
return conference ? !audioOnly : false;
|
return conference ? !audioOnly : false;
|
||||||
|
@ -68,7 +68,7 @@ function _onImmersiveChange({ getState }) {
|
||||||
const { appState } = state['features/background'];
|
const { appState } = state['features/background'];
|
||||||
|
|
||||||
if (appState === 'active') {
|
if (appState === 'active') {
|
||||||
const { audioOnly } = state['features/base/conference'];
|
const { enabled: audioOnly } = state['features/base/audio-only'];
|
||||||
const conference = getCurrentConference(state);
|
const conference = getCurrentConference(state);
|
||||||
const fullScreen = conference ? !audioOnly : false;
|
const fullScreen = conference ? !audioOnly : false;
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { StateListenerRegistry } from '../../base/redux';
|
||||||
*/
|
*/
|
||||||
StateListenerRegistry.register(
|
StateListenerRegistry.register(
|
||||||
/* selector */ state => {
|
/* selector */ state => {
|
||||||
const { audioOnly } = state['features/base/conference'];
|
const { enabled: audioOnly } = state['features/base/audio-only'];
|
||||||
const conference = getCurrentConference(state);
|
const conference = getCurrentConference(state);
|
||||||
|
|
||||||
return Boolean(conference && audioOnly);
|
return Boolean(conference && audioOnly);
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { StateListenerRegistry } from '../../base/redux';
|
||||||
*/
|
*/
|
||||||
StateListenerRegistry.register(
|
StateListenerRegistry.register(
|
||||||
/* selector */ state => {
|
/* selector */ state => {
|
||||||
const { audioOnly } = state['features/base/conference'];
|
const { enabled: audioOnly } = state['features/base/audio-only'];
|
||||||
const conference = getCurrentConference(state);
|
const conference = getCurrentConference(state);
|
||||||
|
|
||||||
return Boolean(conference && !audioOnly);
|
return Boolean(conference && !audioOnly);
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
createToolbarEvent,
|
createToolbarEvent,
|
||||||
sendAnalytics
|
sendAnalytics
|
||||||
} from '../../analytics';
|
} from '../../analytics';
|
||||||
import { setAudioOnly } from '../../base/conference';
|
import { setAudioOnly } from '../../base/audio-only';
|
||||||
import { translate } from '../../base/i18n';
|
import { translate } from '../../base/i18n';
|
||||||
import {
|
import {
|
||||||
MEDIA_TYPE,
|
MEDIA_TYPE,
|
||||||
|
@ -162,7 +162,7 @@ class VideoMuteButton extends AbstractVideoMuteButton<Props, *> {
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
function _mapStateToProps(state): Object {
|
function _mapStateToProps(state): Object {
|
||||||
const { audioOnly } = state['features/base/conference'];
|
const { enabled: audioOnly } = state['features/base/audio-only'];
|
||||||
const tracks = state['features/base/tracks'];
|
const tracks = state['features/base/tracks'];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import { toggleAudioOnly } from '../../../base/conference';
|
import { toggleAudioOnly } from '../../../base/audio-only';
|
||||||
import { translate } from '../../../base/i18n';
|
import { translate } from '../../../base/i18n';
|
||||||
import { connect } from '../../../base/redux';
|
import { connect } from '../../../base/redux';
|
||||||
import { AbstractButton } from '../../../base/toolbox';
|
import { AbstractButton } from '../../../base/toolbox';
|
||||||
|
@ -66,7 +66,7 @@ class AudioOnlyButton extends AbstractButton<Props, *> {
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
function _mapStateToProps(state): Object {
|
function _mapStateToProps(state): Object {
|
||||||
const { audioOnly } = state['features/base/conference'];
|
const { enabled: audioOnly } = state['features/base/audio-only'];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
_audioOnly: Boolean(audioOnly)
|
_audioOnly: Boolean(audioOnly)
|
||||||
|
|
|
@ -71,7 +71,7 @@ class ToggleCameraButton extends AbstractButton<Props, *> {
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
function _mapStateToProps(state): Object {
|
function _mapStateToProps(state): Object {
|
||||||
const { audioOnly } = state['features/base/conference'];
|
const { enabled: audioOnly } = state['features/base/audio-only'];
|
||||||
const tracks = state['features/base/tracks'];
|
const tracks = state['features/base/tracks'];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -33,7 +33,7 @@ export default class AbstractVideoQualityLabel<P: Props> extends Component<P> {
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
export function _abstractMapStateToProps(state: Object) {
|
export function _abstractMapStateToProps(state: Object) {
|
||||||
const { audioOnly } = state['features/base/conference'];
|
const { enabled: audioOnly } = state['features/base/audio-only'];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
_audioOnly: audioOnly
|
_audioOnly: audioOnly
|
||||||
|
|
|
@ -96,7 +96,7 @@ class OverflowMenuVideoQualityItem extends Component<Props> {
|
||||||
*/
|
*/
|
||||||
function _mapStateToProps(state) {
|
function _mapStateToProps(state) {
|
||||||
return {
|
return {
|
||||||
_audioOnly: state['features/base/conference'].audioOnly,
|
_audioOnly: state['features/base/audio-only'].enabled,
|
||||||
_receiverVideoQuality:
|
_receiverVideoQuality:
|
||||||
state['features/base/conference'].preferredReceiverVideoQuality
|
state['features/base/conference'].preferredReceiverVideoQuality
|
||||||
};
|
};
|
||||||
|
|
|
@ -156,7 +156,7 @@ function _mapResolutionToTranslationsKeys(resolution) {
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
function _mapStateToProps(state) {
|
function _mapStateToProps(state) {
|
||||||
const { audioOnly } = state['features/base/conference'];
|
const { enabled: audioOnly } = state['features/base/audio-only'];
|
||||||
const { resolution, participantId } = state['features/large-video'];
|
const { resolution, participantId } = state['features/large-video'];
|
||||||
const videoTrackOnLargeVideo = getTrackByMediaTypeAndParticipant(
|
const videoTrackOnLargeVideo = getTrackByMediaTypeAndParticipant(
|
||||||
state['features/base/tracks'],
|
state['features/base/tracks'],
|
||||||
|
|
|
@ -4,15 +4,9 @@ import InlineMessage from '@atlaskit/inline-message';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import type { Dispatch } from 'redux';
|
import type { Dispatch } from 'redux';
|
||||||
|
|
||||||
import {
|
import { createToolbarEvent, sendAnalytics } from '../../analytics';
|
||||||
createToolbarEvent,
|
import { setAudioOnly } from '../../base/audio-only';
|
||||||
sendAnalytics
|
import { VIDEO_QUALITY_LEVELS, setPreferredReceiverVideoQuality } from '../../base/conference';
|
||||||
} from '../../analytics';
|
|
||||||
import {
|
|
||||||
VIDEO_QUALITY_LEVELS,
|
|
||||||
setAudioOnly,
|
|
||||||
setPreferredReceiverVideoQuality
|
|
||||||
} from '../../base/conference';
|
|
||||||
import { translate } from '../../base/i18n';
|
import { translate } from '../../base/i18n';
|
||||||
import JitsiMeetJS from '../../base/lib-jitsi-meet';
|
import JitsiMeetJS from '../../base/lib-jitsi-meet';
|
||||||
import { connect } from '../../base/redux';
|
import { connect } from '../../base/redux';
|
||||||
|
@ -406,11 +400,8 @@ class VideoQualitySlider extends Component<Props> {
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
function _mapStateToProps(state) {
|
function _mapStateToProps(state) {
|
||||||
const {
|
const { enabled: audioOnly } = state['features/base/audio-only'];
|
||||||
audioOnly,
|
const { p2p, preferredReceiverVideoQuality } = state['features/base/conference'];
|
||||||
p2p,
|
|
||||||
preferredReceiverVideoQuality
|
|
||||||
} = state['features/base/conference'];
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
_audioOnly: audioOnly,
|
_audioOnly: audioOnly,
|
||||||
|
|
Loading…
Reference in New Issue