Pārlūkot izejas kodu

feat(toolbar-button-clicked) Enhance toolbar buttons with notify click

- add possibility to allow execution of the button's routine besides triggering
`toolbarButtonClicked` API event
- keep backwards compatibility
- get rid of `ToolbarButton`
master
Horatiu Muresan 3 gadus atpakaļ
vecāks
revīzija
197dbfbbcb
Revīzijas autora e-pasta adrese nav piesaistīta nevienam kontam
48 mainītis faili ar 459 papildinājumiem un 524 dzēšanām
  1. 54
    34
      config.js
  2. 4
    2
      modules/API/API.js
  3. 0
    8
      react/features/base/toolbox/components/AbstractAudioMuteButton.js
  4. 42
    4
      react/features/base/toolbox/components/AbstractButton.js
  5. 0
    8
      react/features/base/toolbox/components/AbstractVideoMuteButton.js
  6. 11
    1
      react/features/base/toolbox/components/ToolboxItem.web.js
  7. 27
    1
      react/features/base/toolbox/components/web/ToolboxButtonWithIcon.js
  8. 0
    16
      react/features/chat/components/web/ChatButton.js
  9. 1
    7
      react/features/embed-meeting/components/EmbedMeetingButton.js
  10. 1
    8
      react/features/etherpad/components/SharedDocumentButton.web.js
  11. 1
    7
      react/features/feedback/components/FeedbackButton.web.js
  12. 1
    7
      react/features/invite/components/add-people-dialog/web/InviteButton.js
  13. 1
    7
      react/features/keyboard-shortcuts/components/KeyboardShortcutsButton.web.js
  14. 1
    7
      react/features/local-recording/components/LocalRecordingButton.web.js
  15. 0
    16
      react/features/participants-pane/components/web/ParticipantsPaneButton.js
  16. 73
    0
      react/features/reactions/components/web/RaiseHandButton.js
  17. 24
    20
      react/features/reactions/components/web/ReactionsMenuButton.js
  18. 1
    7
      react/features/recording/components/LiveStream/AbstractLiveStreamButton.js
  19. 1
    7
      react/features/recording/components/Recording/AbstractRecordButton.js
  20. 1
    7
      react/features/screen-share/components/ShareAudioButton.js
  21. 1
    7
      react/features/security/components/security-dialog/AbstractSecurityDialogButton.js
  22. 36
    1
      react/features/security/components/security-dialog/web/PasswordSection.js
  23. 9
    1
      react/features/security/components/security-dialog/web/SecurityDialog.js
  24. 1
    11
      react/features/settings/components/web/SettingsButton.js
  25. 0
    8
      react/features/shared-video/components/web/SharedVideoButton.js
  26. 1
    7
      react/features/speaker-stats/components/web/SpeakerStatsButton.js
  27. 1
    7
      react/features/subtitles/components/AbstractClosedCaptionButton.js
  28. 2
    2
      react/features/toolbox/components/AudioMuteButton.js
  29. 1
    7
      react/features/toolbox/components/DownloadButton.js
  30. 1
    7
      react/features/toolbox/components/HelpButton.js
  31. 1
    7
      react/features/toolbox/components/MuteEveryoneButton.js
  32. 1
    7
      react/features/toolbox/components/MuteEveryonesVideoButton.js
  33. 2
    2
      react/features/toolbox/components/VideoMuteButton.js
  34. 21
    10
      react/features/toolbox/components/web/AudioSettingsButton.js
  35. 0
    16
      react/features/toolbox/components/web/FullscreenButton.js
  36. 13
    32
      react/features/toolbox/components/web/OverflowMenuButton.js
  37. 73
    0
      react/features/toolbox/components/web/OverflowToggleButton.js
  38. 1
    7
      react/features/toolbox/components/web/ProfileButton.js
  39. 0
    16
      react/features/toolbox/components/web/ShareDesktopButton.js
  40. 1
    7
      react/features/toolbox/components/web/ToggleCameraButton.js
  41. 0
    135
      react/features/toolbox/components/web/ToolbarButton.js
  42. 21
    10
      react/features/toolbox/components/web/Toolbox.js
  43. 21
    10
      react/features/toolbox/components/web/VideoSettingsButton.js
  44. 0
    1
      react/features/toolbox/components/web/index.js
  45. 5
    0
      react/features/toolbox/constants.js
  46. 1
    7
      react/features/video-layout/components/TileViewButton.js
  47. 0
    18
      react/features/video-quality/components/VideoQualityButton.web.js
  48. 1
    7
      react/features/virtual-background/components/VideoBackgroundButton.js

+ 54
- 34
config.js Parādīt failu

@@ -615,41 +615,61 @@ var config = {
615 615
     //     alwaysVisible: false
616 616
     // },
617 617
 
618
-    // Toolbar buttons which have their click event exposed through the API on
619
-    // `toolbarButtonClicked` event instead of executing the normal click routine.
618
+    // Toolbar buttons which have their click/tap event exposed through the API on
619
+    // `toolbarButtonClicked`. Passing a string for the button key will
620
+    // prevent execution of the click/tap routine; passing an object with `key` and
621
+    // `preventExecution` flag on false will not prevent execution of the click/tap
622
+    // routine. Below array with mixed mode for passing the buttons.
620 623
     // buttonsWithNotifyClick: [
621
-    //    'camera',
622
-    //    'chat',
623
-    //    'closedcaptions',
624
-    //    'desktop',
625
-    //    'download',
626
-    //    'embedmeeting',
627
-    //    'etherpad',
628
-    //    'feedback',
629
-    //    'filmstrip',
630
-    //    'fullscreen',
631
-    //    'hangup',
632
-    //    'help',
633
-    //    'invite',
634
-    //    'livestreaming',
635
-    //    'microphone',
636
-    //    'mute-everyone',
637
-    //    'mute-video-everyone',
638
-    //    'participants-pane',
639
-    //    'profile',
640
-    //    'raisehand',
641
-    //    'recording',
642
-    //    'security',
643
-    //    'select-background',
644
-    //    'settings',
645
-    //    'shareaudio',
646
-    //    'sharedvideo',
647
-    //    'shortcuts',
648
-    //    'stats',
649
-    //    'tileview',
650
-    //    'toggle-camera',
651
-    //    'videoquality',
652
-    //    '__end'
624
+    //     'camera',
625
+    //     {
626
+    //         key: 'chat',
627
+    //         preventExecution: false
628
+    //     },
629
+    //     {
630
+    //         key: 'closedcaptions',
631
+    //         preventExecution: true
632
+    //     },
633
+    //     'desktop',
634
+    //     'download',
635
+    //     'embedmeeting',
636
+    //     'etherpad',
637
+    //     'feedback',
638
+    //     'filmstrip',
639
+    //     'fullscreen',
640
+    //     'hangup',
641
+    //     'help',
642
+    //     {
643
+    //         key: 'invite',
644
+    //         preventExecution: false
645
+    //     },
646
+    //     'livestreaming',
647
+    //     'microphone',
648
+    //     'mute-everyone',
649
+    //     'mute-video-everyone',
650
+    //     'participants-pane',
651
+    //     'profile',
652
+    //     {
653
+    //         key: 'raisehand',
654
+    //         preventExecution: true
655
+    //     },
656
+    //     'recording',
657
+    //     'security',
658
+    //     'select-background',
659
+    //     'settings',
660
+    //     'shareaudio',
661
+    //     'sharedvideo',
662
+    //     'shortcuts',
663
+    //     'stats',
664
+    //     'tileview',
665
+    //     'toggle-camera',
666
+    //     'videoquality',
667
+    //     // The add passcode button from the security dialog.
668
+    //     {
669
+    //         key: 'add-passcode',
670
+    //         preventExecution: false
671
+    //     }
672
+    //     '__end'
653 673
     // ],
654 674
 
655 675
     // List of pre meeting screens buttons to hide. The values must be one or more of the 5 allowed buttons:

+ 4
- 2
modules/API/API.js Parādīt failu

@@ -1521,12 +1521,14 @@ class API {
1521 1521
      * Notify external application ( if API is enabled) that a toolbar button was clicked.
1522 1522
      *
1523 1523
      * @param {string} key - The key of the toolbar button.
1524
+     * @param {boolean} preventExecution - Whether execution of the button click was prevented or not.
1524 1525
      * @returns {void}
1525 1526
      */
1526
-    notifyToolbarButtonClicked(key: string) {
1527
+    notifyToolbarButtonClicked(key: string, preventExecution: boolean) {
1527 1528
         this._sendEvent({
1528 1529
             name: 'toolbar-button-clicked',
1529
-            key
1530
+            key,
1531
+            preventExecution
1530 1532
         });
1531 1533
     }
1532 1534
 

+ 0
- 8
react/features/base/toolbox/components/AbstractAudioMuteButton.js Parādīt failu

@@ -23,14 +23,6 @@ export default class AbstractAudioMuteButton<P: Props, S: *>
23 23
      * @returns {void}
24 24
      */
25 25
     _handleClick() {
26
-        const { handleClick } = this.props;
27
-
28
-        if (handleClick) {
29
-            handleClick();
30
-
31
-            return;
32
-        }
33
-
34 26
         this._setAudioMuted(!this._isAudioMuted());
35 27
     }
36 28
 

+ 42
- 4
react/features/base/toolbox/components/AbstractButton.js Parādīt failu

@@ -2,6 +2,7 @@
2 2
 
3 3
 import React, { Component } from 'react';
4 4
 
5
+import { NOTIFY_CLICK_MODE } from '../../../toolbox/constants';
5 6
 import { combineStyles } from '../../styles';
6 7
 
7 8
 import type { Styles } from './AbstractToolboxItem';
@@ -14,6 +15,11 @@ export type Props = {
14 15
      */
15 16
     afterClick: ?Function,
16 17
 
18
+    /**
19
+     * The button's key.
20
+     */
21
+    buttonKey?: string,
22
+
17 23
     /**
18 24
      * Extra styles which will be applied in conjunction with `styles` or
19 25
      * `toggledStyles` when the button is disabled;.
@@ -25,6 +31,12 @@ export type Props = {
25 31
      */
26 32
      handleClick?: Function,
27 33
 
34
+    /**
35
+     * Notify mode for `toolbarButtonClicked` event -
36
+     * whether to only notify or to also prevent button click routine.
37
+     */
38
+    notifyMode?: string,
39
+
28 40
     /**
29 41
      * Whether to show the label or not.
30 42
      */
@@ -51,6 +63,8 @@ export type Props = {
51 63
     visible: boolean
52 64
 };
53 65
 
66
+declare var APP: Object;
67
+
54 68
 /**
55 69
  * Default style for disabled buttons.
56 70
  */
@@ -134,6 +148,17 @@ export default class AbstractButton<P: Props, S: *> extends Component<P, S> {
134 148
         this._onClick = this._onClick.bind(this);
135 149
     }
136 150
 
151
+    /**
152
+     * Helper function to be implemented by subclasses, which should be used
153
+     * to handle a key being down.
154
+     *
155
+     * @protected
156
+     * @returns {void}
157
+     */
158
+    _onKeyDown() {
159
+        // To be implemented by subclass.
160
+    }
161
+
137 162
     /**
138 163
      * Helper function to be implemented by subclasses, which should be used
139 164
      * to handle the button being clicked / pressed.
@@ -248,17 +273,29 @@ export default class AbstractButton<P: Props, S: *> extends Component<P, S> {
248 273
     _onClick: (*) => void;
249 274
 
250 275
     /**
251
-     * Handles clicking / pressing the button, and toggles the audio mute state
252
-     * accordingly.
276
+     * Handles clicking / pressing the button.
253 277
      *
254 278
      * @param {Object} e - Event.
255 279
      * @private
256 280
      * @returns {void}
257 281
      */
258 282
     _onClick(e) {
259
-        const { afterClick } = this.props;
283
+        const { afterClick, handleClick, notifyMode, buttonKey } = this.props;
284
+
285
+        if (typeof APP !== 'undefined' && notifyMode) {
286
+            APP.API.notifyToolbarButtonClicked(
287
+                buttonKey, notifyMode === NOTIFY_CLICK_MODE.PREVENT_AND_NOTIFY
288
+            );
289
+        }
290
+
291
+        if (notifyMode !== NOTIFY_CLICK_MODE.PREVENT_AND_NOTIFY) {
292
+            if (handleClick) {
293
+                handleClick();
294
+            }
295
+
296
+            this._handleClick();
297
+        }
260 298
 
261
-        this._handleClick();
262 299
         afterClick && afterClick(e);
263 300
 
264 301
         // blur after click to release focus from button to allow PTT.
@@ -288,6 +325,7 @@ export default class AbstractButton<P: Props, S: *> extends Component<P, S> {
288 325
             <ToolboxItem
289 326
                 disabled = { this._isDisabled() }
290 327
                 onClick = { this._onClick }
328
+                onKeyDown = { this._onKeyDown }
291 329
                 { ...props } />
292 330
         );
293 331
     }

+ 0
- 8
react/features/base/toolbox/components/AbstractVideoMuteButton.js Parādīt failu

@@ -22,14 +22,6 @@ export default class AbstractVideoMuteButton<P : Props, S : *>
22 22
      * @returns {void}
23 23
      */
24 24
     _handleClick() {
25
-        const { handleClick } = this.props;
26
-
27
-        if (handleClick) {
28
-            handleClick();
29
-
30
-            return;
31
-        }
32
-
33 25
         this._setVideoMuted(!this._isVideoMuted());
34 26
     }
35 27
 

+ 11
- 1
react/features/base/toolbox/components/ToolboxItem.web.js Parādīt failu

@@ -6,7 +6,15 @@ import { Icon } from '../../icons';
6 6
 import { Tooltip } from '../../tooltip';
7 7
 
8 8
 import AbstractToolboxItem from './AbstractToolboxItem';
9
-import type { Props } from './AbstractToolboxItem';
9
+import type { Props as AbstractToolboxItemProps } from './AbstractToolboxItem';
10
+
11
+type Props = AbstractToolboxItemProps & {
12
+
13
+    /**
14
+    * On key down handler.
15
+    */
16
+    onKeyDown: Function
17
+};
10 18
 
11 19
 /**
12 20
  * Web implementation of {@code AbstractToolboxItem}.
@@ -53,6 +61,7 @@ export default class ToolboxItem extends AbstractToolboxItem<Props> {
53 61
             disabled,
54 62
             elementAfter,
55 63
             onClick,
64
+            onKeyDown,
56 65
             showLabel,
57 66
             tooltipPosition,
58 67
             toggled
@@ -64,6 +73,7 @@ export default class ToolboxItem extends AbstractToolboxItem<Props> {
64 73
             'aria-label': this.accessibilityLabel,
65 74
             className: className + (disabled ? ' disabled' : ''),
66 75
             onClick: disabled ? undefined : onClick,
76
+            onKeyDown: disabled ? undefined : onKeyDown,
67 77
             onKeyPress: this._onKeyPress,
68 78
             tabIndex: 0,
69 79
             role: showLabel ? 'menuitem' : 'button'

+ 27
- 1
react/features/base/toolbox/components/web/ToolboxButtonWithIcon.js Parādīt failu

@@ -2,11 +2,17 @@
2 2
 
3 3
 import React from 'react';
4 4
 
5
+import { NOTIFY_CLICK_MODE } from '../../../../toolbox/constants';
5 6
 import { Icon } from '../../../icons';
6 7
 import { Tooltip } from '../../../tooltip';
7 8
 
8 9
 type Props = {
9 10
 
11
+    /**
12
+     * The button's key.
13
+     */
14
+    buttonKey?: string,
15
+
10 16
     /**
11 17
      * The decorated component (ToolboxButton).
12 18
      */
@@ -22,6 +28,12 @@ type Props = {
22 28
      */
23 29
     iconDisabled: boolean,
24 30
 
31
+    /**
32
+     * Notify mode for `toolbarButtonClicked` event -
33
+     * whether to only notify or to also prevent button click routine.
34
+     */
35
+    notifyMode?: string,
36
+
25 37
     /**
26 38
      * Click handler for the small icon.
27 39
      */
@@ -68,6 +80,8 @@ type Props = {
68 80
     iconId: string
69 81
 };
70 82
 
83
+declare var APP: Object;
84
+
71 85
 /**
72 86
  * Displays the `ToolboxButtonWithIcon` component.
73 87
  *
@@ -80,6 +94,8 @@ export default function ToolboxButtonWithIcon(props: Props) {
80 94
         icon,
81 95
         iconDisabled,
82 96
         iconTooltip,
97
+        buttonKey,
98
+        notifyMode,
83 99
         onIconClick,
84 100
         onIconKeyDown,
85 101
         styles,
@@ -97,7 +113,17 @@ export default function ToolboxButtonWithIcon(props: Props) {
97 113
             = 'settings-button-small-icon settings-button-small-icon--disabled';
98 114
     } else {
99 115
         iconProps.className = 'settings-button-small-icon';
100
-        iconProps.onClick = onIconClick;
116
+        iconProps.onClick = () => {
117
+            if (typeof APP !== 'undefined' && notifyMode) {
118
+                APP.API.notifyToolbarButtonClicked(
119
+                    buttonKey, notifyMode === NOTIFY_CLICK_MODE.PREVENT_AND_NOTIFY
120
+                );
121
+            }
122
+
123
+            if (notifyMode !== NOTIFY_CLICK_MODE.PREVENT_AND_NOTIFY) {
124
+                onIconClick();
125
+            }
126
+        };
101 127
         iconProps.onKeyDown = onIconKeyDown;
102 128
         iconProps.role = 'button';
103 129
         iconProps.tabIndex = 0;

+ 0
- 16
react/features/chat/components/web/ChatButton.js Parādīt failu

@@ -49,22 +49,6 @@ class ChatButton extends AbstractButton<Props, *> {
49 49
         // Unused.
50 50
     }
51 51
 
52
-    /**
53
-     * Handles clicking / pressing the button, and opens the appropriate dialog.
54
-     *
55
-     * @protected
56
-     * @returns {void}
57
-     */
58
-    _handleClick() {
59
-        const { handleClick } = this.props;
60
-
61
-        if (handleClick) {
62
-            handleClick();
63
-
64
-            return;
65
-        }
66
-    }
67
-
68 52
     /**
69 53
      * Indicates whether this button is in toggled state or not.
70 54
      *

+ 1
- 7
react/features/embed-meeting/components/EmbedMeetingButton.js Parādīt failu

@@ -36,13 +36,7 @@ class EmbedMeetingButton extends AbstractButton<Props, *> {
36 36
      * @returns {void}
37 37
      */
38 38
     _handleClick() {
39
-        const { dispatch, handleClick } = this.props;
40
-
41
-        if (handleClick) {
42
-            handleClick();
43
-
44
-            return;
45
-        }
39
+        const { dispatch } = this.props;
46 40
 
47 41
         sendAnalytics(createToolbarEvent('embed.meeting'));
48 42
         dispatch(openDialog(EmbedMeetingDialog));

+ 1
- 8
react/features/etherpad/components/SharedDocumentButton.web.js Parādīt failu

@@ -9,7 +9,6 @@ import { connect } from '../../base/redux';
9 9
 import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
10 10
 import { toggleDocument } from '../../etherpad/actions';
11 11
 
12
-
13 12
 type Props = AbstractButtonProps & {
14 13
 
15 14
     /**
@@ -59,13 +58,7 @@ class SharedDocumentButton extends AbstractButton<Props, *> {
59 58
      * @returns {void}
60 59
      */
61 60
     _handleClick() {
62
-        const { _editing, dispatch, handleClick } = this.props;
63
-
64
-        if (handleClick) {
65
-            handleClick();
66
-
67
-            return;
68
-        }
61
+        const { _editing, dispatch } = this.props;
69 62
 
70 63
         sendAnalytics(createToolbarEvent(
71 64
             'toggle.etherpad',

+ 1
- 7
react/features/feedback/components/FeedbackButton.web.js Parādīt failu

@@ -40,13 +40,7 @@ class FeedbackButton extends AbstractButton<Props, *> {
40 40
      * @returns {void}
41 41
      */
42 42
     _handleClick() {
43
-        const { _conference, dispatch, handleClick } = this.props;
44
-
45
-        if (handleClick) {
46
-            handleClick();
47
-
48
-            return;
49
-        }
43
+        const { _conference, dispatch } = this.props;
50 44
 
51 45
         sendAnalytics(createToolbarEvent('feedback'));
52 46
         dispatch(openFeedbackDialog(_conference));

+ 1
- 7
react/features/invite/components/add-people-dialog/web/InviteButton.js Parādīt failu

@@ -34,13 +34,7 @@ class InviteButton extends AbstractButton<Props, *> {
34 34
      * @returns {void}
35 35
      */
36 36
     _handleClick() {
37
-        const { dispatch, handleClick } = this.props;
38
-
39
-        if (handleClick) {
40
-            handleClick();
41
-
42
-            return;
43
-        }
37
+        const { dispatch } = this.props;
44 38
 
45 39
         sendAnalytics(createToolbarEvent('invite'));
46 40
         dispatch(beginAddPeople());

+ 1
- 7
react/features/keyboard-shortcuts/components/KeyboardShortcutsButton.web.js Parādīt failu

@@ -34,13 +34,7 @@ class KeyboardShortcutsButton extends AbstractButton<Props, *> {
34 34
      * @returns {void}
35 35
      */
36 36
     _handleClick() {
37
-        const { dispatch, handleClick } = this.props;
38
-
39
-        if (handleClick) {
40
-            handleClick();
41
-
42
-            return;
43
-        }
37
+        const { dispatch } = this.props;
44 38
 
45 39
         sendAnalytics(createToolbarEvent('shortcuts'));
46 40
         dispatch(openKeyboardShortcutsDialog());

+ 1
- 7
react/features/local-recording/components/LocalRecordingButton.web.js Parādīt failu

@@ -36,13 +36,7 @@ class LocalRecording extends AbstractButton<Props, *> {
36 36
      * @returns {void}
37 37
      */
38 38
     _handleClick() {
39
-        const { dispatch, handleClick } = this.props;
40
-
41
-        if (handleClick) {
42
-            handleClick();
43
-
44
-            return;
45
-        }
39
+        const { dispatch } = this.props;
46 40
 
47 41
         sendAnalytics(createToolbarEvent('local.recording'));
48 42
         dispatch(openDialog(LocalRecordingInfoDialog));

+ 0
- 16
react/features/participants-pane/components/web/ParticipantsPaneButton.js Parādīt failu

@@ -25,22 +25,6 @@ class ParticipantsPaneButton extends AbstractButton<Props, *> {
25 25
     label = 'toolbar.participants';
26 26
     tooltip = 'toolbar.participants';
27 27
 
28
-    /**
29
-     * Handles clicking / pressing the button, and opens the appropriate dialog.
30
-     *
31
-     * @protected
32
-     * @returns {void}
33
-     */
34
-    _handleClick() {
35
-        const { handleClick } = this.props;
36
-
37
-        if (handleClick) {
38
-            handleClick();
39
-
40
-            return;
41
-        }
42
-    }
43
-
44 28
     /**
45 29
      * Indicates whether this button is in toggled state or not.
46 30
      *

+ 73
- 0
react/features/reactions/components/web/RaiseHandButton.js Parādīt failu

@@ -0,0 +1,73 @@
1
+// @flow
2
+
3
+import { translate } from '../../../base/i18n';
4
+import { IconRaisedHand } from '../../../base/icons';
5
+import { getLocalParticipant, hasRaisedHand } from '../../../base/participants';
6
+import { connect } from '../../../base/redux';
7
+import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
8
+
9
+
10
+/**
11
+ * The type of the React {@code Component} props of {@link RaiseHandButton}.
12
+ */
13
+type Props = AbstractButtonProps & {
14
+
15
+    /**
16
+     * Whether or not the hand is raised.
17
+     */
18
+     raisedHand: boolean,
19
+};
20
+
21
+/**
22
+ * Implementation of a button for raising hand.
23
+ */
24
+class RaiseHandButton extends AbstractButton<Props, *> {
25
+    accessibilityLabel = 'toolbar.accessibilityLabel.raiseHand';
26
+    icon = IconRaisedHand;
27
+    label = 'toolbar.raiseHand';
28
+    toggledLabel = 'toolbar.raiseHand';
29
+
30
+    /**
31
+     * Retrieves tooltip dynamically.
32
+     */
33
+    get tooltip() {
34
+        return 'toolbar.raiseHand';
35
+    }
36
+
37
+    /**
38
+     * Required by linter due to AbstractButton overwritten prop being writable.
39
+     *
40
+     * @param {string} _value - The value.
41
+     */
42
+    set tooltip(_value) {
43
+        // Unused.
44
+    }
45
+
46
+    /**
47
+     * Indicates whether this button is in toggled state or not.
48
+     *
49
+     * @override
50
+     * @protected
51
+     * @returns {boolean}
52
+     */
53
+    _isToggled() {
54
+        return this.props.raisedHand;
55
+    }
56
+}
57
+
58
+
59
+/**
60
+ * Function that maps parts of Redux state tree into component props.
61
+ *
62
+ * @param {Object} state - Redux state.
63
+ * @returns {Object}
64
+ */
65
+const mapStateToProps = state => {
66
+    const localParticipant = getLocalParticipant(state);
67
+
68
+    return {
69
+        raisedHand: hasRaisedHand(localParticipant)
70
+    };
71
+};
72
+
73
+export default translate(connect(mapStateToProps)(RaiseHandButton));

+ 24
- 20
react/features/reactions/components/web/ReactionsMenuButton.js Parādīt failu

@@ -4,16 +4,15 @@ import React, { useCallback } from 'react';
4 4
 
5 5
 import { isMobileBrowser } from '../../../base/environment/utils';
6 6
 import { translate } from '../../../base/i18n';
7
-import { IconArrowUp, IconRaisedHand } from '../../../base/icons';
8
-import { getLocalParticipant, hasRaisedHand } from '../../../base/participants';
7
+import { IconArrowUp } from '../../../base/icons';
9 8
 import { connect } from '../../../base/redux';
10 9
 import { ToolboxButtonWithIcon } from '../../../base/toolbox/components';
11
-import ToolbarButton from '../../../toolbox/components/web/ToolbarButton';
12 10
 import { toggleReactionsMenuVisibility } from '../../actions.web';
13 11
 import { type ReactionEmojiProps } from '../../constants';
14 12
 import { getReactionsQueue, isReactionsEnabled } from '../../functions.any';
15 13
 import { getReactionsMenuVisibility } from '../../functions.web';
16 14
 
15
+import RaiseHandButton from './RaiseHandButton';
17 16
 import ReactionEmoji from './ReactionEmoji';
18 17
 import ReactionsMenuPopup from './ReactionsMenuPopup';
19 18
 
@@ -24,6 +23,11 @@ type Props = {
24 23
      */
25 24
     _reactionsEnabled: Boolean,
26 25
 
26
+    /**
27
+     * The button's key.
28
+     */
29
+     buttonKey?: string,
30
+
27 31
     /**
28 32
      * Redux dispatch function.
29 33
      */
@@ -45,9 +49,10 @@ type Props = {
45 49
     isMobile: boolean,
46 50
 
47 51
     /**
48
-     * Whether or not the local participant's hand is raised.
52
+     * Notify mode for `toolbarButtonClicked` event -
53
+     * whether to only notify or to also prevent button click routine.
49 54
      */
50
-    raisedHand: boolean,
55
+    notifyMode?: string,
51 56
 
52 57
     /**
53 58
      * The array of reactions to be displayed.
@@ -70,11 +75,12 @@ declare var APP: Object;
70 75
  */
71 76
 function ReactionsMenuButton({
72 77
     _reactionsEnabled,
78
+    buttonKey,
73 79
     dispatch,
74 80
     handleClick,
75 81
     isOpen,
76 82
     isMobile,
77
-    raisedHand,
83
+    notifyMode,
78 84
     reactionsQueue,
79 85
     t
80 86
 }: Props) {
@@ -82,30 +88,31 @@ function ReactionsMenuButton({
82 88
         dispatch(toggleReactionsMenuVisibility());
83 89
     }, [ dispatch ]);
84 90
 
85
-    const raiseHandButton = (<ToolbarButton
86
-        accessibilityLabel = { t('toolbar.accessibilityLabel.raiseHand') }
87
-        icon = { IconRaisedHand }
88
-        key = 'raise-hand'
89
-        onClick = { handleClick }
90
-        toggled = { raisedHand }
91
-        tooltip = { t('toolbar.raiseHand') } />);
92
-
93 91
     return (
94 92
         <div className = 'reactions-menu-popup-container'>
95 93
             <ReactionsMenuPopup>
96
-                {!_reactionsEnabled || isMobile ? raiseHandButton
94
+                {!_reactionsEnabled || isMobile ? (
95
+                    <RaiseHandButton
96
+                        buttonKey = { buttonKey }
97
+                        handleClick = { handleClick }
98
+                        notifyMode = { notifyMode } />)
97 99
                     : (
98 100
                         <ToolboxButtonWithIcon
99 101
                             ariaControls = 'reactions-menu-dialog'
100 102
                             ariaExpanded = { isOpen }
101 103
                             ariaHasPopup = { true }
102 104
                             ariaLabel = { t('toolbar.accessibilityLabel.reactionsMenu') }
105
+                            buttonKey = { buttonKey }
103 106
                             icon = { IconArrowUp }
104 107
                             iconDisabled = { false }
105 108
                             iconId = 'reactions-menu-button'
106 109
                             iconTooltip = { t(`toolbar.${isOpen ? 'closeReactionsMenu' : 'openReactionsMenu'}`) }
110
+                            notifyMode = { notifyMode }
107 111
                             onIconClick = { toggleReactionsMenu }>
108
-                            {raiseHandButton}
112
+                            <RaiseHandButton
113
+                                buttonKey = { buttonKey }
114
+                                handleClick = { handleClick }
115
+                                notifyMode = { notifyMode } />
109 116
                         </ToolboxButtonWithIcon>
110 117
                     )}
111 118
             </ReactionsMenuPopup>
@@ -125,14 +132,11 @@ function ReactionsMenuButton({
125 132
  * @returns {Object}
126 133
  */
127 134
 function mapStateToProps(state) {
128
-    const localParticipant = getLocalParticipant(state);
129
-
130 135
     return {
131 136
         _reactionsEnabled: isReactionsEnabled(state),
132 137
         isOpen: getReactionsMenuVisibility(state),
133 138
         isMobile: isMobileBrowser(),
134
-        reactionsQueue: getReactionsQueue(state),
135
-        raisedHand: hasRaisedHand(localParticipant)
139
+        reactionsQueue: getReactionsQueue(state)
136 140
     };
137 141
 }
138 142
 

+ 1
- 7
react/features/recording/components/LiveStream/AbstractLiveStreamButton.js Parādīt failu

@@ -77,13 +77,7 @@ export default class AbstractLiveStreamButton<P: Props> extends AbstractButton<P
77 77
      * @returns {void}
78 78
      */
79 79
     async _handleClick() {
80
-        const { _isLiveStreamRunning, dispatch, handleClick } = this.props;
81
-
82
-        if (handleClick) {
83
-            handleClick();
84
-
85
-            return;
86
-        }
80
+        const { _isLiveStreamRunning, dispatch } = this.props;
87 81
 
88 82
         const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(FEATURES.RECORDING));
89 83
 

+ 1
- 7
react/features/recording/components/Recording/AbstractRecordButton.js Parādīt failu

@@ -78,13 +78,7 @@ export default class AbstractRecordButton<P: Props> extends AbstractButton<P, *>
78 78
      * @returns {void}
79 79
      */
80 80
     async _handleClick() {
81
-        const { _isRecordingRunning, dispatch, handleClick } = this.props;
82
-
83
-        if (handleClick) {
84
-            handleClick();
85
-
86
-            return;
87
-        }
81
+        const { _isRecordingRunning, dispatch } = this.props;
88 82
 
89 83
         sendAnalytics(createToolbarEvent(
90 84
             'recording.button',

+ 1
- 7
react/features/screen-share/components/ShareAudioButton.js Parādīt failu

@@ -47,13 +47,7 @@ class ShareAudioButton extends AbstractButton<Props, *> {
47 47
      * @returns {void}
48 48
      */
49 49
     _handleClick() {
50
-        const { dispatch, handleClick } = this.props;
51
-
52
-        if (handleClick) {
53
-            handleClick();
54
-
55
-            return;
56
-        }
50
+        const { dispatch } = this.props;
57 51
 
58 52
         dispatch(startAudioScreenShareFlow());
59 53
         dispatch(setOverflowMenuVisible(false));

+ 1
- 7
react/features/security/components/security-dialog/AbstractSecurityDialogButton.js Parādīt failu

@@ -56,13 +56,7 @@ export default class AbstractSecurityDialogButton<P: Props, S:*>
56 56
      * @returns {void}
57 57
      */
58 58
     _handleClick() {
59
-        const { _locked, handleClick } = this.props;
60
-
61
-        if (handleClick) {
62
-            handleClick();
63
-
64
-            return;
65
-        }
59
+        const { _locked } = this.props;
66 60
 
67 61
         sendAnalytics(createToolbarEvent('toggle.security', { enable: !_locked }));
68 62
         this._handleClickSecurityButton();

+ 36
- 1
react/features/security/components/security-dialog/web/PasswordSection.js Parādīt failu

@@ -6,11 +6,19 @@ import React, { useRef } from 'react';
6 6
 
7 7
 import { translate } from '../../../../base/i18n';
8 8
 import { copyText } from '../../../../base/util';
9
+import { NOTIFY_CLICK_MODE } from '../../../../toolbox/constants';
9 10
 
10 11
 import PasswordForm from './PasswordForm';
11 12
 
13
+const KEY = 'add-passcode';
14
+
12 15
 type Props = {
13 16
 
17
+    /**
18
+     * Toolbar buttons which have their click exposed through the API.
19
+     */
20
+     buttonsWithNotifyClick: Array<string | Object>,
21
+
14 22
     /**
15 23
      * Whether or not the current user can modify the current password.
16 24
      */
@@ -59,12 +67,15 @@ type Props = {
59 67
     t: Function
60 68
 };
61 69
 
70
+declare var APP: Object;
71
+
62 72
 /**
63 73
  * Component that handles the password manipulation from the invite dialog.
64 74
  *
65 75
  * @returns {React$Element<any>}
66 76
  */
67 77
 function PasswordSection({
78
+    buttonsWithNotifyClick,
68 79
     canEditPassword,
69 80
     conference,
70 81
     locked,
@@ -97,7 +108,31 @@ function PasswordSection({
97 108
      * @returns {void}
98 109
      */
99 110
     function onTogglePasswordEditState() {
100
-        setPasswordEditEnabled(!passwordEditEnabled);
111
+        if (typeof APP === 'undefined' || !buttonsWithNotifyClick?.length) {
112
+            setPasswordEditEnabled(!passwordEditEnabled);
113
+
114
+            return;
115
+        }
116
+
117
+        let notifyMode;
118
+        const notify = buttonsWithNotifyClick.find(
119
+            (btn: string | Object) =>
120
+                (typeof btn === 'string' && btn === KEY)
121
+                || (typeof btn === 'object' && btn.key === KEY)
122
+        );
123
+
124
+        if (notify) {
125
+            notifyMode = typeof notify === 'string' || notify.preventExecution
126
+                ? NOTIFY_CLICK_MODE.PREVENT_AND_NOTIFY
127
+                : NOTIFY_CLICK_MODE.ONLY_NOTIFY;
128
+            APP.API.notifyToolbarButtonClicked(
129
+                KEY, notifyMode === NOTIFY_CLICK_MODE.PREVENT_AND_NOTIFY
130
+            );
131
+        }
132
+
133
+        if (notifyMode === NOTIFY_CLICK_MODE.ONLY_NOTIFY) {
134
+            setPasswordEditEnabled(!passwordEditEnabled);
135
+        }
101 136
     }
102 137
 
103 138
     /**

+ 9
- 1
react/features/security/components/security-dialog/web/SecurityDialog.js Parādīt failu

@@ -13,6 +13,11 @@ import PasswordSection from './PasswordSection';
13 13
 
14 14
 type Props = {
15 15
 
16
+    /**
17
+     * Toolbar buttons which have their click exposed through the API.
18
+     */
19
+     _buttonsWithNotifyClick: Array<string | Object>,
20
+
16 21
     /**
17 22
      * Whether or not the current user can modify the current password.
18 23
      */
@@ -57,6 +62,7 @@ type Props = {
57 62
  * @returns {React$Element<any>}
58 63
  */
59 64
 function SecurityDialog({
65
+    _buttonsWithNotifyClick,
60 66
     _canEditPassword,
61 67
     _conference,
62 68
     _locked,
@@ -82,6 +88,7 @@ function SecurityDialog({
82 88
             <div className = 'security-dialog'>
83 89
                 <LobbySection />
84 90
                 <PasswordSection
91
+                    buttonsWithNotifyClick = { _buttonsWithNotifyClick }
85 92
                     canEditPassword = { _canEditPassword }
86 93
                     conference = { _conference }
87 94
                     locked = { _locked }
@@ -117,11 +124,12 @@ function mapStateToProps(state) {
117 124
         locked,
118 125
         password
119 126
     } = state['features/base/conference'];
120
-    const { roomPasswordNumberOfDigits } = state['features/base/config'];
127
+    const { roomPasswordNumberOfDigits, buttonsWithNotifyClick } = state['features/base/config'];
121 128
 
122 129
     const showE2ee = Boolean(e2eeSupported) && isLocalParticipantModerator(state);
123 130
 
124 131
     return {
132
+        _buttonsWithNotifyClick: buttonsWithNotifyClick,
125 133
         _canEditPassword: isLocalParticipantModerator(state),
126 134
         _conference: conference,
127 135
         _dialIn: state['features/invite'],

+ 1
- 11
react/features/settings/components/web/SettingsButton.js Parādīt failu

@@ -40,17 +40,7 @@ class SettingsButton extends AbstractButton<Props, *> {
40 40
      * @returns {void}
41 41
      */
42 42
     _handleClick() {
43
-        const {
44
-            defaultTab = SETTINGS_TABS.DEVICES,
45
-            dispatch,
46
-            handleClick
47
-        } = this.props;
48
-
49
-        if (handleClick) {
50
-            handleClick();
51
-
52
-            return;
53
-        }
43
+        const { defaultTab = SETTINGS_TABS.DEVICES, dispatch } = this.props;
54 44
 
55 45
         sendAnalytics(createToolbarEvent('settings'));
56 46
         dispatch(openSettingsDialog(defaultTab));

+ 0
- 8
react/features/shared-video/components/web/SharedVideoButton.js Parādīt failu

@@ -66,14 +66,6 @@ class SharedVideoButton extends AbstractButton<Props, *> {
66 66
      * @returns {void}
67 67
      */
68 68
     _handleClick() {
69
-        const { handleClick } = this.props;
70
-
71
-        if (handleClick) {
72
-            handleClick();
73
-
74
-            return;
75
-        }
76
-
77 69
         this._doToggleSharedVideo();
78 70
     }
79 71
 

+ 1
- 7
react/features/speaker-stats/components/web/SpeakerStatsButton.js Parādīt failu

@@ -20,13 +20,7 @@ class SpeakerStatsButton extends AbstractSpeakerStatsButton {
20 20
      * @returns {void}
21 21
      */
22 22
     _handleClick() {
23
-        const { dispatch, handleClick } = this.props;
24
-
25
-        if (handleClick) {
26
-            handleClick();
27
-
28
-            return;
29
-        }
23
+        const { dispatch } = this.props;
30 24
 
31 25
         sendAnalytics(createToolbarEvent('speaker.stats'));
32 26
         dispatch(openDialog(SpeakerStats));

+ 1
- 7
react/features/subtitles/components/AbstractClosedCaptionButton.js Parādīt failu

@@ -38,13 +38,7 @@ export class AbstractClosedCaptionButton
38 38
      * @returns {void}
39 39
      */
40 40
     async _handleClick() {
41
-        const { _requestingSubtitles, dispatch, handleClick } = this.props;
42
-
43
-        if (handleClick) {
44
-            handleClick();
45
-
46
-            return;
47
-        }
41
+        const { _requestingSubtitles, dispatch } = this.props;
48 42
 
49 43
         sendAnalytics(createToolbarEvent('transcribing.ccButton',
50 44
             {

+ 2
- 2
react/features/toolbox/components/AudioMuteButton.js Parādīt failu

@@ -10,7 +10,7 @@ import { getFeatureFlag, AUDIO_MUTE_BUTTON_ENABLED } from '../../base/flags';
10 10
 import { translate } from '../../base/i18n';
11 11
 import { MEDIA_TYPE } from '../../base/media';
12 12
 import { connect } from '../../base/redux';
13
-import { AbstractAudioMuteButton } from '../../base/toolbox/components';
13
+import { AbstractAudioMuteButton, AbstractButton } from '../../base/toolbox/components';
14 14
 import type { AbstractButtonProps } from '../../base/toolbox/components';
15 15
 import { isLocalTrackMuted } from '../../base/tracks';
16 16
 import { muteLocal } from '../../video-menu/actions';
@@ -120,7 +120,7 @@ class AudioMuteButton extends AbstractAudioMuteButton<Props, *> {
120 120
                 ACTION_SHORTCUT_TRIGGERED,
121 121
                 { enable: !this._isAudioMuted() }));
122 122
 
123
-        super._handleClick();
123
+        AbstractButton.prototype._onClick.call(this);
124 124
     }
125 125
 
126 126
     /**

+ 1
- 7
react/features/toolbox/components/DownloadButton.js Parādīt failu

@@ -32,13 +32,7 @@ class DownloadButton extends AbstractButton<Props, *> {
32 32
      * @returns {void}
33 33
      */
34 34
     _handleClick() {
35
-        const { _downloadAppsUrl, handleClick } = this.props;
36
-
37
-        if (handleClick) {
38
-            handleClick();
39
-
40
-            return;
41
-        }
35
+        const { _downloadAppsUrl } = this.props;
42 36
 
43 37
         sendAnalytics(createToolbarEvent('download.pressed'));
44 38
         openURLInBrowser(_downloadAppsUrl);

+ 1
- 7
react/features/toolbox/components/HelpButton.js Parādīt failu

@@ -33,13 +33,7 @@ class HelpButton extends AbstractButton<Props, *> {
33 33
      * @returns {void}
34 34
      */
35 35
     _handleClick() {
36
-        const { _userDocumentationURL, handleClick } = this.props;
37
-
38
-        if (handleClick) {
39
-            handleClick();
40
-
41
-            return;
42
-        }
36
+        const { _userDocumentationURL } = this.props;
43 37
 
44 38
         sendAnalytics(createToolbarEvent('help.pressed'));
45 39
         openURLInBrowser(_userDocumentationURL);

+ 1
- 7
react/features/toolbox/components/MuteEveryoneButton.js Parādīt failu

@@ -39,13 +39,7 @@ class MuteEveryoneButton extends AbstractButton<Props, *> {
39 39
      * @returns {void}
40 40
      */
41 41
     _handleClick() {
42
-        const { dispatch, localParticipantId, handleClick } = this.props;
43
-
44
-        if (handleClick) {
45
-            handleClick();
46
-
47
-            return;
48
-        }
42
+        const { dispatch, localParticipantId } = this.props;
49 43
 
50 44
         sendAnalytics(createToolbarEvent('mute.everyone.pressed'));
51 45
         dispatch(openDialog(MuteEveryoneDialog, {

+ 1
- 7
react/features/toolbox/components/MuteEveryonesVideoButton.js Parādīt failu

@@ -39,13 +39,7 @@ class MuteEveryonesVideoButton extends AbstractButton<Props, *> {
39 39
      * @returns {void}
40 40
      */
41 41
     _handleClick() {
42
-        const { dispatch, localParticipantId, handleClick } = this.props;
43
-
44
-        if (handleClick) {
45
-            handleClick();
46
-
47
-            return;
48
-        }
42
+        const { dispatch, localParticipantId } = this.props;
49 43
 
50 44
         sendAnalytics(createToolbarEvent('mute.everyone.pressed'));
51 45
         dispatch(openDialog(MuteEveryonesVideoDialog, {

+ 2
- 2
react/features/toolbox/components/VideoMuteButton.js Parādīt failu

@@ -16,7 +16,7 @@ import {
16 16
     setVideoMuted
17 17
 } from '../../base/media';
18 18
 import { connect } from '../../base/redux';
19
-import { AbstractVideoMuteButton } from '../../base/toolbox/components';
19
+import { AbstractButton, AbstractVideoMuteButton } from '../../base/toolbox/components';
20 20
 import type { AbstractButtonProps } from '../../base/toolbox/components';
21 21
 import { getLocalVideoType, isLocalCameraTrackMuted } from '../../base/tracks';
22 22
 import { isVideoMuteButtonDisabled } from '../functions';
@@ -146,7 +146,7 @@ class VideoMuteButton extends AbstractVideoMuteButton<Props, *> {
146 146
                 ACTION_SHORTCUT_TRIGGERED,
147 147
                 { enable: !this._isVideoMuted() }));
148 148
 
149
-        super._handleClick();
149
+        AbstractButton.prototype._onClick.call(this);
150 150
     }
151 151
 
152 152
     /**

+ 21
- 10
react/features/toolbox/components/web/AudioSettingsButton.js Parādīt failu

@@ -15,6 +15,11 @@ import AudioMuteButton from '../AudioMuteButton';
15 15
 
16 16
 type Props = {
17 17
 
18
+    /**
19
+     * The button's key.
20
+     */
21
+     buttonKey?: string,
22
+
18 23
     /**
19 24
      * External handler for click action.
20 25
      */
@@ -35,6 +40,12 @@ type Props = {
35 40
      */
36 41
     isDisabled: boolean,
37 42
 
43
+    /**
44
+     * Notify mode for `toolbarButtonClicked` event -
45
+     * whether to only notify or to also prevent button click routine.
46
+     */
47
+    notifyMode?: string,
48
+
38 49
     /**
39 50
      * Used for translation.
40 51
      */
@@ -94,13 +105,7 @@ class AudioSettingsButton extends Component<Props> {
94 105
      * @returns {void}
95 106
      */
96 107
     _onClick() {
97
-        const { handleClick, onAudioOptionsClick } = this.props;
98
-
99
-        if (handleClick) {
100
-            handleClick();
101
-
102
-            return;
103
-        }
108
+        const { onAudioOptionsClick } = this.props;
104 109
 
105 110
         onAudioOptionsClick();
106 111
     }
@@ -111,7 +116,7 @@ class AudioSettingsButton extends Component<Props> {
111 116
      * @inheritdoc
112 117
      */
113 118
     render() {
114
-        const { handleClick, hasPermissions, isDisabled, visible, isOpen, t } = this.props;
119
+        const { hasPermissions, isDisabled, visible, isOpen, buttonKey, notifyMode, t } = this.props;
115 120
         const settingsDisabled = !hasPermissions
116 121
             || isDisabled
117 122
             || !JitsiMeetJS.mediaDevices.isMultipleAudioInputSupported();
@@ -123,16 +128,22 @@ class AudioSettingsButton extends Component<Props> {
123 128
                     ariaExpanded = { isOpen }
124 129
                     ariaHasPopup = { true }
125 130
                     ariaLabel = { t('toolbar.audioSettings') }
131
+                    buttonKey = { buttonKey }
126 132
                     icon = { IconArrowUp }
127 133
                     iconDisabled = { settingsDisabled }
128 134
                     iconId = 'audio-settings-button'
129 135
                     iconTooltip = { t('toolbar.audioSettings') }
136
+                    notifyMode = { notifyMode }
130 137
                     onIconClick = { this._onClick }
131 138
                     onIconKeyDown = { this._onEscClick }>
132
-                    <AudioMuteButton handleClick = { handleClick } />
139
+                    <AudioMuteButton
140
+                        buttonKey = { buttonKey }
141
+                        notifyMode = { notifyMode } />
133 142
                 </ToolboxButtonWithIcon>
134 143
             </AudioSettingsPopup>
135
-        ) : <AudioMuteButton handleClick = { handleClick } />;
144
+        ) : <AudioMuteButton
145
+            buttonKey = { buttonKey }
146
+            notifyMode = { notifyMode } />;
136 147
     }
137 148
 }
138 149
 

+ 0
- 16
react/features/toolbox/components/web/FullscreenButton.js Parādīt failu

@@ -61,22 +61,6 @@ class FullscreenButton extends AbstractButton<Props, *> {
61 61
         // Unused.
62 62
     }
63 63
 
64
-    /**
65
-     * Handles clicking / pressing the button, and opens the appropriate dialog.
66
-     *
67
-     * @protected
68
-     * @returns {void}
69
-     */
70
-    _handleClick() {
71
-        const { handleClick } = this.props;
72
-
73
-        if (handleClick) {
74
-            handleClick();
75
-
76
-            return;
77
-        }
78
-    }
79
-
80 64
     /**
81 65
      * Indicates whether this button is in toggled state or not.
82 66
      *

+ 13
- 32
react/features/toolbox/components/web/OverflowMenuButton.js Parādīt failu

@@ -5,7 +5,6 @@ import React, { Component } from 'react';
5 5
 
6 6
 import { createToolbarEvent, sendAnalytics } from '../../../analytics';
7 7
 import { translate } from '../../../base/i18n';
8
-import { IconHorizontalPoints } from '../../../base/icons';
9 8
 import { connect } from '../../../base/redux';
10 9
 import { ReactionEmoji, ReactionsMenu } from '../../../reactions/components';
11 10
 import { type ReactionEmojiProps } from '../../../reactions/constants';
@@ -13,7 +12,7 @@ import { getReactionsQueue } from '../../../reactions/functions.any';
13 12
 
14 13
 import Drawer from './Drawer';
15 14
 import JitsiPortal from './JitsiPortal';
16
-import ToolbarButton from './ToolbarButton';
15
+import OverflowToggleButton from './OverflowToggleButton';
17 16
 
18 17
 /**
19 18
  * The type of the React {@code Component} props of {@link OverflowMenuButton}.
@@ -78,8 +77,8 @@ class OverflowMenuButton extends Component<Props> {
78 77
 
79 78
         // Bind event handlers so they are only bound once per instance.
80 79
         this._onCloseDialog = this._onCloseDialog.bind(this);
81
-        this._onToggleDialogVisibility
82
-            = this._onToggleDialogVisibility.bind(this);
80
+        this._toggleDialogVisibility
81
+            = this._toggleDialogVisibility.bind(this);
83 82
         this._onEscClick = this._onEscClick.bind(this);
84 83
     }
85 84
 
@@ -113,7 +112,10 @@ class OverflowMenuButton extends Component<Props> {
113 112
                 {
114 113
                     overflowDrawer ? (
115 114
                         <>
116
-                            {this._renderToolbarButton()}
115
+                            <OverflowToggleButton
116
+                                handleClick = { this._toggleDialogVisibility }
117
+                                isOpen = { isOpen }
118
+                                onKeyDown = { this._onEscClick } />
117 119
                             <JitsiPortal>
118 120
                                 <Drawer
119 121
                                     isOpen = { isOpen }
@@ -136,7 +138,10 @@ class OverflowMenuButton extends Component<Props> {
136 138
                             isOpen = { isOpen }
137 139
                             onClose = { this._onCloseDialog }
138 140
                             placement = 'top-end'>
139
-                            {this._renderToolbarButton()}
141
+                            <OverflowToggleButton
142
+                                handleClick = { this._toggleDialogVisibility }
143
+                                isOpen = { isOpen }
144
+                                onKeyDown = { this._onEscClick } />
140 145
                         </InlineDialog>
141 146
                     )
142 147
                 }
@@ -144,30 +149,6 @@ class OverflowMenuButton extends Component<Props> {
144 149
         );
145 150
     }
146 151
 
147
-    _renderToolbarButton: () => React$Node;
148
-
149
-    /**
150
-     * Renders the actual toolbar overflow menu button.
151
-     *
152
-     * @returns {ReactElement}
153
-     */
154
-    _renderToolbarButton() {
155
-        const { ariaControls, isOpen, t } = this.props;
156
-
157
-        return (
158
-            <ToolbarButton
159
-                accessibilityLabel =
160
-                    { t('toolbar.accessibilityLabel.moreActions') }
161
-                aria-controls = { ariaControls }
162
-                aria-haspopup = 'true'
163
-                icon = { IconHorizontalPoints }
164
-                onClick = { this._onToggleDialogVisibility }
165
-                onKeyDown = { this._onEscClick }
166
-                toggled = { isOpen }
167
-                tooltip = { t('toolbar.moreActions') } />
168
-        );
169
-    }
170
-
171 152
     _onCloseDialog: () => void;
172 153
 
173 154
     /**
@@ -181,7 +162,7 @@ class OverflowMenuButton extends Component<Props> {
181 162
         this.props.onVisibilityChange(false);
182 163
     }
183 164
 
184
-    _onToggleDialogVisibility: () => void;
165
+    _toggleDialogVisibility: () => void;
185 166
 
186 167
     /**
187 168
      * Callback invoked to signal that an event has occurred that should change
@@ -190,7 +171,7 @@ class OverflowMenuButton extends Component<Props> {
190 171
      * @private
191 172
      * @returns {void}
192 173
      */
193
-    _onToggleDialogVisibility() {
174
+    _toggleDialogVisibility() {
194 175
         sendAnalytics(createToolbarEvent('overflow'));
195 176
 
196 177
         this.props.onVisibilityChange(!this.props.isOpen);

+ 73
- 0
react/features/toolbox/components/web/OverflowToggleButton.js Parādīt failu

@@ -0,0 +1,73 @@
1
+// @flow
2
+
3
+import { translate } from '../../../base/i18n';
4
+import { IconHorizontalPoints } from '../../../base/icons';
5
+import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
6
+
7
+
8
+/**
9
+ * The type of the React {@code Component} props of {@link OverflowToggleButton}.
10
+ */
11
+type Props = AbstractButtonProps & {
12
+
13
+    /**
14
+     * Whether the more options menu is open.
15
+     */
16
+    isOpen: boolean,
17
+
18
+    /**
19
+     * External handler for key down action.
20
+     */
21
+     onKeyDown: Function,
22
+};
23
+
24
+/**
25
+ * Implementation of a button for toggleing the overflow menu.
26
+ */
27
+class OverflowToggleButton extends AbstractButton<Props, *> {
28
+    accessibilityLabel = 'toolbar.accessibilityLabel.moreActions';
29
+    icon = IconHorizontalPoints;
30
+    label = 'toolbar.moreActions';
31
+    toggledLabel = 'toolbar.moreActions';
32
+
33
+    /**
34
+     * Retrieves tooltip dynamically.
35
+     */
36
+    get tooltip() {
37
+        return 'toolbar.moreActions';
38
+    }
39
+
40
+    /**
41
+     * Required by linter due to AbstractButton overwritten prop being writable.
42
+     *
43
+     * @param {string} _value - The value.
44
+     */
45
+    set tooltip(_value) {
46
+        // Unused.
47
+    }
48
+
49
+    /**
50
+     * Indicates whether this button is in toggled state or not.
51
+     *
52
+     * @override
53
+     * @protected
54
+     * @returns {boolean}
55
+     */
56
+    _isToggled() {
57
+        return this.props.isOpen;
58
+    }
59
+
60
+    /**
61
+     * Indicates whether a key was pressed.
62
+     *
63
+     * @override
64
+     * @protected
65
+     * @returns {boolean}
66
+     */
67
+    _onKeyDown() {
68
+        this.props.onKeyDown();
69
+    }
70
+}
71
+
72
+
73
+export default translate(OverflowToggleButton);

+ 1
- 7
react/features/toolbox/components/web/ProfileButton.js Parādīt failu

@@ -95,13 +95,7 @@ class ProfileButton extends AbstractButton<Props, *> {
95 95
      * @returns {void}
96 96
      */
97 97
     _handleClick() {
98
-        const { dispatch, _unclickable, handleClick } = this.props;
99
-
100
-        if (handleClick) {
101
-            handleClick();
102
-
103
-            return;
104
-        }
98
+        const { dispatch, _unclickable } = this.props;
105 99
 
106 100
         if (!_unclickable) {
107 101
             sendAnalytics(createToolbarEvent('profile'));

+ 0
- 16
react/features/toolbox/components/web/ShareDesktopButton.js Parādīt failu

@@ -67,22 +67,6 @@ class ShareDesktopButton extends AbstractButton<Props, *> {
67 67
         // Unused.
68 68
     }
69 69
 
70
-    /**
71
-     * Handles clicking / pressing the button, and opens the appropriate dialog.
72
-     *
73
-     * @protected
74
-     * @returns {void}
75
-     */
76
-    _handleClick() {
77
-        const { handleClick } = this.props;
78
-
79
-        if (handleClick) {
80
-            handleClick();
81
-
82
-            return;
83
-        }
84
-    }
85
-
86 70
     /**
87 71
      * Indicates whether this button is in toggled state or not.
88 72
      *

+ 1
- 7
react/features/toolbox/components/web/ToggleCameraButton.js Parādīt failu

@@ -41,13 +41,7 @@ class ToggleCameraButton extends AbstractButton<Props, any> {
41 41
      * @returns {void}
42 42
      */
43 43
     _handleClick() {
44
-        const { dispatch, handleClick } = this.props;
45
-
46
-        if (handleClick) {
47
-            handleClick();
48
-
49
-            return;
50
-        }
44
+        const { dispatch } = this.props;
51 45
 
52 46
         dispatch(toggleCamera());
53 47
     }

+ 0
- 135
react/features/toolbox/components/web/ToolbarButton.js Parādīt failu

@@ -1,135 +0,0 @@
1
-/* @flow */
2
-
3
-import React from 'react';
4
-
5
-import { Icon } from '../../../base/icons';
6
-import { Tooltip } from '../../../base/tooltip';
7
-import AbstractToolbarButton from '../AbstractToolbarButton';
8
-import type { Props as AbstractToolbarButtonProps }
9
-    from '../AbstractToolbarButton';
10
-
11
-/**
12
- * The type of the React {@code Component} props of {@link ToolbarButton}.
13
- */
14
-export type Props = AbstractToolbarButtonProps & {
15
-
16
-    /**
17
-     * The text to display in the tooltip.
18
-     */
19
-    tooltip: string,
20
-
21
-    /**
22
-     * From which direction the tooltip should appear, relative to the
23
-     * button.
24
-     */
25
-    tooltipPosition: string,
26
-
27
-    /**
28
-     * KeyDown handler.
29
-     */
30
-    onKeyDown?: Function
31
-};
32
-
33
-/**
34
- * Represents a button in the toolbar.
35
- *
36
- * @augments AbstractToolbarButton
37
- */
38
-class ToolbarButton extends AbstractToolbarButton<Props> {
39
-    /**
40
-     * Default values for {@code ToolbarButton} component's properties.
41
-     *
42
-     * @static
43
-     */
44
-    static defaultProps = {
45
-        tooltipPosition: 'top'
46
-    };
47
-
48
-    /**
49
-     * Initializes a new {@code ToolbarButton} instance.
50
-     *
51
-     * @inheritdoc
52
-     */
53
-    constructor(props: Props) {
54
-        super(props);
55
-
56
-        this._onKeyPress = this._onKeyPress.bind(this);
57
-        this._onClick = this._onClick.bind(this);
58
-    }
59
-
60
-    _onKeyPress: (Object) => void;
61
-
62
-    /**
63
-     * Handles 'Enter' and Space key on the button to trigger onClick for accessibility.
64
-     *
65
-     * @param {Object} event - The key event.
66
-     * @private
67
-     * @returns {void}
68
-     */
69
-    _onKeyPress(event) {
70
-        if (event.key === 'Enter' || event.key === ' ') {
71
-            event.preventDefault();
72
-            this.props.onClick();
73
-        }
74
-    }
75
-    _onClick: (Object) => void;
76
-
77
-    /**
78
-     * Handles button click.
79
-     *
80
-     * @param {Object} e - The key event.
81
-     * @private
82
-     * @returns {void}
83
-     */
84
-    _onClick(e) {
85
-        this.props.onClick(e);
86
-
87
-        // blur after click to release focus from button to allow PTT.
88
-        e && e.currentTarget && e.currentTarget.blur();
89
-    }
90
-
91
-    /**
92
-     * Renders the button of this {@code ToolbarButton}.
93
-     *
94
-     * @param {Object} children - The children, if any, to be rendered inside
95
-     * the button. Presumably, contains the icon of this {@code ToolbarButton}.
96
-     * @protected
97
-     * @returns {ReactElement} The button of this {@code ToolbarButton}.
98
-     */
99
-    _renderButton(children) {
100
-        return (
101
-            <div
102
-                aria-label = { this.props.accessibilityLabel }
103
-                aria-pressed = { this.props.toggled }
104
-                className = 'toolbox-button'
105
-                onClick = { this._onClick }
106
-                onKeyDown = { this.props.onKeyDown }
107
-                onKeyPress = { this._onKeyPress }
108
-                role = 'button'
109
-                tabIndex = { 0 }>
110
-                { this.props.tooltip
111
-                    ? <Tooltip
112
-                        content = { this.props.tooltip }
113
-                        position = { this.props.tooltipPosition }>
114
-                        { children }
115
-                    </Tooltip>
116
-                    : children }
117
-            </div>
118
-        );
119
-    }
120
-
121
-    /**
122
-     * Renders the icon of this {@code ToolbarButton}.
123
-     *
124
-     * @inheritdoc
125
-     */
126
-    _renderIcon() {
127
-        return (
128
-            <div className = { `toolbox-icon ${this.props.toggled ? 'toggled' : ''}` }>
129
-                <Icon src = { this.props.icon } />
130
-            </div>
131
-        );
132
-    }
133
-}
134
-
135
-export default ToolbarButton;

+ 21
- 10
react/features/toolbox/components/web/Toolbox.js Parādīt failu

@@ -76,7 +76,7 @@ import {
76 76
     setToolbarHovered,
77 77
     showToolbox
78 78
 } from '../../actions';
79
-import { THRESHOLDS, NOT_APPLICABLE, DRAWER_MAX_HEIGHT } from '../../constants';
79
+import { THRESHOLDS, NOT_APPLICABLE, DRAWER_MAX_HEIGHT, NOTIFY_CLICK_MODE } from '../../constants';
80 80
 import { isToolboxVisible } from '../../functions';
81 81
 import DownloadButton from '../DownloadButton';
82 82
 import HangupButton from '../HangupButton';
@@ -106,7 +106,7 @@ type Props = {
106 106
     /**
107 107
      * Toolbar buttons which have their click exposed through the API.
108 108
      */
109
-    _buttonsWithNotifyClick: Array<string>,
109
+    _buttonsWithNotifyClick: Array<string | Object>,
110 110
 
111 111
     /**
112 112
      * Whether or not the chat feature is currently displayed.
@@ -819,22 +819,31 @@ class Toolbox extends Component<Props> {
819 819
     }
820 820
 
821 821
     /**
822
-     * Overwrites click handlers for buttons in case click is exposed through the iframe API.
822
+     * Sets the notify click mode for the buttons.
823 823
      *
824 824
      * @param {Object} buttons - The list of toolbar buttons.
825 825
      * @returns {void}
826 826
      */
827
-    _overwriteButtonsClickHandlers(buttons) {
827
+    _setButtonsNotifyClickMode(buttons) {
828 828
         if (typeof APP === 'undefined' || !this.props._buttonsWithNotifyClick?.length) {
829 829
             return;
830 830
         }
831 831
 
832 832
         Object.values(buttons).forEach((button: any) => {
833
-            if (
834
-                typeof button === 'object'
835
-                && this.props._buttonsWithNotifyClick.includes(button.key)
836
-            ) {
837
-                button.handleClick = () => APP.API.notifyToolbarButtonClicked(button.key);
833
+            if (typeof button === 'object') {
834
+                const notify = this.props._buttonsWithNotifyClick.find(
835
+                    (btn: string | Object) =>
836
+                        (typeof btn === 'string' && btn === button.key)
837
+                        || (typeof btn === 'object' && btn.key === button.key)
838
+                );
839
+
840
+                if (notify) {
841
+                    const notifyMode = typeof notify === 'string' || notify.preventExecution
842
+                        ? NOTIFY_CLICK_MODE.PREVENT_AND_NOTIFY
843
+                        : NOTIFY_CLICK_MODE.ONLY_NOTIFY;
844
+
845
+                    button.notifyMode = notifyMode;
846
+                }
838 847
             }
839 848
         });
840 849
     }
@@ -854,7 +863,7 @@ class Toolbox extends Component<Props> {
854 863
 
855 864
         const buttons = this._getAllButtons();
856 865
 
857
-        this._overwriteButtonsClickHandlers(buttons);
866
+        this._setButtonsNotifyClickMode(buttons);
858 867
         const isHangupVisible = isToolbarButtonEnabled('hangup', _toolbarButtons);
859 868
         const { order } = THRESHOLDS.find(({ width }) => _clientWidth > width)
860 869
             || THRESHOLDS[THRESHOLDS.length - 1];
@@ -1265,6 +1274,7 @@ class Toolbox extends Component<Props> {
1265 1274
                         {mainMenuButtons.map(({ Content, key, ...rest }) => Content !== Separator && (
1266 1275
                             <Content
1267 1276
                                 { ...rest }
1277
+                                buttonKey = { key }
1268 1278
                                 key = { key } />))}
1269 1279
 
1270 1280
                         {Boolean(overflowMenuButtons.length) && (
@@ -1292,6 +1302,7 @@ class Toolbox extends Component<Props> {
1292 1302
                                                 {showSeparator && <Separator key = { `hr${group}` } />}
1293 1303
                                                 <Content
1294 1304
                                                     { ...rest }
1305
+                                                    buttonKey = { key }
1295 1306
                                                     key = { key }
1296 1307
                                                     showLabel = { true } />
1297 1308
                                             </Fragment>

+ 21
- 10
react/features/toolbox/components/web/VideoSettingsButton.js Parādīt failu

@@ -16,6 +16,11 @@ import VideoMuteButton from '../VideoMuteButton';
16 16
 
17 17
 type Props = {
18 18
 
19
+    /**
20
+     * The button's key.
21
+     */
22
+     buttonKey?: string,
23
+
19 24
     /**
20 25
      * External handler for click action.
21 26
      */
@@ -41,6 +46,12 @@ type Props = {
41 46
      */
42 47
     isDisabled: boolean,
43 48
 
49
+    /**
50
+     * Notify mode for `toolbarButtonClicked` event -
51
+     * whether to only notify or to also prevent button click routine.
52
+     */
53
+    notifyMode?: string,
54
+
44 55
     /**
45 56
      * Flag controlling the visibility of the button.
46 57
      * VideoSettings popup is currently disabled on mobile browsers
@@ -112,13 +123,7 @@ class VideoSettingsButton extends Component<Props> {
112 123
      * @returns {void}
113 124
      */
114 125
     _onClick() {
115
-        const { handleClick, onVideoOptionsClick } = this.props;
116
-
117
-        if (handleClick) {
118
-            handleClick();
119
-
120
-            return;
121
-        }
126
+        const { onVideoOptionsClick } = this.props;
122 127
 
123 128
         onVideoOptionsClick();
124 129
     }
@@ -129,7 +134,7 @@ class VideoSettingsButton extends Component<Props> {
129 134
      * @inheritdoc
130 135
      */
131 136
     render() {
132
-        const { handleClick, t, visible, isOpen } = this.props;
137
+        const { t, visible, isOpen, buttonKey, notifyMode } = this.props;
133 138
 
134 139
         return visible ? (
135 140
             <VideoSettingsPopup>
@@ -138,16 +143,22 @@ class VideoSettingsButton extends Component<Props> {
138 143
                     ariaExpanded = { isOpen }
139 144
                     ariaHasPopup = { true }
140 145
                     ariaLabel = { this.props.t('toolbar.videoSettings') }
146
+                    buttonKey = { buttonKey }
141 147
                     icon = { IconArrowUp }
142 148
                     iconDisabled = { this._isIconDisabled() }
143 149
                     iconId = 'video-settings-button'
144 150
                     iconTooltip = { t('toolbar.videoSettings') }
151
+                    notifyMode = { notifyMode }
145 152
                     onIconClick = { this._onClick }
146 153
                     onIconKeyDown = { this._onEscClick }>
147
-                    <VideoMuteButton handleClick = { handleClick } />
154
+                    <VideoMuteButton
155
+                        buttonKey = { buttonKey }
156
+                        notifyMode = { notifyMode } />
148 157
                 </ToolboxButtonWithIcon>
149 158
             </VideoSettingsPopup>
150
-        ) : <VideoMuteButton handleClick = { handleClick } />;
159
+        ) : <VideoMuteButton
160
+            buttonKey = { buttonKey }
161
+            notifyMode = { notifyMode } />;
151 162
     }
152 163
 }
153 164
 

+ 0
- 1
react/features/toolbox/components/web/index.js Parādīt failu

@@ -1,6 +1,5 @@
1 1
 export { default as AudioSettingsButton } from './AudioSettingsButton';
2 2
 export { default as VideoSettingsButton } from './VideoSettingsButton';
3
-export { default as ToolbarButton } from './ToolbarButton';
4 3
 export { default as Toolbox } from './Toolbox';
5 4
 export { default as Drawer } from './Drawer';
6 5
 export { default as JitsiPortal } from './JitsiPortal';

+ 5
- 0
react/features/toolbox/constants.js Parādīt failu

@@ -33,3 +33,8 @@ export const NOT_APPLICABLE = 'N/A';
33 33
 export const TOOLBAR_TIMEOUT = 4000;
34 34
 
35 35
 export const DRAWER_MAX_HEIGHT = '80vh - 64px';
36
+
37
+export const NOTIFY_CLICK_MODE = {
38
+    ONLY_NOTIFY: 'ONLY_NOTIFY',
39
+    PREVENT_AND_NOTIFY: 'PREVENT_AND_NOTIFY'
40
+};

+ 1
- 7
react/features/video-layout/components/TileViewButton.js Parādīt failu

@@ -52,13 +52,7 @@ class TileViewButton<P: Props> extends AbstractButton<P, *> {
52 52
      * @returns {void}
53 53
      */
54 54
     _handleClick() {
55
-        const { _tileViewEnabled, dispatch, handleClick } = this.props;
56
-
57
-        if (handleClick) {
58
-            handleClick();
59
-
60
-            return;
61
-        }
55
+        const { _tileViewEnabled, dispatch } = this.props;
62 56
 
63 57
         const value = !_tileViewEnabled;
64 58
 

+ 0
- 18
react/features/video-quality/components/VideoQualityButton.web.js Parādīt failu

@@ -39,24 +39,6 @@ class VideoQualityButton extends AbstractButton<Props, *> {
39 39
     label = 'videoStatus.performanceSettings';
40 40
     tooltip = 'videoStatus.performanceSettings';
41 41
     icon = IconGauge;
42
-
43
-
44
-    /**
45
-     * Handles clicking / pressing the button.
46
-     *
47
-     * @override
48
-     * @protected
49
-     * @returns {void}
50
-     */
51
-    _handleClick() {
52
-        const { handleClick } = this.props;
53
-
54
-        if (handleClick) {
55
-            handleClick();
56
-
57
-            return;
58
-        }
59
-    }
60 42
 }
61 43
 
62 44
 export default translate(VideoQualityButton);

+ 1
- 7
react/features/virtual-background/components/VideoBackgroundButton.js Parādīt failu

@@ -43,13 +43,7 @@ class VideoBackgroundButton extends AbstractButton<Props, *> {
43 43
      * @returns {void}
44 44
      */
45 45
     _handleClick() {
46
-        const { dispatch, handleClick } = this.props;
47
-
48
-        if (handleClick) {
49
-            handleClick();
50
-
51
-            return;
52
-        }
46
+        const { dispatch } = this.props;
53 47
 
54 48
         dispatch(openDialog(VirtualBackgroundDialog));
55 49
     }

Notiek ielāde…
Atcelt
Saglabāt