fix(rn,navigation) wait until the root navigator is initialized

There is a race condition in the root navigatior's initialization.

It's possible that it's initialized a touch too late and SDK users who
try to navigate to a conference end up stuck in the connecting screen
because the navigator is null.

This PR waits for it to be initilized by very unorthodox means, it's a
horrible hack which we need to undo, but for that we need to break
appart the inheritance relationship between App.{web,native},
AbstractApp and BaseApp because it's very inflexible.

The flags are now initialized very early so the naviggator sees if the
welcome page is enabled or not.
This commit is contained in:
Saúl Ibarra Corretgé 2022-05-06 11:20:22 +02:00
parent 1fcb8c8e44
commit 14df119373
5 changed files with 60 additions and 10 deletions

View File

@ -27,11 +27,6 @@ declare var __DEV__;
*/ */
type Props = AbstractAppProps & { type Props = AbstractAppProps & {
/**
* An object of colors that override the default colors of the app/sdk.
*/
colorScheme: ?Object,
/** /**
* Identifier for this app on the native side. * Identifier for this app on the native side.
*/ */
@ -98,13 +93,31 @@ export class App extends AbstractApp {
*/ */
async _extraInit() { async _extraInit() {
const { dispatch, getState } = this.state.store; const { dispatch, getState } = this.state.store;
// We set these early enough so then we avoid any unnecessary re-renders.
dispatch(updateFlags(this.props.flags));
const route = await _getRouteToRender(); const route = await _getRouteToRender();
// We need the root navigator to be set early. // We need the root navigator to be set early.
await this._navigate(route); await this._navigate(route);
// We set these early enough so then we avoid any unnecessary re-renders. // HACK ALERT!
dispatch(updateFlags(this.props.flags)); // Wait until the root navigator is ready.
// We really need to break the inheritance relationship between App,
// AbstractApp and BaseApp, it's very inflexible and cumbersome right now.
const rootNavigationReady = new Promise(resolve => {
const i = setInterval(() => {
const { ready } = getState()['features/app'] || {};
if (ready) {
clearInterval(i);
resolve();
}
}, 50);
});
await rootNavigationReady;
// Check if serverURL is configured externally and not allowed to change. // Check if serverURL is configured externally and not allowed to change.
const serverURLChangeEnabled = getFeatureFlag(getState(), SERVER_URL_CHANGE_ENABLED, true); const serverURLChangeEnabled = getFeatureFlag(getState(), SERVER_URL_CHANGE_ENABLED, true);

View File

@ -0,0 +1,22 @@
import { ReducerRegistry } from '../base/redux';
import { _ROOT_NAVIGATION_READY } from '../mobile/navigation/actionTypes';
/**
* Listen for actions which changes the state of the app feature.
*
* @param {Object} state - The Redux state of the feature features/app.
* @param {Object} action - Action object.
* @param {string} action.type - Type of action.
* @returns {Object}
*/
ReducerRegistry.register('features/app', (state = {}, action) => {
switch (action.type) {
case _ROOT_NAVIGATION_READY:
return {
...state,
ready: action.ready
};
default:
return state;
}
});

View File

@ -8,4 +8,6 @@ import '../mobile/full-screen/reducer';
import '../mobile/watchos/reducer'; import '../mobile/watchos/reducer';
import '../shared-video/reducer'; import '../shared-video/reducer';
import './reducer.native';
import './reducers.any'; import './reducers.any';

View File

@ -0,0 +1 @@
export const _ROOT_NAVIGATION_READY = '_ROOT_NAVIGATION_READY';

View File

@ -2,11 +2,12 @@
import { NavigationContainer } from '@react-navigation/native'; import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack'; import { createStackNavigator } from '@react-navigation/stack';
import React from 'react'; import React, { useCallback } from 'react';
import { SafeAreaProvider } from 'react-native-safe-area-context'; import { SafeAreaProvider } from 'react-native-safe-area-context';
import { connect } from '../../../base/redux'; import { connect } from '../../../base/redux';
import { DialInSummary } from '../../../invite'; import { DialInSummary } from '../../../invite';
import { _ROOT_NAVIGATION_READY } from '../actionTypes';
import { rootNavigationRef } from '../rootNavigationContainerRef'; import { rootNavigationRef } from '../rootNavigationContainerRef';
import { screen } from '../routes'; import { screen } from '../routes';
import { import {
@ -26,6 +27,11 @@ const RootStack = createStackNavigator();
type Props = { type Props = {
/**
* Redux dispatch function.
*/
dispatch: Function,
/** /**
* Is welcome page available? * Is welcome page available?
*/ */
@ -33,14 +39,21 @@ type Props = {
} }
const RootNavigationContainer = ({ isWelcomePageAvailable }: Props) => { const RootNavigationContainer = ({ dispatch, isWelcomePageAvailable }: Props) => {
const initialRouteName = isWelcomePageAvailable const initialRouteName = isWelcomePageAvailable
? screen.root : screen.connecting; ? screen.root : screen.connecting;
const onReady = useCallback(() => {
dispatch({
type: _ROOT_NAVIGATION_READY,
ready: true
});
}, [ dispatch ]);
return ( return (
<SafeAreaProvider> <SafeAreaProvider>
<NavigationContainer <NavigationContainer
independent = { true } independent = { true }
onReady = { onReady }
ref = { rootNavigationRef } ref = { rootNavigationRef }
theme = { navigationContainerTheme }> theme = { navigationContainerTheme }>
<RootStack.Navigator <RootStack.Navigator
@ -89,4 +102,3 @@ function mapStateToProps(state: Object) {
} }
export default connect(mapStateToProps)(RootNavigationContainer); export default connect(mapStateToProps)(RootNavigationContainer);