rn: add a new advanced settings section
Currently only 2 options are implemented, mainly aimed at helping troubleshoot audio related problems: - Disable native call integration (it disables CallKit / ConnectionService) - Disable P2P
This commit is contained in:
parent
fe90e5aa8f
commit
6d16e087d9
|
@ -526,16 +526,20 @@
|
||||||
"title": "Settings"
|
"title": "Settings"
|
||||||
},
|
},
|
||||||
"settingsView": {
|
"settingsView": {
|
||||||
|
"advanced": "Advanced",
|
||||||
"alertOk": "OK",
|
"alertOk": "OK",
|
||||||
"alertTitle": "Warning",
|
"alertTitle": "Warning",
|
||||||
"alertURLText": "The entered server URL is invalid",
|
"alertURLText": "The entered server URL is invalid",
|
||||||
"buildInfoSection": "Build Information",
|
"buildInfoSection": "Build Information",
|
||||||
"conferenceSection": "Conference",
|
"conferenceSection": "Conference",
|
||||||
|
"disableCallIntegration": "Disable native call integration",
|
||||||
|
"disableP2P": "Disable Peer-To-Peer mode",
|
||||||
"displayName": "Display name",
|
"displayName": "Display name",
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
"header": "Settings",
|
"header": "Settings",
|
||||||
"profileSection": "Profile",
|
"profileSection": "Profile",
|
||||||
"serverURL": "Server URL",
|
"serverURL": "Server URL",
|
||||||
|
"showAdvanced": "Show advanced settings",
|
||||||
"startWithAudioMuted": "Start with audio muted",
|
"startWithAudioMuted": "Start with audio muted",
|
||||||
"startWithVideoMuted": "Start with video muted",
|
"startWithVideoMuted": "Start with video muted",
|
||||||
"version": "Version"
|
"version": "Version"
|
||||||
|
|
|
@ -6,7 +6,7 @@ import '../../analytics';
|
||||||
import '../../authentication';
|
import '../../authentication';
|
||||||
import { setColorScheme } from '../../base/color-scheme';
|
import { setColorScheme } from '../../base/color-scheme';
|
||||||
import { DialogContainer } from '../../base/dialog';
|
import { DialogContainer } from '../../base/dialog';
|
||||||
import { updateFlags } from '../../base/flags';
|
import { CALL_INTEGRATION_ENABLED, updateFlags } from '../../base/flags';
|
||||||
import '../../base/jwt';
|
import '../../base/jwt';
|
||||||
import { Platform } from '../../base/react';
|
import { Platform } from '../../base/react';
|
||||||
import {
|
import {
|
||||||
|
@ -100,6 +100,13 @@ export class App extends AbstractApp {
|
||||||
dispatch(setColorScheme(this.props.colorScheme));
|
dispatch(setColorScheme(this.props.colorScheme));
|
||||||
dispatch(updateFlags(this.props.flags));
|
dispatch(updateFlags(this.props.flags));
|
||||||
dispatch(updateSettings(this.props.userInfo || {}));
|
dispatch(updateSettings(this.props.userInfo || {}));
|
||||||
|
|
||||||
|
// Update settings with feature-flag.
|
||||||
|
const callIntegrationEnabled = this.props.flags[CALL_INTEGRATION_ENABLED];
|
||||||
|
|
||||||
|
if (typeof callIntegrationEnabled !== 'undefined') {
|
||||||
|
dispatch(updateSettings({ disableCallIntegration: !callIntegrationEnabled }));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,3 +34,17 @@ export const LOAD_CONFIG_ERROR = 'LOAD_CONFIG_ERROR';
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
export const SET_CONFIG = 'SET_CONFIG';
|
export const SET_CONFIG = 'SET_CONFIG';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The redux action which updates the configuration represented by the feature
|
||||||
|
* base/config. The configuration is defined and consumed by the library
|
||||||
|
* lib-jitsi-meet but some of its properties are consumed by the application
|
||||||
|
* jitsi-meet as well. A merge operation is performed between the existing config
|
||||||
|
* and the passed object.
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* type: _UPDATE_CONFIG,
|
||||||
|
* config: Object
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
export const _UPDATE_CONFIG = '_UPDATE_CONFIG';
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { addKnownDomains } from '../known-domains';
|
||||||
import { MiddlewareRegistry } from '../redux';
|
import { MiddlewareRegistry } from '../redux';
|
||||||
import { parseURIString } from '../util';
|
import { parseURIString } from '../util';
|
||||||
|
|
||||||
import { SET_CONFIG } from './actionTypes';
|
import { _UPDATE_CONFIG, SET_CONFIG } from './actionTypes';
|
||||||
import { _CONFIG_STORE_PREFIX } from './constants';
|
import { _CONFIG_STORE_PREFIX } from './constants';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -93,11 +93,25 @@ function _appWillMount(store, next, action) {
|
||||||
* @private
|
* @private
|
||||||
* @returns {*} The return value of {@code next(action)}.
|
* @returns {*} The return value of {@code next(action)}.
|
||||||
*/
|
*/
|
||||||
function _setConfig({ getState }, next, action) {
|
function _setConfig({ dispatch, getState }, next, action) {
|
||||||
// The reducer is doing some alterations to the config passed in the action,
|
// The reducer is doing some alterations to the config passed in the action,
|
||||||
// so make sure it's the final state by waiting for the action to be
|
// so make sure it's the final state by waiting for the action to be
|
||||||
// reduced.
|
// reduced.
|
||||||
const result = next(action);
|
const result = next(action);
|
||||||
|
const state = getState();
|
||||||
|
|
||||||
|
// Update the config with user defined settings.
|
||||||
|
const settings = state['features/base/settings'];
|
||||||
|
const config = {};
|
||||||
|
|
||||||
|
if (typeof settings.disableP2P !== 'undefined') {
|
||||||
|
config.p2p = { enabled: !settings.disableP2P };
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: _UPDATE_CONFIG,
|
||||||
|
config
|
||||||
|
});
|
||||||
|
|
||||||
// FIXME On Web we rely on the global 'config' variable which gets altered
|
// FIXME On Web we rely on the global 'config' variable which gets altered
|
||||||
// multiple times, before it makes it to the reducer. At some point it may
|
// multiple times, before it makes it to the reducer. At some point it may
|
||||||
|
@ -105,7 +119,7 @@ function _setConfig({ getState }, next, action) {
|
||||||
// different merge methods being used along the way. The global variable
|
// different merge methods being used along the way. The global variable
|
||||||
// must be synchronized with the final state resolved by the reducer.
|
// must be synchronized with the final state resolved by the reducer.
|
||||||
if (typeof window.config !== 'undefined') {
|
if (typeof window.config !== 'undefined') {
|
||||||
window.config = getState()['features/base/config'];
|
window.config = state['features/base/config'];
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -5,7 +5,7 @@ import _ from 'lodash';
|
||||||
import Platform from '../react/Platform';
|
import Platform from '../react/Platform';
|
||||||
import { equals, ReducerRegistry, set } from '../redux';
|
import { equals, ReducerRegistry, set } from '../redux';
|
||||||
|
|
||||||
import { CONFIG_WILL_LOAD, LOAD_CONFIG_ERROR, SET_CONFIG } from './actionTypes';
|
import { _UPDATE_CONFIG, CONFIG_WILL_LOAD, LOAD_CONFIG_ERROR, SET_CONFIG } from './actionTypes';
|
||||||
import { _cleanupConfig } from './functions';
|
import { _cleanupConfig } from './functions';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -56,49 +56,50 @@ const INITIAL_RN_STATE = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ReducerRegistry.register(
|
ReducerRegistry.register('features/base/config', (state = _getInitialState(), action) => {
|
||||||
'features/base/config',
|
switch (action.type) {
|
||||||
(state = _getInitialState(), action) => {
|
case _UPDATE_CONFIG:
|
||||||
switch (action.type) {
|
return _updateConfig(state, action);
|
||||||
case CONFIG_WILL_LOAD:
|
|
||||||
|
case CONFIG_WILL_LOAD:
|
||||||
|
return {
|
||||||
|
error: undefined,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The URL of the location associated with/configured by this
|
||||||
|
* configuration.
|
||||||
|
*
|
||||||
|
* @type URL
|
||||||
|
*/
|
||||||
|
locationURL: action.locationURL
|
||||||
|
};
|
||||||
|
|
||||||
|
case LOAD_CONFIG_ERROR:
|
||||||
|
// XXX LOAD_CONFIG_ERROR is one of the settlement execution paths of
|
||||||
|
// the asynchronous "loadConfig procedure/process" started with
|
||||||
|
// CONFIG_WILL_LOAD. Due to the asynchronous nature of it, whoever
|
||||||
|
// is settling the process needs to provide proof that they have
|
||||||
|
// started it and that the iteration of the process being completed
|
||||||
|
// now is still of interest to the app.
|
||||||
|
if (state.locationURL === action.locationURL) {
|
||||||
return {
|
return {
|
||||||
error: undefined,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The URL of the location associated with/configured by this
|
* The {@link Error} which prevented the loading of the
|
||||||
* configuration.
|
* configuration of the associated {@code locationURL}.
|
||||||
*
|
*
|
||||||
* @type URL
|
* @type Error
|
||||||
*/
|
*/
|
||||||
locationURL: action.locationURL
|
error: action.error
|
||||||
};
|
};
|
||||||
|
|
||||||
case LOAD_CONFIG_ERROR:
|
|
||||||
// XXX LOAD_CONFIG_ERROR is one of the settlement execution paths of
|
|
||||||
// the asynchronous "loadConfig procedure/process" started with
|
|
||||||
// CONFIG_WILL_LOAD. Due to the asynchronous nature of it, whoever
|
|
||||||
// is settling the process needs to provide proof that they have
|
|
||||||
// started it and that the iteration of the process being completed
|
|
||||||
// now is still of interest to the app.
|
|
||||||
if (state.locationURL === action.locationURL) {
|
|
||||||
return {
|
|
||||||
/**
|
|
||||||
* The {@link Error} which prevented the loading of the
|
|
||||||
* configuration of the associated {@code locationURL}.
|
|
||||||
*
|
|
||||||
* @type Error
|
|
||||||
*/
|
|
||||||
error: action.error
|
|
||||||
};
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SET_CONFIG:
|
|
||||||
return _setConfig(state, action);
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
return state;
|
case SET_CONFIG:
|
||||||
});
|
return _setConfig(state, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the initial state of the feature base/config. The mandatory
|
* Gets the initial state of the feature base/config. The mandatory
|
||||||
|
@ -120,11 +121,10 @@ function _getInitialState() {
|
||||||
* Reduces a specific Redux action SET_CONFIG of the feature
|
* Reduces a specific Redux action SET_CONFIG of the feature
|
||||||
* base/lib-jitsi-meet.
|
* base/lib-jitsi-meet.
|
||||||
*
|
*
|
||||||
* @param {Object} state - The Redux state of the feature base/lib-jitsi-meet.
|
* @param {Object} state - The Redux state of the feature base/config.
|
||||||
* @param {Action} action - The Redux action SET_CONFIG to reduce.
|
* @param {Action} action - The Redux action SET_CONFIG to reduce.
|
||||||
* @private
|
* @private
|
||||||
* @returns {Object} The new state of the feature base/lib-jitsi-meet after the
|
* @returns {Object} The new state after the reduction of the specified action.
|
||||||
* reduction of the specified action.
|
|
||||||
*/
|
*/
|
||||||
function _setConfig(state, { config }) {
|
function _setConfig(state, { config }) {
|
||||||
// The mobile app bundles jitsi-meet and lib-jitsi-meet at build time and
|
// The mobile app bundles jitsi-meet and lib-jitsi-meet at build time and
|
||||||
|
@ -207,3 +207,19 @@ function _translateLegacyConfig(oldValue: Object) {
|
||||||
|
|
||||||
return newValue;
|
return newValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the stored configuration with the given extra options.
|
||||||
|
*
|
||||||
|
* @param {Object} state - The Redux state of the feature base/config.
|
||||||
|
* @param {Action} action - The Redux action to reduce.
|
||||||
|
* @private
|
||||||
|
* @returns {Object} The new state after the reduction of the specified action.
|
||||||
|
*/
|
||||||
|
function _updateConfig(state, { config }) {
|
||||||
|
const newState = _.merge({}, state, config);
|
||||||
|
|
||||||
|
_cleanupConfig(newState);
|
||||||
|
|
||||||
|
return equals(state, newState) ? state : newState;
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,13 @@
|
||||||
*/
|
*/
|
||||||
export const CALENDAR_ENABLED = 'calendar.enabled';
|
export const CALENDAR_ENABLED = 'calendar.enabled';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag indicating if call integration (CallKit on iOS, ConnectionService on Android)
|
||||||
|
* should be enabled.
|
||||||
|
* Default: enabled (true).
|
||||||
|
*/
|
||||||
|
export const CALL_INTEGRATION_ENABLED = 'call-integration.enabled';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag indicating if chat should be enabled.
|
* Flag indicating if chat should be enabled.
|
||||||
* Default: enabled (true).
|
* Default: enabled (true).
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import { NativeModules } from 'react-native';
|
||||||
|
|
||||||
|
export * from './functions.any';
|
||||||
|
|
||||||
|
const { AudioMode } = NativeModules;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles changes to the `disableCallIntegration` setting.
|
||||||
|
* On Android (where `AudioMode.setUseConnectionService` is defined) we must update
|
||||||
|
* the native side too, since audio routing works differently.
|
||||||
|
*
|
||||||
|
* @param {boolean} disabled - Whether call integration is disabled or not.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function handleCallIntegrationChange(disabled: boolean) {
|
||||||
|
if (AudioMode.setUseConnectionService) {
|
||||||
|
AudioMode.setUseConnectionService(!disabled);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
export * from './functions.any';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles changes to the `disableCallIntegration` setting.
|
||||||
|
* Noop on web.
|
||||||
|
*
|
||||||
|
* @param {boolean} disabled - Whether call integration is disabled or not.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function handleCallIntegrationChange(disabled: boolean) { // eslint-disable-line no-unused-vars
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import { getLocalParticipant, participantUpdated } from '../participants';
|
||||||
import { MiddlewareRegistry } from '../redux';
|
import { MiddlewareRegistry } from '../redux';
|
||||||
|
|
||||||
import { SETTINGS_UPDATED } from './actionTypes';
|
import { SETTINGS_UPDATED } from './actionTypes';
|
||||||
|
import { handleCallIntegrationChange } from './functions';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The middleware of the feature base/settings. Distributes changes to the state
|
* The middleware of the feature base/settings. Distributes changes to the state
|
||||||
|
@ -19,6 +20,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||||
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case SETTINGS_UPDATED:
|
case SETTINGS_UPDATED:
|
||||||
|
_maybeHandleCallIntegrationChange(action);
|
||||||
_maybeSetAudioOnly(store, action);
|
_maybeSetAudioOnly(store, action);
|
||||||
_updateLocalParticipant(store, action);
|
_updateLocalParticipant(store, action);
|
||||||
}
|
}
|
||||||
|
@ -43,6 +45,19 @@ function _mapSettingsFieldToParticipant(settingsField) {
|
||||||
return settingsField;
|
return settingsField;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates {@code startAudioOnly} flag if it's updated in the settings.
|
||||||
|
*
|
||||||
|
* @param {Object} action - The redux action.
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function _maybeHandleCallIntegrationChange({ settings: { disableCallIntegration } }) {
|
||||||
|
if (typeof disableCallIntegration === 'boolean') {
|
||||||
|
handleCallIntegrationChange(disableCallIntegration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates {@code startAudioOnly} flag if it's updated in the settings.
|
* Updates {@code startAudioOnly} flag if it's updated in the settings.
|
||||||
*
|
*
|
||||||
|
|
|
@ -22,6 +22,8 @@ const DEFAULT_STATE = {
|
||||||
avatarID: undefined,
|
avatarID: undefined,
|
||||||
avatarURL: undefined,
|
avatarURL: undefined,
|
||||||
cameraDeviceId: undefined,
|
cameraDeviceId: undefined,
|
||||||
|
disableCallIntegration: undefined,
|
||||||
|
disableP2P: undefined,
|
||||||
displayName: undefined,
|
displayName: undefined,
|
||||||
email: undefined,
|
email: undefined,
|
||||||
localFlipX: true,
|
localFlipX: true,
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import { CALL_INTEGRATION_ENABLED, getFeatureFlag } from '../../base/flags';
|
||||||
|
import { toState } from '../../base/redux';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if call integration is enabled or not.
|
||||||
|
*
|
||||||
|
* @param {Function|Object} stateful - The redux store or {@code getState}
|
||||||
|
* function.
|
||||||
|
* @returns {string} - Default URL for the app.
|
||||||
|
*/
|
||||||
|
export function isCallIntegrationEnabled(stateful: Function | Object) {
|
||||||
|
const state = toState(stateful);
|
||||||
|
const { disableCallIntegration } = state['features/base/settings'];
|
||||||
|
const flag = getFeatureFlag(state, CALL_INTEGRATION_ENABLED);
|
||||||
|
|
||||||
|
// The feature flag has precedence.
|
||||||
|
return flag ?? !disableCallIntegration;
|
||||||
|
}
|
|
@ -34,6 +34,7 @@ import { _SET_CALL_INTEGRATION_SUBSCRIPTIONS } from './actionTypes';
|
||||||
|
|
||||||
import CallKit from './CallKit';
|
import CallKit from './CallKit';
|
||||||
import ConnectionService from './ConnectionService';
|
import ConnectionService from './ConnectionService';
|
||||||
|
import { isCallIntegrationEnabled } from './functions';
|
||||||
|
|
||||||
const CallIntegration = CallKit || ConnectionService;
|
const CallIntegration = CallKit || ConnectionService;
|
||||||
|
|
||||||
|
@ -139,9 +140,13 @@ function _appWillMount({ dispatch, getState }, next, action) {
|
||||||
* @private
|
* @private
|
||||||
* @returns {*} The value returned by {@code next(action)}.
|
* @returns {*} The value returned by {@code next(action)}.
|
||||||
*/
|
*/
|
||||||
function _conferenceFailed(store, next, action) {
|
function _conferenceFailed({ getState }, next, action) {
|
||||||
const result = next(action);
|
const result = next(action);
|
||||||
|
|
||||||
|
if (!isCallIntegrationEnabled(getState)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// XXX Certain CONFERENCE_FAILED errors are recoverable i.e. they have
|
// XXX Certain CONFERENCE_FAILED errors are recoverable i.e. they have
|
||||||
// prevented the user from joining a specific conference but the app may be
|
// prevented the user from joining a specific conference but the app may be
|
||||||
// able to eventually join the conference.
|
// able to eventually join the conference.
|
||||||
|
@ -173,6 +178,10 @@ function _conferenceFailed(store, next, action) {
|
||||||
function _conferenceJoined({ getState }, next, action) {
|
function _conferenceJoined({ getState }, next, action) {
|
||||||
const result = next(action);
|
const result = next(action);
|
||||||
|
|
||||||
|
if (!isCallIntegrationEnabled(getState)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
const { callUUID } = action.conference;
|
const { callUUID } = action.conference;
|
||||||
|
|
||||||
if (callUUID) {
|
if (callUUID) {
|
||||||
|
@ -201,9 +210,13 @@ function _conferenceJoined({ getState }, next, action) {
|
||||||
* @private
|
* @private
|
||||||
* @returns {*} The value returned by {@code next(action)}.
|
* @returns {*} The value returned by {@code next(action)}.
|
||||||
*/
|
*/
|
||||||
function _conferenceLeft(store, next, action) {
|
function _conferenceLeft({ getState }, next, action) {
|
||||||
const result = next(action);
|
const result = next(action);
|
||||||
|
|
||||||
|
if (!isCallIntegrationEnabled(getState)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
const { callUUID } = action.conference;
|
const { callUUID } = action.conference;
|
||||||
|
|
||||||
if (callUUID) {
|
if (callUUID) {
|
||||||
|
@ -230,6 +243,10 @@ function _conferenceLeft(store, next, action) {
|
||||||
function _conferenceWillJoin({ dispatch, getState }, next, action) {
|
function _conferenceWillJoin({ dispatch, getState }, next, action) {
|
||||||
const result = next(action);
|
const result = next(action);
|
||||||
|
|
||||||
|
if (!isCallIntegrationEnabled(getState)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
const { conference } = action;
|
const { conference } = action;
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const { callHandle, callUUID } = state['features/base/config'];
|
const { callHandle, callUUID } = state['features/base/config'];
|
||||||
|
@ -341,6 +358,11 @@ function _onPerformSetMutedCallAction({ callUUID, muted }) {
|
||||||
function _setAudioOnly({ getState }, next, action) {
|
function _setAudioOnly({ getState }, next, action) {
|
||||||
const result = next(action);
|
const result = next(action);
|
||||||
const state = getState();
|
const state = getState();
|
||||||
|
|
||||||
|
if (!isCallIntegrationEnabled(state)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
const conference = getCurrentConference(state);
|
const conference = getCurrentConference(state);
|
||||||
|
|
||||||
if (conference && conference.callUUID) {
|
if (conference && conference.callUUID) {
|
||||||
|
@ -393,6 +415,11 @@ function _setCallKitSubscriptions({ getState }, next, action) {
|
||||||
*/
|
*/
|
||||||
function _syncTrackState({ getState }, next, action) {
|
function _syncTrackState({ getState }, next, action) {
|
||||||
const result = next(action);
|
const result = next(action);
|
||||||
|
|
||||||
|
if (!isCallIntegrationEnabled(getState)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
const { jitsiTrack } = action.track;
|
const { jitsiTrack } = action.track;
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const conference = getCurrentConference(state);
|
const conference = getCurrentConference(state);
|
||||||
|
|
|
@ -48,7 +48,7 @@ export type Props = {
|
||||||
*
|
*
|
||||||
* @abstract
|
* @abstract
|
||||||
*/
|
*/
|
||||||
export class AbstractSettingsView<P: Props> extends Component<P> {
|
export class AbstractSettingsView<P: Props, S: *> extends Component<P, S> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes a new {@code AbstractSettingsView} instance.
|
* Initializes a new {@code AbstractSettingsView} instance.
|
||||||
|
|
|
@ -32,12 +32,20 @@ type Props = AbstractProps & {
|
||||||
_headerStyles: Object
|
_headerStyles: Object
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to show advanced settings or not.
|
||||||
|
*/
|
||||||
|
showAdvanced: boolean
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The native container rendering the app settings page.
|
* The native container rendering the app settings page.
|
||||||
*
|
*
|
||||||
* @extends AbstractSettingsView
|
* @extends AbstractSettingsView
|
||||||
*/
|
*/
|
||||||
class SettingsView extends AbstractSettingsView<Props> {
|
class SettingsView extends AbstractSettingsView<Props, State> {
|
||||||
_urlField: Object;
|
_urlField: Object;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -48,9 +56,16 @@ class SettingsView extends AbstractSettingsView<Props> {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
showAdvanced: false
|
||||||
|
};
|
||||||
|
|
||||||
// Bind event handlers so they are only bound once per instance.
|
// Bind event handlers so they are only bound once per instance.
|
||||||
this._onBlurServerURL = this._onBlurServerURL.bind(this);
|
this._onBlurServerURL = this._onBlurServerURL.bind(this);
|
||||||
|
this._onDisableCallIntegration = this._onDisableCallIntegration.bind(this);
|
||||||
|
this._onDisableP2P = this._onDisableP2P.bind(this);
|
||||||
this._onRequestClose = this._onRequestClose.bind(this);
|
this._onRequestClose = this._onRequestClose.bind(this);
|
||||||
|
this._onShowAdvanced = this._onShowAdvanced.bind(this);
|
||||||
this._setURLFieldReference = this._setURLFieldReference.bind(this);
|
this._setURLFieldReference = this._setURLFieldReference.bind(this);
|
||||||
this._showURLAlert = this._showURLAlert.bind(this);
|
this._showURLAlert = this._showURLAlert.bind(this);
|
||||||
}
|
}
|
||||||
|
@ -94,6 +109,38 @@ class SettingsView extends AbstractSettingsView<Props> {
|
||||||
|
|
||||||
_onChangeServerURL: (string) => void;
|
_onChangeServerURL: (string) => void;
|
||||||
|
|
||||||
|
_onDisableCallIntegration: (boolean) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the disable call integration change event.
|
||||||
|
*
|
||||||
|
* @param {boolean} newValue - The new value
|
||||||
|
* option.
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_onDisableCallIntegration(newValue) {
|
||||||
|
this._updateSettings({
|
||||||
|
disableCallIntegration: newValue
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_onDisableP2P: (boolean) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the disable P2P change event.
|
||||||
|
*
|
||||||
|
* @param {boolean} newValue - The new value
|
||||||
|
* option.
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_onDisableP2P(newValue) {
|
||||||
|
this._updateSettings({
|
||||||
|
disableP2P: newValue
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
_onRequestClose: () => void;
|
_onRequestClose: () => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -103,9 +150,21 @@ class SettingsView extends AbstractSettingsView<Props> {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_onRequestClose() {
|
_onRequestClose() {
|
||||||
|
this.setState({ showAdvanced: false });
|
||||||
this._processServerURL(true /* hideOnSuccess */);
|
this._processServerURL(true /* hideOnSuccess */);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onShowAdvanced: () => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the advanced settings button.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_onShowAdvanced() {
|
||||||
|
this.setState({ showAdvanced: !this.state.showAdvanced });
|
||||||
|
}
|
||||||
|
|
||||||
_onStartAudioMutedChange: (boolean) => void;
|
_onStartAudioMutedChange: (boolean) => void;
|
||||||
|
|
||||||
_onStartVideoMutedChange: (boolean) => void;
|
_onStartVideoMutedChange: (boolean) => void;
|
||||||
|
@ -133,6 +192,48 @@ class SettingsView extends AbstractSettingsView<Props> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the advanced settings options.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {React$Element}
|
||||||
|
*/
|
||||||
|
_renderAdvancedSettings() {
|
||||||
|
const { _settings } = this.props;
|
||||||
|
const { showAdvanced } = this.state;
|
||||||
|
|
||||||
|
if (!showAdvanced) {
|
||||||
|
return (
|
||||||
|
<FormRow
|
||||||
|
fieldSeparator = { true }
|
||||||
|
label = 'settingsView.showAdvanced'>
|
||||||
|
<Switch
|
||||||
|
onValueChange = { this._onShowAdvanced }
|
||||||
|
value = { showAdvanced } />
|
||||||
|
</FormRow>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<FormRow
|
||||||
|
fieldSeparator = { true }
|
||||||
|
label = 'settingsView.disableCallIntegration'>
|
||||||
|
<Switch
|
||||||
|
onValueChange = { this._onDisableCallIntegration }
|
||||||
|
value = { _settings.disableCallIntegration } />
|
||||||
|
</FormRow>
|
||||||
|
<FormRow
|
||||||
|
fieldSeparator = { true }
|
||||||
|
label = 'settingsView.disableP2P'>
|
||||||
|
<Switch
|
||||||
|
onValueChange = { this._onDisableP2P }
|
||||||
|
value = { _settings.disableP2P } />
|
||||||
|
</FormRow>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the body (under the header) of {@code SettingsView}.
|
* Renders the body (under the header) of {@code SettingsView}.
|
||||||
*
|
*
|
||||||
|
@ -193,12 +294,14 @@ class SettingsView extends AbstractSettingsView<Props> {
|
||||||
<FormSectionHeader
|
<FormSectionHeader
|
||||||
label = 'settingsView.buildInfoSection' />
|
label = 'settingsView.buildInfoSection' />
|
||||||
<FormRow
|
<FormRow
|
||||||
fieldSeparator = { true }
|
|
||||||
label = 'settingsView.version'>
|
label = 'settingsView.version'>
|
||||||
<Text>
|
<Text>
|
||||||
{ `${AppInfo.version} build ${AppInfo.buildNumber}` }
|
{ `${AppInfo.version} build ${AppInfo.buildNumber}` }
|
||||||
</Text>
|
</Text>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
|
<FormSectionHeader
|
||||||
|
label = 'settingsView.advanced' />
|
||||||
|
{ this._renderAdvancedSettings() }
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
|
@ -252,6 +355,8 @@ class SettingsView extends AbstractSettingsView<Props> {
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_updateSettings: (Object) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue