|
@@ -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
|
|