浏览代码

Fix Recording regression caused by 'React Toolbar'

Saúl Ibarra Corretgé reported that Recording shows an error dialog
stating "There was an error connecting to your camera". Hristo Terezov
and Yana Stamcheva traced that the problem originates in
da4425b5c0
and, more specifically, is caused by a different order of execution due
to the move of the invocation of the function Recording.init.

The solution is to bring back the execution location of Recording.init.
j8
Lyubo Marinov 8 年前
父节点
当前提交
ae06a6ce41

+ 4
- 0
modules/UI/UI.js 查看文件

322
 
322
 
323
         $("#videoconference_page").mousemove(debouncedShowToolbar);
323
         $("#videoconference_page").mousemove(debouncedShowToolbar);
324
 
324
 
325
+        // Initialise the recording module.
326
+        if (config.enableRecording) {
327
+            Recording.init(eventEmitter, config.recordingType);
328
+        }
325
         // Initialize side panels
329
         // Initialize side panels
326
         SidePanels.init(eventEmitter);
330
         SidePanels.init(eventEmitter);
327
     } else {
331
     } else {

+ 128
- 128
modules/UI/recording/Recording.js 查看文件

21
 import VideoLayout from '../videolayout/VideoLayout';
21
 import VideoLayout from '../videolayout/VideoLayout';
22
 import Feedback from '../feedback/Feedback.js';
22
 import Feedback from '../feedback/Feedback.js';
23
 
23
 
24
-import { hideToolbox } from '../../../react/features/toolbox';
24
+import { setToolboxEnabled } from '../../../react/features/toolbox';
25
 
25
 
26
 /**
26
 /**
27
  * The dialog for user input.
27
  * The dialog for user input.
35
  * @private
35
  * @private
36
  */
36
  */
37
 function _isRecordingButtonEnabled() {
37
 function _isRecordingButtonEnabled() {
38
-    return interfaceConfig.TOOLBAR_BUTTONS.indexOf("recording") !== -1
39
-            && config.enableRecording && APP.conference.isRecordingSupported();
38
+    return (
39
+        interfaceConfig.TOOLBAR_BUTTONS.indexOf("recording") !== -1
40
+            && config.enableRecording
41
+            && APP.conference.isRecordingSupported());
40
 }
42
 }
41
 
43
 
42
 /**
44
 /**
129
  * Request recording token from the user.
131
  * Request recording token from the user.
130
  * @returns {Promise}
132
  * @returns {Promise}
131
  */
133
  */
132
-function _requestRecordingToken () {
134
+function _requestRecordingToken() {
133
     let titleKey = "dialog.recordingToken";
135
     let titleKey = "dialog.recordingToken";
134
     let messageString = (
136
     let messageString = (
135
         `<input name="recordingToken" type="text"
137
         `<input name="recordingToken" type="text"
164
  * @returns {Promise}
166
  * @returns {Promise}
165
  * @private
167
  * @private
166
  */
168
  */
167
-function _showStopRecordingPrompt (recordingType) {
169
+function _showStopRecordingPrompt(recordingType) {
168
     var title;
170
     var title;
169
     var message;
171
     var message;
170
     var buttonKey;
172
     var buttonKey;
179
         buttonKey = "dialog.stopRecording";
181
         buttonKey = "dialog.stopRecording";
180
     }
182
     }
181
 
183
 
182
-    return new Promise(function (resolve, reject) {
184
+    return new Promise((resolve, reject) => {
183
         dialog = APP.UI.messageHandler.openTwoButtonDialog({
185
         dialog = APP.UI.messageHandler.openTwoButtonDialog({
184
             titleKey: title,
186
             titleKey: title,
185
             msgKey: message,
187
             msgKey: message,
186
             leftButtonKey: buttonKey,
188
             leftButtonKey: buttonKey,
187
-            submitFunction: function(e,v) {
188
-                if (v) {
189
-                    resolve();
190
-                } else {
191
-                    reject();
192
-                }
193
-            },
194
-            closeFunction: function () {
189
+            submitFunction: (e, v) => (v ? resolve : reject)(),
190
+            closeFunction: () => {
195
                 dialog = null;
191
                 dialog = null;
196
             }
192
             }
197
         });
193
         });
250
     /**
246
     /**
251
      * Initializes the recording UI.
247
      * Initializes the recording UI.
252
      */
248
      */
253
-    init (emitter, recordingType) {
254
-        this.eventEmitter = emitter;
249
+    init(eventEmitter, recordingType) {
250
+        this.eventEmitter = eventEmitter;
251
+        this.recordingType = recordingType;
255
 
252
 
256
         this.updateRecordingState(APP.conference.getRecordingState());
253
         this.updateRecordingState(APP.conference.getRecordingState());
257
 
254
 
258
-        this.initRecordingButton(recordingType);
259
-
260
-        // If I am a recorder then I publish my recorder custom role to notify
261
-        // everyone.
262
-        if (config.iAmRecorder) {
263
-            VideoLayout.enableDeviceAvailabilityIcons(
264
-                APP.conference.getMyUserId(), false);
265
-            VideoLayout.setLocalVideoVisible(false);
266
-            Feedback.enableFeedback(false);
267
-            APP.store.dispatch(hideToolbox());
268
-            APP.UI.messageHandler.enableNotifications(false);
269
-            APP.UI.messageHandler.enablePopups(false);
270
-        }
271
-    },
272
-
273
-    /**
274
-     * Initialise the recording button.
275
-     */
276
-    initRecordingButton(recordingType) {
277
-        let selector = $('#toolbar_button_record');
278
-
279
-        let button = selector.get(0);
280
-        UIUtil.setTooltip(button, 'liveStreaming.buttonTooltip', 'right');
281
-
282
         if (recordingType === 'jibri') {
255
         if (recordingType === 'jibri') {
283
             this.baseClass = "fa fa-play-circle";
256
             this.baseClass = "fa fa-play-circle";
284
             this.recordingTitle = "dialog.liveStreaming";
257
             this.recordingTitle = "dialog.liveStreaming";
304
             this.recordingBusy = "liveStreaming.busy";
277
             this.recordingBusy = "liveStreaming.busy";
305
         }
278
         }
306
 
279
 
280
+        // XXX Due to the React-ification of Toolbox, the HTMLElement with id
281
+        // toolbar_button_record may not exist yet.
282
+        $(document).on(
283
+            'click',
284
+            '#toolbar_button_record',
285
+            ev => this._onToolbarButtonClick(ev));
286
+
287
+        // If I am a recorder then I publish my recorder custom role to notify
288
+        // everyone.
289
+        if (config.iAmRecorder) {
290
+            VideoLayout.enableDeviceAvailabilityIcons(
291
+                APP.conference.getMyUserId(), false);
292
+            VideoLayout.setLocalVideoVisible(false);
293
+            Feedback.enableFeedback(false);
294
+            APP.store.dispatch(setToolboxEnabled(false));
295
+            APP.UI.messageHandler.enableNotifications(false);
296
+            APP.UI.messageHandler.enablePopups(false);
297
+        }
298
+    },
299
+
300
+    /**
301
+     * Initialise the recording button.
302
+     */
303
+    initRecordingButton() {
304
+        const selector = $('#toolbar_button_record');
305
+
306
+        UIUtil.setTooltip(selector, 'liveStreaming.buttonTooltip', 'right');
307
+
307
         selector.addClass(this.baseClass);
308
         selector.addClass(this.baseClass);
308
         selector.attr("data-i18n", "[content]" + this.recordingButtonTooltip);
309
         selector.attr("data-i18n", "[content]" + this.recordingButtonTooltip);
309
         APP.translation.translateElement(selector);
310
         APP.translation.translateElement(selector);
310
-
311
-        var self = this;
312
-        selector.click(function () {
313
-            if (dialog)
314
-                return;
315
-            JitsiMeetJS.analytics.sendEvent('recording.clicked');
316
-            switch (self.currentState) {
317
-                case Status.ON:
318
-                case Status.RETRYING:
319
-                case Status.PENDING: {
320
-                    _showStopRecordingPrompt(recordingType).then(
321
-                        () => {
322
-                            self.eventEmitter.emit(UIEvents.RECORDING_TOGGLED);
323
-                            JitsiMeetJS.analytics.sendEvent(
324
-                                'recording.stopped');
325
-                        },
326
-                        () => {});
327
-                    break;
328
-                }
329
-                case Status.AVAILABLE:
330
-                case Status.OFF: {
331
-                    if (recordingType === 'jibri')
332
-                        _requestLiveStreamId().then((streamId) => {
333
-                            self.eventEmitter.emit( UIEvents.RECORDING_TOGGLED,
334
-                                {streamId: streamId});
335
-                            JitsiMeetJS.analytics.sendEvent(
336
-                                'recording.started');
337
-                        }).catch(
338
-                            reason => {
339
-                                if (reason !== APP.UI.messageHandler.CANCEL)
340
-                                    logger.error(reason);
341
-                                else
342
-                                    JitsiMeetJS.analytics.sendEvent(
343
-                                        'recording.canceled');
344
-                            }
345
-                        );
346
-                    else {
347
-                        if (self.predefinedToken) {
348
-                            self.eventEmitter.emit( UIEvents.RECORDING_TOGGLED,
349
-                                {token: self.predefinedToken});
350
-                            JitsiMeetJS.analytics.sendEvent(
351
-                                'recording.started');
352
-                            return;
353
-                        }
354
-
355
-                        _requestRecordingToken().then((token) => {
356
-                            self.eventEmitter.emit( UIEvents.RECORDING_TOGGLED,
357
-                                {token: token});
358
-                            JitsiMeetJS.analytics.sendEvent(
359
-                                'recording.started');
360
-                        }).catch(
361
-                            reason => {
362
-                                if (reason !== APP.UI.messageHandler.CANCEL)
363
-                                    logger.error(reason);
364
-                                else
365
-                                    JitsiMeetJS.analytics.sendEvent(
366
-                                        'recording.canceled');
367
-                            }
368
-                        );
369
-                    }
370
-                    break;
371
-                }
372
-                case Status.BUSY: {
373
-                    dialog = APP.UI.messageHandler.openMessageDialog(
374
-                        self.recordingTitle,
375
-                        self.recordingBusy,
376
-                        null,
377
-                        function () {
378
-                            dialog = null;
379
-                        }
380
-                    );
381
-                    break;
382
-                }
383
-                default: {
384
-                    dialog = APP.UI.messageHandler.openMessageDialog(
385
-                        self.recordingTitle,
386
-                        self.recordingUnavailable,
387
-                        null,
388
-                        function () {
389
-                            dialog = null;
390
-                        }
391
-                    );
392
-                }
393
-            }
394
-        });
395
     },
311
     },
396
 
312
 
397
     /**
313
     /**
398
      * Shows or hides the 'recording' button.
314
      * Shows or hides the 'recording' button.
399
      * @param show {true} to show the recording button, {false} to hide it
315
      * @param show {true} to show the recording button, {false} to hide it
400
      */
316
      */
401
-    showRecordingButton (show) {
317
+    showRecordingButton(show) {
402
         let shouldShow = show && _isRecordingButtonEnabled();
318
         let shouldShow = show && _isRecordingButtonEnabled();
403
         let id = 'toolbar_button_record';
319
         let id = 'toolbar_button_record';
404
 
320
 
425
      * Sets the state of the recording button.
341
      * Sets the state of the recording button.
426
      * @param recordingState gives us the current recording state
342
      * @param recordingState gives us the current recording state
427
      */
343
      */
428
-    updateRecordingUI (recordingState) {
344
+    updateRecordingUI(recordingState) {
429
 
345
 
430
         let oldState = this.currentState;
346
         let oldState = this.currentState;
431
         this.currentState = recordingState;
347
         this.currentState = recordingState;
491
     },
407
     },
492
     // checks whether recording is enabled and whether we have params
408
     // checks whether recording is enabled and whether we have params
493
     // to start automatically recording
409
     // to start automatically recording
494
-    checkAutoRecord () {
410
+    checkAutoRecord() {
495
         if (_isRecordingButtonEnabled && config.autoRecord) {
411
         if (_isRecordingButtonEnabled && config.autoRecord) {
496
             this.predefinedToken = UIUtil.escapeHtml(config.autoRecordToken);
412
             this.predefinedToken = UIUtil.escapeHtml(config.autoRecordToken);
497
             this.eventEmitter.emit(UIEvents.RECORDING_TOGGLED,
413
             this.eventEmitter.emit(UIEvents.RECORDING_TOGGLED,
514
         APP.translation.translateElement(labelSelector);
430
         APP.translation.translateElement(labelSelector);
515
     },
431
     },
516
 
432
 
433
+    /**
434
+     * Handles {@code click} on {@code toolbar_button_record}.
435
+     *
436
+     * @returns {void}
437
+     */
438
+    _onToolbarButtonClick() {
439
+        if (dialog) {
440
+            return;
441
+        }
442
+
443
+        JitsiMeetJS.analytics.sendEvent('recording.clicked');
444
+        switch (this.currentState) {
445
+        case Status.ON:
446
+        case Status.RETRYING:
447
+        case Status.PENDING: {
448
+            _showStopRecordingPrompt(this.recordingType).then(
449
+                () => {
450
+                    this.eventEmitter.emit(UIEvents.RECORDING_TOGGLED);
451
+                    JitsiMeetJS.analytics.sendEvent('recording.stopped');
452
+                },
453
+                () => {});
454
+            break;
455
+        }
456
+        case Status.AVAILABLE:
457
+        case Status.OFF: {
458
+            if (this.recordingType === 'jibri')
459
+                _requestLiveStreamId().then(streamId => {
460
+                    this.eventEmitter.emit(
461
+                        UIEvents.RECORDING_TOGGLED,
462
+                        { streamId });
463
+                    JitsiMeetJS.analytics.sendEvent('recording.started');
464
+                }).catch(reason => {
465
+                    if (reason !== APP.UI.messageHandler.CANCEL)
466
+                        logger.error(reason);
467
+                    else
468
+                        JitsiMeetJS.analytics.sendEvent('recording.canceled');
469
+                });
470
+            else {
471
+                if (this.predefinedToken) {
472
+                    this.eventEmitter.emit(
473
+                        UIEvents.RECORDING_TOGGLED,
474
+                        { token: this.predefinedToken });
475
+                    JitsiMeetJS.analytics.sendEvent('recording.started');
476
+                    return;
477
+                }
478
+
479
+                _requestRecordingToken().then((token) => {
480
+                    this.eventEmitter.emit(
481
+                        UIEvents.RECORDING_TOGGLED,
482
+                        { token });
483
+                    JitsiMeetJS.analytics.sendEvent('recording.started');
484
+                }).catch(reason => {
485
+                    if (reason !== APP.UI.messageHandler.CANCEL)
486
+                        logger.error(reason);
487
+                    else
488
+                        JitsiMeetJS.analytics.sendEvent('recording.canceled');
489
+                });
490
+            }
491
+            break;
492
+        }
493
+        case Status.BUSY: {
494
+            dialog = APP.UI.messageHandler.openMessageDialog(
495
+                this.recordingTitle,
496
+                this.recordingBusy,
497
+                null,
498
+                () => {
499
+                    dialog = null;
500
+                }
501
+            );
502
+            break;
503
+        }
504
+        default: {
505
+            dialog = APP.UI.messageHandler.openMessageDialog(
506
+                this.recordingTitle,
507
+                this.recordingUnavailable,
508
+                null,
509
+                () => {
510
+                    dialog = null;
511
+                }
512
+            );
513
+        }
514
+        }
515
+    },
516
+
517
     /**
517
     /**
518
      * Sets the toggled state of the recording toolbar button.
518
      * Sets the toggled state of the recording toolbar button.
519
      *
519
      *

+ 6
- 4
modules/UI/util/UIUtil.js 查看文件

157
      * @param position the position of the tooltip in relation to the element
157
      * @param position the position of the tooltip in relation to the element
158
      */
158
      */
159
     setTooltip(element, key, position) {
159
     setTooltip(element, key, position) {
160
-        if (element !== null) {
161
-            element.setAttribute('data-tooltip', TOOLTIP_POSITIONS[position]);
162
-            element.setAttribute('data-i18n', '[content]' + key);
160
+        if (element) {
161
+            const selector = element.jquery ? element : $(element);
163
 
162
 
164
-            APP.translation.translateElement($(element));
163
+            selector.attr('data-tooltip', TOOLTIP_POSITIONS[position]);
164
+            selector.attr('data-i18n', `[content]${key}`);
165
+
166
+            APP.translation.translateElement(selector);
165
         }
167
         }
166
     },
168
     },
167
 
169
 

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

21
 export const SET_DEFAULT_TOOLBOX_BUTTONS
21
 export const SET_DEFAULT_TOOLBOX_BUTTONS
22
     = Symbol('SET_DEFAULT_TOOLBOX_BUTTONS');
22
     = Symbol('SET_DEFAULT_TOOLBOX_BUTTONS');
23
 
23
 
24
-/**
25
- * The type of the action which sets the permanent visibility of the Toolbox.
26
- *
27
- * {
28
- *     type: SET_TOOLBOX_ALWAYS_VISIBLE,
29
- *     alwaysVisible: boolean
30
- * }
31
- */
32
-export const SET_TOOLBOX_ALWAYS_VISIBLE = Symbol('SET_TOOLBOX_ALWAYS_VISIBLE');
33
-
34
 /**
24
 /**
35
  * The type of the action which sets the conference subject.
25
  * The type of the action which sets the conference subject.
36
  *
26
  *
73
  */
63
  */
74
 export const SET_TOOLBAR_HOVERED = Symbol('SET_TOOLBAR_HOVERED');
64
 export const SET_TOOLBAR_HOVERED = Symbol('SET_TOOLBAR_HOVERED');
75
 
65
 
66
+/**
67
+ * The type of the action which sets the permanent visibility of the Toolbox.
68
+ *
69
+ * {
70
+ *     type: SET_TOOLBOX_ALWAYS_VISIBLE,
71
+ *     alwaysVisible: boolean
72
+ * }
73
+ */
74
+export const SET_TOOLBOX_ALWAYS_VISIBLE = Symbol('SET_TOOLBOX_ALWAYS_VISIBLE');
75
+
76
+/**
77
+ * The type of the (redux) action which enables/disables the Toolbox.
78
+ *
79
+ * {
80
+ *     type: SET_TOOLBOX_ENABLED,
81
+ *     enabled: boolean
82
+ * }
83
+ */
84
+export const SET_TOOLBOX_ENABLED = Symbol('SET_TOOLBOX_ENABLED');
85
+
76
 /**
86
 /**
77
  * The type of the action which sets a new Toolbox visibility timeout and its
87
  * The type of the action which sets a new Toolbox visibility timeout and its
78
  * delay.
88
  * delay.

+ 18
- 1
react/features/toolbox/actions.native.js 查看文件

5
 import {
5
 import {
6
     CLEAR_TOOLBOX_TIMEOUT,
6
     CLEAR_TOOLBOX_TIMEOUT,
7
     SET_DEFAULT_TOOLBOX_BUTTONS,
7
     SET_DEFAULT_TOOLBOX_BUTTONS,
8
-    SET_TOOLBOX_ALWAYS_VISIBLE,
9
     SET_SUBJECT,
8
     SET_SUBJECT,
10
     SET_SUBJECT_SLIDE_IN,
9
     SET_SUBJECT_SLIDE_IN,
11
     SET_TOOLBAR_BUTTON,
10
     SET_TOOLBAR_BUTTON,
12
     SET_TOOLBAR_HOVERED,
11
     SET_TOOLBAR_HOVERED,
12
+    SET_TOOLBOX_ALWAYS_VISIBLE,
13
+    SET_TOOLBOX_ENABLED,
13
     SET_TOOLBOX_TIMEOUT,
14
     SET_TOOLBOX_TIMEOUT,
14
     SET_TOOLBOX_TIMEOUT_MS,
15
     SET_TOOLBOX_TIMEOUT_MS,
15
     SET_TOOLBOX_VISIBLE
16
     SET_TOOLBOX_VISIBLE
168
 
169
 
169
 /* eslint-disable flowtype/space-before-type-colon */
170
 /* eslint-disable flowtype/space-before-type-colon */
170
 
171
 
172
+/**
173
+ * Enables/disables the toolbox.
174
+ *
175
+ * @param {boolean} enabled - True to enable the toolbox or false to disable it.
176
+ * @returns {{
177
+ *     type: SET_TOOLBOX_ENABLED,
178
+ *     enabled: boolean
179
+ * }}
180
+ */
181
+export function setToolboxEnabled(enabled: boolean): Object {
182
+    return {
183
+        type: SET_TOOLBOX_ENABLED,
184
+        enabled
185
+    };
186
+}
187
+
171
 /**
188
 /**
172
  * Dispatches an action which sets new timeout and clears the previous one.
189
  * Dispatches an action which sets new timeout and clears the previous one.
173
  *
190
  *

+ 10
- 8
react/features/toolbox/actions.web.js 查看文件

3
 import Recording from '../../../modules/UI/recording/Recording';
3
 import Recording from '../../../modules/UI/recording/Recording';
4
 import SideContainerToggler
4
 import SideContainerToggler
5
     from '../../../modules/UI/side_pannels/SideContainerToggler';
5
     from '../../../modules/UI/side_pannels/SideContainerToggler';
6
-import UIEvents from '../../../service/UI/UIEvents';
7
 import UIUtil from '../../../modules/UI/util/UIUtil';
6
 import UIUtil from '../../../modules/UI/util/UIUtil';
7
+import UIEvents from '../../../service/UI/UIEvents';
8
 
8
 
9
 import {
9
 import {
10
     clearToolboxTimeout,
10
     clearToolboxTimeout,
171
  */
171
  */
172
 export function showRecordingButton(): Function {
172
 export function showRecordingButton(): Function {
173
     return (dispatch: Dispatch<*>) => {
173
     return (dispatch: Dispatch<*>) => {
174
-        const eventEmitter = APP.UI.eventEmitter;
175
-        const buttonName = 'recording';
176
-
177
-        dispatch(setToolbarButton(buttonName, {
174
+        dispatch(setToolbarButton('recording', {
178
             hidden: false
175
             hidden: false
179
         }));
176
         }));
180
 
177
 
181
-        Recording.init(eventEmitter, config.recordingType);
178
+        Recording.initRecordingButton();
182
     };
179
     };
183
 }
180
 }
184
 
181
 
234
 export function showToolbox(timeout: number = 0): Object {
231
 export function showToolbox(timeout: number = 0): Object {
235
     return (dispatch: Dispatch<*>, getState: Function) => {
232
     return (dispatch: Dispatch<*>, getState: Function) => {
236
         const state = getState();
233
         const state = getState();
237
-        const { alwaysVisible, timeoutMS, visible } = state['features/toolbox'];
234
+        const {
235
+            alwaysVisible,
236
+            enabled,
237
+            timeoutMS,
238
+            visible
239
+        } = state['features/toolbox'];
238
 
240
 
239
-        if (!visible) {
241
+        if (enabled && !visible) {
240
             dispatch(setToolboxVisible(true));
242
             dispatch(setToolboxVisible(true));
241
             dispatch(setSubjectSlideIn(true));
243
             dispatch(setSubjectSlideIn(true));
242
 
244
 

+ 22
- 7
react/features/toolbox/reducer.js 查看文件

5
 import {
5
 import {
6
     CLEAR_TOOLBOX_TIMEOUT,
6
     CLEAR_TOOLBOX_TIMEOUT,
7
     SET_DEFAULT_TOOLBOX_BUTTONS,
7
     SET_DEFAULT_TOOLBOX_BUTTONS,
8
-    SET_TOOLBOX_ALWAYS_VISIBLE,
9
     SET_SUBJECT,
8
     SET_SUBJECT,
10
     SET_SUBJECT_SLIDE_IN,
9
     SET_SUBJECT_SLIDE_IN,
11
     SET_TOOLBAR_BUTTON,
10
     SET_TOOLBAR_BUTTON,
12
     SET_TOOLBAR_HOVERED,
11
     SET_TOOLBAR_HOVERED,
12
+    SET_TOOLBOX_ALWAYS_VISIBLE,
13
+    SET_TOOLBOX_ENABLED,
13
     SET_TOOLBOX_TIMEOUT,
14
     SET_TOOLBOX_TIMEOUT,
14
     SET_TOOLBOX_TIMEOUT_MS,
15
     SET_TOOLBOX_TIMEOUT_MS,
15
     SET_TOOLBOX_VISIBLE
16
     SET_TOOLBOX_VISIBLE
51
          */
52
          */
52
         alwaysVisible: false,
53
         alwaysVisible: false,
53
 
54
 
55
+        /**
56
+         * The indicator which determines whether the Toolbox is enabled. For
57
+         * example, modules/UI/recording/Recording.js disables the Toolbox.
58
+         *
59
+         * @type {boolean}
60
+         */
61
+        enabled: true,
62
+
54
         /**
63
         /**
55
          * The indicator which determines whether a Toolbar in the Toolbox is
64
          * The indicator which determines whether a Toolbar in the Toolbox is
56
          * hovered.
65
          * hovered.
132
             };
141
             };
133
         }
142
         }
134
 
143
 
135
-        case SET_TOOLBOX_ALWAYS_VISIBLE:
136
-            return {
137
-                ...state,
138
-                alwaysVisible: action.alwaysVisible
139
-            };
140
-
141
         case SET_SUBJECT:
144
         case SET_SUBJECT:
142
             return {
145
             return {
143
                 ...state,
146
                 ...state,
159
                 hovered: action.hovered
162
                 hovered: action.hovered
160
             };
163
             };
161
 
164
 
165
+        case SET_TOOLBOX_ALWAYS_VISIBLE:
166
+            return {
167
+                ...state,
168
+                alwaysVisible: action.alwaysVisible
169
+            };
170
+
171
+        case SET_TOOLBOX_ENABLED:
172
+            return {
173
+                ...state,
174
+                enabled: action.enabled
175
+            };
176
+
162
         case SET_TOOLBOX_TIMEOUT:
177
         case SET_TOOLBOX_TIMEOUT:
163
             return {
178
             return {
164
                 ...state,
179
                 ...state,

正在加载...
取消
保存