diff --git a/react/features/welcome/components/AbstractWelcomePage.js b/react/features/welcome/components/AbstractWelcomePage.js index 309668c46..994ef5637 100644 --- a/react/features/welcome/components/AbstractWelcomePage.js +++ b/react/features/welcome/components/AbstractWelcomePage.js @@ -48,6 +48,7 @@ export class AbstractWelcomePage extends Component { this.state = { animateTimeoutId: null, generatedRoomname: '', + joining: false, room: '', roomPlaceholder: '', updateTimeoutId: null @@ -62,7 +63,18 @@ export class AbstractWelcomePage extends Component { } /** - * This method is executed when component receives new properties. + * Implements React's {@link Component#componentWillMount()}. Invoked + * immediately before mounting occurs. + * + * @inheritdoc + */ + componentWillMount() { + this._mounted = true; + } + + /** + * Implements React's {@link Component#componentWillReceiveProps()}. Invoked + * before this mounted component receives new props. * * @inheritdoc * @param {Object} nextProps - New props component will receive. @@ -72,12 +84,14 @@ export class AbstractWelcomePage extends Component { } /** - * This method is executed when method will be unmounted from DOM. + * Implements React's {@link Component#componentWillUnmount()}. Invoked + * immediately before this component is unmounted and destroyed. * * @inheritdoc */ componentWillUnmount() { this._clearTimeouts(); + this._mounted = false; } /** @@ -128,7 +142,7 @@ export class AbstractWelcomePage extends Component { * otherwise, false. */ _isJoinDisabled() { - return !isRoomValid(this.state.room); + return this.state.joining || !isRoomValid(this.state.room); } /** @@ -141,7 +155,18 @@ export class AbstractWelcomePage extends Component { _onJoin() { const room = this.state.room || this.state.generatedRoomname; - room && this.props.dispatch(appNavigate(room)); + if (room) { + this.setState({ joining: true }); + + // By the time the Promise of appNavigate settles, this component + // may have already been unmounted. + const onAppNavigateSettled = () => { + this._mounted && this.setState({ joining: false }); + }; + + this.props.dispatch(appNavigate(room)) + .then(onAppNavigateSettled, onAppNavigateSettled); + } } /** diff --git a/react/features/welcome/components/WelcomePage.native.js b/react/features/welcome/components/WelcomePage.native.js index a7ee46458..723bf2c5a 100644 --- a/react/features/welcome/components/WelcomePage.native.js +++ b/react/features/welcome/components/WelcomePage.native.js @@ -4,7 +4,7 @@ import { connect } from 'react-redux'; import { translate } from '../../base/i18n'; import { MEDIA_TYPE } from '../../base/media'; -import { Link, Text } from '../../base/react'; +import { Link, LoadingIndicator, Text } from '../../base/react'; import { ColorPalette } from '../../base/styles'; import { createDesiredLocalTracks } from '../../base/tracks'; @@ -41,18 +41,24 @@ class WelcomePage extends AbstractWelcomePage { static propTypes = AbstractWelcomePage.propTypes; /** - * Creates a video track if not already available. + * Implements React's {@link Component#componentWillMount()}. Invoked + * immediately before mounting occurs. Creates a local video track if none + * is available. * * @inheritdoc * @returns {void} */ componentWillMount() { + super.componentWillMount(); + this.props.dispatch(createDesiredLocalTracks(MEDIA_TYPE.VIDEO)); } /** - * Renders a prompt for entering a room name. + * Implements React's {@link Component#render()}. Renders a prompt for + * entering a room name. * + * @inheritdoc * @returns {ReactElement} */ render() { @@ -75,16 +81,9 @@ class WelcomePage extends AbstractWelcomePage { style = { styles.textInput } underlineColorAndroid = 'transparent' value = { this.state.room } /> - - - { t('welcomepage.join') } - - + { + this._renderJoinButton() + } { this._renderLegalese() @@ -93,6 +92,50 @@ class WelcomePage extends AbstractWelcomePage { ); } + /** + * Renders the join button. + * + * @private + * @returns {ReactElement} + */ + _renderJoinButton() { + let children; + + /* eslint-disable no-extra-parens */ + + if (this.state.joining) { + // TouchableHighlight is picky about what its children can be, so + // wrap it in a native component, i.e. View to avoid having to + // modify non-native children. + children = ( + + + + ); + } else { + children = ( + + { this.props.t('welcomepage.join') } + + ); + } + + /* eslint-enable no-extra-parens */ + + return ( + + { + children + } + + ); + } + /** * Renders legal-related content such as Terms of service/use, Privacy * policy, etc. diff --git a/react/features/welcome/components/WelcomePage.web.js b/react/features/welcome/components/WelcomePage.web.js index 75a748d21..a1890c8d8 100644 --- a/react/features/welcome/components/WelcomePage.web.js +++ b/react/features/welcome/components/WelcomePage.web.js @@ -38,7 +38,8 @@ class WelcomePage extends AbstractWelcomePage { } /** - * This method is executed when comonent is mounted. + * Implements React's {@link Component#componentDidMount()}. Invoked + * immediately after this component is mounted. * * @inheritdoc * @returns {void}