浏览代码

[Android] Hardware back in Conference and Dialog

j8
Lyubo Marinov 8 年前
父节点
当前提交
03d337612b

+ 44
- 20
react/features/base/dialog/components/Dialog.native.js 查看文件

1
 import PropTypes from 'prop-types';
1
 import PropTypes from 'prop-types';
2
 import React from 'react';
2
 import React from 'react';
3
-import { StyleSheet, TextInput } from 'react-native';
3
+import { Modal, StyleSheet, TextInput } from 'react-native';
4
 import Prompt from 'react-native-prompt';
4
 import Prompt from 'react-native-prompt';
5
 import { connect } from 'react-redux';
5
 import { connect } from 'react-redux';
6
 
6
 
99
 
99
 
100
         // eslint-disable-next-line no-shadow
100
         // eslint-disable-next-line no-shadow
101
         element = this._mapReactElement(element, element => {
101
         element = this._mapReactElement(element, element => {
102
-            // * If this Dialog has children, they are to be rendered instead of
103
-            //   Prompt's TextInput.
104
-            if (element.type === TextInput) {
102
+            const { type } = element;
103
+
104
+            if (type === Modal) {
105
+                // * Modal handles hardware button presses for back navigation.
106
+                //   Firstly, we don't want Prompt's default behavior to merely
107
+                //   hide the Modal - we want this Dialog to be canceled.
108
+                //   Secondly, we cannot get Prompt's default behavior anyway
109
+                //   because we've removed Prompt and we're preserving whatever
110
+                //   it's rendered only.
111
+                return (
112
+                    React.cloneElement(
113
+                        element,
114
+                        /* props */ {
115
+                            onRequestClose: this._onCancel
116
+                        },
117
+                        ...React.Children.toArray(element.props.children))
118
+                );
119
+            }
120
+
121
+            if (type === TextInput) {
122
+                // * If this Dialog has children, they are to be rendered
123
+                //   instead of Prompt's TextInput.
105
                 if (children) {
124
                 if (children) {
106
                     element = children; // eslint-disable-line no-param-reassign
125
                     element = children; // eslint-disable-line no-param-reassign
107
                     children = undefined;
126
                     children = undefined;
108
                 }
127
                 }
128
+
109
             } else {
129
             } else {
110
                 let { style } = element.props;
130
                 let { style } = element.props;
111
 
131
 
126
                         break;
146
                         break;
127
                     }
147
                     }
128
 
148
 
129
-                    // eslint-disable-next-line no-param-reassign
130
-                    element
131
-                        = React.cloneElement(
149
+                    return (
150
+                        React.cloneElement(
132
                             element,
151
                             element,
133
                             /* props */ {
152
                             /* props */ {
134
                                 style: set(style, _TAG_KEY, undefined)
153
                                 style: set(style, _TAG_KEY, undefined)
135
                             },
154
                             },
136
-                            ...React.Children.toArray(element.props.children));
155
+                            ...React.Children.toArray(element.props.children))
156
+                    );
137
                 }
157
                 }
138
             }
158
             }
139
 
159
 
162
 
182
 
163
         let mapped = f(element);
183
         let mapped = f(element);
164
 
184
 
165
-        if (mapped === element) {
166
-            mapped
167
-                = React.cloneElement(
168
-                    element,
169
-                    /* props */ undefined,
170
-                    ...React.Children.toArray(React.Children.map(
171
-                        element.props.children,
172
-                        function(element) { // eslint-disable-line no-shadow
173
-                            // eslint-disable-next-line no-invalid-this
174
-                            return this._mapReactElement(element, f);
175
-                        },
176
-                        this)));
185
+        if (mapped) {
186
+            const { children } = mapped.props;
187
+
188
+            if (mapped === element || React.Children.count(children)) {
189
+                mapped
190
+                    = React.cloneElement(
191
+                        mapped,
192
+                        /* props */ undefined,
193
+                        ...React.Children.toArray(React.Children.map(
194
+                            children,
195
+                            function(element) { // eslint-disable-line no-shadow
196
+                                // eslint-disable-next-line no-invalid-this
197
+                                return this._mapReactElement(element, f);
198
+                            },
199
+                            this)));
200
+            }
177
         }
201
         }
178
 
202
 
179
         return mapped;
203
         return mapped;

+ 71
- 7
react/features/conference/components/Conference.native.js 查看文件

1
 import PropTypes from 'prop-types';
1
 import PropTypes from 'prop-types';
2
 import React, { Component } from 'react';
2
 import React, { Component } from 'react';
3
-import { View } from 'react-native';
3
+
4
+// eslint-disable-next-line react-native/split-platform-components
5
+import { BackAndroid, BackHandler, View } from 'react-native';
4
 import { connect as reactReduxConnect } from 'react-redux';
6
 import { connect as reactReduxConnect } from 'react-redux';
5
 
7
 
8
+import { appNavigate } from '../../app';
6
 import { connect, disconnect } from '../../base/connection';
9
 import { connect, disconnect } from '../../base/connection';
7
 import { DialogContainer } from '../../base/dialog';
10
 import { DialogContainer } from '../../base/dialog';
8
 import { Container, LoadingIndicator } from '../../base/react';
11
 import { Container, LoadingIndicator } from '../../base/react';
56
          */
59
          */
57
         _onDisconnect: PropTypes.func,
60
         _onDisconnect: PropTypes.func,
58
 
61
 
62
+        /**
63
+         * Handles a hardware button press for back navigation. Leaves the
64
+         * associated <tt>Conference</tt>.
65
+         *
66
+         * @private
67
+         * @returns {boolean} As the associated conference is unconditionally
68
+         * left and exiting the app while it renders a <tt>Conference</tt> is
69
+         * undesired, <tt>true</tt> is always returned.
70
+         */
71
+        _onHardwareBackPress: PropTypes.func,
72
+
59
         /**
73
         /**
60
          * The handler which dispatches the (redux) action setToolboxVisible to
74
          * The handler which dispatches the (redux) action setToolboxVisible to
61
          * show/hide the Toolbox.
75
          * show/hide the Toolbox.
90
          */
104
          */
91
         this._toolboxTimeout = undefined;
105
         this._toolboxTimeout = undefined;
92
 
106
 
93
-        // Bind event handlers so they are only bound once for every instance.
107
+        // Bind event handlers so they are only bound once per instance.
94
         this._onClick = this._onClick.bind(this);
108
         this._onClick = this._onClick.bind(this);
109
+        this._onHardwareBackPress = this._onHardwareBackPress.bind(this);
95
     }
110
     }
96
 
111
 
97
     /**
112
     /**
98
-     * Inits the Toolbox timeout after the component is initially rendered.
113
+     * Implements {@link Component#componentDidMount()}. Invoked immediately
114
+     * after this component is mounted.
99
      *
115
      *
100
      * @inheritdoc
116
      * @inheritdoc
101
      * returns {void}
117
      * returns {void}
102
      */
118
      */
103
     componentDidMount() {
119
     componentDidMount() {
120
+        // Set handling any hardware button presses for back navigation up.
121
+        const backHandler = BackHandler || BackAndroid;
122
+
123
+        if (backHandler) {
124
+            this._backHandler = backHandler;
125
+            backHandler.addEventListener(
126
+                'hardwareBackPress',
127
+                this._onHardwareBackPress);
128
+        }
129
+
104
         this._setToolboxTimeout(this.props._toolboxVisible);
130
         this._setToolboxTimeout(this.props._toolboxVisible);
105
     }
131
     }
106
 
132
 
107
     /**
133
     /**
108
-     * Inits new connection and conference when conference screen is entered.
134
+     * Implements {@link Component#componentWillMount()}. Invoked immediately
135
+     * before mounting occurs. Connects the conference described by the redux
136
+     * store/state.
109
      *
137
      *
110
      * @inheritdoc
138
      * @inheritdoc
111
      * @returns {void}
139
      * @returns {void}
115
     }
143
     }
116
 
144
 
117
     /**
145
     /**
118
-     * Destroys connection, conference and local tracks when conference screen
119
-     * is left. Clears {@link #_toolboxTimeout} before the component unmounts.
146
+     * Implements {@link Component#componentWillUnmount()}. Invoked immediately
147
+     * before this component is unmounted and destroyed. Disconnects the
148
+     * conference described by the redux store/state.
120
      *
149
      *
121
      * @inheritdoc
150
      * @inheritdoc
122
      * @returns {void}
151
      * @returns {void}
123
      */
152
      */
124
     componentWillUnmount() {
153
     componentWillUnmount() {
154
+        // Tear handling any hardware button presses for back navigation down.
155
+        const backHandler = this._backHandler;
156
+
157
+        if (backHandler) {
158
+            this._backHandler = undefined;
159
+            backHandler.removeEventListener(
160
+                'hardwareBackPress',
161
+                this._onHardwareBackPress);
162
+        }
163
+
125
         this._clearToolboxTimeout();
164
         this._clearToolboxTimeout();
126
 
165
 
127
         this.props._onDisconnect();
166
         this.props._onDisconnect();
210
         this._setToolboxTimeout(toolboxVisible);
249
         this._setToolboxTimeout(toolboxVisible);
211
     }
250
     }
212
 
251
 
252
+    /**
253
+     * Handles a hardware button press for back navigation.
254
+     *
255
+     * @returns {boolean} If the hardware button press for back navigation was
256
+     * handled by this <tt>Conference</tt>, then <tt>true</tt>; otherwise,
257
+     * <tt>false</tt>.
258
+     */
259
+    _onHardwareBackPress() {
260
+        return this._backHandler && this.props._onHardwareBackPress();
261
+    }
262
+
213
     /**
263
     /**
214
      * Triggers the default Toolbox timeout.
264
      * Triggers the default Toolbox timeout.
215
      *
265
      *
263
         },
313
         },
264
 
314
 
265
         /**
315
         /**
266
-         * Dispatches an action changing the visiblity of the Toolbox.
316
+         * Handles a hardware button press for back navigation. Leaves the
317
+         * associated <tt>Conference</tt>.
318
+         *
319
+         * @returns {boolean} As the associated conference is unconditionally
320
+         * left and exiting the app while it renders a <tt>Conference</tt> is
321
+         * undesired, <tt>true</tt> is always returned.
322
+         */
323
+        _onHardwareBackPress() {
324
+            dispatch(appNavigate(undefined));
325
+
326
+            return true;
327
+        },
328
+
329
+        /**
330
+         * Dispatches an action changing the visibility of the Toolbox.
267
          *
331
          *
268
          * @param {boolean} visible - True to show the Toolbox or false to hide
332
          * @param {boolean} visible - True to show the Toolbox or false to hide
269
          * it.
333
          * it.

正在加载...
取消
保存