[Android] Fix possible TypeError in multi-JitsiMeetView SDK consumers
If multiple JitsiMeetView instances are created (not necessarily existing at once), it's possible to hit a TypeError when reading the React Component props of the currently mounted App. Anyway, in certain places we're already protecting against that out of abundance of caution so it makes no sense to not protect everywhere.
This commit is contained in:
parent
53971d0b50
commit
bd7c9473e7
|
@ -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;
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import { NativeModules } from 'react-native';
|
||||
|
||||
export * from './functions.any';
|
||||
export * from './getRouteToRender';
|
||||
|
||||
/**
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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'))
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue