diff --git a/react/features/app/functions.any.js b/react/features/app/functions.any.js new file mode 100644 index 000000000..38fa6d14d --- /dev/null +++ b/react/features/app/functions.any.js @@ -0,0 +1,28 @@ +// @flow + +import { toState } from '../base/redux'; + +/** + * Gets the value of a specific React {@code Component} prop of the currently + * mounted {@link App}. + * + * @param {Function|Object} stateful - The redux store or {@code getState} + * function. + * @param {string} propName - The name of the React {@code Component} prop of + * the currently mounted {@code App} to get. + * @returns {*} The value of the specified React {@code Compoennt} prop of the + * currently mounted {@code App}. + */ +export function getAppProp(stateful: Function | Object, propName: string) { + const state = toState(stateful)['features/app']; + + if (state) { + const { app } = state; + + if (app) { + return app.props[propName]; + } + } + + return undefined; +} diff --git a/react/features/app/functions.native.js b/react/features/app/functions.native.js index cbe3ede67..0da77a8f1 100644 --- a/react/features/app/functions.native.js +++ b/react/features/app/functions.native.js @@ -2,6 +2,7 @@ import { NativeModules } from 'react-native'; +export * from './functions.any'; export * from './getRouteToRender'; /** diff --git a/react/features/app/functions.web.js b/react/features/app/functions.web.js index 322ae6133..86bd6a424 100644 --- a/react/features/app/functions.web.js +++ b/react/features/app/functions.web.js @@ -1,4 +1,4 @@ -/* @flow */ +// @flow import { toState } from '../base/redux'; import { getDeepLinkingPage } from '../deep-linking'; @@ -49,6 +49,8 @@ const _INTERCEPT_COMPONENT_RULES = [ } ]; +export * from './functions.any'; + /** * Determines which route is to be rendered in order to depict a specific redux * store. diff --git a/react/features/invite/functions.js b/react/features/invite/functions.js index bf690e926..3faa4b95b 100644 --- a/react/features/invite/functions.js +++ b/react/features/invite/functions.js @@ -1,5 +1,6 @@ // @flow +import { getAppProp } from '../app'; import { getLocalParticipant, PARTICIPANT_ROLE } from '../base/participants'; import { doGetJSON } from '../base/util'; @@ -282,8 +283,7 @@ export function isAddPeopleEnabled(state: Object): boolean { // XXX The mobile/react-native app is capable of disabling the // adding/inviting of people in the current conference. Anyway, the // Web/React app does not have that capability so default appropriately. - const { app } = state['features/app']; - const addPeopleEnabled = app && app.props.addPeopleEnabled; + const addPeopleEnabled = getAppProp(state, 'addPeopleEnabled'); return ( (typeof addPeopleEnabled === 'undefined') @@ -313,9 +313,7 @@ export function isDialOutEnabled(state: Object): boolean { // XXX The mobile/react-native app is capable of disabling of dial-out. // Anyway, the Web/React app does not have that capability so default // appropriately. - const { app } = state['features/app']; - - dialOutEnabled = app && app.props.dialoOutEnabled; + dialOutEnabled = getAppProp(state, 'dialOutEnabled'); return ( (typeof dialOutEnabled === 'undefined') || Boolean(dialOutEnabled)); diff --git a/react/features/invite/middleware.native.js b/react/features/invite/middleware.native.js index 0e7cc851e..9226cd6a6 100644 --- a/react/features/invite/middleware.native.js +++ b/react/features/invite/middleware.native.js @@ -4,7 +4,7 @@ import i18next from 'i18next'; import { NativeEventEmitter, NativeModules } from 'react-native'; import { MiddlewareRegistry } from '../base/redux'; -import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../app'; +import { APP_WILL_MOUNT, APP_WILL_UNMOUNT, getAppProp } from '../app'; import { invite } from './actions'; import { @@ -107,21 +107,15 @@ function _appWillMount({ dispatch, getState }, next, action) { * @private * @returns {*} The value returned by {@code next(action)}. */ -function _beginAddPeople({ getState }, next, action) { +function _beginAddPeople(store, next, action) { const result = next(action); // The JavaScript App needs to provide uniquely identifying information to // the native Invite module so that the latter may match the former to the // native JitsiMeetView which hosts it. - const { app } = getState()['features/app']; + const externalAPIScope = getAppProp(store, 'externalAPIScope'); - if (app) { - const { externalAPIScope } = app.props; - - if (externalAPIScope) { - Invite.beginAddPeople(externalAPIScope); - } - } + externalAPIScope && Invite.beginAddPeople(externalAPIScope); return result; } @@ -139,8 +133,7 @@ function _onInvite({ addPeopleControllerScope, externalAPIScope, invitees }) { // If there are multiple JitsiMeetView instances alive, they will all get // the event, since there is a single bridge, so make sure we don't act if // the event is not for us. - if (getState()['features/app'].app.props.externalAPIScope - !== externalAPIScope) { + if (getAppProp(getState, 'externalAPIScope') !== externalAPIScope) { return; } @@ -167,7 +160,7 @@ function _onPerformQuery( // If there are multiple JitsiMeetView instances alive, they will all get // the event, since there is a single bridge, so make sure we don't act if // the event is not for us. - if (state['features/app'].app.props.externalAPIScope !== externalAPIScope) { + if (getAppProp(state, 'externalAPIScope') !== externalAPIScope) { return; } diff --git a/react/features/mobile/external-api/middleware.js b/react/features/mobile/external-api/middleware.js index 29f5003f8..de8b5d299 100644 --- a/react/features/mobile/external-api/middleware.js +++ b/react/features/mobile/external-api/middleware.js @@ -1,7 +1,8 @@ -/* @flow */ +// @flow import { NativeModules } from 'react-native'; +import { getAppProp } from '../../app'; import { CONFERENCE_FAILED, CONFERENCE_JOINED, @@ -215,22 +216,14 @@ function _sendConferenceFailedOnConnectionError(store, action) { * @private * @returns {void} */ -function _sendEvent( - { getState }: { getState: Function }, - name: string, - data: Object) { +function _sendEvent(store: Object, name: string, data: Object) { // The JavaScript App needs to provide uniquely identifying information to // the native ExternalAPI module so that the latter may match the former to // the native JitsiMeetView which hosts it. - const { app } = getState()['features/app']; + const externalAPIScope = getAppProp(store, 'externalAPIScope'); - if (app) { - const { externalAPIScope } = app.props; - - if (externalAPIScope) { - NativeModules.ExternalAPI.sendEvent(name, data, externalAPIScope); - } - } + externalAPIScope + && NativeModules.ExternalAPI.sendEvent(name, data, externalAPIScope); } /** diff --git a/react/features/mobile/picture-in-picture/actions.js b/react/features/mobile/picture-in-picture/actions.js index 913e91120..4bec87d67 100644 --- a/react/features/mobile/picture-in-picture/actions.js +++ b/react/features/mobile/picture-in-picture/actions.js @@ -2,6 +2,7 @@ import { NativeModules } from 'react-native'; +import { getAppProp } from '../../app'; import { Platform } from '../../base/react'; import { ENTER_PICTURE_IN_PICTURE } from './actionTypes'; @@ -18,13 +19,10 @@ import { ENTER_PICTURE_IN_PICTURE } from './actionTypes'; */ export function enterPictureInPicture() { return (dispatch: Dispatch, getState: Function) => { - const state = getState(); - const { app } = state['features/app']; - // XXX At the time of this writing this action can only be dispatched by // the button which is on the conference view, which means that it's // fine to enter PiP mode. - if (app && app.props.pictureInPictureEnabled) { + if (getAppProp(getState, 'pictureInPictureEnabled')) { const { PictureInPicture } = NativeModules; const p = Platform.OS === 'android' diff --git a/react/features/mobile/picture-in-picture/components/EnterPictureInPictureToolbarButton.js b/react/features/mobile/picture-in-picture/components/EnterPictureInPictureToolbarButton.js index 497404983..b86dbae07 100644 --- a/react/features/mobile/picture-in-picture/components/EnterPictureInPictureToolbarButton.js +++ b/react/features/mobile/picture-in-picture/components/EnterPictureInPictureToolbarButton.js @@ -3,6 +3,7 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; +import { getAppProp } from '../../../app'; import { ToolbarButton } from '../../../toolbox'; import { enterPictureInPicture } from '../actions'; @@ -93,8 +94,6 @@ function _mapDispatchToProps(dispatch) { * }} */ function _mapStateToProps(state) { - const { app } = state['features/app']; - return { /** @@ -104,7 +103,7 @@ function _mapStateToProps(state) { * @type {boolean} */ _pictureInPictureEnabled: - Boolean(app && app.props.pictureInPictureEnabled) + Boolean(getAppProp(state, 'pictureInPictureEnabled')) }; } diff --git a/react/features/welcome/functions.js b/react/features/welcome/functions.js index 760a2c9ce..47e761ca0 100644 --- a/react/features/welcome/functions.js +++ b/react/features/welcome/functions.js @@ -1,5 +1,6 @@ -/* @flow */ +// @flow +import { getAppProp } from '../app'; import { toState } from '../base/redux'; declare var APP: Object; @@ -12,12 +13,12 @@ export * from './roomnameGenerator'; * (e.g. programmatically via the Jitsi Meet SDK for Android and iOS). Not to be * confused with {@link isWelcomePageUserEnabled}. * - * @param {Object|Function} stateOrGetState - The redux state or - * {@link getState} function. + * @param {Function|Object} stateful - The redux state or {@link getState} + * function. * @returns {boolean} If the {@code WelcomePage} is enabled by the app, then * {@code true}; otherwise, {@code false}. */ -export function isWelcomePageAppEnabled(stateOrGetState: Object | Function) { +export function isWelcomePageAppEnabled(stateful: Function | Object) { let b; if (navigator.product === 'ReactNative') { @@ -28,9 +29,7 @@ export function isWelcomePageAppEnabled(stateOrGetState: Object | Function) { // - Enabling/disabling the Welcome page on Web historically // automatically redirects to a random room and that does not make sense // on mobile (right now). - const { app } = toState(stateOrGetState)['features/app']; - - b = Boolean(app && app.props.welcomePageEnabled); + b = Boolean(getAppProp(stateful, 'welcomePageEnabled')); } else { b = true; } @@ -43,15 +42,14 @@ export function isWelcomePageAppEnabled(stateOrGetState: Object | Function) { * herself or through her deployment config(uration). Not to be confused with * {@link isWelcomePageAppEnabled}. * - * @param {Object|Function} stateOrGetState - The redux state or - * {@link getState} function. + * @param {Function|Object} stateful - The redux state or {@link getState} + * function. * @returns {boolean} If the {@code WelcomePage} is enabled by the user, then * {@code true}; otherwise, {@code false}. */ -export function isWelcomePageUserEnabled(stateOrGetState: Object | Function) { +export function isWelcomePageUserEnabled(stateful: Function | Object) { return ( typeof APP === 'undefined' ? true - : toState(stateOrGetState)['features/base/config'] - .enableWelcomePage); + : toState(stateful)['features/base/config'].enableWelcomePage); }