diff --git a/react/features/base/conference/reducer.js b/react/features/base/conference/reducer.js index b02cf4ef8..9b4bf9eb1 100644 --- a/react/features/base/conference/reducer.js +++ b/react/features/base/conference/reducer.js @@ -405,7 +405,10 @@ function _setRoom(state, action) { * * @type {string} */ - return set(state, 'room', room); + return assign(state, { + error: undefined, + room + }); } /** diff --git a/react/features/base/connection/reducer.js b/react/features/base/connection/reducer.js index b19cd7b4c..2d5785a65 100644 --- a/react/features/base/connection/reducer.js +++ b/react/features/base/connection/reducer.js @@ -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); +} diff --git a/react/features/base/jwt/components/CalleeInfoContainer.js b/react/features/base/jwt/components/CalleeInfoContainer.js index 121023c51..a510d695f 100644 --- a/react/features/base/jwt/components/CalleeInfoContainer.js +++ b/react/features/base/jwt/components/CalleeInfoContainer.js @@ -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 = { diff --git a/react/features/overlay/actions.js b/react/features/overlay/actions.js index 726d466e8..a751c5c07 100644 --- a/react/features/overlay/actions.js +++ b/react/features/overlay/actions.js @@ -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(); } }; diff --git a/react/features/overlay/components/AbstractPageReloadOverlay.js b/react/features/overlay/components/AbstractPageReloadOverlay.js index 7ce221e5c..9bcfb8c91 100644 --- a/react/features/overlay/components/AbstractPageReloadOverlay.js +++ b/react/features/overlay/components/AbstractPageReloadOverlay.js @@ -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.`); diff --git a/react/features/overlay/components/OverlayContainer.js b/react/features/overlay/components/OverlayContainer.js index 266fa94b8..8b85dab5b 100644 --- a/react/features/overlay/components/OverlayContainer.js +++ b/react/features/overlay/components/OverlayContainer.js @@ -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; diff --git a/react/features/overlay/components/OverlayFrame.native.js b/react/features/overlay/components/OverlayFrame.native.js new file mode 100644 index 000000000..82f7eb02c --- /dev/null +++ b/react/features/overlay/components/OverlayFrame.native.js @@ -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 { + /** + * Implements React's {@link Component#render()}. + * + * @inheritdoc + * @returns {React$Element} + */ + render() { + return ( + + { this.props.children } + + ); + } +} diff --git a/react/features/overlay/components/OverlayFrame.js b/react/features/overlay/components/OverlayFrame.web.js similarity index 100% rename from react/features/overlay/components/OverlayFrame.js rename to react/features/overlay/components/OverlayFrame.web.js diff --git a/react/features/overlay/components/PageReloadOverlay.native.js b/react/features/overlay/components/PageReloadOverlay.native.js new file mode 100644 index 000000000..7a43b86d8 --- /dev/null +++ b/react/features/overlay/components/PageReloadOverlay.native.js @@ -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 ( + + + + + + + { t(title) } + + + { t(message, { seconds: timeLeft }) } + + + + { t('dialog.rejoinNow') } + + + { t('dialog.Cancel') } + + + + + ); + } +} + +export default translate(connect(abstractMapStateToProps)(PageReloadOverlay)); diff --git a/react/features/overlay/components/PageReloadOverlay.js b/react/features/overlay/components/PageReloadOverlay.web.js similarity index 100% rename from react/features/overlay/components/PageReloadOverlay.js rename to react/features/overlay/components/PageReloadOverlay.web.js diff --git a/react/features/overlay/components/styles.js b/react/features/overlay/components/styles.js new file mode 100644 index 000000000..b7c4dcea8 --- /dev/null +++ b/react/features/overlay/components/styles.js @@ -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' + } +});