[RN] Add reload overlay in case of connection / conference errors

This commit is contained in:
Saúl Ibarra Corretgé 2017-11-24 16:23:40 +01:00 committed by Lyubo Marinov
parent ad497fed7c
commit 87a87eebb9
11 changed files with 274 additions and 13 deletions

View File

@ -405,7 +405,10 @@ function _setRoom(state, action) {
*
* @type {string}
*/
return set(state, 'room', room);
return assign(state, {
error: undefined,
room
});
}
/**

View File

@ -1,6 +1,7 @@
/* @flow */
import { assign, ReducerRegistry } from '../redux';
import { SET_ROOM } from '../conference';
import { assign, set, ReducerRegistry } from '../redux';
import { parseURIString } from '../util';
import {
@ -32,6 +33,9 @@ ReducerRegistry.register(
case SET_LOCATION_URL:
return _setLocationURL(state, action);
case SET_ROOM:
return _setRoom(state);
}
return state;
@ -194,3 +198,16 @@ function _setLocationURL(
options: locationURL ? _constructOptions(locationURL) : undefined
});
}
/**
* Reduces a specific redux action {@link SET_ROOM} of the feature
* base/connection.
*
* @param {Object} state - The redux state of the feature base/connection.
* @private
* @returns {Object} The new state of the feature base/connection after the
* reduction of the specified action.
*/
function _setRoom(state: Object) {
return set(state, 'error', undefined);
}

View File

@ -6,7 +6,7 @@ import { connect } from 'react-redux';
import CalleeInfo from './CalleeInfo';
/**
* The type of the React {@code Component} props of {@link Conference}.
* The type of the React {@code Component} props of {@code CalleeInfoContainer}.
*/
type Props = {

View File

@ -1,3 +1,5 @@
import { appNavigate } from '../app';
import { toURLString } from '../base/util';
import { reload, replace } from '../../../modules/util/helpers';
import {
@ -40,11 +42,13 @@ export function _reloadNow() {
logger.info(`Reloading the conference using URL: ${locationURL}`);
// In an iframe reload with the reload() utility because the replace()
// utility does not work on an iframe.
if (window.self === window.top) {
if (navigator.product === 'ReactNative') {
dispatch(appNavigate(toURLString(locationURL)));
} else if (window.self === window.top) {
replace(locationURL);
} else {
// In an iframe reload with the reload() utility because the
// replace() utility does not work on an iframe.
reload();
}
};

View File

@ -156,10 +156,13 @@ export default class AbstractPageReloadOverlay extends Component<*, *> {
// because the log queue is not flushed before "fabric terminated" is
// sent to the backed.
// FIXME: We should dispatch action for this.
APP.conference.logEvent(
PAGE_RELOAD,
/* value */ undefined,
/* label */ this.props.reason);
if (typeof APP !== 'undefined') {
APP.conference.logEvent(
PAGE_RELOAD,
/* value */ undefined,
/* label */ this.props.reason);
}
logger.info(
`The conference will be reloaded after ${
this.state.timeoutSeconds} seconds.`);

View File

@ -91,10 +91,16 @@ function _getOverlays(filmstripOnly) {
}
} else if (!(overlays = _nonFilmstripOnlyOverlays)) {
overlays = _nonFilmstripOnlyOverlays = [
PageReloadOverlay,
SuspendedOverlay,
UserMediaPermissionsOverlay
PageReloadOverlay
];
// Mobile only has a PageReloadOverlay.
if (navigator.product !== 'ReactNative') {
overlays.push(...[
SuspendedOverlay,
UserMediaPermissionsOverlay
]);
}
}
return overlays;

View File

@ -0,0 +1,36 @@
// @flow
import React, { Component } from 'react';
import { View } from 'react-native';
import { overlayFrame as styles } from './styles';
/**
* The type of the React {@code Component} props of {@code OverlayFrame}.
*/
type Props = {
/**
* The children components to be displayed into the overlay frame.
*/
children?: React$Node,
};
/**
* Implements a React component to act as the frame for overlays.
*/
export default class OverlayFrame extends Component<Props> {
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
* @returns {React$Element}
*/
render() {
return (
<View style = { styles.container }>
{ this.props.children }
</View>
);
}
}

View File

@ -0,0 +1,101 @@
import React from 'react';
import { Text, View } from 'react-native';
import { connect } from 'react-redux';
import { appNavigate } from '../../app';
import { translate } from '../../base/i18n';
import { LoadingIndicator } from '../../base/react';
import { _reloadNow } from '../actions';
import AbstractPageReloadOverlay, { abstractMapStateToProps }
from './AbstractPageReloadOverlay';
import OverlayFrame from './OverlayFrame';
import { pageReloadOverlay as styles } from './styles';
/**
* Implements a React Component for page reload overlay. Shown before the
* conference is reloaded. Shows a warning message and counts down towards the
* reload.
*/
class PageReloadOverlay extends AbstractPageReloadOverlay {
/**
* Initializes a new PageReloadOverlay instance.
*
* @param {Object} props - The read-only properties with which the new
* instance is to be initialized.
* @public
*/
constructor(props) {
super(props);
this._onCancel = this._onCancel.bind(this);
this._onReloadNow = this._onReloadNow.bind(this);
}
/**
* Handle clicking of the "Cancel" button. It will navigate back to the
* welcome page.
*
* @private
* @returns {void}
*/
_onCancel() {
clearInterval(this._interval);
this.props.dispatch(appNavigate(undefined));
}
/**
* Handle clicking on the "Reload Now" button. It will navigate to the same
* conference URL as before immediately, without waiting for the timer to
* kick in.
*
* @private
* @returns {void}
*/
_onReloadNow() {
clearInterval(this._interval);
this.props.dispatch(_reloadNow());
}
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
* @returns {ReactElement}
*/
render() {
const { t } = this.props;
const { message, timeLeft, title } = this.state;
return (
<OverlayFrame>
<View style = { styles.container }>
<View style = { styles.loadingIndicator }>
<LoadingIndicator />
</View>
<Text style = { styles.title }>
{ t(title) }
</Text>
<Text style = { styles.message }>
{ t(message, { seconds: timeLeft }) }
</Text>
<View style = { styles.buttonBox }>
<Text
onPress = { this._onReloadNow }
style = { styles.button } >
{ t('dialog.rejoinNow') }
</Text>
<Text
onPress = { this._onCancel }
style = { styles.button } >
{ t('dialog.Cancel') }
</Text>
</View>
</View>
</OverlayFrame>
);
}
}
export default translate(connect(abstractMapStateToProps)(PageReloadOverlay));

View File

@ -0,0 +1,91 @@
import { BoxModel, ColorPalette, createStyleSheet } from '../../base/styles';
/**
* The default color of text on overlays.
*/
const TEXT_COLOR = ColorPalette.white;
/**
* The React {@code Component} styles of {@code OverlayFrame}.
*/
export const overlayFrame = createStyleSheet({
/**
* Style for a backdrop overlay covering the screen the the overlay is
* rendered.
*/
container: {
backgroundColor: ColorPalette.red,
bottom: 0,
left: 0,
position: 'absolute',
right: 0,
top: 0
}
});
/**
* The React {@code Component} styles of {@code PageReloadOverlay}.
*/
export const pageReloadOverlay = createStyleSheet({
/**
* Style for the buttons on {@code PageReloadOverlay}.
*/
button: {
color: TEXT_COLOR,
fontSize: 20,
marginVertical: BoxModel.margin,
textAlign: 'center'
},
/**
* Style for the "box" surrounding the buttons at the bottom of the page.
*/
buttonBox: {
bottom: BoxModel.margin,
left: 0,
position: 'absolute',
right: 0
},
/**
* Style for the container of the {@code PageReloadOVerlay}.
*/
container: {
flex: 1,
marginBottom: BoxModel.margin,
marginHorizontal: BoxModel.margin,
marginTop: BoxModel.margin * 3
},
/**
* Style for the {@code LoadingIndicator}.
*/
loadingIndicator: {
alignItems: 'center',
bottom: 0,
justifyContent: 'center',
left: 0,
position: 'absolute',
right: 0,
top: 0
},
/**
* Style for the descriptive error message.
*/
message: {
color: TEXT_COLOR,
fontSize: 16,
marginTop: BoxModel.margin,
textAlign: 'center'
},
/**
* Style for the error title.
*/
title: {
color: TEXT_COLOR,
fontSize: 24,
textAlign: 'center'
}
});