浏览代码

Comply w/ coding style

j8
Lyubomir Marinov 8 年前
父节点
当前提交
538af01bf5

+ 9
- 9
conference.js 查看文件

21
 import EventEmitter from "events";
21
 import EventEmitter from "events";
22
 
22
 
23
 import { conferenceFailed } from './react/features/base/conference';
23
 import { conferenceFailed } from './react/features/base/conference';
24
+import {
25
+    isFatalJitsiConnectionError
26
+} from './react/features/base/lib-jitsi-meet';
24
 import {
27
 import {
25
     mediaPermissionPromptVisibilityChanged,
28
     mediaPermissionPromptVisibilityChanged,
26
     suspendDetected
29
     suspendDetected
475
 
478
 
476
 /**
479
 /**
477
  * Handles CONNECTION_FAILED events from lib-jitsi-meet.
480
  * Handles CONNECTION_FAILED events from lib-jitsi-meet.
478
- * @param {JitsiMeetJS.connection.error} error the error reported.
481
+ *
482
+ * @param {JitsiMeetJS.connection.error} error - The reported error.
479
  * @returns {void}
483
  * @returns {void}
480
  * @private
484
  * @private
481
  */
485
  */
482
-function _connectionFailedHandler (error) {
483
-    switch (error) {
484
-    case ConnectionErrors.CONNECTION_DROPPED_ERROR:
485
-    case ConnectionErrors.OTHER_ERROR:
486
-    case ConnectionErrors.SERVER_ERROR: {
487
-        APP.connection.removeEventListener( ConnectionEvents.CONNECTION_FAILED,
486
+function _connectionFailedHandler(error) {
487
+    if (isFatalJitsiConnectionError(error)) {
488
+        APP.connection.removeEventListener(
489
+            ConnectionEvents.CONNECTION_FAILED,
488
             _connectionFailedHandler);
490
             _connectionFailedHandler);
489
         if (room)
491
         if (room)
490
             room.leave();
492
             room.leave();
491
-        break;
492
-    }
493
     }
493
     }
494
 }
494
 }
495
 
495
 

+ 5
- 7
connection.js 查看文件

8
     connectionEstablished,
8
     connectionEstablished,
9
     connectionFailed
9
     connectionFailed
10
 } from './react/features/base/connection';
10
 } from './react/features/base/connection';
11
+import {
12
+    isFatalJitsiConnectionError
13
+} from './react/features/base/lib-jitsi-meet';
11
 
14
 
12
 const ConnectionEvents = JitsiMeetJS.events.connection;
15
 const ConnectionEvents = JitsiMeetJS.events.connection;
13
 const ConnectionErrors = JitsiMeetJS.errors.connection;
16
 const ConnectionErrors = JitsiMeetJS.errors.connection;
75
         connection.addEventListener(
78
         connection.addEventListener(
76
             ConnectionEvents.CONNECTION_FAILED, connectionFailedHandler);
79
             ConnectionEvents.CONNECTION_FAILED, connectionFailedHandler);
77
 
80
 
78
-        function connectionFailedHandler (error, errMsg) {
81
+        function connectionFailedHandler(error, errMsg) {
79
             APP.store.dispatch(connectionFailed(connection, error, errMsg));
82
             APP.store.dispatch(connectionFailed(connection, error, errMsg));
80
 
83
 
81
-            switch (error) {
82
-            case ConnectionErrors.CONNECTION_DROPPED_ERROR:
83
-            case ConnectionErrors.OTHER_ERROR:
84
-            case ConnectionErrors.SERVER_ERROR: {
84
+            if (isFatalJitsiConnectionError(error)) {
85
                 connection.removeEventListener(
85
                 connection.removeEventListener(
86
                     ConnectionEvents.CONNECTION_FAILED,
86
                     ConnectionEvents.CONNECTION_FAILED,
87
                     connectionFailedHandler);
87
                     connectionFailedHandler);
88
-                break;
89
-            }
90
             }
88
             }
91
         }
89
         }
92
 
90
 

+ 2
- 1
react/features/app/functions.web.js 查看文件

1
-/* global APP, JitsiMeetJS, loggingConfig */
1
+/* global APP, loggingConfig */
2
 
2
 
3
 import { isRoomValid } from '../base/conference';
3
 import { isRoomValid } from '../base/conference';
4
+import JitsiMeetJS from '../base/lib-jitsi-meet';
4
 import { RouteRegistry } from '../base/react';
5
 import { RouteRegistry } from '../base/react';
5
 import { interceptComponent } from '../base/util';
6
 import { interceptComponent } from '../base/util';
6
 import { Conference } from '../conference';
7
 import { Conference } from '../conference';

+ 27
- 1
react/features/base/lib-jitsi-meet/functions.js 查看文件

1
 import { loadScript } from '../../base/util';
1
 import { loadScript } from '../../base/util';
2
 
2
 
3
+import JitsiMeetJS from './_';
4
+
5
+declare var APP: Object;
6
+
7
+const JitsiConnectionErrors = JitsiMeetJS.errors.connection;
8
+
9
+/**
10
+ * Determines whether a specific JitsiConnectionErrors instance indicates a
11
+ * fatal JitsiConnection error.
12
+ *
13
+ * FIXME Figure out the category of errors defined by the fucntion and describe
14
+ * that category. I've currently named the category fatal because it appears to
15
+ * be used in the cases of unrecoverable errors that necessitate a reload.
16
+ *
17
+ * @param {string} error - The JitsiConnectionErrors instance to
18
+ * categorize/classify.
19
+ * @returns {boolean} True if the specified JitsiConnectionErrors instance
20
+ * indicates a fatal JitsiConnection error; otherwise, false.
21
+ */
22
+export function isFatalJitsiConnectionError(error: string) {
23
+    return (
24
+        error === JitsiConnectionErrors.CONNECTION_DROPPED_ERROR
25
+            || error === JitsiConnectionErrors.OTHER_ERROR
26
+            || error === JitsiConnectionErrors.SERVER_ERROR);
27
+}
28
+
3
 /**
29
 /**
4
  * Loads config.js file from remote server.
30
  * Loads config.js file from remote server.
5
  *
31
  *
7
  * @param {string} path='/config.js' - Relative pah to config.js file.
33
  * @param {string} path='/config.js' - Relative pah to config.js file.
8
  * @returns {Promise<Object>}
34
  * @returns {Promise<Object>}
9
  */
35
  */
10
-export function loadConfig(host, path = '/config.js') {
36
+export function loadConfig(host: string, path: string = '/config.js') {
11
     // Returns config.js file from global scope. We can't use the version that's
37
     // Returns config.js file from global scope. We can't use the version that's
12
     // being used for the React Native app because the old/current Web app uses
38
     // being used for the React Native app because the old/current Web app uses
13
     // config from the global scope.
39
     // config from the global scope.

+ 1
- 0
react/features/base/util/index.js 查看文件

1
 export * from './interceptComponent';
1
 export * from './interceptComponent';
2
 export * from './loadScript';
2
 export * from './loadScript';
3
+export * from './randomUtil';
3
 export * from './roomnameGenerator';
4
 export * from './roomnameGenerator';

+ 1
- 1
react/features/conference/components/Conference.web.js 查看文件

6
 import { connect, disconnect } from '../../base/connection';
6
 import { connect, disconnect } from '../../base/connection';
7
 import { Watermarks } from '../../base/react';
7
 import { Watermarks } from '../../base/react';
8
 import { FeedbackButton } from '../../feedback';
8
 import { FeedbackButton } from '../../feedback';
9
-
10
 import { OverlayContainer } from '../../overlay';
9
 import { OverlayContainer } from '../../overlay';
11
 
10
 
12
 /**
11
 /**
164
                         </div>
163
                         </div>
165
                     </div>
164
                     </div>
166
                 </div>
165
                 </div>
166
+
167
                 <OverlayContainer />
167
                 <OverlayContainer />
168
             </div>
168
             </div>
169
         );
169
         );

+ 10
- 10
react/features/overlay/actionTypes.js 查看文件

1
 import { Symbol } from '../base/react';
1
 import { Symbol } from '../base/react';
2
 
2
 
3
-/**
4
- * The type of the Redux action which signals that a suspend was detected.
5
- *
6
- * {
7
- *     type: SUSPEND_DETECTED
8
- * }
9
- * @public
10
- */
11
-export const SUSPEND_DETECTED = Symbol('SUSPEND_DETECTED');
12
-
13
 /**
3
 /**
14
  * The type of the Redux action which signals that the prompt for media
4
  * The type of the Redux action which signals that the prompt for media
15
  * permission is visible or not.
5
  * permission is visible or not.
23
  */
13
  */
24
 export const MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED
14
 export const MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED
25
     = Symbol('MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED');
15
     = Symbol('MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED');
16
+
17
+/**
18
+ * The type of the Redux action which signals that a suspend was detected.
19
+ *
20
+ * {
21
+ *     type: SUSPEND_DETECTED
22
+ * }
23
+ * @public
24
+ */
25
+export const SUSPEND_DETECTED = Symbol('SUSPEND_DETECTED');

+ 17
- 18
react/features/overlay/actions.js 查看文件

1
 import {
1
 import {
2
-    SUSPEND_DETECTED,
3
-    MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED
2
+    MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED,
3
+    SUSPEND_DETECTED
4
 } from './actionTypes';
4
 } from './actionTypes';
5
-import './reducer';
6
 
5
 
7
 /**
6
 /**
8
- * Signals that suspend was detected.
7
+ * Signals that the prompt for media permission is visible or not.
9
  *
8
  *
9
+ * @param {boolean} isVisible - If the value is true - the prompt for media
10
+ * permission is visible otherwise the value is false/undefined.
11
+ * @param {string} browser - The name of the current browser.
10
  * @returns {{
12
  * @returns {{
11
- *     type: SUSPEND_DETECTED
13
+ *     type: MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED,
14
+ *     browser: {string},
15
+ *     isVisible: {boolean}
12
  * }}
16
  * }}
13
  * @public
17
  * @public
14
  */
18
  */
15
-export function suspendDetected() {
19
+export function mediaPermissionPromptVisibilityChanged(isVisible, browser) {
16
     return {
20
     return {
17
-        type: SUSPEND_DETECTED
21
+        type: MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED,
22
+        browser,
23
+        isVisible
18
     };
24
     };
19
 }
25
 }
20
 
26
 
21
 /**
27
 /**
22
- * Signals that the prompt for media permission is visible or not.
28
+ * Signals that suspend was detected.
23
  *
29
  *
24
- * @param {boolean} isVisible - If the value is true - the prompt for media
25
- * permission is visible otherwise the value is false/undefined.
26
- * @param {string} browser - The name of the current browser.
27
  * @returns {{
30
  * @returns {{
28
- *     type: MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED,
29
- *     isVisible: {boolean},
30
- *     browser: {string}
31
+ *     type: SUSPEND_DETECTED
31
  * }}
32
  * }}
32
  * @public
33
  * @public
33
  */
34
  */
34
-export function mediaPermissionPromptVisibilityChanged(isVisible, browser) {
35
+export function suspendDetected() {
35
     return {
36
     return {
36
-        type: MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED,
37
-        isVisible,
38
-        browser
37
+        type: SUSPEND_DETECTED
39
     };
38
     };
40
 }
39
 }

+ 35
- 30
react/features/overlay/components/AbstractOverlay.js 查看文件

3
 import React, { Component } from 'react';
3
 import React, { Component } from 'react';
4
 
4
 
5
 /**
5
 /**
6
- * Implements an abstract React Component for overlay - the components which
7
- * are displayed on top of the application covering the whole screen.
6
+ * Implements an abstract React Component for overlay - the components which are
7
+ * displayed on top of the application covering the whole screen.
8
  *
8
  *
9
  * @abstract
9
  * @abstract
10
  */
10
  */
21
 
21
 
22
         this.state = {
22
         this.state = {
23
             /**
23
             /**
24
-             * Indicates the css style of the overlay. if true - lighter  and
25
-             * darker otherwise.
24
+             * Indicates the CSS style of the overlay. If true, then ighter;
25
+             * darker, otherwise.
26
+             *
26
              * @type {boolean}
27
              * @type {boolean}
27
              */
28
              */
28
             isLightOverlay: false
29
             isLightOverlay: false
29
         };
30
         };
30
     }
31
     }
31
 
32
 
32
-    /**
33
-     * Abstract method which should be used by subclasses to provide the overlay
34
-     * content.
35
-     *
36
-     * @returns {ReactElement|null}
37
-     * @protected
38
-     */
39
-    _renderOverlayContent() {
40
-        return null;
41
-    }
42
-
43
     /**
33
     /**
44
      * This method is executed when comonent is mounted.
34
      * This method is executed when comonent is mounted.
45
      *
35
      *
51
         APP.translation.translateElement($('#overlay'));
41
         APP.translation.translateElement($('#overlay'));
52
     }
42
     }
53
 
43
 
54
-    /**
55
-     * Reloads the page.
56
-     *
57
-     * @returns {void}
58
-     * @protected
59
-     */
60
-    _reconnectNow() {
61
-        // FIXME: In future we should dispatch an action here that will result
62
-        // in reload.
63
-        APP.ConferenceUrl.reload();
64
-    }
65
-
66
     /**
44
     /**
67
      * Implements React's {@link Component#render()}.
45
      * Implements React's {@link Component#render()}.
68
      *
46
      *
70
      * @returns {ReactElement|null}
48
      * @returns {ReactElement|null}
71
      */
49
      */
72
     render() {
50
     render() {
73
-        const containerClass = this.state.isLightOverlay
74
-            ? 'overlay__container-light' : 'overlay__container';
51
+        const containerClass
52
+            = this.state.isLightOverlay
53
+                ? 'overlay__container-light'
54
+                : 'overlay__container';
75
 
55
 
76
         return (
56
         return (
77
             <div
57
             <div
78
                 className = { containerClass }
58
                 className = { containerClass }
79
                 id = 'overlay'>
59
                 id = 'overlay'>
80
                 <div className = 'overlay__content'>
60
                 <div className = 'overlay__content'>
81
-                    { this._renderOverlayContent() }
61
+                    {
62
+                        this._renderOverlayContent()
63
+                    }
82
                 </div>
64
                 </div>
83
             </div>
65
             </div>
84
         );
66
         );
85
     }
67
     }
68
+
69
+    /**
70
+     * Reloads the page.
71
+     *
72
+     * @returns {void}
73
+     * @protected
74
+     */
75
+    _reconnectNow() {
76
+        // FIXME: In future we should dispatch an action here that will result
77
+        // in reload.
78
+        APP.ConferenceUrl.reload();
79
+    }
80
+
81
+    /**
82
+     * Abstract method which should be used by subclasses to provide the overlay
83
+     * content.
84
+     *
85
+     * @returns {ReactElement|null}
86
+     * @protected
87
+     */
88
+    _renderOverlayContent() {
89
+        return null;
90
+    }
86
 }
91
 }

+ 56
- 27
react/features/overlay/components/OverlayContainer.js 查看文件

20
     static propTypes = {
20
     static propTypes = {
21
         /**
21
         /**
22
          * The browser which is used currently.
22
          * The browser which is used currently.
23
+         *
23
          * NOTE: Used by UserMediaPermissionsOverlay only.
24
          * NOTE: Used by UserMediaPermissionsOverlay only.
25
+         *
24
          * @private
26
          * @private
25
          * @type {string}
27
          * @type {string}
26
          */
28
          */
27
         _browser: React.PropTypes.string,
29
         _browser: React.PropTypes.string,
28
 
30
 
29
         /**
31
         /**
30
-         * The indicator which determines whether the status of
32
+         * The indicator which determines whether the status of the
31
          * JitsiConnection object has been "established" or not.
33
          * JitsiConnection object has been "established" or not.
34
+         *
32
          * NOTE: Used by PageReloadOverlay only.
35
          * NOTE: Used by PageReloadOverlay only.
36
+         *
33
          * @private
37
          * @private
34
          * @type {boolean}
38
          * @type {boolean}
35
          */
39
          */
38
         /**
42
         /**
39
          * The indicator which determines whether a critical error for reload
43
          * The indicator which determines whether a critical error for reload
40
          * has been received.
44
          * has been received.
45
+         *
41
          * NOTE: Used by PageReloadOverlay only.
46
          * NOTE: Used by PageReloadOverlay only.
47
+         *
42
          * @private
48
          * @private
43
          * @type {boolean}
49
          * @type {boolean}
44
          */
50
          */
47
         /**
53
         /**
48
          * The indicator which determines whether the reload was caused by
54
          * The indicator which determines whether the reload was caused by
49
          * network failure.
55
          * network failure.
56
+         *
50
          * NOTE: Used by PageReloadOverlay only.
57
          * NOTE: Used by PageReloadOverlay only.
58
+         *
51
          * @private
59
          * @private
52
          * @type {boolean}
60
          * @type {boolean}
53
          */
61
          */
54
         _isNetworkFailure: React.PropTypes.bool,
62
         _isNetworkFailure: React.PropTypes.bool,
55
 
63
 
56
         /**
64
         /**
57
-         * The indicator which determines whether the GUM permissions prompt
58
-         * is displayed or not.
65
+         * The indicator which determines whether the GUM permissions prompt is
66
+         * displayed or not.
67
+         *
59
          * NOTE: Used by UserMediaPermissionsOverlay only.
68
          * NOTE: Used by UserMediaPermissionsOverlay only.
69
+         *
60
          * @private
70
          * @private
61
          * @type {boolean}
71
          * @type {boolean}
62
          */
72
          */
64
 
74
 
65
         /**
75
         /**
66
          * The reason for the error that will cause the reload.
76
          * The reason for the error that will cause the reload.
77
+         *
67
          * NOTE: Used by PageReloadOverlay only.
78
          * NOTE: Used by PageReloadOverlay only.
79
+         *
68
          * @private
80
          * @private
69
          * @type {string}
81
          * @type {string}
70
          */
82
          */
71
         _reason: React.PropTypes.string,
83
         _reason: React.PropTypes.string,
72
 
84
 
73
         /**
85
         /**
74
-         * The indicator which determines whether the GUM permissions prompt
75
-         * is displayed or not.
86
+         * The indicator which determines whether the GUM permissions prompt is
87
+         * displayed or not.
88
+         *
76
          * NOTE: Used by SuspendedOverlay only.
89
          * NOTE: Used by SuspendedOverlay only.
90
+         *
77
          * @private
91
          * @private
78
          * @type {string}
92
          * @type {string}
79
          */
93
          */
91
         // FIXME: Temporary workaround until everything is moved to react.
105
         // FIXME: Temporary workaround until everything is moved to react.
92
         APP.UI.overlayVisible
106
         APP.UI.overlayVisible
93
             = (this.props._connectionEstablished && this.props._haveToReload)
107
             = (this.props._connectionEstablished && this.props._haveToReload)
94
-            || this.props._suspendDetected
95
-            || this.props._mediaPermissionPromptVisible;
108
+                || this.props._suspendDetected
109
+                || this.props._mediaPermissionPromptVisible;
96
     }
110
     }
97
 
111
 
98
     /**
112
     /**
133
  *
147
  *
134
  * @param {Object} state - The Redux state.
148
  * @param {Object} state - The Redux state.
135
  * @returns {{
149
  * @returns {{
136
- *      _browser: string,
137
- *      _connectionEstablished: bool,
138
- *      _haveToReload: bool,
139
- *      _isNetworkFailure: bool,
140
- *      _mediaPermissionPromptVisible: bool,
141
- *      _reason: string,
142
- *      _suspendDetected: bool
150
+ *     _browser: string,
151
+ *     _connectionEstablished: bool,
152
+ *     _haveToReload: bool,
153
+ *     _isNetworkFailure: bool,
154
+ *     _mediaPermissionPromptVisible: bool,
155
+ *     _reason: string,
156
+ *     _suspendDetected: bool
143
  * }}
157
  * }}
144
  * @private
158
  * @private
145
  */
159
  */
146
 function _mapStateToProps(state) {
160
 function _mapStateToProps(state) {
161
+    const stateFeaturesOverlay = state['features/overlay'];
162
+
147
     return {
163
     return {
148
         /**
164
         /**
149
          * The browser which is used currently.
165
          * The browser which is used currently.
166
+         *
150
          * NOTE: Used by UserMediaPermissionsOverlay only.
167
          * NOTE: Used by UserMediaPermissionsOverlay only.
168
+         *
151
          * @private
169
          * @private
152
          * @type {string}
170
          * @type {string}
153
          */
171
          */
154
-        _browser: state['features/overlay'].browser,
172
+        _browser: stateFeaturesOverlay.browser,
155
 
173
 
156
         /**
174
         /**
157
-         * The indicator which determines whether the status of
175
+         * The indicator which determines whether the status of the
158
          * JitsiConnection object has been "established" or not.
176
          * JitsiConnection object has been "established" or not.
177
+         *
159
          * NOTE: Used by PageReloadOverlay only.
178
          * NOTE: Used by PageReloadOverlay only.
179
+         *
160
          * @private
180
          * @private
161
          * @type {boolean}
181
          * @type {boolean}
162
          */
182
          */
163
-        _connectionEstablished:
164
-            state['features/overlay'].connectionEstablished,
183
+        _connectionEstablished: stateFeaturesOverlay.connectionEstablished,
165
 
184
 
166
         /**
185
         /**
167
          * The indicator which determines whether a critical error for reload
186
          * The indicator which determines whether a critical error for reload
168
          * has been received.
187
          * has been received.
188
+         *
169
          * NOTE: Used by PageReloadOverlay only.
189
          * NOTE: Used by PageReloadOverlay only.
190
+         *
170
          * @private
191
          * @private
171
          * @type {boolean}
192
          * @type {boolean}
172
          */
193
          */
173
-        _haveToReload: state['features/overlay'].haveToReload,
194
+        _haveToReload: stateFeaturesOverlay.haveToReload,
174
 
195
 
175
         /**
196
         /**
176
          * The indicator which determines whether the reload was caused by
197
          * The indicator which determines whether the reload was caused by
177
          * network failure.
198
          * network failure.
199
+         *
178
          * NOTE: Used by PageReloadOverlay only.
200
          * NOTE: Used by PageReloadOverlay only.
201
+         *
179
          * @private
202
          * @private
180
          * @type {boolean}
203
          * @type {boolean}
181
          */
204
          */
182
-        _isNetworkFailure: state['features/overlay'].isNetworkFailure,
205
+        _isNetworkFailure: stateFeaturesOverlay.isNetworkFailure,
183
 
206
 
184
         /**
207
         /**
185
-         * The indicator which determines whether the GUM permissions prompt
186
-         * is displayed or not.
208
+         * The indicator which determines whether the GUM permissions prompt is
209
+         * displayed or not.
210
+         *
187
          * NOTE: Used by UserMediaPermissionsOverlay only.
211
          * NOTE: Used by UserMediaPermissionsOverlay only.
212
+         *
188
          * @private
213
          * @private
189
          * @type {boolean}
214
          * @type {boolean}
190
          */
215
          */
191
         _mediaPermissionPromptVisible:
216
         _mediaPermissionPromptVisible:
192
-            state['features/overlay'].mediaPermissionPromptVisible,
217
+            stateFeaturesOverlay.mediaPermissionPromptVisible,
193
 
218
 
194
         /**
219
         /**
195
          * The reason for the error that will cause the reload.
220
          * The reason for the error that will cause the reload.
221
+         *
196
          * NOTE: Used by PageReloadOverlay only.
222
          * NOTE: Used by PageReloadOverlay only.
223
+         *
197
          * @private
224
          * @private
198
          * @type {string}
225
          * @type {string}
199
          */
226
          */
200
-        _reason: state['features/overlay'].reason,
227
+        _reason: stateFeaturesOverlay.reason,
201
 
228
 
202
         /**
229
         /**
203
-         * The indicator which determines whether the GUM permissions prompt
204
-         * is displayed or not.
230
+         * The indicator which determines whether the GUM permissions prompt is
231
+         * displayed or not.
232
+         *
205
          * NOTE: Used by SuspendedOverlay only.
233
          * NOTE: Used by SuspendedOverlay only.
234
+         *
206
          * @private
235
          * @private
207
          * @type {string}
236
          * @type {string}
208
          */
237
          */
209
-        _suspendDetected: state['features/overlay'].suspendDetected
238
+        _suspendDetected: stateFeaturesOverlay.suspendDetected
210
     };
239
     };
211
 }
240
 }
212
 
241
 

+ 49
- 160
react/features/overlay/components/PageReloadOverlay.js 查看文件

1
-/* global APP, AJS */
1
+import React from 'react';
2
 
2
 
3
-import React, { Component } from 'react';
4
-
5
-import { randomInt } from '../../base/util/randomUtil';
3
+import { randomInt } from '../../base/util';
6
 
4
 
7
 import AbstractOverlay from './AbstractOverlay';
5
 import AbstractOverlay from './AbstractOverlay';
6
+import ReloadTimer from './ReloadTimer';
8
 
7
 
9
-const logger = require('jitsi-meet-logger').getLogger(__filename);
10
-
11
-/**
12
- * Implements a React Component for the reload timer. Starts counter from
13
- * props.start, adds props.step to the current value on every props.interval
14
- * seconds until the current value reaches props.end. Also displays progress
15
- * bar.
16
- */
17
-class ReloadTimer extends Component {
18
-    /**
19
-     * ReloadTimer component's property types.
20
-     *
21
-     * @static
22
-     */
23
-    static propTypes = {
24
-        /**
25
-         * The end of the timer. When this.state.current reaches this
26
-         * value the timer will stop and call onFinish function.
27
-         * @public
28
-         * @type {number}
29
-         */
30
-        end: React.PropTypes.number,
31
-
32
-        /**
33
-         * The interval in sec for adding this.state.step to this.state.current
34
-         * @public
35
-         * @type {number}
36
-         */
37
-        interval: React.PropTypes.number,
38
-
39
-        /**
40
-         * The function that will be executed when timer finish (when
41
-         * this.state.current === this.props.end)
42
-         */
43
-        onFinish: React.PropTypes.func,
44
-
45
-        /**
46
-         * The start of the timer. The initial value for this.state.current.
47
-         * @public
48
-         * @type {number}
49
-         */
50
-        start: React.PropTypes.number,
51
-
52
-        /**
53
-         * The value which will be added to this.state.current on every step.
54
-         * @public
55
-         * @type {number}
56
-         */
57
-        step: React.PropTypes.number
58
-    }
59
-
60
-    /**
61
-     * Initializes a new ReloadTimer instance.
62
-     *
63
-     * @param {Object} props - The read-only properties with which the new
64
-     * instance is to be initialized.
65
-     * @public
66
-     */
67
-    constructor(props) {
68
-        super(props);
69
-        this.state = {
70
-            current: this.props.start,
71
-            time: Math.abs(this.props.end - this.props.start)
72
-        };
73
-    }
74
-
75
-    /**
76
-     * React Component method that executes once component is mounted.
77
-     *
78
-     * @inheritdoc
79
-     * @returns {void}
80
-     * @protected
81
-     */
82
-    componentDidMount() {
83
-        AJS.progressBars.update('#reloadProgressBar', 0);
84
-        const intervalId = setInterval(() => {
85
-            if (this.state.current === this.props.end) {
86
-                clearInterval(intervalId);
87
-                this.props.onFinish();
88
-
89
-                return;
90
-            }
91
-            this.setState((prevState, props) => {
92
-                return { current: prevState.current + props.step };
93
-            });
94
-        }, Math.abs(this.props.interval) * 1000);
95
-    }
96
-
97
-    /**
98
-     * React Component method that executes once component is updated.
99
-     *
100
-     * @inheritdoc
101
-     * @returns {void}
102
-     * @protected
103
-     */
104
-    componentDidUpdate() {
105
-        AJS.progressBars.update('#reloadProgressBar',
106
-            Math.abs(this.state.current - this.props.start) / this.state.time);
107
-    }
8
+declare var APP: Object;
108
 
9
 
109
-    /**
110
-     * Implements React's {@link Component#render()}.
111
-     *
112
-     * @inheritdoc
113
-     * @returns {ReactElement|null}
114
-     * @public
115
-     */
116
-    render() {
117
-        return (
118
-            <div>
119
-                <div
120
-                    className = 'aui-progress-indicator'
121
-                    id = 'reloadProgressBar'>
122
-                    <span className = 'aui-progress-indicator-value' />
123
-                </div>
124
-                <span
125
-                    className = 'reload_overlay_text'
126
-                    id = 'reloadSeconds'>
127
-                    { this.state.current }
128
-                    <span data-i18n = 'dialog.conferenceReloadTimeLeft' />
129
-                </span>
130
-            </div>
131
-        );
132
-    }
133
-}
10
+const logger = require('jitsi-meet-logger').getLogger(__filename);
134
 
11
 
135
 /**
12
 /**
136
- * Implements a React Component for page reload overlay. Shown before
137
- * the conference is reloaded. Shows a warning message and counts down towards
138
- * the reload.
13
+ * Implements a React Component for page reload overlay. Shown before the
14
+ * conference is reloaded. Shows a warning message and counts down towards the
15
+ * reload.
139
  */
16
  */
140
 export default class PageReloadOverlay extends AbstractOverlay {
17
 export default class PageReloadOverlay extends AbstractOverlay {
141
     /**
18
     /**
172
         super(props);
49
         super(props);
173
 
50
 
174
         /**
51
         /**
175
-         * How long the overlay dialog will be
176
-         * displayed, before the conference will be reloaded.
52
+         * How long the overlay dialog will be displayed, before the conference
53
+         * will be reloaded.
177
          * @type {number}
54
          * @type {number}
178
          */
55
          */
179
         const timeoutSeconds = 10 + randomInt(0, 20);
56
         const timeoutSeconds = 10 + randomInt(0, 20);
194
             ...this.state,
71
             ...this.state,
195
 
72
 
196
             /**
73
             /**
197
-             * Indicates the css style of the overlay. if true - lighter  and
198
-             * darker otherwise.
74
+             * Indicates the css style of the overlay. If true, then lighter;
75
+             * darker, otherwise.
76
+             *
199
              * @type {boolean}
77
              * @type {boolean}
200
              */
78
              */
201
             isLightOverlay,
79
             isLightOverlay,
202
 
80
 
203
             /**
81
             /**
204
-             * The translation key for the title of the overlay
82
+             * The translation key for the title of the overlay.
83
+             *
205
              * @type {string}
84
              * @type {string}
206
              */
85
              */
207
             message,
86
             message,
208
 
87
 
209
             /**
88
             /**
210
-             * How long the overlay dialog will be
211
-             * displayed, before the conference will be reloaded.
89
+             * How long the overlay dialog will be displayed before the
90
+             * conference will be reloaded.
91
+             *
212
              * @type {number}
92
              * @type {number}
213
              */
93
              */
214
             timeoutSeconds,
94
             timeoutSeconds,
215
 
95
 
216
             /**
96
             /**
217
-             * The translation key for the title of the overlay
97
+             * The translation key for the title of the overlay.
98
+             *
218
              * @type {string}
99
              * @type {string}
219
              */
100
              */
220
             title
101
             title
221
         };
102
         };
222
     }
103
     }
223
 
104
 
105
+    /**
106
+     * This method is executed when comonent is mounted.
107
+     *
108
+     * @inheritdoc
109
+     * @returns {void}
110
+     */
111
+    componentDidMount() {
112
+        super.componentDidMount();
113
+
114
+        // FIXME (CallStats - issue) This event will not make it to CallStats
115
+        // because the log queue is not flushed before "fabric terminated" is
116
+        // sent to the backed.
117
+        // FIXME: We should dispatch action for this.
118
+        APP.conference.logEvent(
119
+                'page.reload',
120
+                /* value */ undefined,
121
+                /* label */ this.props.reason);
122
+        logger.info(
123
+                'The conference will be reloaded after '
124
+                    + `${this.state.timeoutSeconds} seconds.`);
125
+    }
126
+
224
     /**
127
     /**
225
      * Renders the button for relaod the page if necessary.
128
      * Renders the button for relaod the page if necessary.
226
      *
129
      *
229
      */
132
      */
230
     _renderButton() {
133
     _renderButton() {
231
         if (this.props.isNetworkFailure) {
134
         if (this.props.isNetworkFailure) {
232
-            const cName = 'button-control button-control_primary '
233
-                + 'button-control_center';
135
+            const className
136
+                = 'button-control button-control_primary button-control_center';
234
 
137
 
235
             /* eslint-disable react/jsx-handler-names */
138
             /* eslint-disable react/jsx-handler-names */
236
 
139
 
237
             return (
140
             return (
238
                 <button
141
                 <button
239
-                    className = { cName }
142
+                    className = { className }
240
                     data-i18n = 'dialog.reconnectNow'
143
                     data-i18n = 'dialog.reconnectNow'
241
                     id = 'reconnectNow'
144
                     id = 'reconnectNow'
242
                     onClick = { this._reconnectNow } />
145
                     onClick = { this._reconnectNow } />
243
             );
146
             );
147
+
148
+
149
+            /* eslint-enable react/jsx-handler-names */
244
         }
150
         }
245
 
151
 
246
         return null;
152
         return null;
275
                 { this._renderButton() }
181
                 { this._renderButton() }
276
             </div>
182
             </div>
277
         );
183
         );
278
-    }
279
-
280
-    /**
281
-     * This method is executed when comonent is mounted.
282
-     *
283
-     * @inheritdoc
284
-     * @returns {void}
285
-     */
286
-    componentDidMount() {
287
-        super.componentDidMount();
288
 
184
 
289
-        // FIXME (CallStats - issue) this event will not make it to
290
-        // the CallStats, because the log queue is not flushed, before
291
-        // "fabric terminated" is sent to the backed
292
-        // FIXME: We should dispatch action for this
293
-        APP.conference.logEvent('page.reload', undefined /* value */,
294
-            this.props.reason /* label */);
295
-        logger.info(`The conference will be reloaded after
296
-            ${this.state.timeoutSeconds} seconds.`);
185
+        /* eslint-enable react/jsx-handler-names */
297
     }
186
     }
298
 }
187
 }

+ 141
- 0
react/features/overlay/components/ReloadTimer.js 查看文件

1
+import React, { Component } from 'react';
2
+
3
+declare var AJS: Object;
4
+
5
+/**
6
+ * Implements a React Component for the reload timer. Starts counter from
7
+ * props.start, adds props.step to the current value on every props.interval
8
+ * seconds until the current value reaches props.end. Also displays progress
9
+ * bar.
10
+ */
11
+export default class ReloadTimer extends Component {
12
+    /**
13
+     * ReloadTimer component's property types.
14
+     *
15
+     * @static
16
+     */
17
+    static propTypes = {
18
+        /**
19
+         * The end of the timer. When this.state.current reaches this value the
20
+         * timer will stop and call onFinish function.
21
+         *
22
+         * @public
23
+         * @type {number}
24
+         */
25
+        end: React.PropTypes.number,
26
+
27
+        /**
28
+         * The interval in sec for adding this.state.step to this.state.current.
29
+         *
30
+         * @public
31
+         * @type {number}
32
+         */
33
+        interval: React.PropTypes.number,
34
+
35
+        /**
36
+         * The function that will be executed when timer finish (when
37
+         * this.state.current === this.props.end)
38
+         */
39
+        onFinish: React.PropTypes.func,
40
+
41
+        /**
42
+         * The start of the timer. The initial value for this.state.current.
43
+         *
44
+         * @public
45
+         * @type {number}
46
+         */
47
+        start: React.PropTypes.number,
48
+
49
+        /**
50
+         * The value which will be added to this.state.current on every step.
51
+         *
52
+         * @public
53
+         * @type {number}
54
+         */
55
+        step: React.PropTypes.number
56
+    }
57
+
58
+    /**
59
+     * Initializes a new ReloadTimer instance.
60
+     *
61
+     * @param {Object} props - The read-only properties with which the new
62
+     * instance is to be initialized.
63
+     * @public
64
+     */
65
+    constructor(props) {
66
+        super(props);
67
+
68
+        this.state = {
69
+            current: this.props.start,
70
+            time: Math.abs(this.props.end - this.props.start)
71
+        };
72
+    }
73
+
74
+    /**
75
+     * React Component method that executes once component is mounted.
76
+     *
77
+     * @inheritdoc
78
+     * @returns {void}
79
+     * @protected
80
+     */
81
+    componentDidMount() {
82
+        AJS.progressBars.update('#reloadProgressBar', 0);
83
+
84
+        const intervalId
85
+            = setInterval(
86
+                    () => {
87
+                        if (this.state.current === this.props.end) {
88
+                            clearInterval(intervalId);
89
+                            this.props.onFinish();
90
+                        } else {
91
+                            this.setState((prevState, props) => {
92
+                                return {
93
+                                    current: prevState.current + props.step
94
+                                };
95
+                            });
96
+                        }
97
+                    },
98
+                    Math.abs(this.props.interval) * 1000);
99
+    }
100
+
101
+    /**
102
+     * React Component method that executes once component is updated.
103
+     *
104
+     * @inheritdoc
105
+     * @returns {void}
106
+     * @protected
107
+     */
108
+    componentDidUpdate() {
109
+        AJS.progressBars.update(
110
+                '#reloadProgressBar',
111
+                Math.abs(this.state.current - this.props.start)
112
+                    / this.state.time);
113
+    }
114
+
115
+    /**
116
+     * Implements React's {@link Component#render()}.
117
+     *
118
+     * @inheritdoc
119
+     * @returns {ReactElement|null}
120
+     * @public
121
+     */
122
+    render() {
123
+        return (
124
+            <div>
125
+                <div
126
+                    className = 'aui-progress-indicator'
127
+                    id = 'reloadProgressBar'>
128
+                    <span className = 'aui-progress-indicator-value' />
129
+                </div>
130
+                <span
131
+                    className = 'reload_overlay_text'
132
+                    id = 'reloadSeconds'>
133
+                    {
134
+                        this.state.current
135
+                    }
136
+                    <span data-i18n = 'dialog.conferenceReloadTimeLeft' />
137
+                </span>
138
+            </div>
139
+        );
140
+    }
141
+}

+ 4
- 2
react/features/overlay/components/SuspendedOverlay.js 查看文件

3
 import AbstractOverlay from './AbstractOverlay';
3
 import AbstractOverlay from './AbstractOverlay';
4
 
4
 
5
 /**
5
 /**
6
- * Implements a React Component for suspended overlay. Shown when suspended
7
- * is detected.
6
+ * Implements a React Component for suspended overlay. Shown when a suspend is
7
+ * detected.
8
  */
8
  */
9
 export default class SuspendedOverlay extends AbstractOverlay {
9
 export default class SuspendedOverlay extends AbstractOverlay {
10
     /**
10
     /**
33
                     onClick = { this._reconnectNow } />
33
                     onClick = { this._reconnectNow } />
34
             </div>
34
             </div>
35
         );
35
         );
36
+
37
+        /* eslint-enable react/jsx-handler-names */
36
     }
38
     }
37
 }
39
 }

+ 9
- 3
react/features/overlay/components/UserMediaPermissionsOverlay.js 查看文件

18
         /**
18
         /**
19
          * The browser which is used currently. The text is different for every
19
          * The browser which is used currently. The text is different for every
20
          * browser.
20
          * browser.
21
+         *
21
          * @public
22
          * @public
22
          * @type {string}
23
          * @type {string}
23
          */
24
          */
37
         this.state = {
38
         this.state = {
38
             /**
39
             /**
39
              * The src value of the image for the policy logo.
40
              * The src value of the image for the policy logo.
41
+             *
40
              * @type {string}
42
              * @type {string}
41
              */
43
              */
42
             policyLogoSrc: interfaceConfig.POLICY_LOGO
44
             policyLogoSrc: interfaceConfig.POLICY_LOGO
72
                     <p
74
                     <p
73
                         className = 'policy__text'
75
                         className = 'policy__text'
74
                         data-i18n = '[html]startupoverlay.policyText' />
76
                         data-i18n = '[html]startupoverlay.policyText' />
75
-                    { this._renderPolicyLogo() }
77
+                    {
78
+                        this._renderPolicyLogo()
79
+                    }
76
                 </div>
80
                 </div>
77
             </div>
81
             </div>
78
         );
82
         );
85
      * @private
89
      * @private
86
      */
90
      */
87
     _renderPolicyLogo() {
91
     _renderPolicyLogo() {
88
-        if (this.state.policyLogoSrc) {
92
+        const { policyLogoSrc } = this.state;
93
+
94
+        if (policyLogoSrc) {
89
             return (
95
             return (
90
                 <div className = 'policy__logo'>
96
                 <div className = 'policy__logo'>
91
-                    <img src = { this.state.policyLogoSrc } />
97
+                    <img src = { policyLogoSrc } />
92
                 </div>
98
                 </div>
93
             );
99
             );
94
         }
100
         }

+ 3
- 1
react/features/overlay/index.js 查看文件

1
-export * from './components';
2
 export * from './actions';
1
 export * from './actions';
2
+export * from './components';
3
+
4
+import './reducer';

+ 38
- 39
react/features/overlay/reducer.js 查看文件

1
-/* global JitsiMeetJS */
2
-
3
 import { CONFERENCE_FAILED } from '../base/conference';
1
 import { CONFERENCE_FAILED } from '../base/conference';
4
 import {
2
 import {
5
     CONNECTION_ESTABLISHED,
3
     CONNECTION_ESTABLISHED,
6
     CONNECTION_FAILED
4
     CONNECTION_FAILED
7
 } from '../base/connection';
5
 } from '../base/connection';
6
+import JitsiMeetJS, {
7
+    isFatalJitsiConnectionError
8
+} from '../base/lib-jitsi-meet';
8
 import {
9
 import {
9
     ReducerRegistry,
10
     ReducerRegistry,
10
-    setStateProperty,
11
-    setStateProperties
11
+    setStateProperties,
12
+    setStateProperty
12
 } from '../base/redux';
13
 } from '../base/redux';
13
 
14
 
14
 import {
15
 import {
16
     SUSPEND_DETECTED
17
     SUSPEND_DETECTED
17
 } from './actionTypes';
18
 } from './actionTypes';
18
 
19
 
20
+const JitsiConferenceErrors = JitsiMeetJS.errors.conference;
21
+const JitsiConnectionErrors = JitsiMeetJS.errors.connection;
19
 const logger = require('jitsi-meet-logger').getLogger(__filename);
22
 const logger = require('jitsi-meet-logger').getLogger(__filename);
20
 
23
 
21
 /**
24
 /**
43
 });
46
 });
44
 
47
 
45
 /**
48
 /**
46
- * Reduces a specific Redux action CONFERENCE_FAILED of the feature
47
- * overlay.
49
+ * Reduces a specific Redux action CONFERENCE_FAILED of the feature overlay.
48
  *
50
  *
49
  * @param {Object} state - The Redux state of the feature overlay.
51
  * @param {Object} state - The Redux state of the feature overlay.
50
  * @param {Action} action - The Redux action CONFERENCE_FAILED to reduce.
52
  * @param {Action} action - The Redux action CONFERENCE_FAILED to reduce.
51
- * @returns {Object} The new state of the feature base/connection after the
52
- * reduction of the specified action.
53
+ * @returns {Object} The new state of the feature overlay after the reduction of
54
+ * the specified action.
53
  * @private
55
  * @private
54
  */
56
  */
55
 function _conferenceFailed(state, action) {
57
 function _conferenceFailed(state, action) {
56
-    const ConferenceErrors = JitsiMeetJS.errors.conference;
58
+    const error = action.error;
57
 
59
 
58
-    if (action.error === ConferenceErrors.FOCUS_LEFT
59
-        || action.error === ConferenceErrors.VIDEOBRIDGE_NOT_AVAILABLE) {
60
+    if (error === JitsiConferenceErrors.FOCUS_LEFT
61
+            || error === JitsiConferenceErrors.VIDEOBRIDGE_NOT_AVAILABLE) {
60
         return setStateProperties(state, {
62
         return setStateProperties(state, {
61
             haveToReload: true,
63
             haveToReload: true,
62
             isNetworkFailure: false,
64
             isNetworkFailure: false,
72
  * overlay.
74
  * overlay.
73
  *
75
  *
74
  * @param {Object} state - The Redux state of the feature overlay.
76
  * @param {Object} state - The Redux state of the feature overlay.
75
- * @returns {Object} The new state of the feature overlay after the
76
- * reduction of the specified action.
77
+ * @returns {Object} The new state of the feature overlay after the reduction of
78
+ * the specified action.
77
  * @private
79
  * @private
78
  */
80
  */
79
 function _connectionEstablished(state) {
81
 function _connectionEstablished(state) {
81
 }
83
 }
82
 
84
 
83
 /**
85
 /**
84
- * Reduces a specific Redux action CONNECTION_FAILED of the feature
85
- * overlay.
86
+ * Reduces a specific Redux action CONNECTION_FAILED of the feature overlay.
86
  *
87
  *
87
  * @param {Object} state - The Redux state of the feature overlay.
88
  * @param {Object} state - The Redux state of the feature overlay.
88
  * @param {Action} action - The Redux action CONNECTION_FAILED to reduce.
89
  * @param {Action} action - The Redux action CONNECTION_FAILED to reduce.
89
- * @returns {Object} The new state of the feature overlay after the
90
- * reduction of the specified action.
90
+ * @returns {Object} The new state of the feature overlay after the reduction of
91
+ * the specified action.
91
  * @private
92
  * @private
92
  */
93
  */
93
 function _connectionFailed(state, action) {
94
 function _connectionFailed(state, action) {
94
-    const ConnectionErrors = JitsiMeetJS.errors.connection;
95
+    const error = action.error;
95
 
96
 
96
-    switch (action.error) {
97
-    case ConnectionErrors.CONNECTION_DROPPED_ERROR:
98
-    case ConnectionErrors.OTHER_ERROR:
99
-    case ConnectionErrors.SERVER_ERROR: {
100
-        logger.error(`XMPP connection error: ${action.errorMessage}`);
97
+    if (isFatalJitsiConnectionError(error)) {
98
+        const errorMessage = action.errorMessage;
99
+
100
+        logger.error(`XMPP connection error: ${errorMessage}`);
101
 
101
 
102
-        // From all of the cases above only CONNECTION_DROPPED_ERROR
103
-        // is considered a network type of failure
104
         return setStateProperties(state, {
102
         return setStateProperties(state, {
105
             haveToReload: true,
103
             haveToReload: true,
104
+
105
+
106
+            // From all of the cases above only CONNECTION_DROPPED_ERROR is
107
+            // considered a network type of failure.
106
             isNetworkFailure:
108
             isNetworkFailure:
107
-                action.error === ConnectionErrors.CONNECTION_DROPPED_ERROR,
108
-            reason: `xmpp-conn-dropped: ${action.errorMessage}`
109
+                error === JitsiConnectionErrors.CONNECTION_DROPPED_ERROR,
110
+            reason: `xmpp-conn-dropped: ${errorMessage}`
109
         });
111
         });
110
     }
112
     }
111
-    }
112
 
113
 
113
     return state;
114
     return state;
114
 }
115
 }
115
 
116
 
116
-
117
 /**
117
 /**
118
- * Reduces a specific Redux action MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED
119
- * of the feature overlay.
118
+ * Reduces a specific Redux action MEDIA_PERMISSION_PROMPT_VISIBILITY_CHANGED of
119
+ * the feature overlay.
120
  *
120
  *
121
  * @param {Object} state - The Redux state of the feature overlay.
121
  * @param {Object} state - The Redux state of the feature overlay.
122
  * @param {Action} action - The Redux action to reduce.
122
  * @param {Action} action - The Redux action to reduce.
123
- * @returns {Object} The new state of the feature overlay after the
124
- * reduction of the specified action.
123
+ * @returns {Object} The new state of the feature overlay after the reduction of
124
+ * the specified action.
125
  * @private
125
  * @private
126
  */
126
  */
127
 function _mediaPermissionPromptVisibilityChanged(state, action) {
127
 function _mediaPermissionPromptVisibilityChanged(state, action) {
128
     return setStateProperties(state, {
128
     return setStateProperties(state, {
129
-        mediaPermissionPromptVisible: action.isVisible,
130
-        browser: action.browser
129
+        browser: action.browser,
130
+        mediaPermissionPromptVisible: action.isVisible
131
     });
131
     });
132
 }
132
 }
133
 
133
 
134
 /**
134
 /**
135
- * Reduces a specific Redux action SUSPEND_DETECTED of the feature
136
- * overlay.
135
+ * Reduces a specific Redux action SUSPEND_DETECTED of the feature overlay.
137
  *
136
  *
138
  * @param {Object} state - The Redux state of the feature overlay.
137
  * @param {Object} state - The Redux state of the feature overlay.
139
- * @returns {Object} The new state of the feature overlay after the
140
- * reduction of the specified action.
138
+ * @returns {Object} The new state of the feature overlay after the reduction of
139
+ * the specified action.
141
  * @private
140
  * @private
142
  */
141
  */
143
 function _suspendDetected(state) {
142
 function _suspendDetected(state) {

正在加载...
取消
保存