[RN] Prepare to display BlankPage more
For example, while config.js and other files are being loaded before the navigation to Conference is feasible.
This commit is contained in:
parent
6545a7a1bb
commit
45a1ae26ca
|
@ -46,22 +46,22 @@ function _appNavigateToMandatoryLocation(
|
||||||
.then(() => dispatch(setRoom(newLocation.room)));
|
.then(() => dispatch(setRoom(newLocation.room)));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies that an attempt to load a config(uration) has completed. Due to
|
* Notifies that an attempt to load a configuration has completed. Due to
|
||||||
* the asynchronous native of the loading, the specified <tt>config</tt> may
|
* the asynchronous nature of the loading, the specified <tt>config</tt> may
|
||||||
* or may not be required by the time the notification arrives.
|
* or may not be required by the time the notification arrives.
|
||||||
*
|
*
|
||||||
* @param {string|undefined} err - If the loading has failed, the error
|
* @param {string|undefined} error - If the loading has failed, the error
|
||||||
* detailing the cause of the failure.
|
* detailing the cause of the failure.
|
||||||
* @param {Object|undefined} config - If the loading has succeeded, the
|
* @param {Object|undefined} config - If the loading has succeeded, the
|
||||||
* loaded config(uration).
|
* loaded configuration.
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
function configLoaded(err, config) {
|
function configLoaded(error, config) {
|
||||||
// FIXME Due to the asynchronous native of the loading, the specified
|
// FIXME Due to the asynchronous nature of the loading, the specified
|
||||||
// config may or may not be required by the time the notification
|
// config may or may not be required by the time the notification
|
||||||
// arrives.
|
// arrives.
|
||||||
|
|
||||||
if (err) {
|
if (error) {
|
||||||
// XXX The failure could be, for example, because of a
|
// XXX The failure could be, for example, because of a
|
||||||
// certificate-related error. In which case the connection will
|
// certificate-related error. In which case the connection will
|
||||||
// fail later in Strophe anyway even if we use the default
|
// fail later in Strophe anyway even if we use the default
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {
|
||||||
import { RouteRegistry } from '../../base/react';
|
import { RouteRegistry } from '../../base/react';
|
||||||
import { MiddlewareRegistry, ReducerRegistry } from '../../base/redux';
|
import { MiddlewareRegistry, ReducerRegistry } from '../../base/redux';
|
||||||
import { toURLString } from '../../base/util';
|
import { toURLString } from '../../base/util';
|
||||||
|
import { BlankPage } from '../../welcome';
|
||||||
|
|
||||||
import { appNavigate, appWillMount, appWillUnmount } from '../actions';
|
import { appNavigate, appWillMount, appWillUnmount } from '../actions';
|
||||||
|
|
||||||
|
@ -194,13 +195,14 @@ export class AbstractApp extends Component {
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
const { route } = this.state;
|
const { route } = this.state;
|
||||||
|
const component = (route && route.component) || BlankPage;
|
||||||
|
|
||||||
if (route) {
|
if (component) {
|
||||||
return (
|
return (
|
||||||
<I18nextProvider i18n = { i18next }>
|
<I18nextProvider i18n = { i18next }>
|
||||||
<Provider store = { this._getStore() }>
|
<Provider store = { this._getStore() }>
|
||||||
{
|
{
|
||||||
this._createElement(route.component)
|
this._createElement(component)
|
||||||
}
|
}
|
||||||
</Provider>
|
</Provider>
|
||||||
</I18nextProvider>
|
</I18nextProvider>
|
||||||
|
@ -372,15 +374,20 @@ export class AbstractApp extends Component {
|
||||||
// onEnter. During the removal of react-router, modifications were
|
// onEnter. During the removal of react-router, modifications were
|
||||||
// minimized by preserving the onEnter interface:
|
// minimized by preserving the onEnter interface:
|
||||||
// (1) Router would provide its nextState to the Route's onEnter. As the
|
// (1) Router would provide its nextState to the Route's onEnter. As the
|
||||||
// role of Router is now this AbstractApp, provide its nextState.
|
// role of Router is now this AbstractApp and we use redux, provide the
|
||||||
|
// redux store instead.
|
||||||
// (2) A replace function would be provided to the Route in case it
|
// (2) A replace function would be provided to the Route in case it
|
||||||
// chose to redirect to another path.
|
// chose to redirect to another path.
|
||||||
route && this._onRouteEnter(route, nextState, pathname => {
|
route && this._onRouteEnter(route, this._getStore(), pathname => {
|
||||||
|
if (pathname) {
|
||||||
this._openURL(pathname);
|
this._openURL(pathname);
|
||||||
|
|
||||||
// Do not proceed with the route because it chose to redirect to
|
// Do not proceed with the route because it chose to redirect to
|
||||||
// another path.
|
// another path.
|
||||||
nextState = undefined;
|
nextState = undefined;
|
||||||
|
} else {
|
||||||
|
nextState.route = undefined;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// XXX React's setState is asynchronous which means that the value of
|
// XXX React's setState is asynchronous which means that the value of
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { isRoomValid } from '../base/conference';
|
||||||
import { RouteRegistry } from '../base/react';
|
import { RouteRegistry } from '../base/react';
|
||||||
import { toState } from '../base/redux';
|
import { toState } from '../base/redux';
|
||||||
import { Conference } from '../conference';
|
import { Conference } from '../conference';
|
||||||
import { Entryway } from '../welcome';
|
import { WelcomePage } from '../welcome';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines which route is to be rendered in order to depict a specific Redux
|
* Determines which route is to be rendered in order to depict a specific Redux
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Component } from 'react';
|
|
||||||
|
|
||||||
import { destroyLocalTracks } from '../../base/tracks';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A React <tt>Component</tt> which represents a blank page. Destroys the local
|
|
||||||
* tracks upon mounting since no media is desired when this component utilized.
|
|
||||||
* Renders nothing.
|
|
||||||
*/
|
|
||||||
export default class AbstractBlankPage extends Component {
|
|
||||||
/**
|
|
||||||
* <tt>AbstractBlankPage</tt> React <tt>Component</tt>'s prop types.
|
|
||||||
*
|
|
||||||
* @static
|
|
||||||
*/
|
|
||||||
static propTypes = {
|
|
||||||
dispatch: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroys the local tracks (if any) since no media is desired when this
|
|
||||||
* component is rendered.
|
|
||||||
*
|
|
||||||
* @inheritdoc
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
componentWillMount() {
|
|
||||||
this.props.dispatch(destroyLocalTracks());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements React's {@link Component#render()}. Returns null because the
|
|
||||||
* purpose of this component is to destroy the local tracks and render
|
|
||||||
* nothing.
|
|
||||||
*
|
|
||||||
* @inheritdoc
|
|
||||||
* @returns {null}
|
|
||||||
*/
|
|
||||||
render() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,39 +1,58 @@
|
||||||
/* @flow */
|
/* @flow */
|
||||||
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React, { Component } from 'react';
|
||||||
import { ActivityIndicator, View } from 'react-native';
|
import { ActivityIndicator, View } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import AbstractBlankPage from './AbstractBlankPage';
|
import { destroyLocalTracks } from '../../base/tracks';
|
||||||
|
|
||||||
|
import { isWelcomePageAppEnabled } from '../functions';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mobile/React Native implementation of <tt>AbstractBlankPage</tt>. Since this
|
* The React <tt>Component</tt> displayed by <tt>AbstractApp</tt> when it has no
|
||||||
* is the <tt>Component</tt> rendered when there is no <tt>WelcomePage</tt>,
|
* <tt>Route</tt> to render. Renders a progress indicator when there are ongoing
|
||||||
* it will show a progress indicator when there are ongoing network requests
|
* network requests.
|
||||||
* (notably, the loading of config.js before joining a conference). The use case
|
|
||||||
* which prompted the introduction of this <tt>Component</tt> is mobile where
|
|
||||||
* SDK users probably disable the <tt>WelcomePage</tt>.
|
|
||||||
*/
|
*/
|
||||||
class BlankPage extends AbstractBlankPage {
|
class BlankPage extends Component {
|
||||||
/**
|
/**
|
||||||
* <tt>BlankPage</tt> React <tt>Component</tt>'s prop types.
|
* <tt>BlankPage</tt> React <tt>Component</tt>'s prop types.
|
||||||
*
|
*
|
||||||
* @static
|
* @static
|
||||||
*/
|
*/
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
...AbstractBlankPage.propTypes,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates whether there is network activity i.e. ongoing network
|
* Indicates whether there is network activity i.e. ongoing network
|
||||||
* requests.
|
* requests.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_networkActivity: PropTypes.bool
|
_networkActivity: PropTypes.bool,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The indicator which determines whether <tt>WelcomePage</tt> is (to
|
||||||
|
* be) rendered.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_welcomePageEnabled: PropTypes.bool,
|
||||||
|
|
||||||
|
dispatch: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys the local tracks (if any) since no media is desired when this
|
||||||
|
* component is rendered.
|
||||||
|
*
|
||||||
|
* @inheritdoc
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
componentWillMount() {
|
||||||
|
this.props._welcomePageEnabled
|
||||||
|
|| this.props.dispatch(destroyLocalTracks());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements React's {@link Component#render()}.
|
* Implements React's {@link Component#render()}.
|
||||||
*
|
*
|
||||||
|
@ -59,7 +78,8 @@ class BlankPage extends AbstractBlankPage {
|
||||||
* @param {Object} state - The redux state.
|
* @param {Object} state - The redux state.
|
||||||
* @private
|
* @private
|
||||||
* @returns {{
|
* @returns {{
|
||||||
* networkActivity: boolean
|
* _networkActivity: boolean,
|
||||||
|
* _welcomePageEnabled: boolean
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
function _mapStateToProps(state) {
|
function _mapStateToProps(state) {
|
||||||
|
@ -67,7 +87,8 @@ function _mapStateToProps(state) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
_networkActivity:
|
_networkActivity:
|
||||||
Boolean(requests && (requests.length || requests.size))
|
Boolean(requests && (requests.length || requests.size)),
|
||||||
|
_welcomePageEnabled: isWelcomePageAppEnabled(state)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1 @@
|
||||||
import AbstractBlankPage from './AbstractBlankPage';
|
export default undefined;
|
||||||
|
|
||||||
/**
|
|
||||||
* Default <tt>BlankPage</tt> implementation for Web/React. It's not currently
|
|
||||||
* in use but it's here for symmetry with mobile/React Native should we choose
|
|
||||||
* to take advantage of it in the future. Destroys the local tracks and renders
|
|
||||||
* nothing.
|
|
||||||
*/
|
|
||||||
export default class BlankPage extends AbstractBlankPage {
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
|
|
||||||
import BlankPage from './BlankPage';
|
|
||||||
import WelcomePage from './WelcomePage';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A React <tt>Component</tt> which is rendered when there is no (valid) room
|
|
||||||
* (name) i.e. it is the opposite of <tt>Conference</tt>. Generally and
|
|
||||||
* historically, it is <tt>WelcomePage</tt>. However, Jitsi Meet SDK for Android
|
|
||||||
* and iOS allows the use of the (JavaScript) app without <tt>WelcomePage</tt>
|
|
||||||
* and it needs to display something between conferences.
|
|
||||||
*/
|
|
||||||
class Entryway extends Component {
|
|
||||||
/**
|
|
||||||
* <tt>Entryway</tt>'s React <tt>Component</tt> prop types.
|
|
||||||
*/
|
|
||||||
static propTypes = {
|
|
||||||
/**
|
|
||||||
* The indicator which determines whether <tt>WelcomePage</tt> is (to
|
|
||||||
* be) rendered.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_welcomePageEnabled: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements React's {@link Component#render()}.
|
|
||||||
*
|
|
||||||
* @inheritdoc
|
|
||||||
* @returns {ReactElement}
|
|
||||||
*/
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
this.props._welcomePageEnabled ? <WelcomePage /> : <BlankPage />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps (parts of) the redux state to the associated Entryway's props.
|
|
||||||
*
|
|
||||||
* @param {Object} state - The redux state.
|
|
||||||
* @private
|
|
||||||
* @returns {{
|
|
||||||
* _welcomePageEnabled: boolean
|
|
||||||
* }}
|
|
||||||
*/
|
|
||||||
function _mapStateToProps(state) {
|
|
||||||
let welcomePageEnabled;
|
|
||||||
|
|
||||||
if (navigator.product === 'ReactNative') {
|
|
||||||
// We introduced the welcomePageEnabled prop on App in Jitsi Meet SDK
|
|
||||||
// for Android and iOS. There isn't a strong reason not to introduce it
|
|
||||||
// on Web but there're a few considerations to be taken before I go
|
|
||||||
// there among which:
|
|
||||||
// - 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 } = state['features/app'];
|
|
||||||
|
|
||||||
welcomePageEnabled = Boolean(app && app.props.welcomePageEnabled);
|
|
||||||
} else {
|
|
||||||
welcomePageEnabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
_welcomePageEnabled: welcomePageEnabled
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(_mapStateToProps)(Entryway);
|
|
|
@ -1,3 +1,2 @@
|
||||||
export { default as BlankPage } from './BlankPage';
|
export { default as BlankPage } from './BlankPage';
|
||||||
export { default as Entryway } from './Entryway';
|
|
||||||
export { default as WelcomePage } from './WelcomePage';
|
export { default as WelcomePage } from './WelcomePage';
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
/* @flow */
|
||||||
|
|
||||||
|
import { toState } from '../base/redux';
|
||||||
|
|
||||||
|
declare var APP: Object;
|
||||||
|
declare var config: Object;
|
||||||
|
|
||||||
|
export * from './roomnameGenerator';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the <tt>WelcomePage</tt> is enabled by the app itself
|
||||||
|
* (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.
|
||||||
|
* @returns {boolean} If the <tt>WelcomePage</tt> is enabled by the app, then
|
||||||
|
* <tt>true</tt>; otherwise, <tt>false</tt>.
|
||||||
|
*/
|
||||||
|
export function isWelcomePageAppEnabled(stateOrGetState: Object | Function) {
|
||||||
|
let b;
|
||||||
|
|
||||||
|
if (navigator.product === 'ReactNative') {
|
||||||
|
// We introduced the welcomePageEnabled prop on App in Jitsi Meet SDK
|
||||||
|
// for Android and iOS. There isn't a strong reason not to introduce it
|
||||||
|
// on Web but there're a few considerations to be taken before I go
|
||||||
|
// there among which:
|
||||||
|
// - 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);
|
||||||
|
} else {
|
||||||
|
b = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the <tt>WelcomePage</tt> is enabled by the user either
|
||||||
|
* 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.
|
||||||
|
* @returns {boolean} If the <tt>WelcomePage</tt> is enabled by the user, then
|
||||||
|
* <tt>true</tt>; otherwise, <tt>false</tt>.
|
||||||
|
*/
|
||||||
|
export function isWelcomePageUserEnabled(stateOrGetState: Object | Function) {
|
||||||
|
return (
|
||||||
|
typeof APP === 'undefined'
|
||||||
|
? true
|
||||||
|
: toState(stateOrGetState)['features/base/config'].enableWelcomePage
|
||||||
|
&& APP.settings.isWelcomePageEnabled());
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
import './route';
|
import './route';
|
||||||
|
|
||||||
export * from './components';
|
export * from './components';
|
||||||
|
export * from './functions';
|
||||||
|
|
|
@ -2,47 +2,34 @@
|
||||||
|
|
||||||
import { RouteRegistry } from '../base/react';
|
import { RouteRegistry } from '../base/react';
|
||||||
|
|
||||||
import { Entryway } from './components';
|
import { WelcomePage } from './components';
|
||||||
import { generateRoomWithoutSeparator } from './roomnameGenerator';
|
import {
|
||||||
|
generateRoomWithoutSeparator,
|
||||||
declare var APP: Object;
|
isWelcomePageAppEnabled,
|
||||||
declare var config: Object;
|
isWelcomePageUserEnabled
|
||||||
|
} from './functions';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register route for Entryway.
|
* Register route for <tt>WelcomePage</tt>.
|
||||||
*/
|
*/
|
||||||
RouteRegistry.register({
|
RouteRegistry.register({
|
||||||
component: Entryway,
|
component: WelcomePage,
|
||||||
onEnter,
|
onEnter,
|
||||||
path: '/'
|
path: '/'
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the Welcome page/screen is disabled, generates a (random) room (name) so
|
* Skips the <tt>WelcomePage</tt> if it is disabled (by the app or the user).
|
||||||
* that the Welcome page/screen is skipped and the Conference page/screen is
|
|
||||||
* presented instead.
|
|
||||||
*
|
*
|
||||||
* @param {Object} nextState - The next Router state.
|
* @param {Object} store - The redux store.
|
||||||
* @param {Function} replace - The function to redirect to another path.
|
* @param {Function} replace - The function to redirect to another path.
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
function onEnter(nextState, replace) {
|
function onEnter({ getState }, replace) {
|
||||||
// The disabling of the Welcome page by redirecting to a random room name is
|
if (isWelcomePageAppEnabled(getState)) {
|
||||||
// a feature (1) we have on Web/React and (2) we do not want on mobile/React
|
isWelcomePageUserEnabled(getState)
|
||||||
// Native (at the time of this writing).
|
|| replace(`/${generateRoomWithoutSeparator()}`);
|
||||||
if (typeof APP === 'object'
|
} else {
|
||||||
|
replace(undefined);
|
||||||
// TODO Technically, there is features/base/config now so it is
|
|
||||||
// preferable to read config(uration) values from there and not rely
|
|
||||||
// on a global variable. However, the redux store is not available
|
|
||||||
// here at the time of this writing. Given the current (1) Web
|
|
||||||
// exclusivity of the feature and (2) the reliance on other global
|
|
||||||
// variables (e.g. APP), go with the global variable for now in
|
|
||||||
// order to minimize the effort involved.
|
|
||||||
&& !(config.enableWelcomePage
|
|
||||||
&& APP.settings.isWelcomePageEnabled())) {
|
|
||||||
const room = generateRoomWithoutSeparator();
|
|
||||||
|
|
||||||
replace(`/${room}`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue