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}
|
||||
*/
|
||||
isAudioOnly() {
|
||||
return Boolean(
|
||||
APP.store.getState()['features/base/conference'].audioOnly);
|
||||
return Boolean(APP.store.getState()['features/base/audio-only'].enabled);
|
||||
},
|
||||
|
||||
videoSwitchInProgress: false,
|
||||
|
|
|
@ -7,8 +7,8 @@ import { toURLString } from '../../base/util';
|
|||
import '../../follow-me';
|
||||
import { OverlayContainer } from '../../overlay';
|
||||
|
||||
// Enable rejoin analytics
|
||||
import '../../rejoin';
|
||||
import '../../base/lastn'; // Register lastN middleware
|
||||
import '../../rejoin'; // Enable rejoin analytics
|
||||
|
||||
import { appNavigate } from '../actions';
|
||||
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';
|
||||
|
||||
/**
|
||||
* 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 current conference.
|
||||
|
@ -152,16 +141,6 @@ export const SET_DESKTOP_SHARING_ENABLED
|
|||
*/
|
||||
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
|
||||
* received from remote participants, even if the user prefers a larger video
|
||||
|
|
|
@ -35,10 +35,8 @@ import {
|
|||
KICKED_OUT,
|
||||
LOCK_STATE_CHANGED,
|
||||
P2P_STATUS_CHANGED,
|
||||
SET_AUDIO_ONLY,
|
||||
SET_DESKTOP_SHARING_ENABLED,
|
||||
SET_FOLLOW_ME,
|
||||
SET_LASTN,
|
||||
SET_MAX_RECEIVER_VIDEO_QUALITY,
|
||||
SET_PASSWORD,
|
||||
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.
|
||||
*
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -4,7 +4,6 @@ import { reloadNow } from '../../app';
|
|||
import {
|
||||
ACTION_PINNED,
|
||||
ACTION_UNPINNED,
|
||||
createAudioOnlyChangedEvent,
|
||||
createConnectionEvent,
|
||||
createOfferAnswerFailedEvent,
|
||||
createPinnedEvent,
|
||||
|
@ -12,7 +11,6 @@ import {
|
|||
} from '../../analytics';
|
||||
import { CONNECTION_ESTABLISHED, CONNECTION_FAILED } from '../connection';
|
||||
import { JitsiConferenceErrors } from '../lib-jitsi-meet';
|
||||
import { setVideoMuted, VIDEO_MUTISM_AUTHORITY } from '../media';
|
||||
import {
|
||||
getParticipantById,
|
||||
getPinnedParticipant,
|
||||
|
@ -20,14 +18,12 @@ import {
|
|||
PIN_PARTICIPANT
|
||||
} from '../participants';
|
||||
import { MiddlewareRegistry, StateListenerRegistry } from '../redux';
|
||||
import UIEvents from '../../../../service/UI/UIEvents';
|
||||
import { TRACK_ADDED, TRACK_REMOVED } from '../tracks';
|
||||
|
||||
import {
|
||||
conferenceFailed,
|
||||
conferenceWillLeave,
|
||||
createConference,
|
||||
setLastN,
|
||||
setSubject
|
||||
} from './actions';
|
||||
import {
|
||||
|
@ -36,16 +32,14 @@ import {
|
|||
CONFERENCE_SUBJECT_CHANGED,
|
||||
CONFERENCE_WILL_LEAVE,
|
||||
DATA_CHANNEL_OPENED,
|
||||
SET_AUDIO_ONLY,
|
||||
SET_LASTN,
|
||||
SET_PENDING_SUBJECT_CHANGE,
|
||||
SET_ROOM
|
||||
} from './actionTypes';
|
||||
import {
|
||||
_addLocalTracksToConference,
|
||||
_removeLocalTracksFromConference,
|
||||
forEachConference,
|
||||
getCurrentConference,
|
||||
_removeLocalTracksFromConference
|
||||
getCurrentConference
|
||||
} from './functions';
|
||||
|
||||
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||
|
@ -93,12 +87,6 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
case PIN_PARTICIPANT:
|
||||
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:
|
||||
return _setRoom(store, next, action);
|
||||
|
||||
|
@ -197,21 +185,10 @@ function _conferenceFailed(store, next, action) {
|
|||
*/
|
||||
function _conferenceJoined({ dispatch, getState }, next, action) {
|
||||
const result = next(action);
|
||||
const { conference } = action;
|
||||
const { pendingSubjectChange } = getState()['features/base/conference'];
|
||||
|
||||
const {
|
||||
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));
|
||||
pendingSubjectChange && dispatch(setSubject(pendingSubjectChange));
|
||||
|
||||
// FIXME: Very dirty solution. This will work on web only.
|
||||
// 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* on the user preference and the internal maximum.
|
||||
|
|
|
@ -15,7 +15,6 @@ import {
|
|||
CONFERENCE_WILL_LEAVE,
|
||||
LOCK_STATE_CHANGED,
|
||||
P2P_STATUS_CHANGED,
|
||||
SET_AUDIO_ONLY,
|
||||
SET_DESKTOP_SHARING_ENABLED,
|
||||
SET_FOLLOW_ME,
|
||||
SET_MAX_RECEIVER_VIDEO_QUALITY,
|
||||
|
@ -76,9 +75,6 @@ ReducerRegistry.register(
|
|||
case P2P_STATUS_CHANGED:
|
||||
return _p2pStatusChanged(state, action);
|
||||
|
||||
case SET_AUDIO_ONLY:
|
||||
return _setAudioOnly(state, action);
|
||||
|
||||
case SET_DESKTOP_SHARING_ENABLED:
|
||||
return _setDesktopSharingEnabled(state, action);
|
||||
|
||||
|
@ -346,20 +342,6 @@ function _p2pStatusChanged(state, action) {
|
|||
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
|
||||
* 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,
|
||||
createStartMutedConfigurationEvent,
|
||||
createSyncTrackStateEvent,
|
||||
createTrackMutedEvent,
|
||||
sendAnalytics
|
||||
} 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 { MiddlewareRegistry } from '../redux';
|
||||
import { getPropertyValue } from '../settings';
|
||||
import { setTrackMuted, TRACK_ADDED } from '../tracks';
|
||||
|
||||
import { setAudioMuted, setCameraFacingMode, setVideoMuted } from './actions';
|
||||
import { CAMERA_FACING_MODE } from './constants';
|
||||
import { CAMERA_FACING_MODE, VIDEO_MUTISM_AUTHORITY } from './constants';
|
||||
import {
|
||||
_AUDIO_INITIAL_MEDIA_STATE,
|
||||
_VIDEO_INITIAL_MEDIA_STATE
|
||||
|
@ -29,6 +33,12 @@ const logger = require('jitsi-meet-logger').getLogger(__filename);
|
|||
*/
|
||||
MiddlewareRegistry.register(store => next => action => {
|
||||
switch (action.type) {
|
||||
case APP_STATE_CHANGED:
|
||||
return _appStateChanged(store, next, action);
|
||||
|
||||
case SET_AUDIO_ONLY:
|
||||
return _setAudioOnly(store, next, action);
|
||||
|
||||
case SET_ROOM:
|
||||
return _setRoom(store, next, action);
|
||||
|
||||
|
@ -45,6 +55,51 @@ MiddlewareRegistry.register(store => 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
|
||||
* dispatched within a specific redux {@code store}.
|
||||
|
|
|
@ -314,7 +314,7 @@ export function shouldRenderParticipantVideo(
|
|||
return false;
|
||||
}
|
||||
|
||||
const audioOnly = state['features/base/conference'].audioOnly;
|
||||
const audioOnly = state['features/base/audio-only'].enabled;
|
||||
const connectionStatus = participant.connectionStatus
|
||||
|| JitsiParticipantConnectionStatus.ACTIVE;
|
||||
const videoTrack = getTrackByMediaTypeAndParticipant(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// @flow
|
||||
|
||||
import { setAudioOnly } from '../conference';
|
||||
import { setAudioOnly } from '../audio-only';
|
||||
import { getLocalParticipant, participantUpdated } from '../participants';
|
||||
import { MiddlewareRegistry } from '../redux';
|
||||
|
||||
|
|
|
@ -4,5 +4,4 @@ export * from './components';
|
|||
export * from './constants';
|
||||
export * from './functions';
|
||||
|
||||
import './middleware';
|
||||
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 { SET_AUDIO_ONLY } from '../../base/audio-only';
|
||||
import { APP_WILL_MOUNT } from '../../base/app';
|
||||
import {
|
||||
CONFERENCE_FAILED,
|
||||
CONFERENCE_LEFT,
|
||||
CONFERENCE_JOINED,
|
||||
SET_AUDIO_ONLY,
|
||||
getCurrentConference
|
||||
} from '../../base/conference';
|
||||
import { MiddlewareRegistry } from '../../base/redux';
|
||||
|
@ -53,8 +53,9 @@ MiddlewareRegistry.register(({ getState }) => next => action => {
|
|||
*/
|
||||
case CONFERENCE_JOINED:
|
||||
case SET_AUDIO_ONLY: {
|
||||
const { audioOnly, conference }
|
||||
= getState()['features/base/conference'];
|
||||
const state = getState();
|
||||
const { conference } = state['features/base/conference'];
|
||||
const { enabled: audioOnly } = state['features/base/audio-only'];
|
||||
|
||||
conference
|
||||
&& (mode = audioOnly
|
||||
|
|
|
@ -1,14 +1,5 @@
|
|||
// @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';
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
* application can be in 3 states: 'active', 'inactive' and 'background'.
|
||||
|
|
|
@ -8,13 +8,9 @@ import { MiddlewareRegistry } from '../../base/redux';
|
|||
|
||||
import {
|
||||
_setAppStateListener as _setAppStateListenerA,
|
||||
_setBackgroundVideoMuted,
|
||||
appStateChanged
|
||||
} from './actions';
|
||||
import {
|
||||
_SET_APP_STATE_LISTENER,
|
||||
APP_STATE_CHANGED
|
||||
} from './actionTypes';
|
||||
import { _SET_APP_STATE_LISTENER } from './actionTypes';
|
||||
|
||||
/**
|
||||
* Middleware that captures App lifetime actions and subscribes to application
|
||||
|
@ -31,15 +27,10 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
case _SET_APP_STATE_LISTENER:
|
||||
return _setAppStateListenerF(store, next, action);
|
||||
|
||||
case APP_STATE_CHANGED:
|
||||
_appStateChanged(store.dispatch, action.appState);
|
||||
break;
|
||||
|
||||
case APP_WILL_MOUNT: {
|
||||
const { dispatch } = store;
|
||||
|
||||
dispatch(
|
||||
_setAppStateListenerA(_onAppStateChange.bind(undefined, dispatch)));
|
||||
dispatch(_setAppStateListenerA(_onAppStateChange.bind(undefined, dispatch)));
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -51,37 +42,6 @@ MiddlewareRegistry.register(store => 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
|
||||
* has changed. Dispatches the change within the (associated) redux store.
|
|
@ -14,9 +14,7 @@ const DEFAULT_STATE = {
|
|||
appState: 'active'
|
||||
};
|
||||
|
||||
ReducerRegistry.register(
|
||||
'features/background',
|
||||
(state = DEFAULT_STATE, action) => {
|
||||
ReducerRegistry.register('features/background', (state = DEFAULT_STATE, action) => {
|
||||
switch (action.type) {
|
||||
case _SET_APP_STATE_LISTENER:
|
||||
return {
|
||||
|
@ -32,4 +30,4 @@ ReducerRegistry.register(
|
|||
}
|
||||
|
||||
return state;
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,13 +6,13 @@ import uuid from 'uuid';
|
|||
import { createTrackMutedEvent, sendAnalytics } from '../../analytics';
|
||||
import { appNavigate } from '../../app';
|
||||
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../../base/app';
|
||||
import { SET_AUDIO_ONLY } from '../../base/audio-only';
|
||||
import {
|
||||
CONFERENCE_FAILED,
|
||||
CONFERENCE_JOINED,
|
||||
CONFERENCE_LEFT,
|
||||
CONFERENCE_WILL_JOIN,
|
||||
CONFERENCE_WILL_LEAVE,
|
||||
SET_AUDIO_ONLY,
|
||||
getConferenceName,
|
||||
getCurrentConference
|
||||
} from '../../base/conference';
|
||||
|
|
|
@ -46,7 +46,7 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
|
||||
StateListenerRegistry.register(
|
||||
/* selector */ state => {
|
||||
const { audioOnly } = state['features/base/conference'];
|
||||
const { enabled: audioOnly } = state['features/base/audio-only'];
|
||||
const conference = getCurrentConference(state);
|
||||
|
||||
return conference ? !audioOnly : false;
|
||||
|
@ -68,7 +68,7 @@ function _onImmersiveChange({ getState }) {
|
|||
const { appState } = state['features/background'];
|
||||
|
||||
if (appState === 'active') {
|
||||
const { audioOnly } = state['features/base/conference'];
|
||||
const { enabled: audioOnly } = state['features/base/audio-only'];
|
||||
const conference = getCurrentConference(state);
|
||||
const fullScreen = conference ? !audioOnly : false;
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import { StateListenerRegistry } from '../../base/redux';
|
|||
*/
|
||||
StateListenerRegistry.register(
|
||||
/* selector */ state => {
|
||||
const { audioOnly } = state['features/base/conference'];
|
||||
const { enabled: audioOnly } = state['features/base/audio-only'];
|
||||
const conference = getCurrentConference(state);
|
||||
|
||||
return Boolean(conference && audioOnly);
|
||||
|
|
|
@ -9,7 +9,7 @@ import { StateListenerRegistry } from '../../base/redux';
|
|||
*/
|
||||
StateListenerRegistry.register(
|
||||
/* selector */ state => {
|
||||
const { audioOnly } = state['features/base/conference'];
|
||||
const { enabled: audioOnly } = state['features/base/audio-only'];
|
||||
const conference = getCurrentConference(state);
|
||||
|
||||
return Boolean(conference && !audioOnly);
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
createToolbarEvent,
|
||||
sendAnalytics
|
||||
} from '../../analytics';
|
||||
import { setAudioOnly } from '../../base/conference';
|
||||
import { setAudioOnly } from '../../base/audio-only';
|
||||
import { translate } from '../../base/i18n';
|
||||
import {
|
||||
MEDIA_TYPE,
|
||||
|
@ -162,7 +162,7 @@ class VideoMuteButton extends AbstractVideoMuteButton<Props, *> {
|
|||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state): Object {
|
||||
const { audioOnly } = state['features/base/conference'];
|
||||
const { enabled: audioOnly } = state['features/base/audio-only'];
|
||||
const tracks = state['features/base/tracks'];
|
||||
|
||||
return {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// @flow
|
||||
|
||||
import { toggleAudioOnly } from '../../../base/conference';
|
||||
import { toggleAudioOnly } from '../../../base/audio-only';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { AbstractButton } from '../../../base/toolbox';
|
||||
|
@ -66,7 +66,7 @@ class AudioOnlyButton extends AbstractButton<Props, *> {
|
|||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state): Object {
|
||||
const { audioOnly } = state['features/base/conference'];
|
||||
const { enabled: audioOnly } = state['features/base/audio-only'];
|
||||
|
||||
return {
|
||||
_audioOnly: Boolean(audioOnly)
|
||||
|
|
|
@ -71,7 +71,7 @@ class ToggleCameraButton extends AbstractButton<Props, *> {
|
|||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state): Object {
|
||||
const { audioOnly } = state['features/base/conference'];
|
||||
const { enabled: audioOnly } = state['features/base/audio-only'];
|
||||
const tracks = state['features/base/tracks'];
|
||||
|
||||
return {
|
||||
|
|
|
@ -33,7 +33,7 @@ export default class AbstractVideoQualityLabel<P: Props> extends Component<P> {
|
|||
* }}
|
||||
*/
|
||||
export function _abstractMapStateToProps(state: Object) {
|
||||
const { audioOnly } = state['features/base/conference'];
|
||||
const { enabled: audioOnly } = state['features/base/audio-only'];
|
||||
|
||||
return {
|
||||
_audioOnly: audioOnly
|
||||
|
|
|
@ -96,7 +96,7 @@ class OverflowMenuVideoQualityItem extends Component<Props> {
|
|||
*/
|
||||
function _mapStateToProps(state) {
|
||||
return {
|
||||
_audioOnly: state['features/base/conference'].audioOnly,
|
||||
_audioOnly: state['features/base/audio-only'].enabled,
|
||||
_receiverVideoQuality:
|
||||
state['features/base/conference'].preferredReceiverVideoQuality
|
||||
};
|
||||
|
|
|
@ -156,7 +156,7 @@ function _mapResolutionToTranslationsKeys(resolution) {
|
|||
* }}
|
||||
*/
|
||||
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 videoTrackOnLargeVideo = getTrackByMediaTypeAndParticipant(
|
||||
state['features/base/tracks'],
|
||||
|
|
|
@ -4,15 +4,9 @@ import InlineMessage from '@atlaskit/inline-message';
|
|||
import React, { Component } from 'react';
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import {
|
||||
createToolbarEvent,
|
||||
sendAnalytics
|
||||
} from '../../analytics';
|
||||
import {
|
||||
VIDEO_QUALITY_LEVELS,
|
||||
setAudioOnly,
|
||||
setPreferredReceiverVideoQuality
|
||||
} from '../../base/conference';
|
||||
import { createToolbarEvent, sendAnalytics } from '../../analytics';
|
||||
import { setAudioOnly } from '../../base/audio-only';
|
||||
import { VIDEO_QUALITY_LEVELS, setPreferredReceiverVideoQuality } from '../../base/conference';
|
||||
import { translate } from '../../base/i18n';
|
||||
import JitsiMeetJS from '../../base/lib-jitsi-meet';
|
||||
import { connect } from '../../base/redux';
|
||||
|
@ -406,11 +400,8 @@ class VideoQualitySlider extends Component<Props> {
|
|||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
const {
|
||||
audioOnly,
|
||||
p2p,
|
||||
preferredReceiverVideoQuality
|
||||
} = state['features/base/conference'];
|
||||
const { enabled: audioOnly } = state['features/base/audio-only'];
|
||||
const { p2p, preferredReceiverVideoQuality } = state['features/base/conference'];
|
||||
|
||||
return {
|
||||
_audioOnly: audioOnly,
|
||||
|
|
Loading…
Reference in New Issue