[RN] LoadingIndicator on WelcomePage

It will replace the "Join" text button while appNavigate lasts.

Note about the implementation: when appNavigate completes the component
may have been unmounted and thus we cannot touch its state. In order to
avoid this problem I added a 'mounted' instance variable which gets set
and reset in componentWillMount / Unmount respectively. This is to avoid
using isMounted, which is highly discouraged.
This commit is contained in:
Saúl Ibarra Corretgé 2017-09-14 11:46:35 +02:00 committed by Lyubo Marinov
parent 35da39becf
commit ca13a9b914
3 changed files with 87 additions and 18 deletions

View File

@ -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);
}
}
/**

View File

@ -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 } />
<TouchableHighlight
accessibilityLabel = { 'Tap to Join.' }
disabled = { this._isJoinDisabled() }
onPress = { this._onJoin }
style = { styles.button }
underlayColor = { ColorPalette.white }>
<Text style = { styles.buttonText }>
{ t('welcomepage.join') }
</Text>
</TouchableHighlight>
{
this._renderJoinButton()
}
</View>
{
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 = (
<View>
<LoadingIndicator />
</View>
);
} else {
children = (
<Text style = { styles.buttonText }>
{ this.props.t('welcomepage.join') }
</Text>
);
}
/* eslint-enable no-extra-parens */
return (
<TouchableHighlight
accessibilityLabel = { 'Tap to Join.' }
disabled = { this._isJoinDisabled() }
onPress = { this._onJoin }
style = { styles.button }
underlayColor = { ColorPalette.white }>
{
children
}
</TouchableHighlight>
);
}
/**
* Renders legal-related content such as Terms of service/use, Privacy
* policy, etc.

View File

@ -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}