ref(TS) Convert some base features to TS (#12138)

This commit is contained in:
Robert Pintilii 2022-09-14 10:54:56 +03:00 committed by GitHub
parent 5502601fb3
commit 95084e1004
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
56 changed files with 352 additions and 312 deletions

17
package-lock.json generated
View File

@ -121,7 +121,7 @@
"react-window": "1.8.6",
"react-youtube": "7.13.1",
"redux": "4.0.4",
"redux-thunk": "2.2.0",
"redux-thunk": "2.4.1",
"resemblejs": "4.0.0",
"seamless-scroll-polyfill": "2.1.8",
"styled-components": "3.4.9",
@ -16941,9 +16941,12 @@
}
},
"node_modules/redux-thunk": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.2.0.tgz",
"integrity": "sha1-5hWhbha0ehmlFXZhM9Hj6Zt4UuU="
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz",
"integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==",
"peerDependencies": {
"redux": "^4"
}
},
"node_modules/regenerate": {
"version": "1.4.2",
@ -33153,9 +33156,9 @@
}
},
"redux-thunk": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.2.0.tgz",
"integrity": "sha1-5hWhbha0ehmlFXZhM9Hj6Zt4UuU="
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz",
"integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q=="
},
"regenerate": {
"version": "1.4.2",

View File

@ -126,7 +126,7 @@
"react-window": "1.8.6",
"react-youtube": "7.13.1",
"redux": "4.0.4",
"redux-thunk": "2.2.0",
"redux-thunk": "2.4.1",
"resemblejs": "4.0.0",
"seamless-scroll-polyfill": "2.1.8",
"styled-components": "3.4.9",

View File

@ -1,4 +1,5 @@
import { AnyAction, Dispatch } from 'redux';
import { AnyAction } from 'redux';
import { ThunkDispatch } from 'redux-thunk';
import { IAnalyticsState } from '../analytics/reducer';
import { IAuthenticationState } from '../authentication/reducer';
@ -75,7 +76,7 @@ import { IVideoSipGW } from '../videosipgw/reducer';
import { IVirtualBackground } from '../virtual-background/reducer';
export interface IStore {
dispatch: Dispatch<AnyAction>;
dispatch: ThunkDispatch<IState, void, AnyAction>;
getState: () => IState;
}

View File

@ -145,7 +145,10 @@ class LoginDialog extends Component<Props, State> {
dispatch
} = this.props;
const { password, username } = this.state;
const jid = toJid(username, configHosts);
const jid = toJid(username, configHosts ?? {
authdomain: '',
domain: ''
});
if (conference) {
dispatch(authenticateAndUpgradeRole(jid, password, conference));
@ -217,7 +220,8 @@ class LoginDialog extends Component<Props, State> {
const { credentials } = error;
if (credentials
&& credentials.jid === toJid(username, configHosts)
&& credentials.jid === toJid(username, configHosts ?? { authdomain: '',
domain: '' })
&& credentials.password === password) {
messageKey = t('dialog.incorrectPassword');
}

View File

@ -60,7 +60,7 @@ export interface IConferenceState {
password?: string;
passwordRequired?: Object;
pendingSubjectChange?: string;
room?: Object;
room?: string;
startAudioMutedPolicy?: boolean;
startReactionsMuted?: boolean;
startVideoMutedPolicy?: boolean;

View File

@ -1,10 +1,9 @@
// @flow
// @ts-ignore
import { jitsiLocalStorage } from '@jitsi/js-utils';
import type { Dispatch } from 'redux';
import { Dispatch } from 'redux';
import { addKnownDomains } from '../known-domains';
import { parseURIString } from '../util';
import { addKnownDomains } from '../known-domains/actions';
import { parseURIString } from '../util/uri';
import {
CONFIG_WILL_LOAD,
@ -13,8 +12,9 @@ import {
UPDATE_CONFIG,
OVERWRITE_CONFIG
} from './actionTypes';
import { IConfig } from './configType';
import { _CONFIG_STORE_PREFIX } from './constants';
import { setConfigFromURLParams } from './functions';
import { setConfigFromURLParams } from './functions.any';
/**
@ -23,7 +23,7 @@ import { setConfigFromURLParams } from './functions';
* @param {Object} config - The new options (to add).
* @returns {Function}
*/
export function updateConfig(config: Object) {
export function updateConfig(config: IConfig) {
return {
type: UPDATE_CONFIG,
config
@ -116,6 +116,8 @@ export function setConfig(config: Object = {}) {
// On Web the config also comes from the window.config global,
// but it is resolved in the loadConfig procedure.
config,
// @ts-ignore
window.interfaceConfig,
locationURL);
@ -142,6 +144,7 @@ export function storeConfig(baseURL: string, config: Object) {
let b = false;
try {
// @ts-ignore
if (typeof window.config === 'undefined' || window.config !== config) {
jitsiLocalStorage.setItem(`${_CONFIG_STORE_PREFIX}/${baseURL}`, JSON.stringify(config));
b = true;
@ -153,7 +156,7 @@ export function storeConfig(baseURL: string, config: Object) {
// If base/config knows a domain, then the app knows it.
if (b) {
try {
dispatch(addKnownDomains(parseURIString(baseURL).host));
dispatch(addKnownDomains(parseURIString(baseURL)?.host));
} catch (e) {
// Ignore the error because the fiddling with "known domains" is
// a side effect here.

View File

@ -105,6 +105,7 @@ export interface IConfig {
scriptURLs?: Array<string>;
};
apiLogLevels?: Array<'warn' | 'log' | 'error' | 'info' | 'debug'>;
appId?: string;
audioLevelsInterval?: number;
audioQuality?: {
opusMaxAverageBitrate?: number | null;
@ -316,6 +317,8 @@ export interface IConfig {
disabled?: boolean;
};
gravatarBaseURL?: string;
guestDialOutStatusUrl?: string;
guestDialOutUrl?: string;
helpCentreURL?: string;
hiddenPremeetingButtons?: Array<'microphone' | 'camera' | 'select-background' | 'invite' | 'settings'>;
hideAddRoomButton?: boolean;
@ -354,7 +357,7 @@ export interface IConfig {
notifyAllParticipants?: boolean;
};
localSubject?: string;
locationURL?: string;
locationURL?: URL;
maxFullResolutionParticipants?: number;
moderatedRoomServiceUrl?: string;
mouseMoveCallbackInterval?: number;
@ -403,12 +406,14 @@ export interface IConfig {
hideStorageWarning?: boolean;
sharingEnabled?: boolean;
};
recordingSharingUrl?: string;
remoteVideoMenu?: {
disableGrantModerator?: boolean;
disableKick?: boolean;
disablePrivateChat?: boolean;
disabled?: boolean;
};
replaceParticipant?: string;
requireDisplayName?: boolean;
resolution?: number;
roomPasswordNumberOfDigits?: number;
@ -417,6 +422,7 @@ export interface IConfig {
enabled?: boolean;
mode?: 'always' | 'recording';
};
serviceUrl?: string;
speakerStatsOrder?: Array<'role' | 'name' | 'hasLeft'>;
startAudioMuted?: boolean;
startAudioOnly?: boolean;
@ -481,4 +487,5 @@ export interface IConfig {
webrtcIceTcpDisable?: boolean;
webrtcIceUdpDisable?: boolean;
websocket?: string;
websocketKeepAliveUrl?: string;
}

View File

@ -1,12 +1,15 @@
// @flow
/* eslint-disable lines-around-comment */
// @ts-ignore
import Bourne from '@hapi/bourne';
// @ts-ignore
import { jitsiLocalStorage } from '@jitsi/js-utils';
import _ from 'lodash';
import { IState } from '../../app/types';
import { browser } from '../lib-jitsi-meet';
import { parseURLParams } from '../util';
import { parseURLParams } from '../util/parseURLParams';
import { IConfig } from './configType';
import CONFIG_WHITELIST from './configWhitelist';
import { _CONFIG_STORE_PREFIX, FEATURE_FLAGS } from './constants';
import INTERFACE_CONFIG_WHITELIST from './interfaceConfigWhitelist';
@ -46,7 +49,7 @@ export function createFakeConfig(baseURL: string) {
* @param {Object} state - The global state.
* @returns {string}
*/
export function getMeetingRegion(state: Object) {
export function getMeetingRegion(state: IState) {
return state['features/base/config']?.deploymentInfo?.region || '';
}
@ -56,7 +59,7 @@ export function getMeetingRegion(state: Object) {
* @param {Object} state - The global state.
* @returns {boolean}
*/
export function getMultipleVideoSupportFeatureFlag(state: Object) {
export function getMultipleVideoSupportFeatureFlag(state: IState) {
return getFeatureFlag(state, FEATURE_FLAGS.MULTIPLE_VIDEO_STREAMS_SUPPORT)
&& getSourceNameSignalingFeatureFlag(state);
}
@ -67,7 +70,7 @@ export function getMultipleVideoSupportFeatureFlag(state: Object) {
* @param {Object} state - The global state.
* @returns {boolean}
*/
export function getMultipleVideoSendingSupportFeatureFlag(state: Object) {
export function getMultipleVideoSendingSupportFeatureFlag(state: IState) {
return navigator.product !== 'ReactNative'
&& getMultipleVideoSupportFeatureFlag(state) && isUnifiedPlanEnabled(state);
}
@ -78,7 +81,7 @@ export function getMultipleVideoSendingSupportFeatureFlag(state: Object) {
* @param {Object} state - The global state.
* @returns {boolean}
*/
export function getSourceNameSignalingFeatureFlag(state: Object) {
export function getSourceNameSignalingFeatureFlag(state: IState) {
return getFeatureFlag(state, FEATURE_FLAGS.SOURCE_NAME_SIGNALING);
}
@ -89,10 +92,10 @@ export function getSourceNameSignalingFeatureFlag(state: Object) {
* @param {string} featureFlag - The name of the feature flag.
* @returns {boolean}
*/
export function getFeatureFlag(state: Object, featureFlag: string) {
export function getFeatureFlag(state: IState, featureFlag: string) {
const featureFlags = state['features/base/config']?.flags || {};
return Boolean(featureFlags[featureFlag]);
return Boolean(featureFlags[featureFlag as keyof typeof featureFlags]);
}
/**
@ -101,7 +104,7 @@ export function getFeatureFlag(state: Object, featureFlag: string) {
* @param {Object} state - The global state.
* @returns {boolean}
*/
export function getDisableRemoveRaisedHandOnFocus(state: Object) {
export function getDisableRemoveRaisedHandOnFocus(state: IState) {
return state['features/base/config']?.disableRemoveRaisedHandOnFocus || false;
}
@ -111,7 +114,7 @@ export function getDisableRemoveRaisedHandOnFocus(state: Object) {
* @param {Object} state - The global state.
* @returns {string}
*/
export function getRecordingSharingUrl(state: Object) {
export function getRecordingSharingUrl(state: IState) {
return state['features/base/config'].recordingSharingUrl;
}
@ -136,7 +139,7 @@ export function getRecordingSharingUrl(state: Object) {
* }.
* @returns {void}
*/
export function overrideConfigJSON(config: ?Object, interfaceConfig: ?Object, json: Object) {
export function overrideConfigJSON(config: IConfig, interfaceConfig: any, json: any) {
for (const configName of Object.keys(json)) {
let configObj;
@ -147,7 +150,7 @@ export function overrideConfigJSON(config: ?Object, interfaceConfig: ?Object, js
}
if (configObj) {
const configJSON
= getWhitelistedJSON(configName, json[configName]);
= getWhitelistedJSON(configName as 'interfaceConfig' | 'config', json[configName]);
if (!_.isEmpty(configJSON)) {
logger.info(
@ -177,7 +180,7 @@ export function overrideConfigJSON(config: ?Object, interfaceConfig: ?Object, js
* @returns {Object} - The result object only with the keys
* that are whitelisted.
*/
export function getWhitelistedJSON(configName: string, configJSON: Object): Object {
export function getWhitelistedJSON(configName: 'interfaceConfig' | 'config', configJSON: any): Object {
if (configName === 'interfaceConfig') {
return _.pick(configJSON, INTERFACE_CONFIG_WHITELIST);
} else if (configName === 'config') {
@ -193,9 +196,9 @@ export function getWhitelistedJSON(configName: string, configJSON: Object): Obje
* @param {Object} state - The state of the app.
* @returns {boolean}
*/
export function isNameReadOnly(state: Object): boolean {
return state['features/base/config'].disableProfile
|| state['features/base/config'].readOnlyName;
export function isNameReadOnly(state: IState): boolean {
return Boolean(state['features/base/config'].disableProfile
|| state['features/base/config'].readOnlyName);
}
/**
@ -204,7 +207,7 @@ export function isNameReadOnly(state: Object): boolean {
* @param {Object} state - The state of the app.
* @returns {boolean}
*/
export function isDisplayNameVisible(state: Object): boolean {
export function isDisplayNameVisible(state: IState): boolean {
return !state['features/base/config'].hideDisplayName;
}
@ -214,7 +217,7 @@ export function isDisplayNameVisible(state: Object): boolean {
* @param {Object} state - The state of the app.
* @returns {boolean}
*/
export function isUnifiedPlanEnabled(state: Object): boolean {
export function isUnifiedPlanEnabled(state: IState): boolean {
const { enableUnifiedOnChrome = true } = state['features/base/config'];
return browser.supportsUnifiedPlan()
@ -232,7 +235,7 @@ export function isUnifiedPlanEnabled(state: Object): boolean {
* from {@code baseURL} and stored with {@code storeConfig} if it was restored;
* otherwise, {@code undefined}.
*/
export function restoreConfig(baseURL: string): ?Object {
export function restoreConfig(baseURL: string) {
const key = `${_CONFIG_STORE_PREFIX}/${baseURL}`;
const config = jitsiLocalStorage.getItem(key);
@ -266,9 +269,9 @@ export function restoreConfig(baseURL: string): ?Object {
* @returns {void}
*/
export function setConfigFromURLParams(
config: ?Object, interfaceConfig: ?Object, location: Object) {
config: IConfig, interfaceConfig: any, location: string | URL) {
const params = parseURLParams(location);
const json = {};
const json: any = {};
// At this point we have:
// params = {
@ -292,7 +295,7 @@ export function setConfigFromURLParams(
for (const param of Object.keys(params)) {
let base = json;
const names = param.split('.');
const last = names.pop();
const last = names.pop() ?? '';
for (const name of names) {
base = base[name] = base[name] || {};

View File

@ -1,8 +1,10 @@
// @flow
import { NativeModules } from 'react-native';
import { getFeatureFlag, REPLACE_PARTICIPANT } from '../flags';
import { IState } from '../../app/types';
import { REPLACE_PARTICIPANT } from '../flags/constants';
import { getFeatureFlag } from '../flags/functions';
import { IConfig } from './configType';
export * from './functions.any';
@ -12,7 +14,8 @@ export * from './functions.any';
* @param {*} config - The configuration which needs to be cleaned up.
* @returns {void}
*/
export function _cleanupConfig(config: Object) {
export function _cleanupConfig(config: IConfig) {
config.analytics ??= {};
config.analytics.scriptURLs = [];
if (NativeModules.AppInfo.LIBRE_BUILD) {
delete config.analytics?.amplitudeAPPKey;
@ -29,6 +32,6 @@ export function _cleanupConfig(config: Object) {
* @param {Object} state - The state of the app.
* @returns {boolean}
*/
export function getReplaceParticipant(state: Object): string {
export function getReplaceParticipant(state: IState): string {
return getFeatureFlag(state, REPLACE_PARTICIPANT, false);
}

View File

@ -1,5 +1,6 @@
// @flow
import { IState } from '../../app/types';
import { IConfig } from './configType';
import { TOOLBAR_BUTTONS } from './constants';
export * from './functions.any';
@ -10,7 +11,7 @@ export * from './functions.any';
* @param {*} config - The configuration which needs to be cleaned up.
* @returns {void}
*/
export function _cleanupConfig(config: Object) { // eslint-disable-line no-unused-vars
export function _cleanupConfig(config: IConfig) { // eslint-disable-line @typescript-eslint/no-unused-vars
}
/**
@ -19,7 +20,7 @@ export function _cleanupConfig(config: Object) { // eslint-disable-line no-unuse
* @param {Object} state - The state of the app.
* @returns {string}
*/
export function getDialOutStatusUrl(state: Object): string {
export function getDialOutStatusUrl(state: IState): string | undefined {
return state['features/base/config'].guestDialOutStatusUrl;
}
@ -29,7 +30,7 @@ export function getDialOutStatusUrl(state: Object): string {
* @param {Object} state - The state of the app.
* @returns {string}
*/
export function getDialOutUrl(state: Object): string {
export function getDialOutUrl(state: IState): string | undefined {
return state['features/base/config'].guestDialOutUrl;
}
@ -39,7 +40,7 @@ export function getDialOutUrl(state: Object): string {
* @param {Object} state - The state of the app.
* @returns {boolean}
*/
export function getReplaceParticipant(state: Object): string {
export function getReplaceParticipant(state: IState): string | undefined {
return state['features/base/config'].replaceParticipant;
}
@ -49,7 +50,7 @@ export function getReplaceParticipant(state: Object): string {
* @param {Object} state - The redux state.
* @returns {Array<string>} - The list of enabled toolbar buttons.
*/
export function getToolbarButtons(state: Object): Array<string> {
export function getToolbarButtons(state: IState): Array<string> {
const { toolbarButtons } = state['features/base/config'];
return Array.isArray(toolbarButtons) ? toolbarButtons : TOOLBAR_BUTTONS;
@ -63,7 +64,7 @@ export function getToolbarButtons(state: Object): Array<string> {
* @param {Object|Array<string>} state - The redux state or the array with the enabled buttons.
* @returns {boolean} - True if the button is enabled and false otherwise.
*/
export function isToolbarButtonEnabled(buttonName: string, state: Object | Array<string>) {
export function isToolbarButtonEnabled(buttonName: string, state: IState | Array<string>) {
const buttons = Array.isArray(state) ? state : getToolbarButtons(state);
return buttons.includes(buttonName);
@ -75,7 +76,7 @@ export function isToolbarButtonEnabled(buttonName: string, state: Object | Array
* @param {Object} state - The state of the app.
* @returns {boolean}
*/
export function areAudioLevelsEnabled(state: Object): boolean {
export function areAudioLevelsEnabled(state: IState): boolean {
// Default to false for React Native as audio levels are of no interest to the mobile app.
return navigator.product !== 'ReactNative' && !state['features/base/config'].disableAudioLevels;
}

View File

@ -1,13 +1,11 @@
// @flow
import { getBackendSafeRoomName } from '../util';
import { getBackendSafeRoomName } from '../util/uri';
/**
* Builds and returns the room name.
*
* @returns {string}
*/
export default function getRoomName(): ?string {
export default function getRoomName(): string | undefined {
const path = window.location.pathname;
// The last non-directory component of the path (name) is the room.

View File

@ -1,5 +1,3 @@
// @flow
import { getLogger } from '../logging/functions';
export default getLogger('features/base/config');

View File

@ -1,9 +1,13 @@
import { AnyAction } from 'redux';
import { IStore } from '../../app/types';
import { getFeatureFlag } from '../flags/functions';
import { MiddlewareRegistry } from '../redux';
import { updateSettings } from '../settings';
import MiddlewareRegistry from '../redux/MiddlewareRegistry';
import { updateSettings } from '../settings/actions';
import { SET_CONFIG, OVERWRITE_CONFIG } from './actionTypes';
import { updateConfig } from './actions';
import { IConfig } from './configType';
/**
* The middleware of the feature {@code base/config}.
@ -37,7 +41,7 @@ MiddlewareRegistry.register(store => next => action => {
* @private
* @returns {*} The return value of {@code next(action)}.
*/
function _setConfig({ dispatch, getState }, next, action) {
function _setConfig({ dispatch, getState }: IStore, next: Function, action: AnyAction) {
// 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
// reduced.
@ -46,7 +50,7 @@ function _setConfig({ dispatch, getState }, next, action) {
// Update the config with user defined settings.
const settings = state['features/base/settings'];
const config = {};
const config: IConfig = {};
if (typeof settings.disableP2P !== 'undefined') {
config.p2p = { enabled: !settings.disableP2P };
@ -77,7 +81,9 @@ function _setConfig({ dispatch, getState }, next, action) {
// not be the global variable which is being modified anymore due to
// different merge methods being used along the way. The global variable
// must be synchronized with the final state resolved by the reducer.
// @ts-ignore
if (typeof window.config !== 'undefined') {
// @ts-ignore
window.config = state['features/base/config'];
}
@ -96,7 +102,7 @@ function _setConfig({ dispatch, getState }, next, action) {
* @private
* @returns {*} The return value of {@code next(action)}.
*/
function _updateSettings({ dispatch }, next, action) {
function _updateSettings({ dispatch }: IStore, next: Function, action: AnyAction) {
const { config: { doNotFlipLocalVideo } } = action;
if (doNotFlipLocalVideo === true) {

View File

@ -1,10 +1,11 @@
import _ from 'lodash';
import { IState } from '../../app/types';
import {
appendURLParam,
getBackendSafeRoomName,
parseURIString
} from '../util';
} from '../util/uri';
import logger from './logger';
@ -16,7 +17,7 @@ import logger from './logger';
* @returns {Object} The options to be passed to the constructor of
* {@code JitsiConnection}.
*/
export function constructOptions(state) {
export function constructOptions(state: IState) {
// Deep clone the options to make sure we don't modify the object in the
// redux store.
const options = _.cloneDeep(state['features/base/config']);
@ -36,14 +37,14 @@ export function constructOptions(state) {
if (bosh.startsWith('//')) {
// By default our config.js doesn't include the protocol.
bosh = `${locationURL.protocol}${bosh}`;
bosh = `${locationURL?.protocol}${bosh}`;
} else if (bosh.startsWith('/')) {
// Handle relative URLs, which won't work on mobile.
const {
protocol,
host,
contextRoot
} = parseURIString(locationURL.href);
} = parseURIString(locationURL?.href);
bosh = `${protocol}//${host}${contextRoot || '/'}${bosh.substr(1)}`;
}
@ -60,10 +61,10 @@ export function constructOptions(state) {
if (serviceUrl && room) {
const roomName = getBackendSafeRoomName(room);
options.serviceUrl = appendURLParam(serviceUrl, 'room', roomName);
options.serviceUrl = appendURLParam(serviceUrl, 'room', roomName ?? '');
if (options.websocketKeepAliveUrl) {
options.websocketKeepAliveUrl = appendURLParam(options.websocketKeepAliveUrl, 'room', roomName);
options.websocketKeepAliveUrl = appendURLParam(options.websocketKeepAliveUrl, 'room', roomName ?? '');
}
}

View File

@ -1,8 +1,9 @@
// @flow
import type { Dispatch } from 'redux';
/* eslint-disable lines-around-comment */
import { Dispatch } from 'redux';
// @ts-ignore
import { conferenceLeft, conferenceWillLeave } from '../conference/actions';
// @ts-ignore
import { getCurrentConference } from '../conference/functions';
import JitsiMeetJS, { JitsiConnectionEvents } from '../lib-jitsi-meet';
@ -36,34 +37,34 @@ export type ConnectionFailedError = {
/**
* The XMPP user's ID.
*/
jid: string,
jid: string;
/**
* The XMPP user's password.
*/
password: string
},
password: string;
};
/**
* The details about the connection failed event.
*/
details?: Object,
details?: Object;
/**
* Error message.
*/
message?: string,
message?: string;
/**
* One of {@link JitsiConnectionError} constants (defined in
* lib-jitsi-meet).
*/
name: string,
name: string;
/**
* Indicates whether this event is recoverable or not.
*/
recoverable?: boolean
recoverable?: boolean;
};
/**
@ -73,7 +74,7 @@ export type ConnectionFailedError = {
* @param {string} [password] - The XMPP user's password.
* @returns {Function}
*/
export function connect(id: ?string, password: ?string) {
export function connect(id?: string, password?: string) {
return (dispatch: Dispatch<any>, getState: Function) => {
const state = getState();
const options = constructOptions(state);
@ -141,7 +142,7 @@ export function connect(id: ?string, password: ?string) {
function _onConnectionFailed( // eslint-disable-line max-params
err: string,
msg: string,
credentials: Object,
credentials: any,
details: Object) {
unsubscribe();
dispatch(
@ -253,7 +254,7 @@ export function connectionFailed(
* connection: JitsiConnection
* }}
*/
function _connectionWillConnect(connection) {
function _connectionWillConnect(connection: Object) {
return {
type: CONNECTION_WILL_CONNECT,
connection
@ -286,7 +287,7 @@ export function disconnect() {
promise
= conference_.leave()
.catch(error => {
.catch((error: Error) => {
logger.warn(
'JitsiConference.leave() rejected with:',
error);
@ -328,7 +329,7 @@ export function disconnect() {
* locationURL: URL
* }}
*/
export function setLocationURL(locationURL: ?URL) {
export function setLocationURL(locationURL?: URL) {
return {
type: SET_LOCATION_URL,
locationURL

View File

@ -1,12 +1,8 @@
// @flow
import { IStore } from '../../app/types';
import { configureInitialDevices } from '../devices/actions';
import { getBackendSafeRoomName } from '../util/uri';
import type { Dispatch } from 'redux';
declare var APP: Object;
declare var config: Object;
import { configureInitialDevices } from '../devices';
import { getBackendSafeRoomName } from '../util';
declare const APP: any;
export {
connectionDisconnected,
@ -24,7 +20,7 @@ export * from './actions.any';
* @returns {Promise<JitsiConnection>}
*/
export function connect() {
return (dispatch: Dispatch<any>, getState: Function) => {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const room = getBackendSafeRoomName(getState()['features/base/conference'].room);
// XXX For web based version we use conference initialization logic
@ -32,7 +28,7 @@ export function connect() {
return dispatch(configureInitialDevices()).then(
() => APP.conference.init({
roomName: room
}).catch(error => {
}).catch((error: Error) => {
APP.API.notifyConferenceLeft(APP.conference.roomName);
logger.error(error);
}));
@ -46,7 +42,7 @@ export function connect() {
* request for call feedback.
* @returns {Function}
*/
export function disconnect(requestFeedback: boolean = false) {
export function disconnect(requestFeedback = false) {
// XXX For web based version we use conference hanging up logic from the old
// app.
return () => APP.conference.hangup(requestFeedback);

View File

@ -1,5 +1,3 @@
// @flow
/**
* The name of the {@code JitsiConnection} property which identifies the {@code JitsiConference} currently associated
* with it.

View File

@ -1,7 +1,6 @@
/* @flow */
import { toState } from '../redux';
import { toURLString } from '../util';
import { IStateful } from '../app/types';
import { toState } from '../redux/functions';
import { toURLString } from '../util/uri';
import { getURLWithoutParams } from './utils';
@ -13,7 +12,7 @@ import { getURLWithoutParams } from './utils';
* @returns {string|undefined}
* @private
*/
export function getCurrentConferenceUrl(stateful: Function | Object) {
export function getCurrentConferenceUrl(stateful: IStateful) {
const state = toState(stateful);
let currentUrl;
@ -40,7 +39,7 @@ export function getCurrentConferenceUrl(stateful: Function | Object) {
* to be stripped.
* @returns {string}
*/
export function getInviteURL(stateOrGetState: Function | Object): string {
export function getInviteURL(stateOrGetState: IStateful): string {
const state = toState(stateOrGetState);
let locationURL
= state instanceof URL
@ -75,7 +74,7 @@ export function getInviteURL(stateOrGetState: Function | Object): string {
* @param {Function|Object} stateOrGetState - The redux state or redux's {@code getState} function.
* @returns {boolean}
*/
export function isInviteURLReady(stateOrGetState: Function | Object): boolean {
export function isInviteURLReady(stateOrGetState: IStateful): boolean {
const state = toState(stateOrGetState);
return Boolean(state['features/base/connection'].locationURL || state['features/base/config'].locationURL);
@ -90,6 +89,6 @@ export function isInviteURLReady(stateOrGetState: Function | Object): boolean {
* @returns {string} A string in the form of a JID (i.e.
* {@code user@server.com}).
*/
export function toJid(id: string, { authdomain, domain }: Object): string {
export function toJid(id: string, { authdomain, domain }: { authdomain?: string; domain?: string; }): string {
return id.indexOf('@') >= 0 ? id : `${id}@${authdomain || domain}`;
}

View File

@ -1,5 +1,3 @@
// @flow
import { getLogger } from '../logging/functions';
export default getLogger('features/base/connection');

View File

@ -1,8 +1,7 @@
import { IStore } from '../../app/types';
import JitsiMeetJS from '../lib-jitsi-meet';
import {
getUserSelectedOutputDeviceId,
updateSettings
} from '../settings';
import { updateSettings } from '../settings/actions';
import { getUserSelectedOutputDeviceId } from '../settings/functions.any';
import {
ADD_PENDING_DEVICE_REQUEST,
@ -55,7 +54,7 @@ const DEVICE_TYPE_TO_SETTINGS_KEYS = {
* request: Object
* }}
*/
export function addPendingDeviceRequest(request) {
export function addPendingDeviceRequest(request: Object) {
return {
type: ADD_PENDING_DEVICE_REQUEST,
request
@ -68,7 +67,7 @@ export function addPendingDeviceRequest(request) {
* @returns {Function}
*/
export function configureInitialDevices() {
return (dispatch, getState) => {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const deviceLabels = getDevicesFromURL(getState());
let updateSettingsPromise;
@ -86,9 +85,9 @@ export function configureInitialDevices() {
name: 'setDevice',
device: {
kind: key.toLowerCase(),
label: deviceLabels[key]
label: deviceLabels[key as keyof typeof deviceLabels]
},
// eslint-disable-next-line no-empty-function
// eslint-disable-next-line @typescript-eslint/no-empty-function
responseCallback() {}
}));
});
@ -96,14 +95,17 @@ export function configureInitialDevices() {
return;
}
const newSettings = {};
const newSettings: any = {};
Object.keys(deviceLabels).forEach(key => {
const label = deviceLabels[key];
const label = deviceLabels[key as keyof typeof deviceLabels];
// @ts-ignore
const deviceId = getDeviceIdByLabel(state, label, key);
if (deviceId) {
const settingsTranslationMap = DEVICE_TYPE_TO_SETTINGS_KEYS[key];
const settingsTranslationMap = DEVICE_TYPE_TO_SETTINGS_KEYS[
key as keyof typeof DEVICE_TYPE_TO_SETTINGS_KEYS];
newSettings[settingsTranslationMap.currentDeviceId] = deviceId;
newSettings[settingsTranslationMap.userSelectedDeviceId] = deviceId;
@ -135,12 +137,12 @@ export function configureInitialDevices() {
* @returns {Function}
*/
export function getAvailableDevices() {
return dispatch => new Promise(resolve => {
return (dispatch: IStore['dispatch']) => new Promise(resolve => {
const { mediaDevices } = JitsiMeetJS;
if (mediaDevices.isDeviceListAvailable()
&& mediaDevices.isDeviceChangeAvailable()) {
mediaDevices.enumerateDevices(devices => {
mediaDevices.enumerateDevices((devices: any) => {
dispatch(updateDeviceList(devices));
resolve(devices);
@ -163,7 +165,7 @@ export function getAvailableDevices() {
* error: Object
* }}
*/
export function notifyCameraError(error) {
export function notifyCameraError(error: Error) {
return {
type: NOTIFY_CAMERA_ERROR,
error
@ -182,7 +184,7 @@ export function notifyCameraError(error) {
* error: Object
* }}
*/
export function notifyMicError(error) {
export function notifyMicError(error: Error) {
return {
type: NOTIFY_MIC_ERROR,
error
@ -211,7 +213,7 @@ export function removePendingDeviceRequests() {
* deviceId: string
* }}
*/
export function setAudioInputDevice(deviceId) {
export function setAudioInputDevice(deviceId: string) {
return {
type: SET_AUDIO_INPUT_DEVICE,
deviceId
@ -225,8 +227,8 @@ export function setAudioInputDevice(deviceId) {
* @param {string} deviceId - The id of the new audio input device.
* @returns {Function}
*/
export function setAudioInputDeviceAndUpdateSettings(deviceId) {
return function(dispatch, getState) {
export function setAudioInputDeviceAndUpdateSettings(deviceId: string) {
return function(dispatch: IStore['dispatch'], getState: IStore['getState']) {
const deviceLabel = getDeviceLabelById(getState(), deviceId, 'audioInput');
dispatch(setAudioInputDevice(deviceId));
@ -243,8 +245,8 @@ export function setAudioInputDeviceAndUpdateSettings(deviceId) {
* @param {string} deviceId - The id of the new output device.
* @returns {Function}
*/
export function setAudioOutputDevice(deviceId) {
return function(dispatch, getState) {
export function setAudioOutputDevice(deviceId: string) {
return function(dispatch: IStore['dispatch'], getState: IStore['getState']) {
const deviceLabel = getDeviceLabelById(getState(), deviceId, 'audioOutput');
return setAudioOutputDeviceId(deviceId, dispatch, true, deviceLabel);
@ -260,7 +262,7 @@ export function setAudioOutputDevice(deviceId) {
* deviceId: string
* }}
*/
export function setVideoInputDevice(deviceId) {
export function setVideoInputDevice(deviceId: string) {
return {
type: SET_VIDEO_INPUT_DEVICE,
deviceId
@ -274,8 +276,8 @@ export function setVideoInputDevice(deviceId) {
* @param {string} deviceId - The id of the new video input device.
* @returns {Function}
*/
export function setVideoInputDeviceAndUpdateSettings(deviceId) {
return function(dispatch, getState) {
export function setVideoInputDeviceAndUpdateSettings(deviceId: string) {
return function(dispatch: IStore['dispatch'], getState: IStore['getState']) {
const deviceLabel = getDeviceLabelById(getState(), deviceId, 'videoInput');
dispatch(setVideoInputDevice(deviceId));
@ -296,7 +298,7 @@ export function setVideoInputDeviceAndUpdateSettings(deviceId) {
* devices: Array<MediaDeviceInfo>
* }}
*/
export function updateDeviceList(devices) {
export function updateDeviceList(devices: MediaDeviceInfo[]) {
return {
type: UPDATE_DEVICE_LIST,
devices
@ -314,7 +316,7 @@ export function updateDeviceList(devices) {
* oldDevices: Array<MediaDeviceInfo>
* }}
*/
export function checkAndNotifyForNewDevice(newDevices, oldDevices) {
export function checkAndNotifyForNewDevice(newDevices: MediaDeviceInfo[], oldDevices: MediaDeviceInfo[]) {
return {
type: CHECK_AND_NOTIFY_FOR_NEW_DEVICE,
newDevices,
@ -331,7 +333,7 @@ export function checkAndNotifyForNewDevice(newDevices, oldDevices) {
* permissions: Object
* }}
*/
export function devicePermissionsChanged(permissions) {
export function devicePermissionsChanged(permissions: Object) {
return {
type: DEVICE_PERMISSIONS_CHANGED,
permissions

View File

@ -1,12 +1,14 @@
// @flow
import { IState } from '../../app/types';
import JitsiMeetJS from '../lib-jitsi-meet';
import { updateSettings } from '../settings';
import { parseURLParams } from '../util';
import { updateSettings } from '../settings/actions';
import { ISettingsState } from '../settings/reducer';
import { parseURLParams } from '../util/parseURLParams';
import logger from './logger';
import { IDevicesState } from './reducer';
declare var APP: Object;
declare const APP: any;
const webrtcKindToJitsiKindTranslator = {
audioinput: 'audioInput',
@ -22,14 +24,16 @@ const webrtcKindToJitsiKindTranslator = {
* @returns {boolean} - True if the labels are already initialized and false
* otherwise.
*/
export function areDeviceLabelsInitialized(state: Object) {
export function areDeviceLabelsInitialized(state: IState) {
// TODO: Replace with something that doesn't use APP when the conference.js logic is reactified.
if (APP.conference._localTracksInitialized) {
return true;
}
for (const type of [ 'audioInput', 'audioOutput', 'videoInput' ]) {
if ((state['features/base/devices'].availableDevices[type] || []).find(d => Boolean(d.label))) {
const availableDevices = state['features/base/devices'].availableDevices;
if ((availableDevices[type as keyof typeof availableDevices] || []).find(d => Boolean(d.label))) {
return true;
}
}
@ -56,14 +60,15 @@ export function getAudioOutputDeviceId() {
* of the preceding types.
* @returns {string|undefined}
*/
export function getDefaultDeviceId(state: Object, kind: string) {
const kindToSearch = webrtcKindToJitsiKindTranslator[kind] || kind;
const defaultDevice = (state['features/base/devices'].availableDevices[kindToSearch] || [])
export function getDefaultDeviceId(state: IState, kind: string) {
const kindToSearch = webrtcKindToJitsiKindTranslator[kind as keyof typeof webrtcKindToJitsiKindTranslator] || kind;
const availableDevices = state['features/base/devices'].availableDevices;
const defaultDevice = (availableDevices[kindToSearch as keyof typeof availableDevices] || [])
.find(d => d.deviceId === 'default');
// Find the device with a matching group id.
const matchingDevice = (state['features/base/devices'].availableDevices[kindToSearch] || [])
.find(d => d.deviceId !== 'default' && d.groupId === defaultDevice.groupId);
const matchingDevice = (availableDevices[kindToSearch as keyof typeof availableDevices] || [])
.find(d => d.deviceId !== 'default' && d.groupId === defaultDevice?.groupId);
if (matchingDevice) {
return matchingDevice.deviceId;
@ -80,11 +85,12 @@ export function getDefaultDeviceId(state: Object, kind: string) {
* of the preceding types.
* @returns {string|undefined}
*/
export function getDeviceIdByLabel(state: Object, label: string, kind: string) {
const kindToSearch = webrtcKindToJitsiKindTranslator[kind] || kind;
export function getDeviceIdByLabel(state: IState, label: string, kind: string) {
const kindToSearch = webrtcKindToJitsiKindTranslator[kind as keyof typeof webrtcKindToJitsiKindTranslator] || kind;
const availableDevices = state['features/base/devices'].availableDevices;
const device
= (state['features/base/devices'].availableDevices[kindToSearch] || [])
= (availableDevices[kindToSearch as keyof typeof availableDevices] || [])
.find(d => d.label === label);
if (device) {
@ -102,11 +108,12 @@ export function getDeviceIdByLabel(state: Object, label: string, kind: string) {
* of the preceding types.
* @returns {string|undefined}
*/
export function getDeviceLabelById(state: Object, id: string, kind: string) {
const kindToSearch = webrtcKindToJitsiKindTranslator[kind] || kind;
export function getDeviceLabelById(state: IState, id: string, kind: string) {
const kindToSearch = webrtcKindToJitsiKindTranslator[kind as keyof typeof webrtcKindToJitsiKindTranslator] || kind;
const availableDevices = state['features/base/devices'].availableDevices;
const device
= (state['features/base/devices'].availableDevices[kindToSearch] || [])
= (availableDevices[kindToSearch as keyof typeof availableDevices] || [])
.find(d => d.deviceId === id);
if (device) {
@ -120,9 +127,9 @@ export function getDeviceLabelById(state: Object, id: string, kind: string) {
* @param {Object} state - The redux state.
* @returns {Object|undefined}
*/
export function getDevicesFromURL(state: Object) {
export function getDevicesFromURL(state: IState) {
const urlParams
= parseURLParams(state['features/base/connection'].locationURL);
= parseURLParams(state['features/base/connection'].locationURL ?? '');
const audioOutput = urlParams['devices.audioOutput'];
const videoInput = urlParams['devices.videoInput'];
@ -132,7 +139,7 @@ export function getDevicesFromURL(state: Object) {
return undefined;
}
const devices = {};
const devices: IDevicesState['availableDevices'] = {};
audioOutput && (devices.audioOutput = audioOutput);
videoInput && (devices.videoInput = videoInput);
@ -150,7 +157,7 @@ export function getDevicesFromURL(state: Object) {
* are device type and the values are arrays with devices matching the device
* type.
*/
export function groupDevicesByKind(devices: Object[]): Object {
export function groupDevicesByKind(devices: MediaDeviceInfo[]): IDevicesState['availableDevices'] {
return {
audioInput: devices.filter(device => device.kind === 'audioinput'),
audioOutput: devices.filter(device => device.kind === 'audiooutput'),
@ -165,7 +172,7 @@ export function groupDevicesByKind(devices: Object[]): Object {
* @private
* @returns {Array<MediaDeviceInfo>} Filtered audio devices.
*/
export function filterAudioDevices(devices: Object[]): Object {
export function filterAudioDevices(devices: MediaDeviceInfo[]) {
return devices.filter(device => device.kind === 'audioinput');
}
@ -197,8 +204,8 @@ export function formatDeviceLabel(label: string) {
* @param {Object} state - The state of the application.
* @returns {Object[]}
*/
export function getAudioInputDeviceData(state: Object) {
return state['features/base/devices'].availableDevices.audioInput.map(
export function getAudioInputDeviceData(state: IState) {
return state['features/base/devices'].availableDevices.audioInput?.map(
({ deviceId, label }) => {
return {
deviceId,
@ -213,8 +220,8 @@ export function getAudioInputDeviceData(state: Object) {
* @param {Object} state - The state of the application.
* @returns {Object[]}
*/
export function getAudioOutputDeviceData(state: Object) {
return state['features/base/devices'].availableDevices.audioOutput.map(
export function getAudioOutputDeviceData(state: IState) {
return state['features/base/devices'].availableDevices.audioOutput?.map(
({ deviceId, label }) => {
return {
deviceId,
@ -229,8 +236,8 @@ export function getAudioOutputDeviceData(state: Object) {
* @param {Object} state - The state of the application.
* @returns {string[]}
*/
export function getVideoDeviceIds(state: Object) {
return state['features/base/devices'].availableDevices.videoInput.map(({ deviceId }) => deviceId);
export function getVideoDeviceIds(state: IState) {
return state['features/base/devices'].availableDevices.videoInput?.map(({ deviceId }) => deviceId);
}
/**
@ -241,12 +248,14 @@ export function getVideoDeviceIds(state: Object) {
*
* @returns {boolean}
*/
export function hasAvailableDevices(state: Object, type: string) {
export function hasAvailableDevices(state: IState, type: string) {
if (state['features/base/devices'] === undefined) {
return true;
}
return state['features/base/devices'].availableDevices[type].length > 0;
const availableDevices = state['features/base/devices'].availableDevices;
return Number(availableDevices[type as keyof typeof availableDevices]?.length) > 0;
}
/**
@ -260,10 +269,10 @@ export function hasAvailableDevices(state: Object, type: string) {
* @returns {Promise}
*/
export function setAudioOutputDeviceId(
newId: string = 'default',
newId = 'default',
dispatch: Function,
userSelection: boolean = false,
newLabel: ?string): Promise<*> {
userSelection = false,
newLabel?: string): Promise<any> {
logger.debug(`setAudioOutputDevice: ${String(newLabel)}[${newId}]`);
@ -275,7 +284,7 @@ export function setAudioOutputDeviceId(
return JitsiMeetJS.mediaDevices.setAudioOutputDevice(newId)
.then(() => {
const newSettings = {
const newSettings: Partial<ISettingsState> = {
audioOutputDeviceId: newId,
userSelectedAudioOutputDeviceId: undefined,
userSelectedAudioOutputDeviceLabel: undefined

View File

@ -29,9 +29,9 @@ const DEFAULT_STATE: IDevicesState = {
export interface IDevicesState {
availableDevices: {
audioInput: MediaDeviceInfo[];
audioOutput: MediaDeviceInfo[];
videoInput: MediaDeviceInfo[];
audioInput?: MediaDeviceInfo[];
audioOutput?: MediaDeviceInfo[];
videoInput?: MediaDeviceInfo[];
};
pendingRequests: Object[];
permissions: {

View File

@ -1,5 +1,3 @@
// @flow
import { UPDATE_FLAGS } from './actionTypes';
/**

View File

@ -1,7 +1,7 @@
// @flow
// @ts-ignore
import { getAppProp } from '../app';
import { toState } from '../redux';
import { IStateful } from '../app/types';
import { toState } from '../redux/functions';
/**
* Gets the value of a specific feature flag.
@ -14,11 +14,11 @@ import { toState } from '../redux';
* @returns {*} The value of the specified React {@code Component} prop of the
* currently mounted {@code App}.
*/
export function getFeatureFlag(stateful: Function | Object, flag: string, defaultValue: any) {
export function getFeatureFlag(stateful: IStateful, flag: string, defaultValue?: any) {
const state = toState(stateful)['features/base/flags'];
if (state) {
const value = state[flag];
const value = state[flag as keyof typeof state];
if (typeof value !== 'undefined') {
return value;

View File

@ -1,5 +1,3 @@
// @flow
import { ADD_KNOWN_DOMAINS } from './actionTypes';
/**

View File

@ -1,10 +1,10 @@
// @flow
// @ts-ignore
import { getDefaultURL } from '../../app/functions';
import { APP_WILL_MOUNT } from '../app';
import { SET_ROOM } from '../conference';
import { MiddlewareRegistry } from '../redux';
import { parseURIString } from '../util';
import { IStore } from '../../app/types';
import { APP_WILL_MOUNT } from '../app/actionTypes';
import { SET_ROOM } from '../conference/actionTypes';
import MiddlewareRegistry from '../redux/MiddlewareRegistry';
import { parseURIString } from '../util/uri';
import { addKnownDomains } from './actions';
@ -32,10 +32,10 @@ MiddlewareRegistry.register(store => next => action => {
* @private
* @returns {Promise}
*/
function _appWillMount({ dispatch, getState }) {
function _appWillMount({ dispatch, getState }: IStore) {
const defaultURL = parseURIString(getDefaultURL(getState));
dispatch(addKnownDomains(defaultURL.host));
dispatch(addKnownDomains(defaultURL?.host));
}
/**
@ -46,7 +46,7 @@ function _appWillMount({ dispatch, getState }) {
* @private
* @returns {Promise}
*/
function _setRoom({ dispatch, getState }) {
function _setRoom({ dispatch, getState }: IStore) {
const { locationURL } = getState()['features/base/connection'];
let host;

View File

@ -1,7 +1,6 @@
/* @flow */
import { toState } from '../redux';
import { getPropertyValue } from '../settings';
import { IStateful } from '../app/types';
import { toState } from '../redux/functions';
import { getPropertyValue } from '../settings/functions.any';
import { VIDEO_MUTISM_AUTHORITY } from './constants';
@ -32,7 +31,7 @@ const START_WITH_AUDIO_VIDEO_MUTED_SOURCES = {
* {@code getState} function.
* @returns {boolean}
*/
export function isAudioMuted(stateful: Function | Object) {
export function isAudioMuted(stateful: IStateful) {
return Boolean(toState(stateful)['features/base/media'].audio.muted);
}
@ -43,7 +42,7 @@ export function isAudioMuted(stateful: Function | Object) {
* {@code getState} function.
* @returns {boolean}
*/
export function isVideoMutedByAudioOnly(stateful: Function | Object) {
export function isVideoMutedByAudioOnly(stateful: IStateful) {
return (
_isVideoMutedByAuthority(stateful, VIDEO_MUTISM_AUTHORITY.AUDIO_ONLY));
}
@ -60,7 +59,7 @@ export function isVideoMutedByAudioOnly(stateful: Function | Object) {
* {@code videoMutismAuthority}, then {@code true}; otherwise, {@code false}.
*/
function _isVideoMutedByAuthority(
stateful: Function | Object,
stateful: IStateful,
videoMutismAuthority: number) {
const { muted } = toState(stateful)['features/base/media'].video;
@ -74,7 +73,7 @@ function _isVideoMutedByAuthority(
* @param {Object|Function} stateful - The redux state object or {@code getState} function.
* @returns {boolean} - The computed startWithAudioMuted value that will be used.
*/
export function getStartWithAudioMuted(stateful: Object | Function) {
export function getStartWithAudioMuted(stateful: IStateful) {
return Boolean(getPropertyValue(stateful, 'startWithAudioMuted', START_WITH_AUDIO_VIDEO_MUTED_SOURCES));
}
@ -84,7 +83,7 @@ export function getStartWithAudioMuted(stateful: Object | Function) {
* @param {Object|Function} stateful - The redux state object or {@code getState} function.
* @returns {boolean} - The computed startWithVideoMuted value that will be used.
*/
export function getStartWithVideoMuted(stateful: Object | Function) {
export function getStartWithVideoMuted(stateful: IStateful) {
return Boolean(getPropertyValue(stateful, 'startWithVideoMuted', START_WITH_AUDIO_VIDEO_MUTED_SOURCES));
}
@ -94,7 +93,7 @@ export function getStartWithVideoMuted(stateful: Object | Function) {
* @param {Function|Object} stateful - The redux store, state, or {@code getState} function.
* @returns {boolean}
*/
export function isVideoMuted(stateful: Function | Object) {
export function isVideoMuted(stateful: IStateful) {
return Boolean(toState(stateful)['features/base/media'].video.muted);
}
@ -105,7 +104,7 @@ export function isVideoMuted(stateful: Function | Object) {
* {@code getState} function.
* @returns {boolean}
*/
export function isVideoMutedByUser(stateful: Function | Object) {
export function isVideoMutedByUser(stateful: IStateful) {
return _isVideoMutedByAuthority(stateful, VIDEO_MUTISM_AUTHORITY.USER);
}
@ -120,7 +119,7 @@ export function isVideoMutedByUser(stateful: Function | Object) {
* otherwise, false.
*/
export function shouldRenderVideoTrack(
videoTrack: ?{ muted: boolean, videoStarted: boolean },
videoTrack: { muted: boolean; videoStarted: boolean; },
waitForVideoStarted: boolean) {
return (
videoTrack

View File

@ -1,5 +1,3 @@
// @flow
import { getLogger } from '../logging/functions';
export default getLogger('features/base/media');

View File

@ -80,16 +80,16 @@ export function getActiveSpeakersToBeDisplayed(stateful: IStateful) {
}
let availableSlotsForActiveSpeakers = visibleRemoteParticipants.size;
if (activeSpeakers.has(dominantSpeaker)) {
activeSpeakers.delete(dominantSpeaker);
if (activeSpeakers.has(dominantSpeaker ?? '')) {
activeSpeakers.delete(dominantSpeaker ?? '');
}
// Add dominant speaker to the beginning of the list (not including self) since the active speaker list is always
// alphabetically sorted.
if (dominantSpeaker && dominantSpeaker !== getLocalParticipant(state).id) {
if (dominantSpeaker && dominantSpeaker !== getLocalParticipant(state)?.id) {
const updatedSpeakers = Array.from(activeSpeakers);
updatedSpeakers.splice(0, 0, [ dominantSpeaker, getParticipantById(state, dominantSpeaker)?.name ]);
updatedSpeakers.splice(0, 0, [ dominantSpeaker, getParticipantById(state, dominantSpeaker)?.name ?? '' ]);
activeSpeakers = new Map(updatedSpeakers);
}
@ -359,11 +359,11 @@ export function getParticipantDisplayName(stateful: IStateful, id: string): stri
}
if (participant.local) {
return defaultLocalDisplayName;
return defaultLocalDisplayName ?? '';
}
}
return defaultRemoteDisplayName;
return defaultRemoteDisplayName ?? '';
}
/**
@ -439,7 +439,7 @@ export function getPinnedParticipant(stateful: IStateful) {
if (stageFilmstrip) {
const { activeParticipants } = state['features/filmstrip'];
const id = activeParticipants.find((p: Participant) => p.pinned)?.participantId;
const id = activeParticipants.find(p => p.pinned)?.participantId;
return id ? getParticipantById(stateful, id) : undefined;
}

View File

@ -30,6 +30,7 @@ export interface Participant {
export interface LocalParticipant extends Participant {
audioOutputDeviceId?: string;
cameraDeviceId?: string;
jwtId?: string;
micDeviceId?: string;
startWithAudioMuted?: boolean;
startWithVideoMuted?: boolean;

View File

@ -1,6 +1,7 @@
import _ from 'lodash';
import { connect as reduxConnect } from 'react-redux';
import { IState } from '../../app/types';
import { IStateful } from '../app/types';
/**
@ -135,7 +136,7 @@ function _set<T extends Object>(
* returned.
* @returns {Object} The redux state.
*/
export function toState(stateful: IStateful) {
export function toState(stateful: IStateful): IState {
if (stateful) {
if (typeof stateful === 'function') {
return stateful();
@ -149,5 +150,6 @@ export function toState(stateful: IStateful) {
}
}
// @ts-ignore
return stateful;
}

View File

@ -1,5 +1,3 @@
// @flow
import _ from 'lodash';
import MiddlewareRegistry from './MiddlewareRegistry';

View File

@ -1,4 +1,5 @@
import { SETTINGS_UPDATED } from './actionTypes';
import { ISettingsState } from './reducer';
/**
* Create an action for when the settings are updated.
@ -23,7 +24,7 @@ import { SETTINGS_UPDATED } from './actionTypes';
* }
* }}
*/
export function updateSettings(settings) {
export function updateSettings(settings: Partial<ISettingsState>) {
return {
type: SETTINGS_UPDATED,
settings

View File

@ -1,5 +1,3 @@
// @flow
/**
* The default server URL to open if no other was specified.
*/

View File

@ -1,10 +1,14 @@
// @flow
import { CONFIG_WHITELIST } from '../config';
import { getParticipantCount } from '../participants';
import { toState } from '../redux';
import { parseURLParams } from '../util';
import { IState } from '../../app/types';
import { IStateful } from '../app/types';
import CONFIG_WHITELIST from '../config/configWhitelist';
import { IConfigState } from '../config/reducer';
import { IJwtState } from '../jwt/reducer';
import { getParticipantCount } from '../participants/functions';
import { toState } from '../redux/functions';
import { parseURLParams } from '../util/parseURLParams';
import { DEFAULT_SERVER_URL } from './constants';
import { ISettingsState } from './reducer';
/**
* Returns the effective value of a configuration/preference/setting by applying
@ -23,9 +27,9 @@ import { DEFAULT_SERVER_URL } from './constants';
* @returns {any}
*/
export function getPropertyValue(
stateful: Object | Function,
stateful: IStateful,
propertyName: string,
sources?: Object
sources?: any
) {
// Default values don't play nicely with partial objects and we want to make
// the function easy to use without exhaustively defining all flags:
@ -45,10 +49,10 @@ export function getPropertyValue(
// jwt
if (sources.jwt) {
const value = state['features/base/jwt'][propertyName];
const value = state['features/base/jwt'][propertyName as keyof IJwtState];
if (typeof value !== 'undefined') {
return value[propertyName];
return value[propertyName as keyof typeof value];
}
}
@ -56,7 +60,7 @@ export function getPropertyValue(
if (sources.urlParams) {
if (CONFIG_WHITELIST.indexOf(propertyName) !== -1) {
const urlParams
= parseURLParams(state['features/base/connection'].locationURL);
= parseURLParams(state['features/base/connection'].locationURL ?? '');
const value = urlParams[`config.${propertyName}`];
if (typeof value !== 'undefined') {
@ -67,7 +71,7 @@ export function getPropertyValue(
// settings
if (sources.settings) {
const value = state['features/base/settings'][propertyName];
const value = state['features/base/settings'][propertyName as keyof ISettingsState];
if (typeof value !== 'undefined') {
return value;
@ -76,7 +80,7 @@ export function getPropertyValue(
// config
if (sources.config) {
const value = state['features/base/config'][propertyName];
const value = state['features/base/config'][propertyName as keyof IConfigState];
if (typeof value !== 'undefined') {
return value;
@ -93,7 +97,7 @@ export function getPropertyValue(
* {@code getState} function.
* @returns {string} - The currently configured server URL.
*/
export function getServerURL(stateful: Object | Function) {
export function getServerURL(stateful: IStateful) {
const state = toState(stateful);
return state['features/base/settings'].serverURL || DEFAULT_SERVER_URL;
@ -107,7 +111,7 @@ export function getServerURL(stateful: Object | Function) {
* {@code getState} function.
* @returns {string}
*/
export function getUserSelectedCameraDeviceId(stateful: Object | Function) {
export function getUserSelectedCameraDeviceId(stateful: IStateful) {
const state = toState(stateful);
const {
userSelectedCameraDeviceId,
@ -135,7 +139,7 @@ export function getUserSelectedCameraDeviceId(stateful: Object | Function) {
* {@code getState} function.
* @returns {string}
*/
export function getUserSelectedMicDeviceId(stateful: Object | Function) {
export function getUserSelectedMicDeviceId(stateful: IStateful) {
const state = toState(stateful);
const {
userSelectedMicDeviceId,
@ -163,7 +167,7 @@ export function getUserSelectedMicDeviceId(stateful: Object | Function) {
* {@code getState} function.
* @returns {string}
*/
export function getUserSelectedOutputDeviceId(stateful: Object | Function) {
export function getUserSelectedOutputDeviceId(stateful: IStateful) {
const state = toState(stateful);
const {
userSelectedAudioOutputDeviceId,
@ -202,13 +206,19 @@ export function getUserSelectedOutputDeviceId(stateful: Object | Function) {
* @private
* @returns {string} The preferred device ID to use for media.
*/
function _getUserSelectedDeviceId(options) {
function _getUserSelectedDeviceId(options: {
availableDevices: MediaDeviceInfo[] | undefined;
matchRegex?: RegExp;
replacement?: string;
userSelectedDeviceId?: string;
userSelectedDeviceLabel?: string;
}) {
const {
availableDevices,
matchRegex,
matchRegex = '',
userSelectedDeviceId,
userSelectedDeviceLabel,
replacement
replacement = ''
} = options;
// If there is no label at all, there is no need to fall back to checking
@ -217,7 +227,7 @@ function _getUserSelectedDeviceId(options) {
return userSelectedDeviceId;
}
const foundMatchingBasedonDeviceId = availableDevices.find(
const foundMatchingBasedonDeviceId = availableDevices?.find(
candidate => candidate.deviceId === userSelectedDeviceId);
// Prioritize matching the deviceId
@ -228,7 +238,7 @@ function _getUserSelectedDeviceId(options) {
const strippedDeviceLabel
= matchRegex ? userSelectedDeviceLabel.replace(matchRegex, replacement)
: userSelectedDeviceLabel;
const foundMatchBasedOnLabel = availableDevices.find(candidate => {
const foundMatchBasedOnLabel = availableDevices?.find(candidate => {
const { label } = candidate;
if (!label) {
@ -253,7 +263,7 @@ function _getUserSelectedDeviceId(options) {
* @param {Object} state - The state of the application.
* @returns {boolean}
*/
export function shouldHideShareAudioHelper(state: Object): boolean {
export function shouldHideShareAudioHelper(state: IState): boolean | undefined {
return state['features/base/settings'].hideShareAudioHelper;
}
@ -264,7 +274,7 @@ export function shouldHideShareAudioHelper(state: Object): boolean {
* @param {Object} state - Redux state.
* @returns {boolean}
*/
export function shouldHideSelfView(state: Object) {
export function shouldHideSelfView(state: IState) {
return getParticipantCount(state) === 1 ? false : getHideSelfView(state);
}
@ -274,6 +284,6 @@ export function shouldHideSelfView(state: Object) {
* @param {Object} state - Redux state.
* @returns {boolean}
*/
export function getHideSelfView(state: Object) {
export function getHideSelfView(state: IState) {
return state['features/base/config'].disableSelfView || state['features/base/settings'].disableSelfView;
}

View File

@ -1,5 +1,3 @@
// @flow
import { NativeModules } from 'react-native';
import DefaultPreference from 'react-native-default-preference';
@ -31,6 +29,6 @@ export function handleCallIntegrationChange(disabled: boolean) {
* @returns {void}
*/
export function handleCrashReportingChange(disabled: boolean) {
DefaultPreference.setName('jitsi-default-preferences').then(
DefaultPreference.setName('jitsi-default-preferences').then( // @ts-ignore
DefaultPreference.set('isCrashReportingDisabled', disabled.toString()));
}

View File

@ -1,4 +1,5 @@
// @flow
/* eslint-disable @typescript-eslint/no-unused-vars */
import { IState } from '../../app/types';
export * from './functions.any';
@ -8,7 +9,7 @@ export * from './functions.any';
* @param {Object} state - The state of the application.
* @returns {void}
*/
export function getCurrentCameraDeviceId(state: Object) {
export function getCurrentCameraDeviceId(state: IState) {
return getDeviceIdByType(state, 'isVideoTrack');
}
@ -18,7 +19,7 @@ export function getCurrentCameraDeviceId(state: Object) {
* @param {Object} state - The state of the application.
* @returns {void}
*/
export function getCurrentMicDeviceId(state: Object) {
export function getCurrentMicDeviceId(state: IState) {
return getDeviceIdByType(state, 'isAudioTrack');
}
@ -28,7 +29,7 @@ export function getCurrentMicDeviceId(state: Object) {
* @param {Object} state - The state of the application.
* @returns {void}
*/
export function getCurrentOutputDeviceId(state: Object) {
export function getCurrentOutputDeviceId(state: IState) {
return state['features/base/settings'].audioOutputDeviceId;
}
@ -39,11 +40,11 @@ export function getCurrentOutputDeviceId(state: Object) {
* @param {string} isType - Can be 'isVideoTrack' | 'isAudioTrack'.
* @returns {string}
*/
function getDeviceIdByType(state: Object, isType: string) {
function getDeviceIdByType(state: IState, isType: string) {
const [ deviceId ] = state['features/base/tracks']
.map(t => t.jitsiTrack)
.filter(t => t && t.isLocal() && t[isType]())
.map(t => t.getDeviceId());
.map(t => t.jitsiTrack)
.filter(t => t?.isLocal() && t[isType as keyof typeof t]())
.map(t => t.getDeviceId());
return deviceId || '';
}
@ -54,7 +55,7 @@ function getDeviceIdByType(state: Object, isType: string) {
* @param {Object} state - The state of the application.
* @returns {string}
*/
export function getDisplayName(state: Object): string {
export function getDisplayName(state: IState): string {
return state['features/base/settings'].displayName || '';
}
@ -66,8 +67,8 @@ export function getDisplayName(state: Object): string {
* @param {boolean} disabled - Whether call integration is disabled or not.
* @returns {void}
*/
export function handleCallIntegrationChange(disabled: boolean) { // eslint-disable-line no-unused-vars
}
// eslint-disable-next-line @typescript-eslint/no-empty-function, require-jsdoc
export function handleCallIntegrationChange(disabled: boolean) { }
/**
* Handles changes to the `disableCrashReporting` setting.
@ -76,5 +77,5 @@ export function handleCallIntegrationChange(disabled: boolean) { // eslint-disab
* @param {boolean} disabled - Whether crash reporting is disabled or not.
* @returns {void}
*/
export function handleCrashReportingChange(disabled: boolean) { // eslint-disable-line no-unused-vars
}
// eslint-disable-next-line @typescript-eslint/no-empty-function, require-jsdoc
export function handleCrashReportingChange(disabled: boolean) { }

View File

@ -1,5 +1,3 @@
// @flow
import { getLogger } from '../logging/functions';
export default getLogger('features/base/settings');

View File

@ -1,19 +1,26 @@
// @flow
/* eslint-disable lines-around-comment */
import _ from 'lodash';
import { AnyAction } from 'redux';
import { IStore } from '../../app/types';
import { PREJOIN_INITIALIZED } from '../../prejoin/actionTypes';
// @ts-ignore
import { setPrejoinPageVisibility } from '../../prejoin/actions';
import { APP_WILL_MOUNT } from '../app';
import { setAudioOnly } from '../audio-only';
import { APP_WILL_MOUNT } from '../app/actionTypes';
import { setAudioOnly } from '../audio-only/actions';
import { SET_LOCATION_URL } from '../connection/actionTypes'; // minimize imports to avoid circular imports
// @ts-ignore
import { getJwtName } from '../jwt/functions';
import { getLocalParticipant, participantUpdated } from '../participants';
import { MiddlewareRegistry } from '../redux';
import { parseURLParams } from '../util';
import { participantUpdated } from '../participants/actions';
import { getLocalParticipant } from '../participants/functions';
import MiddlewareRegistry from '../redux/MiddlewareRegistry';
import { parseURLParams } from '../util/parseURLParams';
import { SETTINGS_UPDATED } from './actionTypes';
import { updateSettings } from './actions';
// @ts-ignore
import { handleCallIntegrationChange, handleCrashReportingChange } from './functions';
import { ISettingsState } from './reducer';
/**
@ -57,7 +64,7 @@ MiddlewareRegistry.register(store => next => action => {
* @private
* @returns {void}
*/
function _initializeShowPrejoin({ dispatch, getState }) {
function _initializeShowPrejoin({ dispatch, getState }: IStore) {
const { userSelectedSkipPrejoin } = getState()['features/base/settings'];
if (userSelectedSkipPrejoin) {
@ -72,7 +79,7 @@ function _initializeShowPrejoin({ dispatch, getState }) {
* @private
* @returns {void}
*/
function _initializeCallIntegration({ getState }) {
function _initializeCallIntegration({ getState }: IStore) {
const { disableCallIntegration } = getState()['features/base/settings'];
if (typeof disableCallIntegration === 'boolean') {
@ -88,7 +95,7 @@ function _initializeCallIntegration({ getState }) {
* @param {string} settingsField - The name of the settings field to map.
* @returns {string}
*/
function _mapSettingsFieldToParticipant(settingsField) {
function _mapSettingsFieldToParticipant(settingsField: string) {
switch (settingsField) {
case 'displayName':
return 'name';
@ -104,7 +111,9 @@ function _mapSettingsFieldToParticipant(settingsField) {
* @private
* @returns {void}
*/
function _maybeHandleCallIntegrationChange({ settings: { disableCallIntegration } }) {
function _maybeHandleCallIntegrationChange({ settings: { disableCallIntegration } }: {
settings: Partial<ISettingsState>;
}) {
if (typeof disableCallIntegration === 'boolean') {
handleCallIntegrationChange(disableCallIntegration);
}
@ -117,7 +126,9 @@ function _maybeHandleCallIntegrationChange({ settings: { disableCallIntegration
* @private
* @returns {void}
*/
function _maybeCrashReportingChange({ settings: { disableCrashReporting } }) {
function _maybeCrashReportingChange({ settings: { disableCrashReporting } }: {
settings: Partial<ISettingsState>;
}) {
if (typeof disableCrashReporting === 'boolean') {
handleCrashReportingChange(disableCrashReporting);
}
@ -132,8 +143,8 @@ function _maybeCrashReportingChange({ settings: { disableCrashReporting } }) {
* @returns {void}
*/
function _maybeSetAudioOnly(
{ dispatch },
{ settings: { startAudioOnly } }) {
{ dispatch }: IStore,
{ settings: { startAudioOnly } }: { settings: Partial<ISettingsState>; }) {
if (typeof startAudioOnly === 'boolean') {
dispatch(setAudioOnly(startAudioOnly));
}
@ -146,7 +157,7 @@ function _maybeSetAudioOnly(
* @private
* @returns {void}
*/
function _maybeUpdateDisplayName({ dispatch, getState }) {
function _maybeUpdateDisplayName({ dispatch, getState }: IStore) {
const state = getState();
const hasJwt = Boolean(state['features/base/jwt'].jwt);
@ -169,7 +180,7 @@ function _maybeUpdateDisplayName({ dispatch, getState }) {
* @private
* @returns {void}
*/
function _updateLocalParticipant({ dispatch, getState }, action) {
function _updateLocalParticipant({ dispatch, getState }: IStore, action: AnyAction) {
const { settings } = action;
const localParticipant = getLocalParticipant(getState());
const newLocalParticipant = {
@ -178,12 +189,15 @@ function _updateLocalParticipant({ dispatch, getState }, action) {
for (const key in settings) {
if (settings.hasOwnProperty(key)) {
newLocalParticipant[_mapSettingsFieldToParticipant(key)]
newLocalParticipant[_mapSettingsFieldToParticipant(key) as keyof typeof newLocalParticipant]
= settings[key];
}
}
dispatch(participantUpdated(newLocalParticipant));
dispatch(participantUpdated({
...newLocalParticipant,
id: newLocalParticipant.id ?? ''
}));
}
@ -194,9 +208,9 @@ function _updateLocalParticipant({ dispatch, getState }, action) {
* @private
* @returns {void}
*/
function _updateLocalParticipantFromUrl({ dispatch, getState }) {
function _updateLocalParticipantFromUrl({ dispatch, getState }: IStore) {
const urlParams
= parseURLParams(getState()['features/base/connection'].locationURL);
= parseURLParams(getState()['features/base/connection'].locationURL ?? '');
const urlEmail = urlParams['userInfo.email'];
const urlDisplayName = urlParams['userInfo.displayName'];

View File

@ -52,18 +52,18 @@ const DEFAULT_STATE: ISettingsState = {
export interface ISettingsState {
audioOutputDeviceId?: string | boolean;
avatarURL?: string | boolean;
avatarURL?: string;
cameraDeviceId?: string | boolean;
disableCallIntegration?: boolean;
disableCrashReporting?: boolean;
disableP2P?: boolean;
disableSelfView?: boolean;
displayName?: string | boolean;
email?: string | boolean;
displayName?: string;
email?: string;
hideShareAudioHelper?: boolean;
localFlipX?: boolean;
micDeviceId?: string | boolean;
serverURL?: string | boolean;
serverURL?: string;
soundsIncomingMessage?: boolean;
soundsParticipantJoined?: boolean;
soundsParticipantKnocking?: boolean;
@ -73,12 +73,12 @@ export interface ISettingsState {
startAudioOnly?: boolean;
startWithAudioMuted?: boolean;
startWithVideoMuted?: boolean;
userSelectedAudioOutputDeviceId?: string | boolean;
userSelectedAudioOutputDeviceLabel?: string | boolean;
userSelectedCameraDeviceId?: string | boolean;
userSelectedCameraDeviceLabel?: string | boolean;
userSelectedMicDeviceId?: string | boolean;
userSelectedMicDeviceLabel?: string | boolean;
userSelectedAudioOutputDeviceId?: string;
userSelectedAudioOutputDeviceLabel?: string;
userSelectedCameraDeviceId?: string;
userSelectedCameraDeviceLabel?: string;
userSelectedMicDeviceId?: string;
userSelectedMicDeviceLabel?: string;
userSelectedNotifications?: {
[key: string]: boolean;
} | boolean;
@ -97,6 +97,7 @@ const filterSubtree: ISettingsState = {};
Object.keys(DEFAULT_STATE).forEach(key => {
const key1 = key as keyof typeof filterSubtree;
// @ts-ignore
filterSubtree[key1] = true;
});

View File

@ -16,7 +16,7 @@ import {
interface ITrack {
isReceivingData: boolean;
jitsiTrack: Object;
jitsiTrack: any;
lastMediaEvent?: string;
local: boolean;
mediaType: string;

View File

@ -1,5 +1,3 @@
// @flow
import { getLogger } from '../logging/functions';
export default getLogger('features/base/util');

View File

@ -24,7 +24,7 @@ const blacklist = [ '__proto__', 'constructor', 'prototype' ];
export function parseURLParams(
url: URL | string,
dontParse = false,
source = 'hash'): Object {
source = 'hash') {
if (typeof url === 'string') {
// eslint-disable-next-line no-param-reassign
url = new URL(url);

View File

@ -330,7 +330,7 @@ export function parseStandardURIString(str: string) {
* search: string
* }}
*/
export function parseURIString(uri?: string) {
export function parseURIString(uri?: string): any {
if (typeof uri !== 'string') {
return undefined;
}

View File

@ -1276,7 +1276,7 @@ function _mapStateToProps(state: IState, ownProps: any): Object {
const { gifUrl: gifSrc } = getGifForParticipant(state, id);
const mode = getGifDisplayMode(state);
const participantId = isLocal ? getLocalParticipant(state).id : participantID;
const participantId = isLocal ? getLocalParticipant(state)?.id : participantID;
return {
_audioTrack,

View File

@ -50,7 +50,7 @@ const AbstractPollAnswer = (Component: ComponentType<AbstractProps>) => (props:
const poll: Poll = useSelector((state: IState) => state['features/polls'].polls[pollId]);
const { id: localId } = useSelector(getLocalParticipant);
const { id: localId } = useSelector(getLocalParticipant) ?? { id: '' };
const [ checkBoxStates, setCheckBoxState ] = useState(() => {
if (poll.lastVote !== null) {

View File

@ -33,7 +33,7 @@ const PollAnswer = (props: AbstractProps) => {
<>
<Text style = { dialogStyles.questionText } >{ poll.question }</Text>
<Text style = { dialogStyles.questionOwnerText } >{
t('polls.by', { name: localParticipant.name })
t('polls.by', { name: localParticipant?.name })
}
</Text>
<View style = { chatStyles.answerContent }>

View File

@ -5,7 +5,6 @@ import clsx from 'clsx';
import React, { Component } from 'react';
import { WithTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { createReactionMenuEvent, createToolbarEvent } from '../../../analytics/AnalyticsEvents';
import { sendAnalytics } from '../../../analytics/functions';
@ -53,7 +52,7 @@ interface Props extends WithTranslation {
/**
* The ID of the local participant.
*/
_localParticipantID: String;
_localParticipantID?: string;
/**
* Whether or not the local participant's hand is raised.
@ -241,7 +240,7 @@ function mapStateToProps(state: IState) {
const localParticipant = getLocalParticipant(state);
return {
_localParticipantID: localParticipant.id,
_localParticipantID: localParticipant?.id,
_isMobile: isMobileBrowser(),
_isGifEnabled: isGifEnabled(state),
_isGifMenuVisible: isGifsMenuOpen(state),
@ -258,10 +257,7 @@ function mapStateToProps(state: IState) {
function mapDispatchToProps(dispatch: IStore['dispatch']) {
return {
dispatch,
...bindActionCreators(
{
_dockToolbox: dockToolbox
}, dispatch)
_dockToolbox: (dock: boolean) => dispatch(dockToolbox(dock))
};
}

View File

@ -72,8 +72,8 @@ export async function sendReactionsWebhook(state: IState, reactions: Array<strin
sessionId: conference.sessionId,
submitted: Date.now(),
reactions,
participantId: localParticipant.jwtId,
participantName: localParticipant.name,
participantId: localParticipant?.jwtId,
participantName: localParticipant?.name,
participantJid: jid
};

View File

@ -204,7 +204,7 @@ MiddlewareRegistry.register((store: IStore) => (next: Function) => (action: any)
const { disableReactionsModeration } = state['features/base/config'];
const customActions = [ 'notify.reactionSounds' ];
const customFunctions = [ () => dispatch(updateSettings({
const customFunctions: Function[] = [ () => dispatch(updateSettings({
soundsReactions: false
})) ];

View File

@ -303,7 +303,7 @@ function _mapStateToProps(state: IState, ownProps: Partial<Props>) {
_showLocalVideoFlipButton: !disableLocalVideoFlip && videoTrack?.videoType !== 'desktop',
_showHideSelfViewButton: showHideSelfViewButton,
_overflowDrawer: overflowDrawer,
_localParticipantId: localParticipant.id,
_localParticipantId: localParticipant?.id,
_showConnectionInfo: showConnectionInfo,
_showPinToStage: isStageFilmstripAvailable(state)
};