Browse Source

fix(reactions) Reactions improvements (#9964)

* Register shortcuts on mount

* Changed icon for reactions menu

* Enable reactions by default

* Fix unreadCount bug

When having unread messages and sending a reaction the unread count now shows the correct count

* Fix overflow menu bottom color when reactions are enabled

* Revert raise hand icon

* Update raise hand functionality

On desktop show raise button with arrow for reactions. Only show raise hand in the reactions menu on mobile

* Fix lint error

Add required prop to ToolboxButtonWithIcon

* Legacy support for enable reactions

If disableReactions is undefined treat it as true

* Remove unnecessary code

* Fix unread counter showing negative count

* Fix unreadCount with reactions

UnreadCount ignores all reactions messages

* Fixed typo

* Fix background color
master
robertpin 4 years ago
parent
commit
584ec7c82e
No account linked to committer's email address

+ 2
- 2
config.js View File

@@ -77,8 +77,8 @@ var config = {
77 77
     // Disables moderator indicators.
78 78
     // disableModeratorIndicator: false,
79 79
 
80
-    // Enables reactions feature.
81
-    // enableReactions: false,
80
+    // Disables the reactions feature.
81
+    // disableReactions: true,
82 82
 
83 83
     // Disables polls feature.
84 84
     // disablePolls: false,

+ 1
- 1
css/_reactions-menu.scss View File

@@ -2,7 +2,7 @@
2 2
 
3 3
 .reactions-menu {
4 4
 	width: 280px;
5
-	background: #292929;
5
+	background: $menuBG;
6 6
 	box-shadow: 0px 3px 16px rgba(0, 0, 0, 0.6), 0px 0px 4px 1px rgba(0, 0, 0, 0.25);
7 7
 	border-radius: 3px;
8 8
 	padding: 16px;

+ 1
- 1
react/features/base/config/configWhitelist.js View File

@@ -100,6 +100,7 @@ export default [
100 100
     'disableNS',
101 101
     'disablePolls',
102 102
     'disableProfile',
103
+    'disableReactions',
103 104
     'disableRecordAudioNotification',
104 105
     'disableRemoteControl',
105 106
     'disableRemoteMute',
@@ -127,7 +128,6 @@ export default [
127 128
     'enableLayerSuspension',
128 129
     'enableLipSync',
129 130
     'enableOpusRed',
130
-    'enableReactions',
131 131
     'enableRemb',
132 132
     'enableSaveLogs',
133 133
     'enableScreenshotCapture',

+ 5
- 1
react/features/base/dialog/components/native/BottomSheet.js View File

@@ -145,6 +145,7 @@ class BottomSheet extends PureComponent<Props> {
145 145
                             renderHeader
146 146
                                 ? _styles.sheetHeader
147 147
                                 : _styles.sheet,
148
+                            renderFooter && _styles.sheetFooter,
148 149
                             style,
149 150
                             {
150 151
                                 maxHeight: _height - 100
@@ -154,7 +155,10 @@ class BottomSheet extends PureComponent<Props> {
154 155
                         <ScrollView
155 156
                             bounces = { false }
156 157
                             showsVerticalScrollIndicator = { false }
157
-                            style = { addScrollViewPadding && styles.scrollView } >
158
+                            style = { [
159
+                                renderFooter && _styles.sheet,
160
+                                addScrollViewPadding && styles.scrollView
161
+                            ] } >
158 162
                             { this.props.children }
159 163
                         </ScrollView>
160 164
                         { renderFooter && renderFooter() }

+ 7
- 0
react/features/base/dialog/components/native/styles.js View File

@@ -213,6 +213,13 @@ ColorSchemeRegistry.register('BottomSheet', {
213 213
      */
214 214
     sheetHeader: {
215 215
         backgroundColor: BaseTheme.palette.ui02
216
+    },
217
+
218
+    /**
219
+     * Bottom sheet's background color with footer.
220
+     */
221
+    sheetFooter: {
222
+        backgroundColor: BaseTheme.palette.bottomSheet
216 223
     }
217 224
 });
218 225
 

+ 3
- 0
react/features/chat/actions.any.js View File

@@ -22,6 +22,8 @@ import {
22 22
  * "error" or "local" or "remote".
23 23
  * @param {string} messageDetails.timestamp - A timestamp to display for when
24 24
  * the message was received.
25
+ * @param {string} messageDetails.isReaction - Whether or not the
26
+ * message is a reaction message.
25 27
  * @returns {{
26 28
  *     type: ADD_MESSAGE,
27 29
  *     displayName: string,
@@ -29,6 +31,7 @@ import {
29 31
  *     message: string,
30 32
  *     messageType: string,
31 33
  *     timestamp: string,
34
+ *     isReaction: boolean
32 35
  * }}
33 36
  */
34 37
 export function addMessage(messageDetails: Object) {

+ 18
- 2
react/features/chat/functions.js View File

@@ -69,14 +69,30 @@ export function getUnreadCount(state: Object) {
69 69
         return 0;
70 70
     }
71 71
 
72
+    let reactionMessages = 0;
73
+
72 74
     if (navigator.product === 'ReactNative') {
73 75
         // React native stores the messages in a reversed order.
74
-        return messages.indexOf(lastReadMessage);
76
+        const lastReadIndex = messages.indexOf(lastReadMessage);
77
+
78
+        for (let i = 0; i < lastReadIndex; i++) {
79
+            if (messages[i].isReaction) {
80
+                reactionMessages++;
81
+            }
82
+        }
83
+
84
+        return lastReadIndex - reactionMessages;
75 85
     }
76 86
 
77 87
     const lastReadIndex = messages.lastIndexOf(lastReadMessage);
78 88
 
79
-    return messagesCount - (lastReadIndex + 1);
89
+    for (let i = lastReadIndex + 1; i < messagesCount; i++) {
90
+        if (messages[i].isReaction) {
91
+            reactionMessages++;
92
+        }
93
+    }
94
+
95
+    return messagesCount - (lastReadIndex + 1) - reactionMessages;
80 96
 }
81 97
 
82 98
 /**

+ 13
- 5
react/features/chat/middleware.js View File

@@ -68,7 +68,12 @@ MiddlewareRegistry.register(store => next => action => {
68 68
 
69 69
     switch (action.type) {
70 70
     case ADD_MESSAGE:
71
-        unreadCount = action.hasRead ? 0 : getUnreadCount(getState()) + 1;
71
+        unreadCount = getUnreadCount(getState());
72
+        if (action.isReaction) {
73
+            action.hasRead = false;
74
+        } else {
75
+            unreadCount = action.hasRead ? 0 : unreadCount + 1;
76
+        }
72 77
         isOpen = getState()['features/chat'].isOpen;
73 78
 
74 79
         if (typeof APP !== 'undefined') {
@@ -171,7 +176,7 @@ MiddlewareRegistry.register(store => next => action => {
171 176
             message: action.message,
172 177
             privateMessage: false,
173 178
             timestamp: Date.now()
174
-        }, false);
179
+        }, false, true);
175 180
     }
176 181
     }
177 182
 
@@ -270,7 +275,7 @@ function _addChatMsgListener(conference, store) {
270 275
                         message: getReactionMessageFromBuffer(eventData.reactions),
271 276
                         privateMessage: false,
272 277
                         timestamp: eventData.timestamp
273
-                    }, false);
278
+                    }, false, true);
274 279
                 }
275 280
             }
276 281
         });
@@ -304,11 +309,13 @@ function _handleChatError({ dispatch }, error) {
304 309
  * @param {Store} store - The Redux store.
305 310
  * @param {Object} message - The message object.
306 311
  * @param {boolean} shouldPlaySound - Whether or not to play the incoming message sound.
312
+ * @param {boolean} isReaction - Whether or not the message is a reaction message.
307 313
  * @returns {void}
308 314
  */
309 315
 function _handleReceivedMessage({ dispatch, getState },
310 316
         { id, message, privateMessage, timestamp },
311
-        shouldPlaySound = true
317
+        shouldPlaySound = true,
318
+        isReaction = false
312 319
 ) {
313 320
     // Logic for all platforms:
314 321
     const state = getState();
@@ -337,7 +344,8 @@ function _handleReceivedMessage({ dispatch, getState },
337 344
         message,
338 345
         privateMessage,
339 346
         recipient: getParticipantDisplayName(state, localParticipant.id),
340
-        timestamp: millisecondsTimestamp
347
+        timestamp: millisecondsTimestamp,
348
+        isReaction
341 349
     }));
342 350
 
343 351
     if (typeof APP !== 'undefined') {

+ 1
- 0
react/features/chat/reducer.js View File

@@ -28,6 +28,7 @@ ReducerRegistry.register('features/chat', (state = DEFAULT_STATE, action) => {
28 28
             displayName: action.displayName,
29 29
             error: action.error,
30 30
             id: action.id,
31
+            isReaction: action.isReaction,
31 32
             messageType: action.messageType,
32 33
             message: action.message,
33 34
             privateMessage: action.privateMessage,

+ 30
- 21
react/features/reactions/components/web/ReactionsMenu.js View File

@@ -8,6 +8,7 @@ import {
8 8
     createToolbarEvent,
9 9
     sendAnalytics
10 10
 } from '../../../analytics';
11
+import { isMobileBrowser } from '../../../base/environment/utils';
11 12
 import { translate } from '../../../base/i18n';
12 13
 import { getLocalParticipant, participantUpdated } from '../../../base/participants';
13 14
 import { connect } from '../../../base/redux';
@@ -21,34 +22,39 @@ import ReactionButton from './ReactionButton';
21 22
 type Props = {
22 23
 
23 24
     /**
24
-     * Used for translation.
25
+     * Docks the toolbox
25 26
      */
26
-    t: Function,
27
+    _dockToolbox: Function,
27 28
 
28 29
     /**
29
-     * Whether or not the local participant's hand is raised.
30
+     * Whether or not it's a mobile browser.
30 31
      */
31
-    _raisedHand: boolean,
32
+    _isMobile: boolean,
32 33
 
33 34
     /**
34 35
      * The ID of the local participant.
35 36
      */
36 37
     _localParticipantID: String,
37 38
 
39
+    /**
40
+     * Whether or not the local participant's hand is raised.
41
+     */
42
+    _raisedHand: boolean,
43
+
38 44
     /**
39 45
      * The Redux Dispatch function.
40 46
      */
41 47
     dispatch: Function,
42 48
 
43 49
     /**
44
-     * Docks the toolbox
50
+     * Whether or not it's displayed in the overflow menu.
45 51
      */
46
-    _dockToolbox: Function,
52
+    overflowMenu: boolean,
47 53
 
48 54
     /**
49
-     * Whether or not it's displayed in the overflow menu.
55
+     * Used for translation.
50 56
      */
51
-    overflowMenu: boolean
57
+    t: Function
52 58
 };
53 59
 
54 60
 declare var APP: Object;
@@ -177,25 +183,27 @@ class ReactionsMenu extends Component<Props> {
177 183
      * @inheritdoc
178 184
      */
179 185
     render() {
180
-        const { _raisedHand, t, overflowMenu } = this.props;
186
+        const { _raisedHand, t, overflowMenu, _isMobile } = this.props;
181 187
 
182 188
         return (
183 189
             <div className = { `reactions-menu ${overflowMenu ? 'overflow' : ''}` }>
184 190
                 <div className = 'reactions-row'>
185 191
                     { this._getReactionButtons() }
186 192
                 </div>
187
-                <div className = 'raise-hand-row'>
188
-                    <ReactionButton
189
-                        accessibilityLabel = { t('toolbar.accessibilityLabel.raiseHand') }
190
-                        icon = '✋'
191
-                        key = 'raisehand'
192
-                        label = {
193
-                            `${t(`toolbar.${_raisedHand ? 'lowerYourHand' : 'raiseYourHand'}`)}
194
-                            ${overflowMenu ? '' : ' (R)'}`
195
-                        }
196
-                        onClick = { this._onToolbarToggleRaiseHand }
197
-                        toggled = { true } />
198
-                </div>
193
+                {_isMobile && (
194
+                    <div className = 'raise-hand-row'>
195
+                        <ReactionButton
196
+                            accessibilityLabel = { t('toolbar.accessibilityLabel.raiseHand') }
197
+                            icon = '✋'
198
+                            key = 'raisehand'
199
+                            label = {
200
+                                `${t(`toolbar.${_raisedHand ? 'lowerYourHand' : 'raiseYourHand'}`)}
201
+                                ${overflowMenu ? '' : ' (R)'}`
202
+                            }
203
+                            onClick = { this._onToolbarToggleRaiseHand }
204
+                            toggled = { true } />
205
+                    </div>
206
+                )}
199 207
             </div>
200 208
         );
201 209
     }
@@ -212,6 +220,7 @@ function mapStateToProps(state) {
212 220
 
213 221
     return {
214 222
         _localParticipantID: localParticipant.id,
223
+        _isMobile: isMobileBrowser(),
215 224
         _raisedHand: localParticipant.raisedHand
216 225
     };
217 226
 }

+ 53
- 20
react/features/reactions/components/web/ReactionsMenuButton.js View File

@@ -2,14 +2,16 @@
2 2
 
3 3
 import React from 'react';
4 4
 
5
+import { isMobileBrowser } from '../../../base/environment/utils';
5 6
 import { translate } from '../../../base/i18n';
6
-import { IconRaisedHand } from '../../../base/icons';
7
+import { IconArrowUp, IconRaisedHand } from '../../../base/icons';
7 8
 import { getLocalParticipant } from '../../../base/participants';
8 9
 import { connect } from '../../../base/redux';
10
+import { ToolboxButtonWithIcon } from '../../../base/toolbox/components';
9 11
 import ToolbarButton from '../../../toolbox/components/web/ToolbarButton';
10 12
 import { toggleReactionsMenuVisibility } from '../../actions.web';
11 13
 import { type ReactionEmojiProps } from '../../constants';
12
-import { getReactionsQueue } from '../../functions.any';
14
+import { getReactionsQueue, isReactionsEnabled } from '../../functions.any';
13 15
 import { getReactionsMenuVisibility } from '../../functions.web';
14 16
 
15 17
 import ReactionEmoji from './ReactionEmoji';
@@ -18,34 +20,44 @@ import ReactionsMenuPopup from './ReactionsMenuPopup';
18 20
 type Props = {
19 21
 
20 22
     /**
21
-     * Used for translation.
23
+     * Whether or not reactions are enabled.
22 24
      */
23
-    t: Function,
25
+    _reactionsEnabled: Boolean,
24 26
 
25 27
     /**
26
-     * Whether or not the local participant's hand is raised.
28
+     * Redux dispatch function.
27 29
      */
28
-    raisedHand: boolean,
30
+    dispatch: Function,
29 31
 
30 32
     /**
31
-     * Click handler for the reaction button. Toggles the reactions menu.
33
+     * Click handler for raise hand functionality.
32 34
      */
33
-    onReactionsClick: Function,
35
+    handleClick: Function,
34 36
 
35 37
     /**
36 38
      * Whether or not the reactions menu is open.
37 39
      */
38 40
     isOpen: boolean,
39 41
 
42
+    /**
43
+     * Whether or not it's a mobile browser.
44
+     */
45
+    isMobile: boolean,
46
+
47
+    /**
48
+     * Whether or not the local participant's hand is raised.
49
+     */
50
+    raisedHand: boolean,
51
+
40 52
     /**
41 53
      * The array of reactions to be displayed.
42 54
      */
43 55
     reactionsQueue: Array<ReactionEmojiProps>,
44 56
 
45 57
     /**
46
-     * Redux dispatch function.
58
+     * Used for translation.
47 59
      */
48
-    dispatch: Function
60
+    t: Function
49 61
 };
50 62
 
51 63
 
@@ -57,11 +69,14 @@ declare var APP: Object;
57 69
  * @returns {ReactElement}
58 70
  */
59 71
 function ReactionsMenuButton({
60
-    t,
61
-    raisedHand,
72
+    _reactionsEnabled,
73
+    dispatch,
74
+    handleClick,
62 75
     isOpen,
76
+    isMobile,
77
+    raisedHand,
63 78
     reactionsQueue,
64
-    dispatch
79
+    t
65 80
 }: Props) {
66 81
 
67 82
     /**
@@ -73,16 +88,32 @@ function ReactionsMenuButton({
73 88
         dispatch(toggleReactionsMenuVisibility());
74 89
     }
75 90
 
91
+    const raiseHandButton = (<ToolbarButton
92
+        accessibilityLabel = { t('toolbar.accessibilityLabel.raiseHand') }
93
+        icon = { IconRaisedHand }
94
+        key = 'raise-hand'
95
+        onClick = { handleClick }
96
+        toggled = { raisedHand }
97
+        tooltip = { t('toolbar.raiseHand') } />);
98
+
76 99
     return (
77 100
         <div className = 'reactions-menu-popup-container'>
78 101
             <ReactionsMenuPopup>
79
-                <ToolbarButton
80
-                    accessibilityLabel = { t('toolbar.accessibilityLabel.reactionsMenu') }
81
-                    icon = { IconRaisedHand }
82
-                    key = 'reactions'
83
-                    onClick = { toggleReactionsMenu }
84
-                    toggled = { raisedHand }
85
-                    tooltip = { t(`toolbar.${isOpen ? 'closeReactionsMenu' : 'openReactionsMenu'}`) } />
102
+                {!_reactionsEnabled || isMobile ? raiseHandButton
103
+                    : (
104
+                        <ToolboxButtonWithIcon
105
+                            ariaControls = 'reactions-menu-dialog'
106
+                            ariaExpanded = { isOpen }
107
+                            ariaHasPopup = { true }
108
+                            ariaLabel = { t('toolbar.accessibilityLabel.reactionsMenu') }
109
+                            icon = { IconArrowUp }
110
+                            iconDisabled = { false }
111
+                            iconId = 'reactions-menu-button'
112
+                            iconTooltip = { t(`toolbar.${isOpen ? 'closeReactionsMenu' : 'openReactionsMenu'}`) }
113
+                            onIconClick = { toggleReactionsMenu }>
114
+                            {raiseHandButton}
115
+                        </ToolboxButtonWithIcon>
116
+                    )}
86 117
             </ReactionsMenuPopup>
87 118
             {reactionsQueue.map(({ reaction, uid }, index) => (<ReactionEmoji
88 119
                 index = { index }
@@ -103,7 +134,9 @@ function mapStateToProps(state) {
103 134
     const localParticipant = getLocalParticipant(state);
104 135
 
105 136
     return {
137
+        _reactionsEnabled: isReactionsEnabled(state),
106 138
         isOpen: getReactionsMenuVisibility(state),
139
+        isMobile: isMobileBrowser(),
107 140
         reactionsQueue: getReactionsQueue(state),
108 141
         raisedHand: localParticipant?.raisedHand
109 142
     };

+ 3
- 3
react/features/reactions/functions.any.js View File

@@ -151,11 +151,11 @@ export function getReactionsSoundsThresholds(reactions: Array<string>) {
151 151
  * @returns {boolean}
152 152
  */
153 153
 export function isReactionsEnabled(state: Object) {
154
-    const { enableReactions } = state['features/base/config'];
154
+    const { disableReactions } = state['features/base/config'];
155 155
 
156 156
     if (navigator.product === 'ReactNative') {
157
-        return enableReactions && getFeatureFlag(state, REACTIONS_ENABLED, true);
157
+        return !disableReactions && getFeatureFlag(state, REACTIONS_ENABLED, true);
158 158
     }
159 159
 
160
-    return enableReactions;
160
+    return !disableReactions;
161 161
 }

+ 5
- 5
react/features/settings/components/web/SoundsTab.js View File

@@ -15,6 +15,11 @@ declare var APP: Object;
15 15
 export type Props = {
16 16
     ...$Exact<AbstractDialogTabProps>,
17 17
 
18
+    /**
19
+     * Whether or not the reactions feature is enabled.
20
+     */
21
+    enableReactions: Boolean,
22
+
18 23
     /**
19 24
      * Whether or not the sound for the incoming message should play.
20 25
      */
@@ -40,11 +45,6 @@ export type Props = {
40 45
     */
41 46
     soundsReactions: Boolean,
42 47
 
43
-    /**
44
-     * Whether or not the reactions feature is enabled.
45
-     */
46
-    enableReactions: Boolean,
47
-
48 48
     /**
49 49
      * Invoked to obtain translated strings.
50 50
      */

+ 2
- 1
react/features/settings/functions.js View File

@@ -10,6 +10,7 @@ import {
10 10
 import { toState } from '../base/redux';
11 11
 import { parseStandardURIString } from '../base/util';
12 12
 import { isFollowMeActive } from '../follow-me';
13
+import { isReactionsEnabled } from '../reactions/functions.any';
13 14
 
14 15
 import { SS_DEFAULT_FRAME_RATE, SS_SUPPORTED_FRAMERATES } from './constants';
15 16
 
@@ -174,7 +175,7 @@ export function getSoundsTabProps(stateful: Object | Function) {
174 175
         soundsTalkWhileMuted,
175 176
         soundsReactions
176 177
     } = state['features/base/settings'];
177
-    const { enableReactions } = state['features/base/config'];
178
+    const enableReactions = isReactionsEnabled(state);
178 179
 
179 180
     return {
180 181
         soundsIncomingMessage,

+ 0
- 84
react/features/toolbox/components/web/RaiseHandButton.js View File

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

+ 8
- 60
react/features/toolbox/components/web/Toolbox.js View File

@@ -17,7 +17,6 @@ import { translate } from '../../../base/i18n';
17 17
 import JitsiMeetJS from '../../../base/lib-jitsi-meet';
18 18
 import {
19 19
     getLocalParticipant,
20
-    getParticipantCount,
21 20
     haveParticipantWithScreenSharingFeature,
22 21
     raiseHand
23 22
 } from '../../../base/participants';
@@ -87,7 +86,6 @@ import AudioSettingsButton from './AudioSettingsButton';
87 86
 import FullscreenButton from './FullscreenButton';
88 87
 import OverflowMenuButton from './OverflowMenuButton';
89 88
 import ProfileButton from './ProfileButton';
90
-import RaiseHandButton from './RaiseHandButton';
91 89
 import Separator from './Separator';
92 90
 import ShareDesktopButton from './ShareDesktopButton';
93 91
 import ToggleCameraButton from './ToggleCameraButton';
@@ -180,11 +178,6 @@ type Props = {
180 178
      */
181 179
     _overflowMenuVisible: boolean,
182 180
 
183
-    /**
184
-     * Number of participants in the conference.
185
-     */
186
-    _participantCount: number,
187
-
188 181
     /**
189 182
      * Whether or not the participants pane is open.
190 183
      */
@@ -254,16 +247,12 @@ type Props = {
254 247
 
255 248
 declare var APP: Object;
256 249
 
257
-type State = {
258
-    reactionsShortcutsRegistered: boolean
259
-};
260
-
261 250
 /**
262 251
  * Implements the conference toolbox on React/Web.
263 252
  *
264 253
  * @extends Component
265 254
  */
266
-class Toolbox extends Component<Props, State> {
255
+class Toolbox extends Component<Props> {
267 256
     /**
268 257
      * Initializes a new {@code Toolbox} instance.
269 258
      *
@@ -273,10 +262,6 @@ class Toolbox extends Component<Props, State> {
273 262
     constructor(props: Props) {
274 263
         super(props);
275 264
 
276
-        this.state = {
277
-            reactionsShortcutsRegistered: false
278
-        };
279
-
280 265
         // Bind event handlers so they are only bound once per instance.
281 266
         this._onMouseOut = this._onMouseOut.bind(this);
282 267
         this._onMouseOver = this._onMouseOver.bind(this);
@@ -306,7 +291,7 @@ class Toolbox extends Component<Props, State> {
306 291
      * @returns {void}
307 292
      */
308 293
     componentDidMount() {
309
-        const { _toolbarButtons, t, dispatch, _reactionsEnabled, _participantCount } = this.props;
294
+        const { _toolbarButtons, t, dispatch, _reactionsEnabled } = this.props;
310 295
         const KEYBOARD_SHORTCUTS = [
311 296
             isToolbarButtonEnabled('videoquality', _toolbarButtons) && {
312 297
                 character: 'A',
@@ -355,7 +340,7 @@ class Toolbox extends Component<Props, State> {
355 340
             }
356 341
         });
357 342
 
358
-        if (_reactionsEnabled && _participantCount > 1) {
343
+        if (_reactionsEnabled) {
359 344
             const REACTION_SHORTCUTS = Object.keys(REACTIONS).map(key => {
360 345
                 const onShortcutSendReaction = () => {
361 346
                     dispatch(addReactionToBuffer(key));
@@ -389,7 +374,7 @@ class Toolbox extends Component<Props, State> {
389 374
      * @inheritdoc
390 375
      */
391 376
     componentDidUpdate(prevProps) {
392
-        const { _dialog, _reactionsEnabled, _participantCount, dispatch, t } = this.props;
377
+        const { _dialog, dispatch } = this.props;
393 378
 
394 379
 
395 380
         if (prevProps._overflowMenuVisible
@@ -398,41 +383,6 @@ class Toolbox extends Component<Props, State> {
398 383
             this._onSetOverflowVisible(false);
399 384
             dispatch(setToolbarHovered(false));
400 385
         }
401
-
402
-        if (!this.state.reactionsShortcutsRegistered
403
-            && (prevProps._reactionsEnabled !== _reactionsEnabled
404
-            || prevProps._participantCount !== _participantCount)) {
405
-            if (_reactionsEnabled && _participantCount > 1) {
406
-                // eslint-disable-next-line react/no-did-update-set-state
407
-                this.setState({
408
-                    reactionsShortcutsRegistered: true
409
-                });
410
-                const REACTION_SHORTCUTS = Object.keys(REACTIONS).map(key => {
411
-                    const onShortcutSendReaction = () => {
412
-                        dispatch(addReactionToBuffer(key));
413
-                        sendAnalytics(createShortcutEvent(
414
-                            `reaction.${key}`
415
-                        ));
416
-                    };
417
-
418
-                    return {
419
-                        character: REACTIONS[key].shortcutChar,
420
-                        exec: onShortcutSendReaction,
421
-                        helpDescription: t(`toolbar.reaction${key.charAt(0).toUpperCase()}${key.slice(1)}`),
422
-                        altKey: true
423
-                    };
424
-                });
425
-
426
-                REACTION_SHORTCUTS.forEach(shortcut => {
427
-                    APP.keyboardshortcut.registerShortcut(
428
-                        shortcut.character,
429
-                        null,
430
-                        shortcut.exec,
431
-                        shortcut.helpDescription,
432
-                        shortcut.altKey);
433
-                });
434
-            }
435
-        }
436 386
     }
437 387
 
438 388
     /**
@@ -445,7 +395,7 @@ class Toolbox extends Component<Props, State> {
445 395
         [ 'A', 'C', 'D', 'R', 'S' ].forEach(letter =>
446 396
             APP.keyboardshortcut.unregisterShortcut(letter));
447 397
 
448
-        if (this.props._reactionsEnabled && this.state.reactionsShortcutsRegistered) {
398
+        if (this.props._reactionsEnabled) {
449 399
             Object.keys(REACTIONS).map(key => REACTIONS[key].shortcutChar)
450 400
                 .forEach(letter =>
451 401
                     APP.keyboardshortcut.unregisterShortcut(letter, true));
@@ -613,8 +563,7 @@ class Toolbox extends Component<Props, State> {
613 563
         const {
614 564
             _feedbackConfigured,
615 565
             _isMobile,
616
-            _screenSharing,
617
-            _reactionsEnabled
566
+            _screenSharing
618 567
         } = this.props;
619 568
 
620 569
         const microphone = {
@@ -651,8 +600,8 @@ class Toolbox extends Component<Props, State> {
651 600
 
652 601
         const raisehand = {
653 602
             key: 'raisehand',
654
-            Content: _reactionsEnabled ? ReactionsMenuButton : RaiseHandButton,
655
-            handleClick: _reactionsEnabled ? null : this._onToolbarToggleRaiseHand,
603
+            Content: ReactionsMenuButton,
604
+            handleClick: this._onToolbarToggleRaiseHand,
656 605
             group: 2
657 606
         };
658 607
 
@@ -1388,7 +1337,6 @@ function _mapStateToProps(state, ownProps) {
1388 1337
         _localParticipantID: localParticipant?.id,
1389 1338
         _localVideo: localVideo,
1390 1339
         _overflowMenuVisible: overflowMenuVisible,
1391
-        _participantCount: getParticipantCount(state),
1392 1340
         _participantsPaneOpen: getParticipantsPaneOpen(state),
1393 1341
         _raisedHand: localParticipant?.raisedHand,
1394 1342
         _reactionsEnabled: isReactionsEnabled(state),

Loading…
Cancel
Save