浏览代码

[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.
j8
Saúl Ibarra Corretgé 8 年前
父节点
当前提交
ca13a9b914

+ 29
- 4
react/features/welcome/components/AbstractWelcomePage.js 查看文件

48
         this.state = {
48
         this.state = {
49
             animateTimeoutId: null,
49
             animateTimeoutId: null,
50
             generatedRoomname: '',
50
             generatedRoomname: '',
51
+            joining: false,
51
             room: '',
52
             room: '',
52
             roomPlaceholder: '',
53
             roomPlaceholder: '',
53
             updateTimeoutId: null
54
             updateTimeoutId: null
62
     }
63
     }
63
 
64
 
64
     /**
65
     /**
65
-     * This method is executed when component receives new properties.
66
+     * Implements React's {@link Component#componentWillMount()}. Invoked
67
+     * immediately before mounting occurs.
68
+     *
69
+     * @inheritdoc
70
+     */
71
+    componentWillMount() {
72
+        this._mounted = true;
73
+    }
74
+
75
+    /**
76
+     * Implements React's {@link Component#componentWillReceiveProps()}. Invoked
77
+     * before this mounted component receives new props.
66
      *
78
      *
67
      * @inheritdoc
79
      * @inheritdoc
68
      * @param {Object} nextProps - New props component will receive.
80
      * @param {Object} nextProps - New props component will receive.
72
     }
84
     }
73
 
85
 
74
     /**
86
     /**
75
-     * This method is executed when method will be unmounted from DOM.
87
+     * Implements React's {@link Component#componentWillUnmount()}. Invoked
88
+     * immediately before this component is unmounted and destroyed.
76
      *
89
      *
77
      * @inheritdoc
90
      * @inheritdoc
78
      */
91
      */
79
     componentWillUnmount() {
92
     componentWillUnmount() {
80
         this._clearTimeouts();
93
         this._clearTimeouts();
94
+        this._mounted = false;
81
     }
95
     }
82
 
96
 
83
     /**
97
     /**
128
      * otherwise, false.
142
      * otherwise, false.
129
      */
143
      */
130
     _isJoinDisabled() {
144
     _isJoinDisabled() {
131
-        return !isRoomValid(this.state.room);
145
+        return this.state.joining || !isRoomValid(this.state.room);
132
     }
146
     }
133
 
147
 
134
     /**
148
     /**
141
     _onJoin() {
155
     _onJoin() {
142
         const room = this.state.room || this.state.generatedRoomname;
156
         const room = this.state.room || this.state.generatedRoomname;
143
 
157
 
144
-        room && this.props.dispatch(appNavigate(room));
158
+        if (room) {
159
+            this.setState({ joining: true });
160
+
161
+            // By the time the Promise of appNavigate settles, this component
162
+            // may have already been unmounted.
163
+            const onAppNavigateSettled = () => {
164
+                this._mounted && this.setState({ joining: false });
165
+            };
166
+
167
+            this.props.dispatch(appNavigate(room))
168
+                .then(onAppNavigateSettled, onAppNavigateSettled);
169
+        }
145
     }
170
     }
146
 
171
 
147
     /**
172
     /**

+ 56
- 13
react/features/welcome/components/WelcomePage.native.js 查看文件

4
 
4
 
5
 import { translate } from '../../base/i18n';
5
 import { translate } from '../../base/i18n';
6
 import { MEDIA_TYPE } from '../../base/media';
6
 import { MEDIA_TYPE } from '../../base/media';
7
-import { Link, Text } from '../../base/react';
7
+import { Link, LoadingIndicator, Text } from '../../base/react';
8
 import { ColorPalette } from '../../base/styles';
8
 import { ColorPalette } from '../../base/styles';
9
 import { createDesiredLocalTracks } from '../../base/tracks';
9
 import { createDesiredLocalTracks } from '../../base/tracks';
10
 
10
 
41
     static propTypes = AbstractWelcomePage.propTypes;
41
     static propTypes = AbstractWelcomePage.propTypes;
42
 
42
 
43
     /**
43
     /**
44
-     * Creates a video track if not already available.
44
+     * Implements React's {@link Component#componentWillMount()}. Invoked
45
+     * immediately before mounting occurs. Creates a local video track if none
46
+     * is available.
45
      *
47
      *
46
      * @inheritdoc
48
      * @inheritdoc
47
      * @returns {void}
49
      * @returns {void}
48
      */
50
      */
49
     componentWillMount() {
51
     componentWillMount() {
52
+        super.componentWillMount();
53
+
50
         this.props.dispatch(createDesiredLocalTracks(MEDIA_TYPE.VIDEO));
54
         this.props.dispatch(createDesiredLocalTracks(MEDIA_TYPE.VIDEO));
51
     }
55
     }
52
 
56
 
53
     /**
57
     /**
54
-     * Renders a prompt for entering a room name.
58
+     * Implements React's {@link Component#render()}. Renders a prompt for
59
+     * entering a room name.
55
      *
60
      *
61
+     * @inheritdoc
56
      * @returns {ReactElement}
62
      * @returns {ReactElement}
57
      */
63
      */
58
     render() {
64
     render() {
75
                         style = { styles.textInput }
81
                         style = { styles.textInput }
76
                         underlineColorAndroid = 'transparent'
82
                         underlineColorAndroid = 'transparent'
77
                         value = { this.state.room } />
83
                         value = { this.state.room } />
78
-                    <TouchableHighlight
79
-                        accessibilityLabel = { 'Tap to Join.' }
80
-                        disabled = { this._isJoinDisabled() }
81
-                        onPress = { this._onJoin }
82
-                        style = { styles.button }
83
-                        underlayColor = { ColorPalette.white }>
84
-                        <Text style = { styles.buttonText }>
85
-                            { t('welcomepage.join') }
86
-                        </Text>
87
-                    </TouchableHighlight>
84
+                    {
85
+                        this._renderJoinButton()
86
+                    }
88
                 </View>
87
                 </View>
89
                 {
88
                 {
90
                     this._renderLegalese()
89
                     this._renderLegalese()
93
         );
92
         );
94
     }
93
     }
95
 
94
 
95
+    /**
96
+     * Renders the join button.
97
+     *
98
+     * @private
99
+     * @returns {ReactElement}
100
+     */
101
+    _renderJoinButton() {
102
+        let children;
103
+
104
+        /* eslint-disable no-extra-parens */
105
+
106
+        if (this.state.joining) {
107
+            // TouchableHighlight is picky about what its children can be, so
108
+            // wrap it in a native component, i.e. View to avoid having to
109
+            // modify non-native children.
110
+            children = (
111
+                <View>
112
+                    <LoadingIndicator />
113
+                </View>
114
+            );
115
+        } else {
116
+            children = (
117
+                <Text style = { styles.buttonText }>
118
+                    { this.props.t('welcomepage.join') }
119
+                </Text>
120
+            );
121
+        }
122
+
123
+        /* eslint-enable no-extra-parens */
124
+
125
+        return (
126
+            <TouchableHighlight
127
+                accessibilityLabel = { 'Tap to Join.' }
128
+                disabled = { this._isJoinDisabled() }
129
+                onPress = { this._onJoin }
130
+                style = { styles.button }
131
+                underlayColor = { ColorPalette.white }>
132
+                {
133
+                    children
134
+                }
135
+            </TouchableHighlight>
136
+        );
137
+    }
138
+
96
     /**
139
     /**
97
      * Renders legal-related content such as Terms of service/use, Privacy
140
      * Renders legal-related content such as Terms of service/use, Privacy
98
      * policy, etc.
141
      * policy, etc.

+ 2
- 1
react/features/welcome/components/WelcomePage.web.js 查看文件

38
     }
38
     }
39
 
39
 
40
     /**
40
     /**
41
-     * This method is executed when comonent is mounted.
41
+     * Implements React's {@link Component#componentDidMount()}. Invoked
42
+     * immediately after this component is mounted.
42
      *
43
      *
43
      * @inheritdoc
44
      * @inheritdoc
44
      * @returns {void}
45
      * @returns {void}

正在加载...
取消
保存