瀏覽代碼

Add hint box with dynamic join button

j8
zbettenbuk 7 年之前
父節點
當前提交
e23d4317eb

+ 1
- 0
lang/main.json 查看文件

49
         "appDescription": "Go ahead, video chat with the whole team. In fact, invite everyone you know. __app__ is a fully encrypted, 100% open source video conferencing solution that you can use all day, every day, for free — with no account needed.",
49
         "appDescription": "Go ahead, video chat with the whole team. In fact, invite everyone you know. __app__ is a fully encrypted, 100% open source video conferencing solution that you can use all day, every day, for free — with no account needed.",
50
         "audioOnlyLabel": "Voice",
50
         "audioOnlyLabel": "Voice",
51
         "go": "GO",
51
         "go": "GO",
52
+        "hintText": "Enter a room name you want to join to, or simply create a new room name, eg. MeetingWithJohn",
52
         "join": "JOIN",
53
         "join": "JOIN",
53
         "privacy": "Privacy",
54
         "privacy": "Privacy",
54
         "roomname": "Enter room name",
55
         "roomname": "Enter room name",

+ 11
- 2
react/features/base/react/components/native/LoadingIndicator.js 查看文件

5
 
5
 
6
 import { ColorPalette } from '../../../styles';
6
 import { ColorPalette } from '../../../styles';
7
 
7
 
8
+type Props = {
9
+
10
+    /**
11
+     * Prop to set the size of the indicator. This is the same as the
12
+     * prop of the native component.
13
+     */
14
+    size: 'large' | 'small'
15
+};
16
+
8
 /**
17
 /**
9
  * An animated, large react-native {@link ActivityIndicator} which is considered
18
  * An animated, large react-native {@link ActivityIndicator} which is considered
10
  * a suitable visualization of long-running processes with indeterminate amounts
19
  * a suitable visualization of long-running processes with indeterminate amounts
11
  * of work to be done.
20
  * of work to be done.
12
  */
21
  */
13
-export default class LoadingIndicator extends Component<*> {
22
+export default class LoadingIndicator extends Component<Props> {
14
     /**
23
     /**
15
      * Implements React's {@link Component#render()}.
24
      * Implements React's {@link Component#render()}.
16
      *
25
      *
22
             <ActivityIndicator
31
             <ActivityIndicator
23
                 animating = { true }
32
                 animating = { true }
24
                 color = { ColorPalette.white }
33
                 color = { ColorPalette.white }
25
-                size = { 'large' }
34
+                size = { this.props.size || 'large' }
26
                 { ...this.props } />
35
                 { ...this.props } />
27
         );
36
         );
28
     }
37
     }

+ 8
- 1
react/features/recent-list/components/AbstractRecentList.js 查看文件

9
  */
9
  */
10
 type Props = {
10
 type Props = {
11
 
11
 
12
+    /**
13
+     * Indicates if the list is disabled or not.
14
+     */
15
+    disabled: boolean,
16
+
12
     /**
17
     /**
13
      * The redux store's {@code dispatch} function.
18
      * The redux store's {@code dispatch} function.
14
      */
19
      */
31
      * @returns {void}
36
      * @returns {void}
32
      */
37
      */
33
     _onJoin(room) {
38
     _onJoin(room) {
34
-        room && this.props.dispatch(appNavigate(room));
39
+        const { disabled, dispatch } = this.props;
40
+
41
+        !disabled && room && dispatch(appNavigate(room));
35
     }
42
     }
36
 
43
 
37
     /**
44
     /**

+ 11
- 5
react/features/recent-list/components/RecentList.native.js 查看文件

28
      *
28
      *
29
      * @inheritdoc
29
      * @inheritdoc
30
      */
30
      */
31
-    constructor() {
32
-        super();
31
+    constructor(props) {
32
+        super(props);
33
 
33
 
34
         // Bind event handlers so they are only bound once per instance.
34
         // Bind event handlers so they are only bound once per instance.
35
         this._getAvatarStyle = this._getAvatarStyle.bind(this);
35
         this._getAvatarStyle = this._getAvatarStyle.bind(this);
47
      * @returns {ReactElement}
47
      * @returns {ReactElement}
48
      */
48
      */
49
     render() {
49
     render() {
50
-        if (!this.props || !this.props._recentList) {
50
+        const { _recentList, disabled } = this.props;
51
+
52
+        if (!_recentList) {
51
             return null;
53
             return null;
52
         }
54
         }
53
 
55
 
54
         const listViewDataSource
56
         const listViewDataSource
55
             = this.dataSource.cloneWithRows(
57
             = this.dataSource.cloneWithRows(
56
-                getRecentRooms(this.props._recentList));
58
+                getRecentRooms(_recentList));
57
 
59
 
58
         return (
60
         return (
59
-            <View style = { styles.container }>
61
+            <View
62
+                style = { [
63
+                    styles.container,
64
+                    disabled ? styles.containerDisabled : null
65
+                ] }>
60
                 <ListView
66
                 <ListView
61
                     dataSource = { listViewDataSource }
67
                     dataSource = { listViewDataSource }
62
                     enableEmptySections = { true }
68
                     enableEmptySections = { true }

+ 7
- 0
react/features/recent-list/components/styles.js 查看文件

87
         flex: 1
87
         flex: 1
88
     },
88
     },
89
 
89
 
90
+    /**
91
+     * Shows the container disabled.
92
+     */
93
+    containerDisabled: {
94
+        opacity: 0.2
95
+    },
96
+
90
     /**
97
     /**
91
      * Second line of the list (date). May be extended with server name later.
98
      * Second line of the list (date). May be extended with server name later.
92
      */
99
      */

+ 5
- 0
react/features/welcome/components/AbstractWelcomePage.js 查看文件

14
  */
14
  */
15
 type Props = {
15
 type Props = {
16
 
16
 
17
+    /**
18
+     * Boolean to indicate if the room field is focused or not.
19
+     */
20
+    _fieldFocused: boolean,
21
+
17
     /**
22
     /**
18
      * The user's profile.
23
      * The user's profile.
19
      */
24
      */

+ 94
- 5
react/features/welcome/components/WelcomePage.native.js 查看文件

1
 import React from 'react';
1
 import React from 'react';
2
 import {
2
 import {
3
+    Animated,
3
     Keyboard,
4
     Keyboard,
4
     SafeAreaView,
5
     SafeAreaView,
5
     Switch,
6
     Switch,
59
     constructor(props) {
60
     constructor(props) {
60
         super(props);
61
         super(props);
61
 
62
 
63
+        this.state.hintBoxAnimation = new Animated.Value(0);
64
+
65
+        this._getHintBoxStyle = this._getHintBoxStyle.bind(this);
66
+        this._onFieldFocusChange = this._onFieldFocusChange.bind(this);
62
         this._onShowSideBar = this._onShowSideBar.bind(this);
67
         this._onShowSideBar = this._onShowSideBar.bind(this);
63
         this._onStartAudioOnlyChange = this._onStartAudioOnlyChange.bind(this);
68
         this._onStartAudioOnlyChange = this._onStartAudioOnlyChange.bind(this);
69
+        this._renderHintBox = this._renderHintBox.bind(this);
64
     }
70
     }
65
 
71
 
66
     /**
72
     /**
124
                             autoComplete = { false }
130
                             autoComplete = { false }
125
                             autoCorrect = { false }
131
                             autoCorrect = { false }
126
                             autoFocus = { false }
132
                             autoFocus = { false }
133
+                            onBlur = { this._onFieldFocusChange(false) }
127
                             onChangeText = { this._onRoomChange }
134
                             onChangeText = { this._onRoomChange }
135
+                            onFocus = { this._onFieldFocusChange(true) }
128
                             onSubmitEditing = { this._onJoin }
136
                             onSubmitEditing = { this._onJoin }
129
                             placeholder = { t('welcomepage.roomname') }
137
                             placeholder = { t('welcomepage.roomname') }
130
                             placeholderTextColor = { PLACEHOLDER_TEXT_COLOR }
138
                             placeholderTextColor = { PLACEHOLDER_TEXT_COLOR }
133
                             underlineColorAndroid = 'transparent'
141
                             underlineColorAndroid = 'transparent'
134
                             value = { this.state.room } />
142
                             value = { this.state.room } />
135
                         {
143
                         {
136
-                            this._renderJoinButton()
144
+                            this._renderHintBox()
137
                         }
145
                         }
138
-                        <RecentList />
146
+                        <RecentList disabled = { this.state._fieldFocused } />
139
                     </SafeAreaView>
147
                     </SafeAreaView>
140
                     <AppSettings />
148
                     <AppSettings />
141
                 </View>
149
                 </View>
144
         );
152
         );
145
     }
153
     }
146
 
154
 
155
+    /**
156
+     * Constructs a style array to handle the hint box animation.
157
+     *
158
+     * @private
159
+     * @returns {Array<Object>}
160
+     */
161
+    _getHintBoxStyle() {
162
+        return [
163
+            styles.hintContainer,
164
+            {
165
+                opacity: this.state.hintBoxAnimation
166
+            }
167
+        ];
168
+    }
169
+
170
+    /**
171
+     * Callback for when the room field's focus changes so the hint box
172
+     * must be rendered or removed.
173
+     *
174
+     * @private
175
+     * @param {boolean} focused - The focused state of the field.
176
+     * @returns {Function}
177
+     */
178
+    _onFieldFocusChange(focused) {
179
+        return () => {
180
+            if (focused) {
181
+                this.setState({
182
+                    _fieldFocused: true
183
+                });
184
+            }
185
+
186
+            Animated.timing(this.state.hintBoxAnimation, {
187
+                duration: 300,
188
+                toValue: focused ? 1 : 0
189
+            }).start(animationState => {
190
+                if (animationState.finished && !focused) {
191
+                    this.setState({
192
+                        _fieldFocused: false
193
+                    });
194
+                }
195
+            });
196
+        };
197
+    }
198
+
147
     /**
199
     /**
148
      * Toggles the side bar.
200
      * Toggles the side bar.
149
      *
201
      *
171
         }));
223
         }));
172
     }
224
     }
173
 
225
 
226
+    /**
227
+     * Renders the hint box if necessary.
228
+     *
229
+     * @private
230
+     * @returns {React$Node}
231
+     */
232
+    _renderHintBox() {
233
+        if (this.state._fieldFocused) {
234
+            const { t } = this.props;
235
+
236
+            return (
237
+                <Animated.View
238
+                    style = { this._getHintBoxStyle() }>
239
+                    <View style = { styles.hintTextContainer } >
240
+                        <Text>
241
+                            { t('welcomepage.hintText') }
242
+                        </Text>
243
+                    </View>
244
+                    <View style = { styles.hintButtonContainer } >
245
+                        {
246
+                            this._renderJoinButton()
247
+                        }
248
+                    </View>
249
+                </Animated.View>
250
+            );
251
+        }
252
+
253
+        return null;
254
+    }
255
+
174
     /**
256
     /**
175
      * Renders the join button.
257
      * Renders the join button.
176
      *
258
      *
188
             // modify non-native children.
270
             // modify non-native children.
189
             children = (
271
             children = (
190
                 <View>
272
                 <View>
191
-                    <LoadingIndicator color = { styles.buttonText.color } />
273
+                    <LoadingIndicator
274
+                        color = { styles.buttonText.color }
275
+                        size = 'small' />
192
                 </View>
276
                 </View>
193
             );
277
             );
194
         } else {
278
         } else {
201
 
285
 
202
         /* eslint-enable no-extra-parens */
286
         /* eslint-enable no-extra-parens */
203
 
287
 
288
+        const buttonDisabled = this._isJoinDisabled();
289
+
204
         return (
290
         return (
205
             <TouchableHighlight
291
             <TouchableHighlight
206
                 accessibilityLabel = { 'Tap to Join.' }
292
                 accessibilityLabel = { 'Tap to Join.' }
207
-                disabled = { this._isJoinDisabled() }
293
+                disabled = { buttonDisabled }
208
                 onPress = { this._onJoin }
294
                 onPress = { this._onJoin }
209
-                style = { styles.button }
295
+                style = { [
296
+                    styles.button,
297
+                    buttonDisabled ? styles.buttonDisabled : null
298
+                ] }
210
                 underlayColor = { ColorPalette.white }>
299
                 underlayColor = { ColorPalette.white }>
211
                 {
300
                 {
212
                     children
301
                     children

+ 41
- 4
react/features/welcome/components/styles.js 查看文件

54
         borderColor: ColorPalette.blue,
54
         borderColor: ColorPalette.blue,
55
         borderRadius: 4,
55
         borderRadius: 4,
56
         borderWidth: 1,
56
         borderWidth: 1,
57
-        height: 40,
57
+        height: 30,
58
         justifyContent: 'center',
58
         justifyContent: 'center',
59
-        marginBottom: BoxModel.margin,
60
-        marginTop: BoxModel.margin
59
+        paddingHorizontal: 20
60
+    },
61
+
62
+    /**
63
+     * Renders the button visually disabled.
64
+     */
65
+    buttonDisabled: {
66
+        backgroundColor: '#cccccc',
67
+        borderColor: '#999999'
61
     },
68
     },
62
 
69
 
63
     /**
70
     /**
66
     buttonText: {
73
     buttonText: {
67
         alignSelf: 'center',
74
         alignSelf: 'center',
68
         color: ColorPalette.white,
75
         color: ColorPalette.white,
69
-        fontSize: 18
76
+        fontSize: 14
70
     },
77
     },
71
 
78
 
72
     /**
79
     /**
86
         justifyContent: 'space-between'
93
         justifyContent: 'space-between'
87
     },
94
     },
88
 
95
 
96
+    /**
97
+     * Container for the button on the hint box.
98
+     */
99
+    hintButtonContainer: {
100
+        flexDirection: 'row',
101
+        justifyContent: 'flex-end'
102
+    },
103
+
104
+    /**
105
+     * Container for the text on the hint box.
106
+     */
107
+    hintTextContainer: {
108
+        marginBottom: 2 * BoxModel.margin
109
+    },
110
+
111
+    /**
112
+     * Container for the hint box.
113
+     */
114
+    hintContainer: {
115
+        backgroundColor: ColorPalette.white,
116
+        borderColor: ColorPalette.white,
117
+        borderRadius: 4,
118
+        borderWidth: 1,
119
+        flexDirection: 'column',
120
+        marginVertical: 5,
121
+        overflow: 'hidden',
122
+        paddingHorizontal: BoxModel.padding,
123
+        paddingVertical: 2 * BoxModel.padding
124
+    },
125
+
89
     /**
126
     /**
90
      * Container for the items in the side bar.
127
      * Container for the items in the side bar.
91
      */
128
      */

Loading…
取消
儲存