[RN] Add conference connecting overlay

This commit is contained in:
Bettenbuk Zoltan 2019-04-09 17:53:12 +02:00 committed by Saúl Ibarra Corretgé
parent 84b917d708
commit 8bb56be317
16 changed files with 246 additions and 73 deletions

View File

@ -57,6 +57,9 @@
},
"title": "Chat"
},
"connectingOverlay": {
"joiningRoom": "Connecting you to your meeting..."
},
"connection": {
"ATTACHED": "Attached",
"AUTHENTICATING": "Authenticating",

View File

@ -55,12 +55,12 @@ export function appNavigate(uri: ?string) {
}
location.protocol || (location.protocol = 'https:');
const { contextRoot, host, room } = location;
const locationURL = new URL(location.toString());
dispatch(configWillLoad(locationURL));
dispatch(configWillLoad(locationURL, room));
let protocol = location.protocol.toLowerCase();
const { contextRoot, host, room } = location;
// The React Native app supports an app-specific scheme which is sure to not
// be supported by fetch.

View File

@ -117,7 +117,7 @@ export default class BaseApp extends Component<*, State> {
render() {
const { route: { component }, store } = this.state;
if (store && component) {
if (store) {
return (
<I18nextProvider i18n = { i18next }>
<Provider store = { store }>
@ -160,7 +160,7 @@ export default class BaseApp extends Component<*, State> {
* @protected
*/
_createMainElement(component, props) {
return React.createElement(component, props || {});
return component ? React.createElement(component, props || {}) : null;
}
/**

View File

@ -29,6 +29,10 @@ export default {
'LargeVideo': {
background: ColorPalette.black
},
'LoadConfigOverlay': {
background: ColorPalette.black,
text: ColorPalette.white
},
'Thumbnail': {
activeParticipantHighlight: ColorPalette.blue,
activeParticipantTint: ColorPalette.black,

View File

@ -4,7 +4,8 @@
*
* {
* type: CONFIG_WILL_LOAD,
* locationURL: URL
* locationURL: URL,
* room: string
* }
*/
export const CONFIG_WILL_LOAD = 'CONFIG_WILL_LOAD';

View File

@ -15,15 +15,18 @@ import { setConfigFromURLParams } from './functions';
*
* @param {URL} locationURL - The URL of the location which necessitated the
* loading of a configuration.
* @param {string} room - The name of the room (conference) for which we're loading the config for.
* @returns {{
* type: CONFIG_WILL_LOAD,
* locationURL: URL
* locationURL: URL,
* room: string
* }}
*/
export function configWillLoad(locationURL: URL) {
export function configWillLoad(locationURL: URL, room: string) {
return {
type: CONFIG_WILL_LOAD,
locationURL
locationURL,
room
};
}

View File

@ -7,6 +7,11 @@ import { ColorPalette } from '../../../styles';
type Props = {
/**
* The color of the spinner.
*/
color: ?string,
/**
* Prop to set the size of the indicator. This is the same as the
* prop of the native component.
@ -27,6 +32,7 @@ export default class LoadingIndicator extends Component<Props> {
* @returns {ReactElement}
*/
render() {
const { color = ColorPalette.white } = this.props;
let { size = 'large' } = this.props;
if (size === 'medium') {
@ -35,7 +41,7 @@ export default class LoadingIndicator extends Component<Props> {
const props = {
animating: true,
color: ColorPalette.white,
color,
...this.props,
size
};
@ -43,7 +49,6 @@ export default class LoadingIndicator extends Component<Props> {
return (
<ActivityIndicator
animating = { true }
color = { ColorPalette.white }
{ ...props }
size = { size } />
);

View File

@ -14,16 +14,6 @@
export const MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED
= 'MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED';
/**
* The type of the Redux action which signals that a suspend was detected.
*
* {
* type: SUSPEND_DETECTED
* }
* @public
*/
export const SUSPEND_DETECTED = 'SUSPEND_DETECTED';
/**
* Adjust the state of the fatal error which shows/hides the reload screen. See
* action methods's description for more info about each of the fields.
@ -35,3 +25,13 @@ export const SUSPEND_DETECTED = 'SUSPEND_DETECTED';
* @public
*/
export const SET_FATAL_ERROR = 'SET_FATAL_ERROR';
/**
* The type of the Redux action which signals that a suspend was detected.
*
* {
* type: SUSPEND_DETECTED
* }
* @public
*/
export const SUSPEND_DETECTED = 'SUSPEND_DETECTED';

View File

@ -0,0 +1,92 @@
// @flow
import React, { Component } from 'react';
import { SafeAreaView, Text } from 'react-native';
import { ColorSchemeRegistry } from '../../../base/color-scheme';
import { translate } from '../../../base/i18n';
import { LoadingIndicator } from '../../../base/react';
import { connect } from '../../../base/redux';
import { StyleType } from '../../../base/styles';
import OverlayFrame from './OverlayFrame';
import styles from './styles';
type Props = {
/**
* The color schemed style of the component.
*/
_styles: StyleType,
/**
* The Function to be invoked to translate i18n keys.
*/
t: Function
};
/**
* Implements an overlay to tell the user that there is an operation in progress in the background during connect
* so then the app doesn't seem hung.
*/
class LoadConfigOverlay extends Component<Props> {
/**
* Determines whether this overlay needs to be rendered (according to a
* specific redux state). Called by {@link OverlayContainer}.
*
* @param {Object} state - The redux state.
* @returns {boolean} - If this overlay needs to be rendered, {@code true};
* {@code false}, otherwise.
*/
static needsRender(state: Object) {
return Boolean(state['features/overlay'].loadConfigOverlayVisible);
}
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
* @returns {ReactElement}
*/
render() {
const { _styles } = this.props;
return (
<OverlayFrame>
<SafeAreaView
style = { [
styles.loadingOverlayWrapper,
_styles.loadingOverlayWrapper
] }>
<LoadingIndicator
color = { _styles.indicatorColor }
size = 'large'
style = { styles.connectIndicator } />
<Text
style = { [
styles.loadingOverlayText,
_styles.loadingOverlayText
] }>
{ this.props.t('connectingOverlay.joiningRoom') }
</Text>
</SafeAreaView>
</OverlayFrame>
);
}
}
/**
* Maps part of the Redux state to the props of this component.
*
* @param {Object} state - The Redux state.
* @returns {{
* _styles: StyleType
* }}
*/
function _mapStateToProps(state) {
return {
_styles: ColorSchemeRegistry.get(state, 'LoadConfigOverlay')
};
}
export default translate(connect(_mapStateToProps)(LoadConfigOverlay));

View File

@ -3,7 +3,7 @@
import React, { Component, type Node } from 'react';
import { SafeAreaView, View } from 'react-native';
import { overlayFrame as styles } from './styles';
import styles from './styles';
/**
* The type of the React {@code Component} props of {@code OverlayFrame}.

View File

@ -1,4 +1,5 @@
// @flow
export { default as LoadConfigOverlay } from './LoadConfigOverlay';
export { default as OverlayFrame } from './OverlayFrame';
export { default as PageReloadOverlay } from './PageReloadOverlay';

View File

@ -2,12 +2,17 @@
import { StyleSheet } from 'react-native';
import { ColorPalette } from '../../../base/styles';
import { ColorSchemeRegistry, schemeColor } from '../../../base/color-scheme';
import { BoxModel, ColorPalette } from '../../../base/styles';
/**
* The React {@code Component} styles of {@code OverlayFrame}.
* The React {@code Component} styles of the overlay feature.
*/
export const overlayFrame = createStyleSheet({
export default {
connectIndicator: {
margin: BoxModel.margin
},
/**
* Style for a backdrop overlay covering the screen the the overlay is
* rendered.
@ -17,7 +22,33 @@ export const overlayFrame = createStyleSheet({
backgroundColor: ColorPalette.black
},
loadingOverlayText: {
color: ColorPalette.white
},
loadingOverlayWrapper: {
alignItems: 'center',
flex: 1,
flexDirection: 'column',
justifyContent: 'center'
},
safeContainer: {
flex: 1
}
};
/**
* Color schemed styles for all the component based on the abstract dialog.
*/
ColorSchemeRegistry.register('LoadConfigOverlay', {
indicatorColor: schemeColor('text'),
loadingOverlayText: {
color: schemeColor('text')
},
loadingOverlayWrapper: {
backgroundColor: schemeColor('background')
}
});

View File

@ -1,49 +1,6 @@
// @flow
import {
PageReloadFilmstripOnlyOverlay,
PageReloadOverlay,
SuspendedFilmstripOnlyOverlay,
SuspendedOverlay,
UserMediaPermissionsFilmstripOnlyOverlay,
UserMediaPermissionsOverlay
} from './components';
declare var interfaceConfig: Object;
/**
* Returns the list of available overlays that might be rendered.
*
* @private
* @returns {Array<?React$ComponentType<*>>}
*/
function _getOverlays() {
const filmstripOnly
= typeof interfaceConfig === 'object' && interfaceConfig.filmStripOnly;
let overlays;
if (filmstripOnly) {
overlays = [
PageReloadFilmstripOnlyOverlay,
SuspendedFilmstripOnlyOverlay,
UserMediaPermissionsFilmstripOnlyOverlay
];
} else {
overlays = [
PageReloadOverlay
];
}
// Mobile only has a PageReloadOverlay.
if (navigator.product !== 'ReactNative') {
overlays.push(...[
SuspendedOverlay,
UserMediaPermissionsOverlay
]);
}
return overlays;
}
import { getOverlays } from './overlays';
/**
* Returns the overlay to be currently rendered.
@ -52,7 +9,7 @@ function _getOverlays() {
* @returns {?React$ComponentType<*>}
*/
export function getOverlayToRender(state: Object) {
for (const overlay of _getOverlays()) {
for (const overlay of getOverlays()) {
// react-i18n / react-redux wrap components and thus we cannot access
// the wrapped component's static methods directly.
const component = overlay.WrappedComponent || overlay;

View File

@ -0,0 +1,18 @@
// @flow
import {
LoadConfigOverlay,
PageReloadOverlay
} from './components/native';
/**
* Returns the list of available platform specific overlays.
*
* @returns {Array<React$Element>}
*/
export function getOverlays(): Array<React$Element<*>> {
return [
LoadConfigOverlay,
PageReloadOverlay
];
}

View File

@ -0,0 +1,38 @@
// @flow
import {
PageReloadFilmstripOnlyOverlay,
PageReloadOverlay,
SuspendedFilmstripOnlyOverlay,
SuspendedOverlay,
UserMediaPermissionsFilmstripOnlyOverlay,
UserMediaPermissionsOverlay
} from './components/web';
declare var interfaceConfig: Object;
/**
* Returns the list of available platform specific overlays.
*
* @returns {Array<Object>}
*/
export function getOverlays(): Array<Object> {
const overlays = [
SuspendedOverlay,
UserMediaPermissionsOverlay
];
const filmstripOnly
= typeof interfaceConfig === 'object' && interfaceConfig.filmStripOnly;
if (filmstripOnly) {
overlays.push(
PageReloadFilmstripOnlyOverlay,
SuspendedFilmstripOnlyOverlay,
UserMediaPermissionsFilmstripOnlyOverlay);
} else {
overlays.push(PageReloadOverlay);
}
return overlays;
}

View File

@ -1,5 +1,6 @@
// @flow
import { CONFIG_WILL_LOAD, LOAD_CONFIG_ERROR, SET_CONFIG } from '../base/config';
import { assign, ReducerRegistry, set } from '../base/redux';
import {
@ -15,6 +16,13 @@ import {
*/
ReducerRegistry.register('features/overlay', (state = { }, action) => {
switch (action.type) {
case CONFIG_WILL_LOAD:
return _setShowLoadConfigOverlay(state, Boolean(action.room));
case LOAD_CONFIG_ERROR:
case SET_CONFIG:
return _setShowLoadConfigOverlay(false);
case MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED:
return _mediaPermissionPromptVisibilityChanged(state, action);
@ -48,15 +56,15 @@ function _mediaPermissionPromptVisibilityChanged(
}
/**
* Reduces a specific redux action SUSPEND_DETECTED of the feature overlay.
* Sets the {@code LoadConfigOverlay} overlay visible or not.
*
* @param {Object} state - The redux state of the feature overlay.
* @private
* @param {boolean} show - Whether to show or not the overlay.
* @returns {Object} The new state of the feature overlay after the reduction of
* the specified action.
*/
function _suspendDetected(state) {
return set(state, 'suspendDetected', true);
function _setShowLoadConfigOverlay(state, show) {
return set(state, 'loadConfigOverlayVisible', show);
}
/**
@ -72,3 +80,15 @@ function _suspendDetected(state) {
function _setFatalError(state, { fatalError }) {
return set(state, 'fatalError', fatalError);
}
/**
* Reduces a specific redux action SUSPEND_DETECTED of the feature overlay.
*
* @param {Object} state - The redux state of the feature overlay.
* @private
* @returns {Object} The new state of the feature overlay after the reduction of
* the specified action.
*/
function _suspendDetected(state) {
return set(state, 'suspendDetected', true);
}