浏览代码

ref(recording): convert recording label to react (#1915)

* ref(recording): convert recording label to react

- Create a RecordingLabel component for displaying the current
  recording state, as reflected in the redux store. This is
  needed for 1-on-1 mode to be completely in redux.
- Update the store with the recording state so RecordingLabel
  can update itself.
- Remove previous logic for updating the non-react label, which
  includes event emitting for filmstrip visibility changes,
  as RecordingLabel is hooked into redux updates.

* ref(recording): use status and type constants from lib

* make label really dumb, move logic back to Recording
j8
virtuacoplenny 8 年前
父节点
当前提交
735a596afe

+ 1
- 2
css/_recording.scss 查看文件

@@ -1,4 +1,3 @@
1 1
 .recordingSpinner {
2
-    display: none;
3 2
     vertical-align: top;
4
-}
3
+}

+ 5
- 2
css/modals/video-quality/_video-quality.scss 查看文件

@@ -154,12 +154,15 @@
154 154
     bottom: 45%;
155 155
     border-radius: 2px;
156 156
     display: none;
157
-    -webkit-transition: all 2s 2s linear;
158
-    transition: all 2s 2s linear;
157
+    padding: 10px;
158
+    transform: translate(-50%, 0);
159 159
     z-index: $centeredVideoLabelZ;
160 160
 
161 161
     &.moveToCorner {
162 162
         bottom: auto;
163
+        transform: none;
164
+        -webkit-transition: all 2s 2s linear;
165
+        transition: all 2s 2s linear;
163 166
     }
164 167
 }
165 168
 

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

@@ -22,6 +22,50 @@ import VideoLayout from '../videolayout/VideoLayout';
22 22
 
23 23
 import { setToolboxEnabled } from '../../../react/features/toolbox';
24 24
 import { setNotificationsEnabled } from '../../../react/features/notifications';
25
+import {
26
+    hideRecordingLabel,
27
+    updateRecordingState
28
+} from '../../../react/features/recording';
29
+
30
+const Status = JitsiMeetJS.constants.recordingStatus;
31
+
32
+/**
33
+ * Translation keys to use for display in the UI when recording the conference
34
+ * but not streaming live.
35
+ *
36
+ * @private
37
+ * @type {Object}
38
+ */
39
+export const RECORDING_TRANSLATION_KEYS = {
40
+    failedToStartKey: 'recording.failedToStart',
41
+    recordingBusy: 'liveStreaming.busy',
42
+    recordingButtonTooltip: 'recording.buttonTooltip',
43
+    recordingErrorKey: 'recording.error',
44
+    recordingOffKey: 'recording.off',
45
+    recordingOnKey: 'recording.on',
46
+    recordingPendingKey: 'recording.pending',
47
+    recordingTitle: 'dialog.recording',
48
+    recordingUnavailable: 'recording.unavailable'
49
+};
50
+
51
+/**
52
+ * Translation keys to use for display in the UI when the recording mode is
53
+ * currently streaming live.
54
+ *
55
+ * @private
56
+ * @type {Object}
57
+ */
58
+export const STREAMING_TRANSLATION_KEYS = {
59
+    failedToStartKey: 'liveStreaming.failedToStart',
60
+    recordingBusy: 'liveStreaming.busy',
61
+    recordingButtonTooltip: 'liveStreaming.buttonTooltip',
62
+    recordingErrorKey: 'liveStreaming.error',
63
+    recordingOffKey: 'liveStreaming.off',
64
+    recordingOnKey: 'liveStreaming.on',
65
+    recordingPendingKey: 'liveStreaming.pending',
66
+    recordingTitle: 'dialog.liveStreaming',
67
+    recordingUnavailable: 'liveStreaming.unavailable'
68
+};
25 69
 
26 70
 /**
27 71
  * The dialog for user input.
@@ -194,57 +238,6 @@ function _showStopRecordingPrompt(recordingType) {
194 238
     });
195 239
 }
196 240
 
197
-/**
198
- * Moves the element given by {selector} to the top right corner of the screen.
199
- * Set additional classes that can be used to style the selector relative to the
200
- * state of the filmstrip.
201
- *
202
- * @param selector the selector for the element to move
203
- * @param move {true} to move the element, {false} to move it back to its intial
204
- * position
205
- */
206
-function moveToCorner(selector, move) {
207
-    let moveToCornerClass = "moveToCorner";
208
-    let containsClass = selector.hasClass(moveToCornerClass);
209
-
210
-    if (move && !containsClass)
211
-        selector.addClass(moveToCornerClass);
212
-    else if (!move && containsClass)
213
-        selector.removeClass(moveToCornerClass);
214
-
215
-    const {
216
-        remoteVideosVisible,
217
-        visible
218
-    } = APP.store.getState()['features/filmstrip'];
219
-    const filmstripWasHidden = selector.hasClass('without-filmstrip');
220
-    const filmstipIsOpening = filmstripWasHidden && visible;
221
-    selector.toggleClass('opening', filmstipIsOpening);
222
-
223
-    selector.toggleClass('with-filmstrip', visible);
224
-    selector.toggleClass('without-filmstrip', !visible);
225
-
226
-    selector.toggleClass('with-remote-videos', remoteVideosVisible);
227
-    selector.toggleClass('without-remote-videos', !remoteVideosVisible);
228
-}
229
-
230
-/**
231
- * The status of the recorder.
232
- * FIXME: Those constants should come from the library.
233
- * @type {{ON: string, OFF: string, AVAILABLE: string,
234
- * UNAVAILABLE: string, PENDING: string}}
235
- */
236
-var Status = {
237
-    ON: "on",
238
-    OFF: "off",
239
-    AVAILABLE: "available",
240
-    UNAVAILABLE: "unavailable",
241
-    PENDING: "pending",
242
-    RETRYING: "retrying",
243
-    ERROR: "error",
244
-    FAILED: "failed",
245
-    BUSY: "busy"
246
-};
247
-
248 241
 /**
249 242
  * Checks whether if the given status is either PENDING or RETRYING
250 243
  * @param status {Status} Jibri status to be checked
@@ -271,27 +264,11 @@ var Recording = {
271 264
 
272 265
         if (recordingType === 'jibri') {
273 266
             this.baseClass = "fa fa-play-circle";
274
-            this.recordingTitle = "dialog.liveStreaming";
275
-            this.recordingOnKey = "liveStreaming.on";
276
-            this.recordingOffKey = "liveStreaming.off";
277
-            this.recordingPendingKey = "liveStreaming.pending";
278
-            this.failedToStartKey = "liveStreaming.failedToStart";
279
-            this.recordingErrorKey = "liveStreaming.error";
280
-            this.recordingButtonTooltip = "liveStreaming.buttonTooltip";
281
-            this.recordingUnavailable = "liveStreaming.unavailable";
282
-            this.recordingBusy = "liveStreaming.busy";
267
+            Object.assign(this, STREAMING_TRANSLATION_KEYS);
283 268
         }
284 269
         else {
285 270
             this.baseClass = "icon-recEnable";
286
-            this.recordingTitle = "dialog.recording";
287
-            this.recordingOnKey = "recording.on";
288
-            this.recordingOffKey = "recording.off";
289
-            this.recordingPendingKey = "recording.pending";
290
-            this.failedToStartKey = "recording.failedToStart";
291
-            this.recordingErrorKey = "recording.error";
292
-            this.recordingButtonTooltip = "recording.buttonTooltip";
293
-            this.recordingUnavailable = "recording.unavailable";
294
-            this.recordingBusy = "liveStreaming.busy";
271
+            Object.assign(this, RECORDING_TRANSLATION_KEYS);
295 272
         }
296 273
 
297 274
         // XXX Due to the React-ification of Toolbox, the HTMLElement with id
@@ -311,10 +288,6 @@ var Recording = {
311 288
             APP.store.dispatch(setNotificationsEnabled(false));
312 289
             APP.UI.messageHandler.enablePopups(false);
313 290
         }
314
-
315
-        this.eventEmitter.addListener(UIEvents.UPDATED_FILMSTRIP_DISPLAY, () =>{
316
-            this._updateStatusLabel();
317
-        });
318 291
     },
319 292
 
320 293
     /**
@@ -364,65 +337,85 @@ var Recording = {
364 337
         let oldState = this.currentState;
365 338
         this.currentState = recordingState;
366 339
 
367
-        // TODO: handle recording state=available
368
-        if (recordingState === Status.ON ||
369
-            recordingState === Status.RETRYING) {
340
+        let labelDisplayConfiguration;
341
+
342
+        switch (recordingState) {
343
+        case Status.ON:
344
+        case Status.RETRYING: {
345
+            labelDisplayConfiguration = {
346
+                centered: false,
347
+                key: this.recordingOnKey,
348
+                showSpinner: recordingState === Status.RETRYING
349
+            };
370 350
 
371 351
             this._setToolbarButtonToggled(true);
372 352
 
373
-            this._updateStatusLabel(this.recordingOnKey, false);
353
+            break;
374 354
         }
375
-        else if (recordingState === Status.OFF
376
-                || recordingState === Status.UNAVAILABLE
377
-                || recordingState === Status.BUSY
378
-                || recordingState === Status.FAILED) {
379
-
380
-            // We don't want to do any changes if this is
381
-            // an availability change.
382
-            if (oldState !== Status.ON
383
-                && !isStartingStatus(oldState))
384
-                return;
385 355
 
386
-            this._setToolbarButtonToggled(false);
356
+        case Status.OFF:
357
+        case Status.BUSY:
358
+        case Status.FAILED:
359
+        case Status.UNAVAILABLE: {
360
+            const wasInStartingStatus = isStartingStatus(oldState);
387 361
 
388
-            let messageKey;
389
-            if (isStartingStatus(oldState))
390
-                messageKey = this.failedToStartKey;
391
-            else
392
-                messageKey = this.recordingOffKey;
362
+            // We don't want UI changes if this is an availability change.
363
+            if (oldState !== Status.ON && !wasInStartingStatus) {
364
+                APP.store.dispatch(updateRecordingState({ recordingState }));
365
+                return;
366
+            }
393 367
 
394
-            this._updateStatusLabel(messageKey, true);
368
+            labelDisplayConfiguration = {
369
+                centered: true,
370
+                key: wasInStartingStatus
371
+                    ? this.failedToStartKey
372
+                    : this.recordingOffKey
373
+            };
374
+
375
+            this._setToolbarButtonToggled(false);
395 376
 
396 377
             setTimeout(function(){
397
-                $('#recordingLabel').css({display: "none"});
378
+                APP.store.dispatch(hideRecordingLabel());
398 379
             }, 5000);
380
+
381
+            break;
399 382
         }
400
-        else if (recordingState === Status.PENDING) {
383
+
384
+        case Status.PENDING: {
385
+            labelDisplayConfiguration = {
386
+                centered: true,
387
+                key: this.recordingPendingKey
388
+            };
401 389
 
402 390
             this._setToolbarButtonToggled(false);
403 391
 
404
-            this._updateStatusLabel(this.recordingPendingKey, true);
392
+            break;
405 393
         }
406
-        else if (recordingState === Status.ERROR
407
-                    || recordingState === Status.FAILED) {
394
+
395
+        case Status.ERROR: {
396
+            labelDisplayConfiguration = {
397
+                centered: true,
398
+                key: this.recordingErrorKey
399
+            };
408 400
 
409 401
             this._setToolbarButtonToggled(false);
410 402
 
411
-            this._updateStatusLabel(this.recordingErrorKey, true);
403
+            break;
412 404
         }
413 405
 
414
-        let labelSelector = $('#recordingLabel');
415
-
416
-        // We don't show the label for available state.
417
-        if (recordingState !== Status.AVAILABLE
418
-            && !labelSelector.is(":visible"))
419
-            labelSelector.css({display: "inline-block"});
406
+        // Return an empty label display configuration to indicate no label
407
+        // should be displayed. The Status.AVAIABLE case is handled here.
408
+        default: {
409
+            labelDisplayConfiguration = null;
410
+        }
411
+        }
420 412
 
421
-        // Recording spinner
422
-        let spinnerId = 'recordingSpinner';
423
-        UIUtil.setVisible(
424
-            spinnerId, recordingState === Status.RETRYING);
413
+        APP.store.dispatch(updateRecordingState({
414
+            labelDisplayConfiguration,
415
+            recordingState
416
+        }));
425 417
     },
418
+
426 419
     // checks whether recording is enabled and whether we have params
427 420
     // to start automatically recording
428 421
     checkAutoRecord() {
@@ -432,21 +425,6 @@ var Recording = {
432 425
                                     this.predefinedToken);
433 426
         }
434 427
     },
435
-    /**
436
-     * Updates the status label.
437
-     * @param textKey the text to show
438
-     * @param isCentered indicates if the label should be centered on the window
439
-     * or moved to the top right corner.
440
-     */
441
-    _updateStatusLabel(textKey, isCentered) {
442
-        let labelSelector = $('#recordingLabel');
443
-        let labelTextSelector = $('#recordingLabelText');
444
-
445
-        moveToCorner(labelSelector, !isCentered);
446
-
447
-        labelTextSelector.attr("data-i18n", textKey);
448
-        APP.translation.translateElement(labelSelector);
449
-    },
450 428
 
451 429
     /**
452 430
      * Handles {@code click} on {@code toolbar_button_record}.

+ 0
- 16
react/features/filmstrip/middleware.js 查看文件

@@ -4,12 +4,6 @@ import { MiddlewareRegistry } from '../base/redux';
4 4
 import { SET_CALL_OVERLAY_VISIBLE } from '../jwt';
5 5
 
6 6
 import Filmstrip from '../../../modules/UI/videolayout/Filmstrip';
7
-import UIEvents from '../../../service/UI/UIEvents';
8
-
9
-import {
10
-    SET_FILMSTRIP_REMOTE_VIDEOS_VISIBLITY,
11
-    SET_FILMSTRIP_VISIBILITY
12
-} from './actionTypes';
13 7
 
14 8
 declare var APP: Object;
15 9
 
@@ -37,16 +31,6 @@ MiddlewareRegistry.register(({ getState }) => next => action => {
37 31
             return result;
38 32
         }
39 33
         break;
40
-
41
-    case SET_FILMSTRIP_REMOTE_VIDEOS_VISIBLITY:
42
-    case SET_FILMSTRIP_VISIBILITY: {
43
-        const result = next(action);
44
-
45
-        typeof APP === 'undefined'
46
-            || APP.UI.emitEvent(UIEvents.UPDATED_FILMSTRIP_DISPLAY);
47
-
48
-        return result;
49
-    }
50 34
     }
51 35
 
52 36
     return next(action);

+ 2
- 11
react/features/large-video/components/LargeVideo.web.js 查看文件

@@ -4,6 +4,7 @@ import React, { Component } from 'react';
4 4
 
5 5
 import { Watermarks } from '../../base/react';
6 6
 import { VideoQualityLabel } from '../../video-quality';
7
+import { RecordingLabel } from '../../recording';
7 8
 
8 9
 declare var interfaceConfig: Object;
9 10
 
@@ -67,18 +68,8 @@ export default class LargeVideo extends Component {
67 68
                     </div>
68 69
                 </div>
69 70
                 <span id = 'localConnectionMessage' />
70
-
71 71
                 { interfaceConfig.filmStripOnly ? null : <VideoQualityLabel /> }
72
-
73
-                <span
74
-                    className = 'video-state-indicator centeredVideoLabel'
75
-                    id = 'recordingLabel'>
76
-                    <span id = 'recordingLabelText' />
77
-                    <img
78
-                        className = 'recordingSpinner'
79
-                        id = 'recordingSpinner'
80
-                        src = 'images/spin.svg' />
81
-                </span>
72
+                <RecordingLabel />
82 73
             </div>
83 74
         );
84 75
     }

+ 22
- 0
react/features/recording/actionTypes.js 查看文件

@@ -0,0 +1,22 @@
1
+/**
2
+ * The type of Redux action which signals for the label indicating current
3
+ * recording state to stop displaying.
4
+ *
5
+ * {
6
+ *     type: HIDE_RECORDING_LABEL
7
+ * }
8
+ * @public
9
+ */
10
+export const HIDE_RECORDING_LABEL = Symbol('HIDE_RECORDING_LABEL');
11
+
12
+/**
13
+ * The type of Redux action which updates the current known state of the
14
+ * recording feature.
15
+ *
16
+ * {
17
+ *     type: RECORDING_STATE_UPDATED,
18
+ *     recordingState: string
19
+ * }
20
+ * @public
21
+ */
22
+export const RECORDING_STATE_UPDATED = Symbol('RECORDING_STATE_UPDATED');

+ 31
- 0
react/features/recording/actions.js 查看文件

@@ -0,0 +1,31 @@
1
+import { HIDE_RECORDING_LABEL, RECORDING_STATE_UPDATED } from './actionTypes';
2
+
3
+/**
4
+ * Hides any displayed recording label, regardless of current recording state.
5
+ *
6
+ * @returns {{
7
+ *     type: HIDE_RECORDING_LABEL
8
+ * }}
9
+ */
10
+export function hideRecordingLabel() {
11
+    return {
12
+        type: HIDE_RECORDING_LABEL
13
+    };
14
+}
15
+
16
+/**
17
+ * Updates the redux state for the recording feature.
18
+ *
19
+ * @param {Object} recordingState - The new state to merge with the existing
20
+ * state in redux.
21
+ * @returns {{
22
+ *     type: RECORDING_STATE_UPDATED,
23
+ *     recordingState: Object
24
+ * }}
25
+ */
26
+export function updateRecordingState(recordingState = {}) {
27
+    return {
28
+        type: RECORDING_STATE_UPDATED,
29
+        recordingState
30
+    };
31
+}

+ 0
- 0
react/features/recording/components/RecordingLabel.native.js 查看文件


+ 171
- 0
react/features/recording/components/RecordingLabel.web.js 查看文件

@@ -0,0 +1,171 @@
1
+import React, { Component } from 'react';
2
+import { connect } from 'react-redux';
3
+
4
+import { translate } from '../../base/i18n';
5
+
6
+/**
7
+ * Implements a React {@link Component} which displays the current state of
8
+ * conference recording. Currently it uses CSS to display itself automatically
9
+ * when there is a recording state update.
10
+ *
11
+ * @extends {Component}
12
+ */
13
+class RecordingLabel extends Component {
14
+    /**
15
+     * {@code RecordingLabel} component's property types.
16
+     *
17
+     * @static
18
+     */
19
+    static propTypes = {
20
+        /**
21
+         * Whether or not the filmstrip is currently visible or toggled to
22
+         * hidden. Depending on the filmstrip state, different CSS classes will
23
+         * be set to allow for adjusting of {@code RecordingLabel} positioning.
24
+         */
25
+        _filmstripVisible: React.PropTypes.bool,
26
+
27
+        /**
28
+         * An object to describe the {@code RecordingLabel} content. If no
29
+         * translation key to display is specified, the label will apply CSS to
30
+         * itself so it can be made invisible.
31
+         * {{
32
+         *     centered: boolean,
33
+         *     key: string,
34
+         *     showSpinner: boolean
35
+         * }}
36
+         */
37
+        _labelDisplayConfiguration: React.PropTypes.object,
38
+
39
+        /**
40
+         * Whether or not remote videos within the filmstrip are currently
41
+         * visible. Depending on the visibility state, coupled with filmstrip
42
+         * visibility, CSS classes will be set to allow for adjusting of
43
+         * {@code RecordingLabel} positioning.
44
+         */
45
+        _remoteVideosVisible: React.PropTypes.bool,
46
+
47
+        /**
48
+         * Invoked to obtain translated string.
49
+         */
50
+        t: React.PropTypes.func
51
+    };
52
+
53
+    /**
54
+     * Initializes a new {@code RecordingLabel} instance.
55
+     *
56
+     * @param {Object} props - The read-only properties with which the new
57
+     * instance is to be initialized.
58
+     */
59
+    constructor(props) {
60
+        super(props);
61
+
62
+        this.state = {
63
+            /**
64
+             * Whether or not the filmstrip was not visible but has transitioned
65
+             * in the latest component update to visible. This boolean is used
66
+             * to set a class for position animations.
67
+             *
68
+             * @type {boolean}
69
+             */
70
+            filmstripBecomingVisible: false
71
+        };
72
+    }
73
+
74
+    /**
75
+     * Updates the state for whether or not the filmstrip is being toggled to
76
+     * display after having being hidden.
77
+     *
78
+     * @inheritdoc
79
+     * @param {Object} nextProps - The read-only props which this Component will
80
+     * receive.
81
+     * @returns {void}
82
+     */
83
+    componentWillReceiveProps(nextProps) {
84
+        this.setState({
85
+            filmstripBecomingVisible: nextProps._filmstripVisible
86
+                && !this.props._filmstripVisible
87
+        });
88
+    }
89
+
90
+    /**
91
+     * Implements React's {@link Component#render()}.
92
+     *
93
+     * @inheritdoc
94
+     * @returns {ReactElement}
95
+     */
96
+    render() {
97
+        const { _labelDisplayConfiguration } = this.props;
98
+        const { centered, key, showSpinner } = _labelDisplayConfiguration || {};
99
+
100
+        const isVisible = Boolean(key);
101
+        const rootClassName = [
102
+            'video-state-indicator centeredVideoLabel',
103
+            isVisible ? 'show-inline' : '',
104
+            centered ? '' : 'moveToCorner',
105
+            this.state.filmstripBecomingVisible ? 'opening' : '',
106
+            this.props._filmstripVisible
107
+                ? 'with-filmstrip' : 'without-filmstrip',
108
+            this.props._remoteVideosVisible
109
+                ? 'with-remote-videos' : 'without-remote-videos'
110
+        ].join(' ');
111
+
112
+        return (
113
+            <span
114
+                className = { rootClassName }
115
+                id = 'recordingLabel'>
116
+                <span id = 'recordingLabelText'>
117
+                    { this.props.t(key) }
118
+                </span>
119
+                { showSpinner
120
+                    ? <img
121
+                        className = 'recordingSpinner'
122
+                        id = 'recordingSpinner'
123
+                        src = 'images/spin.svg' />
124
+                    : null }
125
+            </span>
126
+        );
127
+    }
128
+}
129
+
130
+/**
131
+ * Maps (parts of) the Redux state to the associated {@code RecordingLabel}
132
+ * component's props.
133
+ *
134
+ * @param {Object} state - The Redux state.
135
+ * @private
136
+ * @returns {{
137
+ *     _filmstripVisible: boolean,
138
+ *     _labelDisplayConfiguration: Object,
139
+ *     _remoteVideosVisible: boolean,
140
+ * }}
141
+ */
142
+function _mapStateToProps(state) {
143
+    const { remoteVideosVisible, visible } = state['features/filmstrip'];
144
+    const { labelDisplayConfiguration } = state['features/recording'];
145
+
146
+    return {
147
+        /**
148
+         * Whether or not the filmstrip is currently set to be displayed.
149
+         *
150
+         * @type {boolean}
151
+         */
152
+        _filmstripVisible: visible,
153
+
154
+        /**
155
+         * An object describing how {@code RecordingLabel} should display its
156
+         * contents.
157
+         *
158
+         * @type {Object}
159
+         */
160
+        _labelDisplayConfiguration: labelDisplayConfiguration,
161
+
162
+        /**
163
+         * Whether or not remote videos are displayed in the filmstrip.
164
+         *
165
+         * @type {boolean}
166
+         */
167
+        _remoteVideosVisible: remoteVideosVisible
168
+    };
169
+}
170
+
171
+export default translate(connect(_mapStateToProps)(RecordingLabel));

+ 1
- 0
react/features/recording/components/index.js 查看文件

@@ -0,0 +1 @@
1
+export { default as RecordingLabel } from './RecordingLabel';

+ 4
- 0
react/features/recording/index.js 查看文件

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

+ 24
- 0
react/features/recording/reducer.js 查看文件

@@ -0,0 +1,24 @@
1
+import { ReducerRegistry } from '../base/redux';
2
+import { HIDE_RECORDING_LABEL, RECORDING_STATE_UPDATED } from './actionTypes';
3
+
4
+/**
5
+ * Reduces the Redux actions of the feature features/recording.
6
+ */
7
+ReducerRegistry.register('features/recording', (state = {}, action) => {
8
+    switch (action.type) {
9
+    case HIDE_RECORDING_LABEL:
10
+        return {
11
+            ...state,
12
+            labelDisplayConfiguration: null
13
+        };
14
+
15
+    case RECORDING_STATE_UPDATED:
16
+        return {
17
+            ...state,
18
+            ...action.recordingState
19
+        };
20
+
21
+    default:
22
+        return state;
23
+    }
24
+});

+ 0
- 5
service/UI/UIEvents.js 查看文件

@@ -66,11 +66,6 @@ export default {
66 66
      */
67 67
     TOGGLED_FILMSTRIP: "UI.toggled_filmstrip",
68 68
 
69
-    /**
70
-     * Notifies that the filmstrip has updated its appearance, such as by
71
-     * toggling or removing videos or adding videos.
72
-     */
73
-    UPDATED_FILMSTRIP_DISPLAY: "UI.updated_filmstrip_display",
74 69
     TOGGLE_SCREENSHARING: "UI.toggle_screensharing",
75 70
     TOGGLED_SHARED_DOCUMENT: "UI.toggled_shared_document",
76 71
     CONTACT_CLICKED: "UI.contact_clicked",

正在加载...
取消
保存