浏览代码

fix(reactions) Moved reactions behind feature flag

master
robertpin 3 年前
父节点
当前提交
2d04f3852c
没有帐户链接到提交者的电子邮件

+ 3
- 0
config.js 查看文件

@@ -70,6 +70,9 @@ var config = {
70 70
         // callStatsThreshold: 5 // enable callstats for 5% of the users.
71 71
     },
72 72
 
73
+    // Enables reactions feature.
74
+    enableReactions: false,
75
+
73 76
     // Disables ICE/UDP by filtering out local and remote UDP candidates in
74 77
     // signalling.
75 78
     // webrtcIceUdpDisable: false,

+ 5
- 5
css/_reactions-menu.scss 查看文件

@@ -90,7 +90,7 @@
90 90
 	width: 20%;
91 91
 	bottom: 0;
92 92
 	left: 40%;
93
-	height: 48px;
93
+	height: 0;
94 94
 }
95 95
 
96 96
 .reactions-menu-popup-container,
@@ -111,8 +111,8 @@ $reactionCount: 20;
111 111
 	line-height: 32px;
112 112
 	width: 32px;
113 113
 	height: 32px;
114
-	top: 32px;
115
-	left: 10px;
114
+	top: 0;
115
+	left: 20px;
116 116
 	opacity: 0;
117 117
 	z-index: 1;
118 118
 
@@ -123,8 +123,8 @@ $reactionCount: 20;
123 123
 	@for $i from 1 through $reactionCount {
124 124
 	&.reaction-#{$i} {
125 125
 		animation: animation-#{$i} 5s forwards ease-in-out;
126
-		top: #{random(50, 0)}px;
127
-		left: #{random(-10, 10)}px;
126
+		top: #{random(-40, 10)}px;
127
+		left: #{random(0, 30)}px;
128 128
 	}
129 129
 }
130 130
 }

+ 6
- 0
react/features/base/flags/constants.js 查看文件

@@ -214,3 +214,9 @@ export const VIDEO_SHARE_BUTTON_ENABLED = 'video-share.enabled';
214 214
  * Default: disabled (false).
215 215
  */
216 216
 export const WELCOME_PAGE_ENABLED = 'welcomepage.enabled';
217
+
218
+/**
219
+ * Flag indicating if the reactions feature should be enabled.
220
+ * Default: disabled (false).
221
+ */
222
+export const REACTIONS_ENABLED = 'reactions.enabled';

+ 14
- 5
react/features/toolbox/components/native/OverflowMenu.js 查看文件

@@ -4,6 +4,7 @@ import React, { PureComponent } from 'react';
4 4
 
5 5
 import { ColorSchemeRegistry } from '../../../base/color-scheme';
6 6
 import { BottomSheet, hideDialog, isDialogOpen } from '../../../base/dialog';
7
+import { getFeatureFlag, REACTIONS_ENABLED } from '../../../base/flags';
7 8
 import { connect } from '../../../base/redux';
8 9
 import { StyleType } from '../../../base/styles';
9 10
 import { SharedDocumentButton } from '../../../etherpad';
@@ -22,6 +23,7 @@ import MuteEveryoneButton from '../MuteEveryoneButton';
22 23
 import MuteEveryonesVideoButton from '../MuteEveryonesVideoButton';
23 24
 
24 25
 import AudioOnlyButton from './AudioOnlyButton';
26
+import RaiseHandButton from './RaiseHandButton';
25 27
 import ScreenSharingButton from './ScreenSharingButton.js';
26 28
 import ToggleCameraButton from './ToggleCameraButton';
27 29
 
@@ -50,6 +52,11 @@ type Props = {
50 52
      */
51 53
     _width: number,
52 54
 
55
+    /**
56
+     * Whether or not the reactions feature is enabled.
57
+     */
58
+    _reactionsEnabled: boolean,
59
+
53 60
     /**
54 61
      * Used for hiding the dialog when the selection was completed.
55 62
      */
@@ -102,7 +109,7 @@ class OverflowMenu extends PureComponent<Props, State> {
102 109
      * @returns {ReactElement}
103 110
      */
104 111
     render() {
105
-        const { _bottomSheetStyles, _width } = this.props;
112
+        const { _bottomSheetStyles, _width, _reactionsEnabled } = this.props;
106 113
         const toolbarButtons = getMovableButtons(_width);
107 114
 
108 115
         const buttonProps = {
@@ -128,13 +135,14 @@ class OverflowMenu extends PureComponent<Props, State> {
128 135
         return (
129 136
             <BottomSheet
130 137
                 onCancel = { this._onCancel }
131
-                renderFooter = { toolbarButtons.has('raisehand')
132
-                    ? null
133
-                    : this._renderReactionMenu }>
138
+                renderFooter = { _reactionsEnabled && !toolbarButtons.has('raisehand')
139
+                    ? this._renderReactionMenu
140
+                    : null }>
134 141
                 <AudioRouteButton { ...topButtonProps } />
135 142
                 <ParticipantsPaneButton { ...buttonProps } />
136 143
                 {!toolbarButtons.has('invite') && <InviteButton { ...buttonProps } />}
137 144
                 <AudioOnlyButton { ...buttonProps } />
145
+                {!_reactionsEnabled && !toolbarButtons.has('raisehand') && <RaiseHandButton { ...buttonProps } />}
138 146
                 <SecurityDialogButton { ...buttonProps } />
139 147
                 <ScreenSharingButton { ...buttonProps } />
140 148
                 {!toolbarButtons.has('togglecamera') && <ToggleCameraButton { ...buttonProps } />}
@@ -194,7 +202,8 @@ function _mapStateToProps(state) {
194 202
     return {
195 203
         _bottomSheetStyles: ColorSchemeRegistry.get(state, 'BottomSheet'),
196 204
         _isOpen: isDialogOpen(state, OverflowMenu_),
197
-        _width: state['features/base/responsive-ui'].clientWidth
205
+        _width: state['features/base/responsive-ui'].clientWidth,
206
+        _reactionsEnabled: getFeatureFlag(state, REACTIONS_ENABLED, false)
198 207
     };
199 208
 }
200 209
 

+ 105
- 0
react/features/toolbox/components/native/RaiseHandButton.js 查看文件

@@ -0,0 +1,105 @@
1
+// @flow
2
+
3
+import { type Dispatch } from 'redux';
4
+
5
+import {
6
+    createToolbarEvent,
7
+    sendAnalytics
8
+} from '../../../analytics';
9
+import { RAISE_HAND_ENABLED, getFeatureFlag } from '../../../base/flags';
10
+import { translate } from '../../../base/i18n';
11
+import { IconRaisedHand } from '../../../base/icons';
12
+import {
13
+    getLocalParticipant,
14
+    raiseHand
15
+} from '../../../base/participants';
16
+import { connect } from '../../../base/redux';
17
+import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
18
+
19
+/**
20
+ * The type of the React {@code Component} props of {@link RaiseHandButton}.
21
+ */
22
+type Props = AbstractButtonProps & {
23
+
24
+    /**
25
+     * The local participant.
26
+     */
27
+    _localParticipant: Object,
28
+
29
+    /**
30
+     * Whether the participant raused their hand or not.
31
+     */
32
+    _raisedHand: boolean,
33
+
34
+    /**
35
+     * The redux {@code dispatch} function.
36
+     */
37
+    dispatch: Dispatch<any>
38
+};
39
+
40
+/**
41
+ * An implementation of a button to raise or lower hand.
42
+ */
43
+class RaiseHandButton extends AbstractButton<Props, *> {
44
+    accessibilityLabel = 'toolbar.accessibilityLabel.raiseHand';
45
+    icon = IconRaisedHand;
46
+    label = 'toolbar.raiseYourHand';
47
+    toggledLabel = 'toolbar.lowerYourHand';
48
+
49
+    /**
50
+     * Handles clicking / pressing the button.
51
+     *
52
+     * @override
53
+     * @protected
54
+     * @returns {void}
55
+     */
56
+    _handleClick() {
57
+        this._toggleRaisedHand();
58
+    }
59
+
60
+    /**
61
+     * Indicates whether this button is in toggled state or not.
62
+     *
63
+     * @override
64
+     * @protected
65
+     * @returns {boolean}
66
+     */
67
+    _isToggled() {
68
+        return this.props._raisedHand;
69
+    }
70
+
71
+    /**
72
+     * Toggles the rased hand status of the local participant.
73
+     *
74
+     * @returns {void}
75
+     */
76
+    _toggleRaisedHand() {
77
+        const enable = !this.props._raisedHand;
78
+
79
+        sendAnalytics(createToolbarEvent('raise.hand', { enable }));
80
+
81
+        this.props.dispatch(raiseHand(enable));
82
+    }
83
+}
84
+
85
+/**
86
+ * Maps part of the Redux state to the props of this component.
87
+ *
88
+ * @param {Object} state - The Redux state.
89
+ * @param {Object} ownProps - The properties explicitly passed to the component instance.
90
+ * @private
91
+ * @returns {Props}
92
+ */
93
+function _mapStateToProps(state, ownProps): Object {
94
+    const _localParticipant = getLocalParticipant(state);
95
+    const enabled = getFeatureFlag(state, RAISE_HAND_ENABLED, true);
96
+    const { visible = enabled } = ownProps;
97
+
98
+    return {
99
+        _localParticipant,
100
+        _raisedHand: _localParticipant.raisedHand,
101
+        visible
102
+    };
103
+}
104
+
105
+export default translate(connect(_mapStateToProps)(RaiseHandButton));

+ 16
- 5
react/features/toolbox/components/native/Toolbox.js 查看文件

@@ -4,6 +4,7 @@ import React from 'react';
4 4
 import { SafeAreaView, View } from 'react-native';
5 5
 
6 6
 import { ColorSchemeRegistry } from '../../../base/color-scheme';
7
+import { getFeatureFlag, REACTIONS_ENABLED } from '../../../base/flags';
7 8
 import { connect } from '../../../base/redux';
8 9
 import { StyleType } from '../../../base/styles';
9 10
 import { ChatButton } from '../../../chat';
@@ -16,6 +17,7 @@ import HangupButton from '../HangupButton';
16 17
 import VideoMuteButton from '../VideoMuteButton';
17 18
 
18 19
 import OverflowMenuButton from './OverflowMenuButton';
20
+import RaiseHandButton from './RaiseHandButton';
19 21
 import ToggleCameraButton from './ToggleCameraButton';
20 22
 import styles from './styles';
21 23
 
@@ -39,6 +41,11 @@ type Props = {
39 41
      */
40 42
     _width: number,
41 43
 
44
+    /**
45
+     * Whether or not the reactions feature is enabled.
46
+     */
47
+    _reactionsEnabled: boolean,
48
+
42 49
     /**
43 50
      * The redux {@code dispatch} function.
44 51
      */
@@ -56,7 +63,7 @@ function Toolbox(props: Props) {
56 63
         return null;
57 64
     }
58 65
 
59
-    const { _styles, _width } = props;
66
+    const { _styles, _width, _reactionsEnabled } = props;
60 67
     const { buttonStylesBorderless, hangupButtonStyles, toggledButtonStyles } = _styles;
61 68
     const additionalButtons = getMovableButtons(_width);
62 69
     const backgroundToggledStyle = {
@@ -86,10 +93,13 @@ function Toolbox(props: Props) {
86 93
                           styles = { buttonStylesBorderless }
87 94
                           toggledStyles = { backgroundToggledStyle } />}
88 95
 
89
-                { additionalButtons.has('raisehand')
90
-                    && <ReactionsMenuButton
96
+                { additionalButtons.has('raisehand') && (_reactionsEnabled
97
+                    ? <ReactionsMenuButton
98
+                        styles = { buttonStylesBorderless }
99
+                        toggledStyles = { backgroundToggledStyle } />
100
+                    : <RaiseHandButton
91 101
                         styles = { buttonStylesBorderless }
92
-                        toggledStyles = { backgroundToggledStyle } />}
102
+                        toggledStyles = { backgroundToggledStyle } />)}
93 103
                 {additionalButtons.has('tileview') && <TileViewButton styles = { buttonStylesBorderless } />}
94 104
                 {additionalButtons.has('invite') && <InviteButton styles = { buttonStylesBorderless } />}
95 105
                 {additionalButtons.has('togglecamera')
@@ -119,7 +129,8 @@ function _mapStateToProps(state: Object): Object {
119 129
     return {
120 130
         _styles: ColorSchemeRegistry.get(state, 'Toolbox'),
121 131
         _visible: isToolboxVisible(state),
122
-        _width: state['features/base/responsive-ui'].clientWidth
132
+        _width: state['features/base/responsive-ui'].clientWidth,
133
+        _reactionsEnabled: getFeatureFlag(state, REACTIONS_ENABLED, false)
123 134
     };
124 135
 }
125 136
 

+ 83
- 0
react/features/toolbox/components/web/RaiseHandButton.js 查看文件

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

+ 68
- 35
react/features/toolbox/components/web/Toolbox.js 查看文件

@@ -85,6 +85,7 @@ import AudioSettingsButton from './AudioSettingsButton';
85 85
 import FullscreenButton from './FullscreenButton';
86 86
 import OverflowMenuButton from './OverflowMenuButton';
87 87
 import ProfileButton from './ProfileButton';
88
+import RaiseHandButton from './RaiseHandButton';
88 89
 import Separator from './Separator';
89 90
 import ShareDesktopButton from './ShareDesktopButton';
90 91
 import VideoSettingsButton from './VideoSettingsButton';
@@ -213,7 +214,12 @@ type Props = {
213 214
     /**
214 215
      * Returns the selected virtual source object.
215 216
      */
216
-     _virtualSource: Object,
217
+    _virtualSource: Object,
218
+
219
+    /**
220
+     * Whether or not reactions feature is enabled.
221
+     */
222
+    _reactionsEnabled: boolean,
217 223
 
218 224
     /**
219 225
      * Invoked to active other features of the app.
@@ -259,6 +265,7 @@ class Toolbox extends Component<Props> {
259 265
         this._onToolbarOpenVideoQuality = this._onToolbarOpenVideoQuality.bind(this);
260 266
         this._onToolbarToggleChat = this._onToolbarToggleChat.bind(this);
261 267
         this._onToolbarToggleFullScreen = this._onToolbarToggleFullScreen.bind(this);
268
+        this._onToolbarToggleRaiseHand = this._onToolbarToggleRaiseHand.bind(this);
262 269
         this._onToolbarToggleScreenshare = this._onToolbarToggleScreenshare.bind(this);
263 270
         this._onShortcutToggleTileView = this._onShortcutToggleTileView.bind(this);
264 271
         this._onEscKey = this._onEscKey.bind(this);
@@ -271,7 +278,7 @@ class Toolbox extends Component<Props> {
271 278
      * @returns {void}
272 279
      */
273 280
     componentDidMount() {
274
-        const { _toolbarButtons, t, dispatch } = this.props;
281
+        const { _toolbarButtons, t, dispatch, _reactionsEnabled } = this.props;
275 282
         const KEYBOARD_SHORTCUTS = [
276 283
             isToolbarButtonEnabled('videoquality', _toolbarButtons) && {
277 284
                 character: 'A',
@@ -320,30 +327,32 @@ class Toolbox extends Component<Props> {
320 327
             }
321 328
         });
322 329
 
323
-        const REACTION_SHORTCUTS = Object.keys(REACTIONS).map(key => {
324
-            const onShortcutSendReaction = () => {
325
-                dispatch(addReactionToBuffer(key));
326
-                sendAnalytics(createShortcutEvent(
327
-                    `reaction.${key}`
328
-                ));
329
-            };
330
-
331
-            return {
332
-                character: REACTIONS[key].shortcutChar,
333
-                exec: onShortcutSendReaction,
334
-                helpDescription: t(`toolbar.reaction${key.charAt(0).toUpperCase()}${key.slice(1)}`),
335
-                altKey: true
336
-            };
337
-        });
338
-
339
-        REACTION_SHORTCUTS.forEach(shortcut => {
340
-            APP.keyboardshortcut.registerShortcut(
341
-                shortcut.character,
342
-                null,
343
-                shortcut.exec,
344
-                shortcut.helpDescription,
345
-                shortcut.altKey);
346
-        });
330
+        if (_reactionsEnabled) {
331
+            const REACTION_SHORTCUTS = Object.keys(REACTIONS).map(key => {
332
+                const onShortcutSendReaction = () => {
333
+                    dispatch(addReactionToBuffer(key));
334
+                    sendAnalytics(createShortcutEvent(
335
+                        `reaction.${key}`
336
+                    ));
337
+                };
338
+
339
+                return {
340
+                    character: REACTIONS[key].shortcutChar,
341
+                    exec: onShortcutSendReaction,
342
+                    helpDescription: t(`toolbar.reaction${key.charAt(0).toUpperCase()}${key.slice(1)}`),
343
+                    altKey: true
344
+                };
345
+            });
346
+
347
+            REACTION_SHORTCUTS.forEach(shortcut => {
348
+                APP.keyboardshortcut.registerShortcut(
349
+                    shortcut.character,
350
+                    null,
351
+                    shortcut.exec,
352
+                    shortcut.helpDescription,
353
+                    shortcut.altKey);
354
+            });
355
+        }
347 356
     }
348 357
 
349 358
     /**
@@ -375,9 +384,11 @@ class Toolbox extends Component<Props> {
375 384
         [ 'A', 'C', 'D', 'R', 'S' ].forEach(letter =>
376 385
             APP.keyboardshortcut.unregisterShortcut(letter));
377 386
 
378
-        Object.keys(REACTIONS).map(key => REACTIONS[key].shortcutChar)
379
-            .forEach(letter =>
380
-                APP.keyboardshortcut.unregisterShortcut(letter, true));
387
+        if (this.props._reactionsEnabled) {
388
+            Object.keys(REACTIONS).map(key => REACTIONS[key].shortcutChar)
389
+                .forEach(letter =>
390
+                    APP.keyboardshortcut.unregisterShortcut(letter, true));
391
+        }
381 392
     }
382 393
 
383 394
     /**
@@ -541,7 +552,8 @@ class Toolbox extends Component<Props> {
541 552
         const {
542 553
             _feedbackConfigured,
543 554
             _isMobile,
544
-            _screenSharing
555
+            _screenSharing,
556
+            _reactionsEnabled
545 557
         } = this.props;
546 558
 
547 559
         const microphone = {
@@ -578,7 +590,8 @@ class Toolbox extends Component<Props> {
578 590
 
579 591
         const raisehand = {
580 592
             key: 'raisehand',
581
-            Content: ReactionsMenuButton,
593
+            Content: _reactionsEnabled ? ReactionsMenuButton : RaiseHandButton,
594
+            handleClick: _reactionsEnabled ? null : this._onToolbarToggleRaiseHand,
582 595
             group: 2
583 596
         };
584 597
 
@@ -1054,6 +1067,23 @@ class Toolbox extends Component<Props> {
1054 1067
         this._doToggleFullScreen();
1055 1068
     }
1056 1069
 
1070
+    _onToolbarToggleRaiseHand: () => void;
1071
+
1072
+    /**
1073
+     * Creates an analytics toolbar event and dispatches an action for toggling
1074
+     * raise hand.
1075
+     *
1076
+     * @private
1077
+     * @returns {void}
1078
+     */
1079
+    _onToolbarToggleRaiseHand() {
1080
+        sendAnalytics(createToolbarEvent(
1081
+            'raise.hand',
1082
+            { enable: !this.props._raisedHand }));
1083
+
1084
+        this._doToggleRaiseHand();
1085
+    }
1086
+
1057 1087
     _onToolbarToggleScreenshare: () => void;
1058 1088
 
1059 1089
     /**
@@ -1131,7 +1161,8 @@ class Toolbox extends Component<Props> {
1131 1161
             _isMobile,
1132 1162
             _overflowMenuVisible,
1133 1163
             _toolbarButtons,
1134
-            t
1164
+            t,
1165
+            _reactionsEnabled
1135 1166
         } = this.props;
1136 1167
 
1137 1168
         const toolbarAccLabel = 'toolbar.accessibilityLabel.moreActionsMenu';
@@ -1160,7 +1191,7 @@ class Toolbox extends Component<Props> {
1160 1191
                                 key = 'overflow-menu'
1161 1192
                                 onVisibilityChange = { this._onSetOverflowVisible }
1162 1193
                                 showMobileReactions = {
1163
-                                    overflowMenuButtons.find(({ key }) => key === 'raisehand')
1194
+                                    _reactionsEnabled && overflowMenuButtons.find(({ key }) => key === 'raisehand')
1164 1195
                                 }>
1165 1196
                                 <ul
1166 1197
                                     aria-label = { t(toolbarAccLabel) }
@@ -1171,7 +1202,7 @@ class Toolbox extends Component<Props> {
1171 1202
                                     {overflowMenuButtons.map(({ group, key, Content, ...rest }, index, arr) => {
1172 1203
                                         const showSeparator = index > 0 && arr[index - 1].group !== group;
1173 1204
 
1174
-                                        return key !== 'raisehand'
1205
+                                        return (key !== 'raisehand' || !_reactionsEnabled)
1175 1206
                                             && <>
1176 1207
                                                 {showSeparator && <Separator key = { `hr${group}` } />}
1177 1208
                                                 <Content
@@ -1218,6 +1249,7 @@ function _mapStateToProps(state) {
1218 1249
     const localParticipant = getLocalParticipant(state);
1219 1250
     const localVideo = getLocalVideoTrack(state['features/base/tracks']);
1220 1251
     const { clientWidth } = state['features/base/responsive-ui'];
1252
+    const { enableReactions } = state['features/base/config'];
1221 1253
 
1222 1254
     let desktopSharingDisabledTooltipKey;
1223 1255
 
@@ -1253,7 +1285,8 @@ function _mapStateToProps(state) {
1253 1285
         _screenSharing: isScreenVideoShared(state),
1254 1286
         _toolbarButtons: getToolbarButtons(state),
1255 1287
         _visible: isToolboxVisible(state),
1256
-        _visibleButtons: getToolbarButtons(state)
1288
+        _visibleButtons: getToolbarButtons(state),
1289
+        _reactionsEnabled: enableReactions
1257 1290
     };
1258 1291
 }
1259 1292
 

正在加载...
取消
保存