Преглед на файлове

fix(reactions) fix reactions bugs

* Fix: removed web actions from common middlewares

* Fixed raise hand sound

Fix sound to play on raise not lower and work on keyboard shortcut as well

* Fixed reaction keyboard shortcuts

Register shortcuts only when there's more than one participant

* Enforce reactions feature flag on reaction received

* Disable reactions by default on native

* Enable reactions on native by default

* Sort props alphabetically

* Created isreactionsEnabled function

* Remove unused imports

* Fix. No longer show toolbox on reactions and jibri

On message received don't show toolbox for jibri

* Fix isReactionsEnabled function for native

On native check for flag and config option as well
master
robertpin преди 3 години
родител
ревизия
5367d43c26
No account linked to committer's email address

+ 0
- 5
modules/API/constants.js Целия файл

@@ -15,8 +15,3 @@ export const API_ID = parseURLParams(window.location).jitsi_meet_external_api_id
15 15
  * The payload name for the datachannel/endpoint text message event
16 16
  */
17 17
 export const ENDPOINT_TEXT_MESSAGE_NAME = 'endpoint-text-message';
18
-
19
-/**
20
- * The payload name for the datachannel/endpoint reaction event
21
- */
22
-export const ENDPOINT_REACTION_NAME = 'endpoint-reaction';

+ 2
- 0
react/features/base/participants/middleware.js Целия файл

@@ -6,6 +6,7 @@ import UIEvents from '../../../../service/UI/UIEvents';
6 6
 import { toggleE2EE } from '../../e2ee/actions';
7 7
 import { NOTIFICATION_TIMEOUT, showNotification } from '../../notifications';
8 8
 import { CALLING, INVITED } from '../../presence-status';
9
+import { RAISE_HAND_SOUND_ID } from '../../reactions/constants';
9 10
 import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../app';
10 11
 import {
11 12
     CONFERENCE_WILL_JOIN,
@@ -495,6 +496,7 @@ function _raiseHandUpdated({ dispatch, getState }, conference, participantId, ne
495 496
             },
496 497
             titleKey: 'notify.raisedHand'
497 498
         }, NOTIFICATION_TIMEOUT));
499
+        dispatch(playSound(RAISE_HAND_SOUND_ID));
498 500
     }
499 501
 }
500 502
 

+ 17
- 19
react/features/chat/middleware.js Целия файл

@@ -1,8 +1,5 @@
1 1
 // @flow
2 2
 
3
-import { batch } from 'react-redux';
4
-
5
-import { ENDPOINT_REACTION_NAME } from '../../../modules/API/constants';
6 3
 import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app';
7 4
 import {
8 5
     CONFERENCE_JOINED,
@@ -25,14 +22,13 @@ import { openDisplayNamePrompt } from '../display-name';
25 22
 import { resetNbUnreadPollsMessages } from '../polls/actions';
26 23
 import { ADD_REACTION_MESSAGE } from '../reactions/actionTypes';
27 24
 import { pushReactions } from '../reactions/actions.any';
28
-import { getReactionMessageFromBuffer } from '../reactions/functions.any';
25
+import { ENDPOINT_REACTION_NAME } from '../reactions/constants';
26
+import { getReactionMessageFromBuffer, isReactionsEnabled } from '../reactions/functions.any';
29 27
 import { endpointMessageReceived } from '../subtitles';
30
-import { showToolbox } from '../toolbox/actions';
31 28
 import {
32
-    hideToolbox,
33
-    setToolboxTimeout,
34
-    setToolboxVisible
35
-} from '../toolbox/actions.web';
29
+    showToolbox
30
+} from '../toolbox/actions';
31
+
36 32
 
37 33
 import { ADD_MESSAGE, SEND_MESSAGE, OPEN_CHAT, CLOSE_CHAT, SET_IS_POLL_TAB_FOCUSED } from './actionTypes';
38 34
 import { addMessage, clearMessages } from './actions';
@@ -255,20 +251,19 @@ function _addChatMsgListener(conference, store) {
255 251
     conference.on(
256 252
         JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
257 253
         (...args) => {
254
+            const state = store.getState();
255
+
256
+            if (!isReactionsEnabled(state)) {
257
+                return;
258
+            }
259
+
258 260
             store.dispatch(endpointMessageReceived(...args));
259 261
 
260 262
             if (args && args.length >= 2) {
261 263
                 const [ { _id }, eventData ] = args;
262 264
 
263 265
                 if (eventData.name === ENDPOINT_REACTION_NAME) {
264
-                    batch(() => {
265
-                        store.dispatch(setToolboxVisible(true));
266
-                        store.dispatch(setToolboxTimeout(
267
-                                () => store.dispatch(hideToolbox()),
268
-                                5000)
269
-                        );
270
-                        store.dispatch(pushReactions(eventData.reactions));
271
-                    });
266
+                    store.dispatch(pushReactions(eventData.reactions));
272 267
 
273 268
                     _handleReceivedMessage(store, {
274 269
                         id: _id,
@@ -318,7 +313,7 @@ function _handleReceivedMessage({ dispatch, getState },
318 313
     // Logic for all platforms:
319 314
     const state = getState();
320 315
     const { isOpen: isChatOpen } = state['features/chat'];
321
-    const { disableIncomingMessageSound } = state['features/base/config'];
316
+    const { disableIncomingMessageSound, iAmRecorder } = state['features/base/config'];
322 317
     const { soundsIncomingMessage: soundEnabled } = state['features/base/settings'];
323 318
 
324 319
     if (!disableIncomingMessageSound && soundEnabled && shouldPlaySound && !isChatOpen) {
@@ -356,7 +351,10 @@ function _handleReceivedMessage({ dispatch, getState },
356 351
             ts: timestamp
357 352
         });
358 353
 
359
-        dispatch(showToolbox(4000));
354
+        if (!iAmRecorder) {
355
+            dispatch(showToolbox(4000));
356
+        }
357
+
360 358
     }
361 359
 }
362 360
 

+ 0
- 0
react/features/reactions/actions.native.js Целия файл


+ 4
- 15
react/features/reactions/components/web/ReactionsMenu.js Целия файл

@@ -11,11 +11,10 @@ import {
11 11
 import { translate } from '../../../base/i18n';
12 12
 import { getLocalParticipant, getParticipantCount, participantUpdated } from '../../../base/participants';
13 13
 import { connect } from '../../../base/redux';
14
-import { playSound } from '../../../base/sounds';
15 14
 import { dockToolbox } from '../../../toolbox/actions.web';
16 15
 import { addReactionToBuffer } from '../../actions.any';
17 16
 import { toggleReactionsMenuVisibility } from '../../actions.web';
18
-import { RAISE_HAND_SOUND_ID, REACTIONS } from '../../constants';
17
+import { REACTIONS } from '../../constants';
19 18
 
20 19
 import ReactionButton from './ReactionButton';
21 20
 
@@ -54,12 +53,7 @@ type Props = {
54 53
     /**
55 54
      * Whether or not it's displayed in the overflow menu.
56 55
      */
57
-    overflowMenu: boolean,
58
-
59
-    /**
60
-     * Whether or not reaction sounds are enabled.
61
-     */
62
-    _reactionSounds: boolean
56
+    overflowMenu: boolean
63 57
 };
64 58
 
65 59
 declare var APP: Object;
@@ -112,16 +106,13 @@ class ReactionsMenu extends Component<Props> {
112 106
      * @returns {void}
113 107
      */
114 108
     _onToolbarToggleRaiseHand() {
115
-        const { dispatch, _raisedHand, _reactionSounds } = this.props;
109
+        const { dispatch, _raisedHand } = this.props;
116 110
 
117 111
         sendAnalytics(createToolbarEvent(
118 112
             'raise.hand',
119 113
             { enable: !_raisedHand }));
120 114
         this._doToggleRaiseHand();
121 115
         dispatch(toggleReactionsMenuVisibility());
122
-        if (_reactionSounds && _raisedHand) {
123
-            dispatch(playSound(RAISE_HAND_SOUND_ID));
124
-        }
125 116
     }
126 117
 
127 118
     /**
@@ -223,13 +214,11 @@ class ReactionsMenu extends Component<Props> {
223 214
  */
224 215
 function mapStateToProps(state) {
225 216
     const localParticipant = getLocalParticipant(state);
226
-    const { soundsReactions } = state['features/base/settings'];
227 217
 
228 218
     return {
229 219
         _localParticipantID: localParticipant.id,
230 220
         _raisedHand: localParticipant.raisedHand,
231
-        _participantCount: getParticipantCount(state),
232
-        _reactionSounds: soundsReactions
221
+        _participantCount: getParticipantCount(state)
233 222
     };
234 223
 }
235 224
 

+ 5
- 0
react/features/reactions/constants.js Целия файл

@@ -9,6 +9,11 @@ import {
9 9
     SILENCE_SOUND_FILES
10 10
 } from './sounds';
11 11
 
12
+/**
13
+ * The payload name for the datachannel/endpoint reaction event
14
+ */
15
+export const ENDPOINT_REACTION_NAME = 'endpoint-reaction';
16
+
12 17
 /**
13 18
  * The audio ID prefix of the audio element for which the {@link playAudio} action is
14 19
  * triggered when a new laugh reaction is received.

+ 17
- 0
react/features/reactions/functions.any.js Целия файл

@@ -2,6 +2,7 @@
2 2
 
3 3
 import uuid from 'uuid';
4 4
 
5
+import { getFeatureFlag, REACTIONS_ENABLED } from '../base/flags';
5 6
 import { getLocalParticipant } from '../base/participants';
6 7
 import { extractFqnFromPath } from '../dynamic-branding/functions';
7 8
 
@@ -142,3 +143,19 @@ export function getReactionsSoundsThresholds(reactions: Array<string>) {
142 143
         };
143 144
     });
144 145
 }
146
+
147
+/**
148
+ * Whether or not the reactions are enabled.
149
+ *
150
+ * @param {Object} state - The Redux state object.
151
+ * @returns {boolean}
152
+ */
153
+export function isReactionsEnabled(state: Object) {
154
+    const { enableReactions } = state['features/base/config'];
155
+
156
+    if (navigator.product === 'ReactNative') {
157
+        return enableReactions && getFeatureFlag(state, REACTIONS_ENABLED, true);
158
+    }
159
+
160
+    return enableReactions;
161
+}

+ 3
- 4
react/features/reactions/middleware.js Целия файл

@@ -2,7 +2,6 @@
2 2
 
3 3
 import { batch } from 'react-redux';
4 4
 
5
-import { ENDPOINT_REACTION_NAME } from '../../../modules/API/constants';
6 5
 import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app';
7 6
 import { MiddlewareRegistry } from '../base/redux';
8 7
 import { updateSettings } from '../base/settings';
@@ -17,6 +16,7 @@ import {
17 16
     PUSH_REACTIONS,
18 17
     SHOW_SOUNDS_NOTIFICATION
19 18
 } from './actionTypes';
19
+import { displayReactionSoundsNotification } from './actions';
20 20
 import {
21 21
     addReactionsToChat,
22 22
     flushReactionBuffer,
@@ -24,8 +24,7 @@ import {
24 24
     sendReactions,
25 25
     setReactionQueue
26 26
 } from './actions.any';
27
-import { displayReactionSoundsNotification } from './actions.web';
28
-import { RAISE_HAND_SOUND_ID, REACTIONS, SOUNDS_THRESHOLDS } from './constants';
27
+import { ENDPOINT_REACTION_NAME, RAISE_HAND_SOUND_ID, REACTIONS, SOUNDS_THRESHOLDS } from './constants';
29 28
 import {
30 29
     getReactionMessageFromBuffer,
31 30
     getReactionsSoundsThresholds,
@@ -128,7 +127,7 @@ MiddlewareRegistry.register(store => next => action => {
128 127
         const reactions = action.reactions;
129 128
 
130 129
         batch(() => {
131
-            if (!notificationDisplayed && soundsReactions) {
130
+            if (!notificationDisplayed && soundsReactions && displayReactionSoundsNotification) {
132 131
                 dispatch(displayReactionSoundsNotification());
133 132
             }
134 133
             if (soundsReactions) {

+ 2
- 2
react/features/toolbox/components/native/OverflowMenu.js Целия файл

@@ -5,13 +5,13 @@ import { Divider } from 'react-native-paper';
5 5
 
6 6
 import { ColorSchemeRegistry } from '../../../base/color-scheme';
7 7
 import { BottomSheet, hideDialog, isDialogOpen } from '../../../base/dialog';
8
-import { getFeatureFlag, REACTIONS_ENABLED } from '../../../base/flags';
9 8
 import { connect } from '../../../base/redux';
10 9
 import { StyleType } from '../../../base/styles';
11 10
 import { SharedDocumentButton } from '../../../etherpad';
12 11
 import { AudioRouteButton } from '../../../mobile/audio-mode';
13 12
 import { ParticipantsPaneButton } from '../../../participants-pane/components/native';
14 13
 import { ReactionMenu } from '../../../reactions/components';
14
+import { isReactionsEnabled } from '../../../reactions/functions.any';
15 15
 import { LiveStreamButton, RecordButton } from '../../../recording';
16 16
 import SecurityDialogButton from '../../../security/components/security-dialog/SecurityDialogButton';
17 17
 import { SharedVideoButton } from '../../../shared-video/components';
@@ -205,7 +205,7 @@ function _mapStateToProps(state) {
205 205
         _bottomSheetStyles: ColorSchemeRegistry.get(state, 'BottomSheet'),
206 206
         _isOpen: isDialogOpen(state, OverflowMenu_),
207 207
         _width: state['features/base/responsive-ui'].clientWidth,
208
-        _reactionsEnabled: getFeatureFlag(state, REACTIONS_ENABLED, true)
208
+        _reactionsEnabled: isReactionsEnabled(state)
209 209
     };
210 210
 }
211 211
 

+ 2
- 2
react/features/toolbox/components/native/Toolbox.js Целия файл

@@ -4,12 +4,12 @@ import React from 'react';
4 4
 import { SafeAreaView, View } from 'react-native';
5 5
 
6 6
 import { ColorSchemeRegistry } from '../../../base/color-scheme';
7
-import { getFeatureFlag, REACTIONS_ENABLED } from '../../../base/flags';
8 7
 import { connect } from '../../../base/redux';
9 8
 import { StyleType } from '../../../base/styles';
10 9
 import { ChatButton } from '../../../chat';
11 10
 import { ParticipantsPaneButton } from '../../../participants-pane/components/native';
12 11
 import { ReactionsMenuButton } from '../../../reactions/components';
12
+import { isReactionsEnabled } from '../../../reactions/functions.any';
13 13
 import { TileViewButton } from '../../../video-layout';
14 14
 import { isToolboxVisible, getMovableButtons } from '../../functions.native';
15 15
 import AudioMuteButton from '../AudioMuteButton';
@@ -133,7 +133,7 @@ function _mapStateToProps(state: Object): Object {
133 133
         _styles: ColorSchemeRegistry.get(state, 'Toolbox'),
134 134
         _visible: isToolboxVisible(state),
135 135
         _width: state['features/base/responsive-ui'].clientWidth,
136
-        _reactionsEnabled: getFeatureFlag(state, REACTIONS_ENABLED, false)
136
+        _reactionsEnabled: isReactionsEnabled(state)
137 137
     };
138 138
 }
139 139
 

+ 71
- 20
react/features/toolbox/components/web/Toolbox.js Целия файл

@@ -17,6 +17,7 @@ import { translate } from '../../../base/i18n';
17 17
 import JitsiMeetJS from '../../../base/lib-jitsi-meet';
18 18
 import {
19 19
     getLocalParticipant,
20
+    getParticipantCount,
20 21
     haveParticipantWithScreenSharingFeature,
21 22
     raiseHand
22 23
 } from '../../../base/participants';
@@ -41,6 +42,7 @@ import { getParticipantsPaneOpen } from '../../../participants-pane/functions';
41 42
 import { addReactionToBuffer } from '../../../reactions/actions.any';
42 43
 import { ReactionsMenuButton } from '../../../reactions/components';
43 44
 import { REACTIONS } from '../../../reactions/constants';
45
+import { isReactionsEnabled } from '../../../reactions/functions.any';
44 46
 import {
45 47
     LiveStreamButton,
46 48
     RecordButton
@@ -152,10 +154,6 @@ type Props = {
152 154
      */
153 155
     _isProfileDisabled: boolean,
154 156
 
155
-    /**
156
-     * Whether or not the tile view is enabled.
157
-     */
158
-    _tileViewEnabled: boolean,
159 157
 
160 158
     /**
161 159
      * Whether or not the current meeting belongs to a JaaS user.
@@ -177,6 +175,11 @@ type Props = {
177 175
      */
178 176
     _overflowMenuVisible: boolean,
179 177
 
178
+    /**
179
+     * Number of participants in the conference.
180
+     */
181
+    _participantCount: number,
182
+
180 183
     /**
181 184
      * Whether or not the participants pane is open.
182 185
      */
@@ -187,6 +190,11 @@ type Props = {
187 190
      */
188 191
     _raisedHand: boolean,
189 192
 
193
+    /**
194
+     * Whether or not reactions feature is enabled.
195
+     */
196
+    _reactionsEnabled: boolean,
197
+
190 198
     /**
191 199
      * Whether or not the local participant is screenSharing.
192 200
      */
@@ -197,6 +205,11 @@ type Props = {
197 205
      */
198 206
     _sharingVideo: boolean,
199 207
 
208
+    /**
209
+     * Whether or not the tile view is enabled.
210
+     */
211
+    _tileViewEnabled: boolean,
212
+
200 213
     /**
201 214
      * The enabled buttons.
202 215
      */
@@ -212,11 +225,6 @@ type Props = {
212 225
      */
213 226
     _virtualSource: Object,
214 227
 
215
-    /**
216
-     * Whether or not reactions feature is enabled.
217
-     */
218
-    _reactionsEnabled: boolean,
219
-
220 228
     /**
221 229
      * Invoked to active other features of the app.
222 230
      */
@@ -235,12 +243,16 @@ type Props = {
235 243
 
236 244
 declare var APP: Object;
237 245
 
246
+type State = {
247
+    reactionsShortcutsRegistered: boolean
248
+};
249
+
238 250
 /**
239 251
  * Implements the conference toolbox on React/Web.
240 252
  *
241 253
  * @extends Component
242 254
  */
243
-class Toolbox extends Component<Props> {
255
+class Toolbox extends Component<Props, State> {
244 256
     /**
245 257
      * Initializes a new {@code Toolbox} instance.
246 258
      *
@@ -250,6 +262,10 @@ class Toolbox extends Component<Props> {
250 262
     constructor(props: Props) {
251 263
         super(props);
252 264
 
265
+        this.state = {
266
+            reactionsShortcutsRegistered: false
267
+        };
268
+
253 269
         // Bind event handlers so they are only bound once per instance.
254 270
         this._onMouseOut = this._onMouseOut.bind(this);
255 271
         this._onMouseOver = this._onMouseOver.bind(this);
@@ -279,7 +295,7 @@ class Toolbox extends Component<Props> {
279 295
      * @returns {void}
280 296
      */
281 297
     componentDidMount() {
282
-        const { _toolbarButtons, t, dispatch, _reactionsEnabled } = this.props;
298
+        const { _toolbarButtons, t, dispatch, _reactionsEnabled, _participantCount } = this.props;
283 299
         const KEYBOARD_SHORTCUTS = [
284 300
             isToolbarButtonEnabled('videoquality', _toolbarButtons) && {
285 301
                 character: 'A',
@@ -328,7 +344,7 @@ class Toolbox extends Component<Props> {
328 344
             }
329 345
         });
330 346
 
331
-        if (_reactionsEnabled) {
347
+        if (_reactionsEnabled && _participantCount > 1) {
332 348
             const REACTION_SHORTCUTS = Object.keys(REACTIONS).map(key => {
333 349
                 const onShortcutSendReaction = () => {
334 350
                     dispatch(addReactionToBuffer(key));
@@ -373,6 +389,41 @@ class Toolbox extends Component<Props> {
373 389
             this._onSetOverflowVisible(false);
374 390
             this.props.dispatch(setToolbarHovered(false));
375 391
         }
392
+
393
+        if (!this.state.reactionsShortcutsRegistered
394
+            && (prevProps._reactionsEnabled !== this.props._reactionsEnabled
395
+            || prevProps._participantCount !== this.props._participantCount)) {
396
+            if (this.props._reactionsEnabled && this.props._participantCount > 1) {
397
+                // eslint-disable-next-line react/no-did-update-set-state
398
+                this.setState({
399
+                    reactionsShortcutsRegistered: true
400
+                });
401
+                const REACTION_SHORTCUTS = Object.keys(REACTIONS).map(key => {
402
+                    const onShortcutSendReaction = () => {
403
+                        this.props.dispatch(addReactionToBuffer(key));
404
+                        sendAnalytics(createShortcutEvent(
405
+                            `reaction.${key}`
406
+                        ));
407
+                    };
408
+
409
+                    return {
410
+                        character: REACTIONS[key].shortcutChar,
411
+                        exec: onShortcutSendReaction,
412
+                        helpDescription: this.props.t(`toolbar.reaction${key.charAt(0).toUpperCase()}${key.slice(1)}`),
413
+                        altKey: true
414
+                    };
415
+                });
416
+
417
+                REACTION_SHORTCUTS.forEach(shortcut => {
418
+                    APP.keyboardshortcut.registerShortcut(
419
+                        shortcut.character,
420
+                        null,
421
+                        shortcut.exec,
422
+                        shortcut.helpDescription,
423
+                        shortcut.altKey);
424
+                });
425
+            }
426
+        }
376 427
     }
377 428
 
378 429
     /**
@@ -385,7 +436,7 @@ class Toolbox extends Component<Props> {
385 436
         [ 'A', 'C', 'D', 'R', 'S' ].forEach(letter =>
386 437
             APP.keyboardshortcut.unregisterShortcut(letter));
387 438
 
388
-        if (this.props._reactionsEnabled) {
439
+        if (this.props._reactionsEnabled && this.state.reactionsShortcutsRegistered) {
389 440
             Object.keys(REACTIONS).map(key => REACTIONS[key].shortcutChar)
390 441
                 .forEach(letter =>
391 442
                     APP.keyboardshortcut.unregisterShortcut(letter, true));
@@ -1262,7 +1313,6 @@ function _mapStateToProps(state, ownProps) {
1262 1313
     const localParticipant = getLocalParticipant(state);
1263 1314
     const localVideo = getLocalVideoTrack(state['features/base/tracks']);
1264 1315
     const { clientWidth } = state['features/base/responsive-ui'];
1265
-    const { enableReactions } = state['features/base/config'];
1266 1316
 
1267 1317
     let desktopSharingDisabledTooltipKey;
1268 1318
 
@@ -1285,29 +1335,30 @@ function _mapStateToProps(state, ownProps) {
1285 1335
     }
1286 1336
 
1287 1337
     return {
1338
+        _backgroundType: state['features/virtual-background'].backgroundType,
1288 1339
         _chatOpen: state['features/chat'].isOpen,
1289 1340
         _clientWidth: clientWidth,
1290 1341
         _conference: conference,
1291 1342
         _desktopSharingEnabled: desktopSharingEnabled,
1292
-        _backgroundType: state['features/virtual-background'].backgroundType,
1293
-        _virtualSource: state['features/virtual-background'].virtualSource,
1294 1343
         _desktopSharingDisabledTooltipKey: desktopSharingDisabledTooltipKey,
1295 1344
         _dialog: Boolean(state['features/base/dialog'].component),
1296 1345
         _feedbackConfigured: Boolean(callStatsID),
1346
+        _fullScreen: fullScreen,
1297 1347
         _isProfileDisabled: Boolean(state['features/base/config'].disableProfile),
1298 1348
         _isMobile: isMobileBrowser(),
1299 1349
         _isVpaasMeeting: isVpaasMeeting(state),
1300
-        _fullScreen: fullScreen,
1301
-        _tileViewEnabled: shouldDisplayTileView(state),
1302 1350
         _localParticipantID: localParticipant?.id,
1303 1351
         _localVideo: localVideo,
1304 1352
         _overflowMenuVisible: overflowMenuVisible,
1353
+        _participantCount: getParticipantCount(state),
1305 1354
         _participantsPaneOpen: getParticipantsPaneOpen(state),
1306 1355
         _raisedHand: localParticipant?.raisedHand,
1356
+        _reactionsEnabled: isReactionsEnabled(state),
1307 1357
         _screenSharing: isScreenVideoShared(state),
1358
+        _tileViewEnabled: shouldDisplayTileView(state),
1308 1359
         _toolbarButtons: toolbarButtons,
1309
-        _visible: isToolboxVisible(state),
1310
-        _reactionsEnabled: enableReactions
1360
+        _virtualSource: state['features/virtual-background'].virtualSource,
1361
+        _visible: isToolboxVisible(state)
1311 1362
     };
1312 1363
 }
1313 1364
 

Loading…
Отказ
Запис