snapshot of https://github.com/jitsi/jitsi-meet.git @ fd1749dd28f4138aa80234fc7f311b19aaffa0e7 (2026-09-15)
This commit is contained in:
606
react/features/base/config/reducer.ts
Normal file
606
react/features/base/config/reducer.ts
Normal file
@@ -0,0 +1,606 @@
|
||||
import { merge, union } from 'lodash-es';
|
||||
|
||||
import { CONFERENCE_INFO } from '../../conference/components/constants';
|
||||
import { TOOLBAR_BUTTONS } from '../../toolbox/constants';
|
||||
import { ToolbarButton } from '../../toolbox/types';
|
||||
import { CONNECTION_PROPERTIES_UPDATED } from '../connection/actionTypes';
|
||||
import ReducerRegistry from '../redux/ReducerRegistry';
|
||||
import { equals } from '../redux/functions';
|
||||
|
||||
import {
|
||||
CONFIG_WILL_LOAD,
|
||||
LOAD_CONFIG_ERROR,
|
||||
OVERWRITE_CONFIG,
|
||||
SET_CONFIG,
|
||||
UPDATE_CONFIG
|
||||
} from './actionTypes';
|
||||
import {
|
||||
IConfig,
|
||||
IDeeplinkingConfig,
|
||||
IDeeplinkingDesktopConfig,
|
||||
IDeeplinkingMobileConfig
|
||||
} from './configType';
|
||||
import { _cleanupConfig, _setDeeplinkingDefaults } from './functions';
|
||||
|
||||
/**
|
||||
* The initial state of the feature base/config when executing in a
|
||||
* non-React Native environment. The mandatory configuration to be passed to
|
||||
* JitsiMeetJS#init(). The app will download config.js from the Jitsi Meet
|
||||
* deployment and take its values into account but the values below will be
|
||||
* enforced (because they are essential to the correct execution of the
|
||||
* application).
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const INITIAL_NON_RN_STATE: IConfig = {
|
||||
};
|
||||
|
||||
/**
|
||||
* The initial state of the feature base/config when executing in a React Native
|
||||
* environment. The mandatory configuration to be passed to JitsiMeetJS#init().
|
||||
* The app will download config.js from the Jitsi Meet deployment and take its
|
||||
* values into account but the values below will be enforced (because they are
|
||||
* essential to the correct execution of the application).
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const INITIAL_RN_STATE: IConfig = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Mapping between old configs controlling the conference info headers visibility and the
|
||||
* new configs. Needed in order to keep backwards compatibility.
|
||||
*/
|
||||
const CONFERENCE_HEADER_MAPPING = {
|
||||
hideConferenceTimer: [ 'conference-timer' ],
|
||||
hideConferenceSubject: [ 'subject' ],
|
||||
hideParticipantsStats: [ 'participants-count' ],
|
||||
hideRecordingLabel: [ 'recording' ]
|
||||
};
|
||||
|
||||
export interface IConfigState extends IConfig {
|
||||
analysis?: {
|
||||
obfuscateRoomName?: boolean;
|
||||
};
|
||||
error?: Error;
|
||||
oldConfig?: {
|
||||
bosh?: string;
|
||||
focusUserJid?: string;
|
||||
hosts: {
|
||||
domain: string;
|
||||
muc: string;
|
||||
};
|
||||
p2p?: object;
|
||||
websocket?: string;
|
||||
};
|
||||
}
|
||||
|
||||
ReducerRegistry.register<IConfigState>('features/base/config', (state = _getInitialState(), action): IConfigState => {
|
||||
switch (action.type) {
|
||||
case UPDATE_CONFIG:
|
||||
return _updateConfig(state, action);
|
||||
|
||||
case CONFIG_WILL_LOAD:
|
||||
return {
|
||||
error: undefined,
|
||||
|
||||
/**
|
||||
* The URL of the location associated with/configured by this
|
||||
* configuration.
|
||||
*
|
||||
* @type URL
|
||||
*/
|
||||
locationURL: action.locationURL
|
||||
};
|
||||
|
||||
case CONNECTION_PROPERTIES_UPDATED: {
|
||||
const { region, shard } = action.properties;
|
||||
const { deploymentInfo } = state;
|
||||
|
||||
if (deploymentInfo?.region === region && deploymentInfo?.shard === shard) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
deploymentInfo: JSON.parse(JSON.stringify({
|
||||
...deploymentInfo,
|
||||
region,
|
||||
shard
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
case OVERWRITE_CONFIG:
|
||||
return {
|
||||
...state,
|
||||
...action.config
|
||||
};
|
||||
}
|
||||
|
||||
return state;
|
||||
});
|
||||
|
||||
/**
|
||||
* Gets the initial state of the feature base/config. The mandatory
|
||||
* configuration to be passed to JitsiMeetJS#init(). The app will download
|
||||
* config.js from the Jitsi Meet deployment and take its values into account but
|
||||
* the values below will be enforced (because they are essential to the correct
|
||||
* execution of the application).
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
function _getInitialState() {
|
||||
return (
|
||||
navigator.product === 'ReactNative'
|
||||
? INITIAL_RN_STATE
|
||||
: INITIAL_NON_RN_STATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduces a specific Redux action SET_CONFIG of the feature
|
||||
* base/lib-jitsi-meet.
|
||||
*
|
||||
* @param {IConfig} state - The Redux state of the feature base/config.
|
||||
* @param {Action} action - The Redux action SET_CONFIG to reduce.
|
||||
* @private
|
||||
* @returns {Object} The new state after the reduction of the specified action.
|
||||
*/
|
||||
function _setConfig(state: IConfig, { config }: { config: IConfig; }) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
config = _translateLegacyConfig(config);
|
||||
|
||||
const { audioQuality } = config;
|
||||
const hdAudioOptions = {};
|
||||
|
||||
if (audioQuality?.stereo) {
|
||||
Object.assign(hdAudioOptions, {
|
||||
disableAP: true,
|
||||
enableNoAudioDetection: false,
|
||||
enableNoisyMicDetection: false,
|
||||
enableTalkWhileMuted: false
|
||||
});
|
||||
}
|
||||
|
||||
const { alwaysShowResizeBar, disableResizable } = config.filmstrip || {};
|
||||
|
||||
if (alwaysShowResizeBar && disableResizable) {
|
||||
config.filmstrip = {
|
||||
...config.filmstrip,
|
||||
alwaysShowResizeBar: false
|
||||
};
|
||||
}
|
||||
|
||||
const newState = merge(
|
||||
{},
|
||||
config,
|
||||
hdAudioOptions,
|
||||
{ error: undefined },
|
||||
|
||||
// The config of _getInitialState() is meant to override the config
|
||||
// downloaded from the Jitsi Meet deployment because the former contains
|
||||
// values that are mandatory.
|
||||
_getInitialState()
|
||||
);
|
||||
|
||||
_cleanupConfig(newState);
|
||||
|
||||
return equals(state, newState) ? state : newState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the conferenceInfo object against the defaults.
|
||||
*
|
||||
* @param {IConfig} config - The old config.
|
||||
* @returns {Object} The processed conferenceInfo object.
|
||||
*/
|
||||
function _getConferenceInfo(config: IConfig) {
|
||||
const { conferenceInfo } = config;
|
||||
|
||||
if (conferenceInfo) {
|
||||
return {
|
||||
alwaysVisible: conferenceInfo.alwaysVisible ?? [ ...CONFERENCE_INFO.alwaysVisible ],
|
||||
autoHide: conferenceInfo.autoHide ?? [ ...CONFERENCE_INFO.autoHide ]
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...CONFERENCE_INFO
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new config {@code Object}, if necessary, out of a specific
|
||||
* interface_config {@code Object} which is in the latest format supported by jitsi-meet.
|
||||
*
|
||||
* @param {Object} oldValue - The config {@code Object} which may or may not be
|
||||
* in the latest form supported by jitsi-meet and from which a new config
|
||||
* {@code Object} is to be constructed if necessary.
|
||||
* @returns {Object} A config {@code Object} which is in the latest format
|
||||
* supported by jitsi-meet.
|
||||
*/
|
||||
function _translateInterfaceConfig(oldValue: IConfig) {
|
||||
const newValue = oldValue;
|
||||
|
||||
if (!Array.isArray(oldValue.toolbarButtons)
|
||||
&& typeof interfaceConfig === 'object' && Array.isArray(interfaceConfig.TOOLBAR_BUTTONS)) {
|
||||
newValue.toolbarButtons = interfaceConfig.TOOLBAR_BUTTONS;
|
||||
}
|
||||
|
||||
if (!oldValue.toolbarConfig) {
|
||||
oldValue.toolbarConfig = {};
|
||||
}
|
||||
|
||||
newValue.toolbarConfig = oldValue.toolbarConfig || {};
|
||||
if (typeof oldValue.toolbarConfig.alwaysVisible !== 'boolean'
|
||||
&& typeof interfaceConfig === 'object'
|
||||
&& typeof interfaceConfig.TOOLBAR_ALWAYS_VISIBLE === 'boolean') {
|
||||
newValue.toolbarConfig.alwaysVisible = interfaceConfig.TOOLBAR_ALWAYS_VISIBLE;
|
||||
}
|
||||
|
||||
if (typeof oldValue.toolbarConfig.initialTimeout !== 'number'
|
||||
&& typeof interfaceConfig === 'object'
|
||||
&& typeof interfaceConfig.INITIAL_TOOLBAR_TIMEOUT === 'number') {
|
||||
newValue.toolbarConfig.initialTimeout = interfaceConfig.INITIAL_TOOLBAR_TIMEOUT;
|
||||
}
|
||||
|
||||
if (typeof oldValue.toolbarConfig.timeout !== 'number'
|
||||
&& typeof interfaceConfig === 'object'
|
||||
&& typeof interfaceConfig.TOOLBAR_TIMEOUT === 'number') {
|
||||
newValue.toolbarConfig.timeout = interfaceConfig.TOOLBAR_TIMEOUT;
|
||||
}
|
||||
|
||||
if (!oldValue.connectionIndicators
|
||||
&& typeof interfaceConfig === 'object'
|
||||
&& (interfaceConfig.hasOwnProperty('CONNECTION_INDICATOR_DISABLED')
|
||||
|| interfaceConfig.hasOwnProperty('CONNECTION_INDICATOR_AUTO_HIDE_ENABLED')
|
||||
|| interfaceConfig.hasOwnProperty('CONNECTION_INDICATOR_AUTO_HIDE_TIMEOUT'))) {
|
||||
newValue.connectionIndicators = {
|
||||
disabled: interfaceConfig.CONNECTION_INDICATOR_DISABLED,
|
||||
autoHide: interfaceConfig.CONNECTION_INDICATOR_AUTO_HIDE_ENABLED,
|
||||
autoHideTimeout: interfaceConfig.CONNECTION_INDICATOR_AUTO_HIDE_TIMEOUT
|
||||
};
|
||||
}
|
||||
|
||||
if (oldValue.disableModeratorIndicator === undefined
|
||||
&& typeof interfaceConfig === 'object'
|
||||
&& interfaceConfig.hasOwnProperty('DISABLE_FOCUS_INDICATOR')) {
|
||||
newValue.disableModeratorIndicator = interfaceConfig.DISABLE_FOCUS_INDICATOR;
|
||||
}
|
||||
|
||||
if (oldValue.defaultLocalDisplayName === undefined
|
||||
&& typeof interfaceConfig === 'object'
|
||||
&& interfaceConfig.hasOwnProperty('DEFAULT_LOCAL_DISPLAY_NAME')) {
|
||||
newValue.defaultLocalDisplayName = interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME;
|
||||
}
|
||||
|
||||
if (oldValue.defaultRemoteDisplayName === undefined
|
||||
&& typeof interfaceConfig === 'object'
|
||||
&& interfaceConfig.hasOwnProperty('DEFAULT_REMOTE_DISPLAY_NAME')) {
|
||||
newValue.defaultRemoteDisplayName = interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME;
|
||||
}
|
||||
|
||||
if (oldValue.defaultLogoUrl === undefined) {
|
||||
if (typeof interfaceConfig === 'object'
|
||||
&& interfaceConfig.hasOwnProperty('DEFAULT_LOGO_URL')) {
|
||||
newValue.defaultLogoUrl = interfaceConfig.DEFAULT_LOGO_URL;
|
||||
} else {
|
||||
newValue.defaultLogoUrl = 'images/watermark.svg';
|
||||
}
|
||||
}
|
||||
|
||||
// if we have `deeplinking` defined, ignore deprecated values, except `disableDeepLinking`.
|
||||
// Otherwise, compose the config.
|
||||
if (oldValue.deeplinking && newValue.deeplinking) { // make TS happy
|
||||
newValue.deeplinking.disabled = oldValue.deeplinking.hasOwnProperty('disabled')
|
||||
? oldValue.deeplinking.disabled
|
||||
: Boolean(oldValue.disableDeepLinking);
|
||||
} else {
|
||||
const disabled = Boolean(oldValue.disableDeepLinking);
|
||||
const deeplinking: IDeeplinkingConfig = {
|
||||
desktop: {} as IDeeplinkingDesktopConfig,
|
||||
hideLogo: false,
|
||||
disabled,
|
||||
android: {} as IDeeplinkingMobileConfig,
|
||||
ios: {} as IDeeplinkingMobileConfig
|
||||
};
|
||||
|
||||
if (typeof interfaceConfig === 'object') {
|
||||
if (deeplinking.desktop) {
|
||||
deeplinking.desktop.appName = interfaceConfig.NATIVE_APP_NAME;
|
||||
}
|
||||
|
||||
deeplinking.hideLogo = Boolean(interfaceConfig.HIDE_DEEP_LINKING_LOGO);
|
||||
deeplinking.android = {
|
||||
appName: interfaceConfig.NATIVE_APP_NAME,
|
||||
appScheme: interfaceConfig.APP_SCHEME,
|
||||
downloadLink: interfaceConfig.MOBILE_DOWNLOAD_LINK_ANDROID,
|
||||
appPackage: interfaceConfig.ANDROID_APP_PACKAGE,
|
||||
fDroidUrl: interfaceConfig.MOBILE_DOWNLOAD_LINK_F_DROID
|
||||
};
|
||||
deeplinking.ios = {
|
||||
appName: interfaceConfig.NATIVE_APP_NAME,
|
||||
appScheme: interfaceConfig.APP_SCHEME,
|
||||
downloadLink: interfaceConfig.MOBILE_DOWNLOAD_LINK_IOS
|
||||
};
|
||||
}
|
||||
newValue.deeplinking = deeplinking;
|
||||
}
|
||||
|
||||
return newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new config {@code Object}, if necessary, out of a specific
|
||||
* config {@code Object} which is in the latest format supported by jitsi-meet.
|
||||
* Such a translation from an old config format to a new/the latest config
|
||||
* format is necessary because the mobile app bundles jitsi-meet and
|
||||
* lib-jitsi-meet at build time and does not download them at runtime from the
|
||||
* deployment on which it will join a conference.
|
||||
*
|
||||
* @param {Object} oldValue - The config {@code Object} which may or may not be
|
||||
* in the latest form supported by jitsi-meet and from which a new config
|
||||
* {@code Object} is to be constructed if necessary.
|
||||
* @returns {Object} A config {@code Object} which is in the latest format
|
||||
* supported by jitsi-meet.
|
||||
*/
|
||||
function _translateLegacyConfig(oldValue: IConfig) {
|
||||
const newValue = _translateInterfaceConfig(oldValue);
|
||||
|
||||
// Translate deprecated config values to new config values.
|
||||
|
||||
const filteredConferenceInfo = Object.keys(CONFERENCE_HEADER_MAPPING).filter(key => oldValue[key as keyof IConfig]);
|
||||
|
||||
if (filteredConferenceInfo.length) {
|
||||
newValue.conferenceInfo = _getConferenceInfo(oldValue);
|
||||
|
||||
filteredConferenceInfo.forEach(key => {
|
||||
newValue.conferenceInfo = oldValue.conferenceInfo ?? {};
|
||||
|
||||
// hideRecordingLabel does not mean not render it at all, but autoHide it
|
||||
if (key === 'hideRecordingLabel') {
|
||||
newValue.conferenceInfo.alwaysVisible
|
||||
= (newValue.conferenceInfo?.alwaysVisible ?? [])
|
||||
.filter(c => !CONFERENCE_HEADER_MAPPING[key].includes(c));
|
||||
newValue.conferenceInfo.autoHide
|
||||
= union(newValue.conferenceInfo.autoHide, CONFERENCE_HEADER_MAPPING[key]);
|
||||
} else {
|
||||
newValue.conferenceInfo.alwaysVisible
|
||||
= (newValue.conferenceInfo.alwaysVisible ?? [])
|
||||
.filter(c => !CONFERENCE_HEADER_MAPPING[key as keyof typeof CONFERENCE_HEADER_MAPPING].includes(c));
|
||||
newValue.conferenceInfo.autoHide
|
||||
= (newValue.conferenceInfo.autoHide ?? []).filter(c =>
|
||||
!CONFERENCE_HEADER_MAPPING[key as keyof typeof CONFERENCE_HEADER_MAPPING].includes(c));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
newValue.welcomePage = oldValue.welcomePage || {};
|
||||
if (oldValue.hasOwnProperty('enableWelcomePage')
|
||||
&& !newValue.welcomePage.hasOwnProperty('disabled')
|
||||
) {
|
||||
newValue.welcomePage.disabled = !oldValue.enableWelcomePage;
|
||||
}
|
||||
|
||||
newValue.disabledSounds = newValue.disabledSounds || [];
|
||||
|
||||
if (oldValue.disableJoinLeaveSounds) {
|
||||
newValue.disabledSounds.unshift('PARTICIPANT_LEFT_SOUND', 'PARTICIPANT_JOINED_SOUND');
|
||||
}
|
||||
|
||||
if (oldValue.disableRecordAudioNotification) {
|
||||
newValue.disabledSounds.unshift(
|
||||
'RECORDING_ON_SOUND',
|
||||
'RECORDING_OFF_SOUND',
|
||||
'LIVE_STREAMING_ON_SOUND',
|
||||
'LIVE_STREAMING_OFF_SOUND'
|
||||
);
|
||||
}
|
||||
|
||||
if (oldValue.disableIncomingMessageSound) {
|
||||
newValue.disabledSounds.unshift('INCOMING_MSG_SOUND');
|
||||
}
|
||||
|
||||
newValue.raisedHands = newValue.raisedHands || {};
|
||||
|
||||
if (oldValue.disableRemoveRaisedHandOnFocus) {
|
||||
newValue.raisedHands.disableRemoveRaisedHandOnFocus = oldValue.disableRemoveRaisedHandOnFocus;
|
||||
}
|
||||
|
||||
newValue.virtualBackground = newValue.virtualBackground || {};
|
||||
|
||||
if (oldValue.hasOwnProperty('disableVirtualBackground')
|
||||
&& !newValue.virtualBackground.hasOwnProperty('disabled')
|
||||
) {
|
||||
newValue.virtualBackground.disabled = oldValue.disableVirtualBackground;
|
||||
}
|
||||
|
||||
if (oldValue.hasOwnProperty('disableAddingBackgroundImages')
|
||||
&& !newValue.virtualBackground.hasOwnProperty('disableAddingImages')
|
||||
) {
|
||||
newValue.virtualBackground.disableAddingImages = oldValue.disableAddingBackgroundImages;
|
||||
}
|
||||
|
||||
if (oldValue.stereo || oldValue.opusMaxAverageBitrate) {
|
||||
newValue.audioQuality = {
|
||||
opusMaxAverageBitrate: oldValue.audioQuality?.opusMaxAverageBitrate ?? oldValue.opusMaxAverageBitrate,
|
||||
stereo: oldValue.audioQuality?.stereo ?? oldValue.stereo
|
||||
};
|
||||
}
|
||||
|
||||
newValue.e2ee = newValue.e2ee || {};
|
||||
|
||||
if (oldValue.e2eeLabels) {
|
||||
newValue.e2ee.labels = oldValue.e2eeLabels;
|
||||
}
|
||||
|
||||
newValue.defaultLocalDisplayName
|
||||
= newValue.defaultLocalDisplayName || 'me';
|
||||
|
||||
if (oldValue.hideAddRoomButton) {
|
||||
newValue.breakoutRooms = {
|
||||
/* eslint-disable-next-line no-extra-parens */
|
||||
...(newValue.breakoutRooms || {}),
|
||||
hideAddRoomButton: oldValue.hideAddRoomButton
|
||||
};
|
||||
}
|
||||
|
||||
newValue.defaultRemoteDisplayName
|
||||
= newValue.defaultRemoteDisplayName || 'Fellow Jitster';
|
||||
|
||||
newValue.transcription = newValue.transcription || {};
|
||||
if (oldValue.transcribingEnabled !== undefined) {
|
||||
newValue.transcription = {
|
||||
...newValue.transcription,
|
||||
enabled: oldValue.transcribingEnabled
|
||||
};
|
||||
}
|
||||
if (oldValue.transcribeWithAppLanguage !== undefined) {
|
||||
newValue.transcription = {
|
||||
...newValue.transcription,
|
||||
useAppLanguage: oldValue.transcribeWithAppLanguage
|
||||
};
|
||||
}
|
||||
if (oldValue.preferredTranscribeLanguage !== undefined) {
|
||||
newValue.transcription = {
|
||||
...newValue.transcription,
|
||||
preferredLanguage: oldValue.preferredTranscribeLanguage
|
||||
};
|
||||
}
|
||||
if (oldValue.autoCaptionOnRecord !== undefined) {
|
||||
newValue.transcription = {
|
||||
...newValue.transcription,
|
||||
autoTranscribeOnRecord: oldValue.autoCaptionOnRecord
|
||||
};
|
||||
}
|
||||
|
||||
newValue.recordingService = newValue.recordingService || {};
|
||||
if (oldValue.fileRecordingsServiceEnabled !== undefined
|
||||
&& newValue.recordingService.enabled === undefined) {
|
||||
newValue.recordingService = {
|
||||
...newValue.recordingService,
|
||||
enabled: oldValue.fileRecordingsServiceEnabled
|
||||
};
|
||||
}
|
||||
if (oldValue.fileRecordingsServiceSharingEnabled !== undefined
|
||||
&& newValue.recordingService.sharingEnabled === undefined) {
|
||||
newValue.recordingService = {
|
||||
...newValue.recordingService,
|
||||
sharingEnabled: oldValue.fileRecordingsServiceSharingEnabled
|
||||
};
|
||||
}
|
||||
|
||||
newValue.liveStreaming = newValue.liveStreaming || {};
|
||||
|
||||
// Migrate config.liveStreamingEnabled
|
||||
if (oldValue.liveStreamingEnabled !== undefined) {
|
||||
newValue.liveStreaming = {
|
||||
...newValue.liveStreaming,
|
||||
enabled: oldValue.liveStreamingEnabled
|
||||
};
|
||||
}
|
||||
|
||||
// Migrate interfaceConfig.LIVE_STREAMING_HELP_LINK
|
||||
if (oldValue.liveStreaming === undefined
|
||||
&& typeof interfaceConfig === 'object'
|
||||
&& interfaceConfig.hasOwnProperty('LIVE_STREAMING_HELP_LINK')) {
|
||||
newValue.liveStreaming = {
|
||||
...newValue.liveStreaming,
|
||||
helpLink: interfaceConfig.LIVE_STREAMING_HELP_LINK
|
||||
};
|
||||
}
|
||||
|
||||
newValue.speakerStats = newValue.speakerStats || {};
|
||||
|
||||
if (oldValue.disableSpeakerStatsSearch !== undefined
|
||||
&& newValue.speakerStats.disableSearch === undefined
|
||||
) {
|
||||
newValue.speakerStats = {
|
||||
...newValue.speakerStats,
|
||||
disableSearch: oldValue.disableSpeakerStatsSearch
|
||||
};
|
||||
}
|
||||
|
||||
if (oldValue.speakerStatsOrder !== undefined
|
||||
&& newValue.speakerStats.order === undefined) {
|
||||
newValue.speakerStats = {
|
||||
...newValue.speakerStats,
|
||||
order: oldValue.speakerStatsOrder
|
||||
};
|
||||
}
|
||||
|
||||
if (oldValue.autoKnockLobby !== undefined
|
||||
&& newValue.lobby?.autoKnock === undefined) {
|
||||
newValue.lobby = {
|
||||
...newValue.lobby || {},
|
||||
autoKnock: oldValue.autoKnockLobby
|
||||
};
|
||||
}
|
||||
|
||||
if (oldValue.enableLobbyChat !== undefined
|
||||
&& newValue.lobby?.enableChat === undefined) {
|
||||
newValue.lobby = {
|
||||
...newValue.lobby || {},
|
||||
enableChat: oldValue.enableLobbyChat
|
||||
};
|
||||
}
|
||||
|
||||
if (oldValue.hideLobbyButton !== undefined
|
||||
&& newValue.securityUi?.hideLobbyButton === undefined) {
|
||||
newValue.securityUi = {
|
||||
...newValue.securityUi || {},
|
||||
hideLobbyButton: oldValue.hideLobbyButton
|
||||
};
|
||||
}
|
||||
|
||||
// Profile button is not available on mobile
|
||||
if (navigator.product !== 'ReactNative') {
|
||||
if (oldValue.disableProfile) {
|
||||
newValue.toolbarButtons = (newValue.toolbarButtons || TOOLBAR_BUTTONS)
|
||||
.filter((button: ToolbarButton) => button !== 'profile');
|
||||
}
|
||||
}
|
||||
|
||||
_setDeeplinkingDefaults(newValue.deeplinking as IDeeplinkingConfig);
|
||||
|
||||
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: IConfig, { config }: { config: IConfig; }) {
|
||||
const newState = merge({}, state, config);
|
||||
|
||||
_cleanupConfig(newState);
|
||||
|
||||
return equals(state, newState) ? state : newState;
|
||||
}
|
||||
Reference in New Issue
Block a user