瀏覽代碼

Remote video menu post-PR improvements

master
Bettenbuk Zoltan 6 年之前
父節點
當前提交
5c0ae10ccb
共有 23 個檔案被更改,包括 534 行新增547 行删除
  1. 4
    0
      lang/main.json
  2. 15
    5
      react/features/base/dialog/components/native/styles.js
  3. 0
    6
      react/features/base/react/components/AbstractContainer.js
  4. 28
    14
      react/features/base/react/components/native/Container.js
  5. 16
    0
      react/features/base/tracks/functions.js
  6. 55
    46
      react/features/filmstrip/components/native/Thumbnail.js
  7. 2
    1
      react/features/mobile/audio-mode/components/styles.js
  8. 46
    0
      react/features/remote-video-menu/components/AbstractKickButton.js
  9. 66
    0
      react/features/remote-video-menu/components/AbstractKickRemoteParticipantDialog.js
  10. 105
    0
      react/features/remote-video-menu/components/AbstractMuteButton.js
  11. 70
    0
      react/features/remote-video-menu/components/AbstractMuteRemoteParticipantDialog.js
  12. 6
    37
      react/features/remote-video-menu/components/native/KickButton.js
  13. 4
    55
      react/features/remote-video-menu/components/native/KickRemoteParticipantDialog.js
  14. 6
    107
      react/features/remote-video-menu/components/native/MuteButton.js
  15. 4
    51
      react/features/remote-video-menu/components/native/MuteRemoteParticipantDialog.js
  16. 1
    1
      react/features/remote-video-menu/components/native/RemoteVideoMenu.js
  17. 6
    0
      react/features/remote-video-menu/components/native/index.js
  18. 8
    3
      react/features/remote-video-menu/components/native/styles.js
  19. 18
    65
      react/features/remote-video-menu/components/web/KickButton.js
  20. 39
    0
      react/features/remote-video-menu/components/web/KickRemoteParticipantDialog.js
  21. 21
    74
      react/features/remote-video-menu/components/web/MuteButton.js
  22. 8
    82
      react/features/remote-video-menu/components/web/MuteRemoteParticipantDialog.js
  23. 6
    0
      react/features/remote-video-menu/components/web/index.js

+ 4
- 0
lang/main.json 查看文件

93
             "fullScreen": "Toggle full screen",
93
             "fullScreen": "Toggle full screen",
94
             "hangup": "Leave the call",
94
             "hangup": "Leave the call",
95
             "invite": "Invite people",
95
             "invite": "Invite people",
96
+            "kick": "Kick participant",
96
             "localRecording": "Toggle local recording controls",
97
             "localRecording": "Toggle local recording controls",
97
             "lockRoom": "Toggle room lock",
98
             "lockRoom": "Toggle room lock",
98
             "moreActions": "Toggle more actions menu",
99
             "moreActions": "Toggle more actions menu",
102
             "profile": "Edit your profile",
103
             "profile": "Edit your profile",
103
             "raiseHand": "Toggle raise hand",
104
             "raiseHand": "Toggle raise hand",
104
             "recording": "Toggle recording",
105
             "recording": "Toggle recording",
106
+            "remoteMute": "Mute participant",
105
             "Settings": "Toggle settings",
107
             "Settings": "Toggle settings",
106
             "sharedvideo": "Toggle Youtube video sharing",
108
             "sharedvideo": "Toggle Youtube video sharing",
107
             "shareRoom": "Invite someone",
109
             "shareRoom": "Invite someone",
385
         "externalInstallationMsg": "You need to install our desktop sharing extension.",
387
         "externalInstallationMsg": "You need to install our desktop sharing extension.",
386
         "inlineInstallationMsg": "You need to install our desktop sharing extension.",
388
         "inlineInstallationMsg": "You need to install our desktop sharing extension.",
387
         "inlineInstallExtension": "Install now",
389
         "inlineInstallExtension": "Install now",
390
+        "kickParticipantButton": "Kick",
388
         "kickParticipantDialog": "Are you sure you want to kick this participant?",
391
         "kickParticipantDialog": "Are you sure you want to kick this participant?",
392
+        "kickParticipantTitle": "Kick this member?",
389
         "muteParticipantTitle": "Mute this member?",
393
         "muteParticipantTitle": "Mute this member?",
390
         "muteParticipantBody": "You won't be able to unmute them, but they can unmute themselves at any time.",
394
         "muteParticipantBody": "You won't be able to unmute them, but they can unmute themselves at any time.",
391
         "muteParticipantDialog": "Are you sure you want to mute this participant? You won't be able to unmute them, but they can unmute themselves at any time.",
395
         "muteParticipantDialog": "Are you sure you want to mute this participant? You won't be able to unmute them, but they can unmute themselves at any time.",

+ 15
- 5
react/features/base/dialog/components/native/styles.js 查看文件

10
 const DIALOG_BORDER_COLOR = 'rgba(255, 255, 255, 0.2)';
10
 const DIALOG_BORDER_COLOR = 'rgba(255, 255, 255, 0.2)';
11
 
11
 
12
 export const FIELD_UNDERLINE = ColorPalette.transparent;
12
 export const FIELD_UNDERLINE = ColorPalette.transparent;
13
+
14
+/**
15
+ * NOTE: These Material guidelines based values are currently only used in
16
+ * dialogs (and related) but later on it would be nice to export it into a base
17
+ * Material feature.
18
+ */
19
+export const MD_FONT_SIZE = 16;
20
+export const MD_ITEM_HEIGHT = 48;
21
+export const MD_ITEM_MARGIN_PADDING = 16;
22
+
13
 export const PLACEHOLDER_COLOR = ColorPalette.lightGrey;
23
 export const PLACEHOLDER_COLOR = ColorPalette.lightGrey;
14
 
24
 
15
 /**
25
 /**
25
     style: {
35
     style: {
26
         alignItems: 'center',
36
         alignItems: 'center',
27
         flexDirection: 'row',
37
         flexDirection: 'row',
28
-        height: 48
38
+        height: MD_ITEM_HEIGHT
29
     },
39
     },
30
 
40
 
31
     /**
41
     /**
42
     labelStyle: {
52
     labelStyle: {
43
         color: ColorPalette.white,
53
         color: ColorPalette.white,
44
         flexShrink: 1,
54
         flexShrink: 1,
45
-        fontSize: 16,
55
+        fontSize: MD_FONT_SIZE,
46
         marginLeft: 32,
56
         marginLeft: 32,
47
         opacity: 0.90
57
         opacity: 0.90
48
     }
58
     }
92
     sheet: {
102
     sheet: {
93
         backgroundColor: 'rgb(0, 3, 6)',
103
         backgroundColor: 'rgb(0, 3, 6)',
94
         flex: 1,
104
         flex: 1,
95
-        paddingHorizontal: 16,
105
+        paddingHorizontal: MD_ITEM_MARGIN_PADDING,
96
         paddingVertical: 8
106
         paddingVertical: 8
97
     }
107
     }
98
 });
108
 });
134
 
144
 
135
     closeStyle: {
145
     closeStyle: {
136
         color: ColorPalette.white,
146
         color: ColorPalette.white,
137
-        fontSize: 16
147
+        fontSize: MD_FONT_SIZE
138
     },
148
     },
139
 
149
 
140
     closeWrapper: {
150
     closeWrapper: {
173
 
183
 
174
     text: {
184
     text: {
175
         color: ColorPalette.white,
185
         color: ColorPalette.white,
176
-        fontSize: 16,
186
+        fontSize: MD_FONT_SIZE,
177
         textAlign: 'center'
187
         textAlign: 'center'
178
     }
188
     }
179
 });
189
 });

+ 0
- 6
react/features/base/react/components/AbstractContainer.js 查看文件

31
      */
31
      */
32
     onClick?: ?Function,
32
     onClick?: ?Function,
33
 
33
 
34
-    /**
35
-     * The event handler/listener to be invoked when this
36
-     * {@code AbstractContainer} is long pressed on React Native.
37
-     */
38
-    onLongPress?: ?Function,
39
-
40
     /**
34
     /**
41
      * The style (as in stylesheet) to be applied to this
35
      * The style (as in stylesheet) to be applied to this
42
      * {@code AbstractContainer}.
36
      * {@code AbstractContainer}.

+ 28
- 14
react/features/base/react/components/native/Container.js 查看文件

8
 } from 'react-native';
8
 } from 'react-native';
9
 
9
 
10
 import AbstractContainer from '../AbstractContainer';
10
 import AbstractContainer from '../AbstractContainer';
11
-import type { Props } from '../AbstractContainer';
11
+import type { Props as AbstractProps } from '../AbstractContainer';
12
+
13
+type Props = AbstractProps & {
14
+
15
+    /**
16
+     * The event handler/listener to be invoked when this
17
+     * {@code AbstractContainer} is long pressed on React Native.
18
+     */
19
+    onLongPress?: ?Function,
20
+};
12
 
21
 
13
 /**
22
 /**
14
  * Represents a container of React Native/mobile {@link Component} children.
23
  * Represents a container of React Native/mobile {@link Component} children.
28
             accessible,
37
             accessible,
29
             onClick,
38
             onClick,
30
             onLongPress,
39
             onLongPress,
31
-            touchFeedback = onClick,
40
+            touchFeedback = Boolean(onClick || onLongPress),
32
             underlayColor,
41
             underlayColor,
33
             visible = true,
42
             visible = true,
34
             ...props
43
             ...props
50
 
59
 
51
         // onClick & touchFeedback
60
         // onClick & touchFeedback
52
         if (element && onClickOrTouchFeedback) {
61
         if (element && onClickOrTouchFeedback) {
62
+            const touchableProps = {
63
+                accessibilityLabel,
64
+                accessible,
65
+                onLongPress,
66
+                onPress: onClick
67
+            };
68
+
53
             element
69
             element
54
-                = React.createElement(
55
-                    touchFeedback
56
-                        ? TouchableHighlight
57
-                        : TouchableWithoutFeedback,
58
-                    {
59
-                        accessibilityLabel,
60
-                        accessible,
61
-                        onLongPress,
62
-                        onPress: onClick,
63
-                        ...touchFeedback && { underlayColor }
64
-                    },
65
-                    element);
70
+                = touchFeedback
71
+                    ? React.createElement(
72
+                        TouchableHighlight,
73
+                        {
74
+                            ...touchableProps,
75
+                            underlayColor
76
+                        },
77
+                        element)
78
+                    : React.createElement(
79
+                        TouchableWithoutFeedback, touchableProps, element);
66
         }
80
         }
67
 
81
 
68
         return element;
82
         return element;

+ 16
- 0
react/features/base/tracks/functions.js 查看文件

209
     return !track || track.muted;
209
     return !track || track.muted;
210
 }
210
 }
211
 
211
 
212
+/**
213
+ * Returns true if the remote track of the given media type and the given
214
+ * participant is muted, false otherwise.
215
+ *
216
+ * @param {Track[]} tracks - List of all tracks.
217
+ * @param {MEDIA_TYPE} mediaType - The media type of tracks to be checked.
218
+ * @param {*} participantId - Participant ID.
219
+ * @returns {boolean}
220
+ */
221
+export function isRemoteTrackMuted(tracks, mediaType, participantId) {
222
+    const track = getTrackByMediaTypeAndParticipant(
223
+        tracks, mediaType, participantId);
224
+
225
+    return !track || track.muted;
226
+}
227
+
212
 /**
228
 /**
213
  * Mutes or unmutes a specific {@code JitsiLocalTrack}. If the muted state of
229
  * Mutes or unmutes a specific {@code JitsiLocalTrack}. If the muted state of
214
  * the specified {@code track} is already in accord with the specified
230
  * the specified {@code track} is already in accord with the specified

+ 55
- 46
react/features/filmstrip/components/native/Thumbnail.js 查看文件

43
      */
43
      */
44
     _largeVideo: Object,
44
     _largeVideo: Object,
45
 
45
 
46
+    /**
47
+     * Handles click/tap event on the thumbnail.
48
+     */
49
+    _onClick: ?Function,
50
+
51
+    /**
52
+     * Handles long press on the thumbnail.
53
+     */
54
+    _onShowRemoteVideoMenu: ?Function,
55
+
46
     /**
56
     /**
47
      * The Redux representation of the participant's video track.
57
      * The Redux representation of the participant's video track.
48
      */
58
      */
83
  * @extends Component
93
  * @extends Component
84
  */
94
  */
85
 class Thumbnail extends Component<Props> {
95
 class Thumbnail extends Component<Props> {
86
-    /**
87
-     * Initializes new Video Thumbnail component.
88
-     *
89
-     * @param {Object} props - Component props.
90
-     */
91
-    constructor(props: Props) {
92
-        super(props);
93
-
94
-        // Bind event handlers so they are only bound once for every instance.
95
-        this._onClick = this._onClick.bind(this);
96
-        this._onShowRemoteVideoMenu = this._onShowRemoteVideoMenu.bind(this);
97
-    }
98
-
99
     /**
96
     /**
100
      * Implements React's {@link Component#render()}.
97
      * Implements React's {@link Component#render()}.
101
      *
98
      *
107
             _audioTrack: audioTrack,
104
             _audioTrack: audioTrack,
108
             _isModerator,
105
             _isModerator,
109
             _largeVideo: largeVideo,
106
             _largeVideo: largeVideo,
107
+            _onClick,
108
+            _onShowRemoteVideoMenu,
110
             _videoTrack: videoTrack,
109
             _videoTrack: videoTrack,
111
             disablePin,
110
             disablePin,
112
             disableTint,
111
             disableTint,
129
 
128
 
130
         return (
129
         return (
131
             <Container
130
             <Container
132
-                onClick = { disablePin ? undefined : this._onClick }
131
+                onClick = { disablePin ? undefined : _onClick }
133
                 onLongPress = {
132
                 onLongPress = {
134
                     showRemoteVideoMenu
133
                     showRemoteVideoMenu
135
-                        ? this._onShowRemoteVideoMenu : undefined }
134
+                        ? _onShowRemoteVideoMenu : undefined }
136
                 style = { [
135
                 style = { [
137
                     styles.thumbnail,
136
                     styles.thumbnail,
138
                     participant.pinned && !disablePin
137
                     participant.pinned && !disablePin
169
             </Container>
168
             </Container>
170
         );
169
         );
171
     }
170
     }
171
+}
172
 
172
 
173
-    _onClick: () => void;
174
-
175
-    /**
176
-     * Handles click/tap event on the thumbnail.
177
-     *
178
-     * @returns {void}
179
-     */
180
-    _onClick() {
181
-        const { dispatch, participant } = this.props;
182
-
183
-        // TODO The following currently ignores interfaceConfig.filmStripOnly.
184
-        dispatch(pinParticipant(participant.pinned ? null : participant.id));
185
-    }
186
-
187
-    _onShowRemoteVideoMenu: () => void;
188
-
189
-    /**
190
-     * Handles long press on the thumbnail.
191
-     *
192
-     * @returns {void}
193
-     */
194
-    _onShowRemoteVideoMenu() {
195
-        const { dispatch, participant } = this.props;
196
-
197
-        dispatch(openDialog(RemoteVideoMenu, {
198
-            participant
199
-        }));
200
-    }
173
+/**
174
+ * Maps part of redux actions to component's props.
175
+ *
176
+ * @param {Function} dispatch - Redux's {@code dispatch} function.
177
+ * @param {Props} ownProps - The own props of the component.
178
+ * @returns {{
179
+ *     _onClick: Function,
180
+ *     _onShowRemoteVideoMenu: Function
181
+ * }}
182
+ */
183
+function _mapDispatchToProps(dispatch: Function, ownProps): Object {
184
+    return {
185
+        /**
186
+         * Handles click/tap event on the thumbnail.
187
+         *
188
+         * @protected
189
+         * @returns {void}
190
+         */
191
+        _onClick() {
192
+            const { participant } = ownProps;
193
+
194
+            dispatch(
195
+                pinParticipant(participant.pinned ? null : participant.id));
196
+        },
197
+
198
+        /**
199
+         * Handles long press on the thumbnail.
200
+         *
201
+         * @returns {void}
202
+         */
203
+        _onShowRemoteVideoMenu() {
204
+            const { participant } = ownProps;
205
+
206
+            dispatch(openDialog(RemoteVideoMenu, {
207
+                participant
208
+            }));
209
+        }
210
+    };
201
 }
211
 }
202
 
212
 
203
 /**
213
 /**
204
  * Function that maps parts of Redux state tree into component props.
214
  * Function that maps parts of Redux state tree into component props.
205
  *
215
  *
206
  * @param {Object} state - Redux state.
216
  * @param {Object} state - Redux state.
207
- * @param {Object} ownProps - Properties of component.
208
- * @private
217
+ * @param {Props} ownProps - Properties of component.
209
  * @returns {{
218
  * @returns {{
210
  *      _audioTrack: Track,
219
  *      _audioTrack: Track,
211
  *      _isModerator: boolean,
220
  *      _isModerator: boolean,
233
     };
242
     };
234
 }
243
 }
235
 
244
 
236
-export default connect(_mapStateToProps)(Thumbnail);
245
+export default connect(_mapStateToProps, _mapDispatchToProps)(Thumbnail);

+ 2
- 1
react/features/mobile/audio-mode/components/styles.js 查看文件

1
 // @flow
1
 // @flow
2
 
2
 
3
+import { MD_ITEM_HEIGHT } from '../../../base/dialog';
3
 import { ColorPalette, createStyleSheet } from '../../../base/styles';
4
 import { ColorPalette, createStyleSheet } from '../../../base/styles';
4
 
5
 
5
 /**
6
 /**
16
     deviceRow: {
17
     deviceRow: {
17
         alignItems: 'center',
18
         alignItems: 'center',
18
         flexDirection: 'row',
19
         flexDirection: 'row',
19
-        height: 48
20
+        height: MD_ITEM_HEIGHT
20
     },
21
     },
21
 
22
 
22
     /**
23
     /**

+ 46
- 0
react/features/remote-video-menu/components/AbstractKickButton.js 查看文件

1
+// @flow
2
+
3
+import { openDialog } from '../../base/dialog';
4
+import { AbstractButton } from '../../base/toolbox';
5
+import type { AbstractButtonProps } from '../../base/toolbox';
6
+
7
+import { KickRemoteParticipantDialog } from '.';
8
+
9
+export type Props = AbstractButtonProps & {
10
+
11
+    /**
12
+     * The redux {@code dispatch} function.
13
+     */
14
+    dispatch: Function,
15
+
16
+    /**
17
+     * The ID of the participant that this button is supposed to kick.
18
+     */
19
+    participantID: string,
20
+
21
+    /**
22
+     * The function to be used to translate i18n labels.
23
+     */
24
+    t: Function
25
+};
26
+
27
+/**
28
+ * An abstract remote video menu button which kicks the remote participant.
29
+ */
30
+export default class AbstractKickButton extends AbstractButton<Props, *> {
31
+    accessibilityLabel = 'toolbar.accessibilityLabel.kick';
32
+    iconName = 'icon-kick';
33
+    label = 'videothumbnail.kick';
34
+
35
+    /**
36
+     * Handles clicking / pressing the button, and kicks the participant.
37
+     *
38
+     * @private
39
+     * @returns {void}
40
+     */
41
+    _handleClick() {
42
+        const { dispatch, participantID } = this.props;
43
+
44
+        dispatch(openDialog(KickRemoteParticipantDialog, { participantID }));
45
+    }
46
+}

+ 66
- 0
react/features/remote-video-menu/components/AbstractKickRemoteParticipantDialog.js 查看文件

1
+// @flow
2
+
3
+import { Component } from 'react';
4
+
5
+import {
6
+    createRemoteVideoMenuButtonEvent,
7
+    sendAnalytics
8
+} from '../../analytics';
9
+import { kickParticipant } from '../../base/participants';
10
+
11
+type Props = {
12
+
13
+    /**
14
+     * The Redux dispatch function.
15
+     */
16
+    dispatch: Function,
17
+
18
+    /**
19
+     * The ID of the remote participant to be kicked.
20
+     */
21
+    participantID: string,
22
+
23
+    /**
24
+     * Function to translate i18n labels.
25
+     */
26
+    t: Function
27
+};
28
+
29
+/**
30
+ * Abstract dialog to confirm a remote participant kick action.
31
+ */
32
+export default class AbstractKickRemoteParticipantDialog
33
+    extends Component<Props> {
34
+    /**
35
+     * Initializes a new {@code AbstractKickRemoteParticipantDialog} instance.
36
+     *
37
+     * @inheritdoc
38
+     */
39
+    constructor(props: Props) {
40
+        super(props);
41
+
42
+        this._onSubmit = this._onSubmit.bind(this);
43
+    }
44
+
45
+    _onSubmit: () => boolean;
46
+
47
+    /**
48
+     * Callback for the confirm button.
49
+     *
50
+     * @private
51
+     * @returns {boolean} - True (to note that the modal should be closed).
52
+     */
53
+    _onSubmit() {
54
+        const { dispatch, participantID } = this.props;
55
+
56
+        sendAnalytics(createRemoteVideoMenuButtonEvent(
57
+            'kick.button',
58
+            {
59
+                'participant_id': participantID
60
+            }));
61
+
62
+        dispatch(kickParticipant(participantID));
63
+
64
+        return true;
65
+    }
66
+}

+ 105
- 0
react/features/remote-video-menu/components/AbstractMuteButton.js 查看文件

1
+// @flow
2
+
3
+import {
4
+    createRemoteVideoMenuButtonEvent,
5
+    sendAnalytics
6
+} from '../../analytics';
7
+import { openDialog } from '../../base/dialog';
8
+import { MEDIA_TYPE } from '../../base/media';
9
+import {
10
+    AbstractButton,
11
+    type AbstractButtonProps
12
+} from '../../base/toolbox';
13
+import { isRemoteTrackMuted } from '../../base/tracks';
14
+
15
+import { MuteRemoteParticipantDialog } from '.';
16
+
17
+export type Props = AbstractButtonProps & {
18
+
19
+    /**
20
+     * Boolean to indicate if the audio track of the participant is muted or
21
+     * not.
22
+     */
23
+    _audioTrackMuted: boolean,
24
+
25
+    /**
26
+     * The redux {@code dispatch} function.
27
+     */
28
+    dispatch: Function,
29
+
30
+    /**
31
+     * The ID of the participant object that this button is supposed to
32
+     * mute/unmute.
33
+     */
34
+    participantID: string,
35
+
36
+    /**
37
+     * The function to be used to translate i18n labels.
38
+     */
39
+    t: Function
40
+};
41
+
42
+/**
43
+ * An abstract remote video menu button which mutes the remote participant.
44
+ */
45
+export default class AbstractMuteButton extends AbstractButton<Props, *> {
46
+    accessibilityLabel = 'toolbar.accessibilityLabel.remoteMute';
47
+    iconName = 'icon-mic-disabled';
48
+    label = 'videothumbnail.domute';
49
+    toggledLabel = 'videothumbnail.muted';
50
+
51
+    /**
52
+     * Handles clicking / pressing the button, and mutes the participant.
53
+     *
54
+     * @private
55
+     * @returns {void}
56
+     */
57
+    _handleClick() {
58
+        const { dispatch, participantID } = this.props;
59
+
60
+        sendAnalytics(createRemoteVideoMenuButtonEvent(
61
+            'mute.button',
62
+            {
63
+                'participant_id': participantID
64
+            }));
65
+
66
+        dispatch(openDialog(MuteRemoteParticipantDialog, { participantID }));
67
+    }
68
+
69
+    /**
70
+     * Renders the item disabled if the participant is muted.
71
+     *
72
+     * @inheritdoc
73
+     */
74
+    _isDisabled() {
75
+        return this.props._audioTrackMuted;
76
+    }
77
+
78
+    /**
79
+     * Renders the item toggled if the participant is muted.
80
+     *
81
+     * @inheritdoc
82
+     */
83
+    _isToggled() {
84
+        return this.props._audioTrackMuted;
85
+    }
86
+}
87
+
88
+/**
89
+ * Function that maps parts of Redux state tree into component props.
90
+ *
91
+ * @param {Object} state - Redux state.
92
+ * @param {Object} ownProps - Properties of component.
93
+ * @private
94
+ * @returns {{
95
+ *      _audioTrackMuted: boolean
96
+ *  }}
97
+ */
98
+export function _mapStateToProps(state: Object, ownProps: Props) {
99
+    const tracks = state['features/base/tracks'];
100
+
101
+    return {
102
+        _audioTrackMuted: isRemoteTrackMuted(
103
+            tracks, MEDIA_TYPE.AUDIO, ownProps.participantID)
104
+    };
105
+}

+ 70
- 0
react/features/remote-video-menu/components/AbstractMuteRemoteParticipantDialog.js 查看文件

1
+// @flow
2
+
3
+import { Component } from 'react';
4
+
5
+import {
6
+    createRemoteMuteConfirmedEvent,
7
+    sendAnalytics
8
+} from '../../analytics';
9
+import { muteRemoteParticipant } from '../../base/participants';
10
+
11
+/**
12
+ * The type of the React {@code Component} props of
13
+ * {@link AbstractMuteRemoteParticipantDialog}.
14
+ */
15
+type Props = {
16
+
17
+    /**
18
+     * The Redux dispatch function.
19
+     */
20
+    dispatch: Function,
21
+
22
+    /**
23
+     * The ID of the remote participant to be muted.
24
+     */
25
+    participantID: string,
26
+
27
+    /**
28
+     * Function to translate i18n labels.
29
+     */
30
+    t: Function
31
+};
32
+
33
+/**
34
+ * Abstract dialog to confirm a remote participant mute action.
35
+ *
36
+ * @extends Component
37
+ */
38
+export default class AbstractMuteRemoteParticipantDialog
39
+    extends Component<Props> {
40
+    /**
41
+     * Initializes a new {@code AbstractMuteRemoteParticipantDialog} instance.
42
+     *
43
+     * @param {Object} props - The read-only properties with which the new
44
+     * instance is to be initialized.
45
+     */
46
+    constructor(props: Props) {
47
+        super(props);
48
+
49
+        // Bind event handlers so they are only bound once per instance.
50
+        this._onSubmit = this._onSubmit.bind(this);
51
+    }
52
+
53
+    _onSubmit: () => boolean;
54
+
55
+    /**
56
+     * Handles the submit button action.
57
+     *
58
+     * @private
59
+     * @returns {boolean} - True (to note that the modal should be closed).
60
+     */
61
+    _onSubmit() {
62
+        const { dispatch, participantID } = this.props;
63
+
64
+        sendAnalytics(createRemoteMuteConfirmedEvent(participantID));
65
+
66
+        dispatch(muteRemoteParticipant(participantID));
67
+
68
+        return true;
69
+    }
70
+}

+ 6
- 37
react/features/remote-video-menu/components/native/KickButton.js 查看文件

2
 
2
 
3
 import { connect } from 'react-redux';
3
 import { connect } from 'react-redux';
4
 
4
 
5
-import { openDialog } from '../../../base/dialog';
6
 import { translate } from '../../../base/i18n';
5
 import { translate } from '../../../base/i18n';
7
-import { AbstractButton } from '../../../base/toolbox';
8
-import type { AbstractButtonProps } from '../../../base/toolbox';
9
 
6
 
10
-import KickRemoteParticipantDialog from './KickRemoteParticipantDialog';
11
-
12
-type Props = AbstractButtonProps & {
13
-
14
-    /**
15
-     * The redux {@code dispatch} function.
16
-     */
17
-    dispatch: Function,
18
-
19
-    /**
20
-     * The participant object that this button is supposed to kick.
21
-     */
22
-    participant: Object
23
-};
7
+import AbstractKickButton from '../AbstractKickButton';
24
 
8
 
25
 /**
9
 /**
26
- * A remote video menu button which kicks the remote participant.
10
+ * We don't need any further implementation for this on mobile, but we keep it
11
+ * here for clarity and consistency with web. Once web uses the
12
+ * {@code AbstractButton} base class, we can remove all these and just use
13
+ * the {@code AbstractKickButton} as {@KickButton}.
27
  */
14
  */
28
-class KickButton extends AbstractButton<Props, *> {
29
-    accessibilityLabel = 'toolbar.accessibilityLabel.audioRoute';
30
-    iconName = 'icon-kick';
31
-    label = 'videothumbnail.kick';
32
-
33
-    /**
34
-     * Handles clicking / pressing the button, and kicks the participant.
35
-     *
36
-     * @private
37
-     * @returns {void}
38
-     */
39
-    _handleClick() {
40
-        const { dispatch, participant } = this.props;
41
-
42
-        dispatch(openDialog(KickRemoteParticipantDialog, { participant }));
43
-    }
44
-}
45
-
46
-export default translate(connect()(KickButton));
15
+export default translate(connect()(AbstractKickButton));

+ 4
- 55
react/features/remote-video-menu/components/native/KickRemoteParticipantDialog.js 查看文件

1
 // @flow
1
 // @flow
2
 
2
 
3
-import React, { Component } from 'react';
3
+import React from 'react';
4
 import { connect } from 'react-redux';
4
 import { connect } from 'react-redux';
5
 
5
 
6
-import {
7
-    createRemoteVideoMenuButtonEvent,
8
-    sendAnalytics
9
-} from '../../../analytics';
10
 import { ConfirmDialog } from '../../../base/dialog';
6
 import { ConfirmDialog } from '../../../base/dialog';
11
 import { translate } from '../../../base/i18n';
7
 import { translate } from '../../../base/i18n';
12
-import { kickParticipant } from '../../../base/participants';
13
 
8
 
14
-type Props = {
15
-
16
-    /**
17
-     * The Redux dispatch function.
18
-     */
19
-    dispatch: Function,
20
-
21
-    /**
22
-     * The remote participant to be kicked.
23
-     */
24
-    participant: Object,
25
-
26
-    /**
27
-     * Function to translate i18n labels.
28
-     */
29
-    t: Function
30
-};
9
+import AbstractKickRemoteParticipantDialog
10
+    from '../AbstractKickRemoteParticipantDialog';
31
 
11
 
32
 /**
12
 /**
33
  * Dialog to confirm a remote participant kick action.
13
  * Dialog to confirm a remote participant kick action.
34
  */
14
  */
35
-class KickRemoteParticipantDialog extends Component<Props> {
36
-    /**
37
-     * Initializes a new {@code KickRemoteParticipantDialog} instance.
38
-     *
39
-     * @inheritdoc
40
-     */
41
-    constructor(props: Props) {
42
-        super(props);
43
-
44
-        this._onSubmit = this._onSubmit.bind(this);
45
-    }
46
-
15
+class KickRemoteParticipantDialog extends AbstractKickRemoteParticipantDialog {
47
     /**
16
     /**
48
      * Implements React's {@link Component#render()}.
17
      * Implements React's {@link Component#render()}.
49
      *
18
      *
59
     }
28
     }
60
 
29
 
61
     _onSubmit: () => boolean;
30
     _onSubmit: () => boolean;
62
-
63
-    /**
64
-     * Callback for the confirm button.
65
-     *
66
-     * @private
67
-     * @returns {boolean} - True (to note that the modal should be closed).
68
-     */
69
-    _onSubmit() {
70
-        const { dispatch, participant } = this.props;
71
-
72
-        sendAnalytics(createRemoteVideoMenuButtonEvent(
73
-            'kick.button',
74
-            {
75
-                'participant_id': participant.id
76
-            }));
77
-
78
-        dispatch(kickParticipant(participant.id));
79
-
80
-        return true;
81
-    }
82
 }
31
 }
83
 
32
 
84
 export default translate(connect()(KickRemoteParticipantDialog));
33
 export default translate(connect()(KickRemoteParticipantDialog));

+ 6
- 107
react/features/remote-video-menu/components/native/MuteButton.js 查看文件

2
 
2
 
3
 import { connect } from 'react-redux';
3
 import { connect } from 'react-redux';
4
 
4
 
5
-import {
6
-    createRemoteVideoMenuButtonEvent,
7
-    sendAnalytics
8
-} from '../../../analytics';
9
-import { openDialog } from '../../../base/dialog';
10
 import { translate } from '../../../base/i18n';
5
 import { translate } from '../../../base/i18n';
11
-import { MEDIA_TYPE } from '../../../base/media';
12
-import {
13
-    AbstractButton,
14
-    type AbstractButtonProps
15
-} from '../../../base/toolbox';
16
-import { getTrackByMediaTypeAndParticipant } from '../../../base/tracks';
17
 
6
 
18
-import MuteRemoteParticipantDialog from './MuteRemoteParticipantDialog';
19
-
20
-type Props = AbstractButtonProps & {
21
-
22
-    /**
23
-     * The audio track of the participant.
24
-     */
25
-    _audioTrack: ?Object,
26
-
27
-    /**
28
-     * The redux {@code dispatch} function.
29
-     */
30
-    dispatch: Function,
31
-
32
-    /**
33
-     * The participant object that this button is supposed to mute/unmute.
34
-     */
35
-    participant: Object
36
-};
37
-
38
-/**
39
- * A remote video menu button which mutes the remote participant.
40
- */
41
-class MuteButton extends AbstractButton<Props, *> {
42
-    accessibilityLabel = 'toolbar.accessibilityLabel.audioRoute';
43
-    iconName = 'icon-mic-disabled';
44
-    label = 'videothumbnail.domute';
45
-    toggledLabel = 'videothumbnail.muted';
46
-
47
-    /**
48
-     * Handles clicking / pressing the button, and mutes the participant.
49
-     *
50
-     * @private
51
-     * @returns {void}
52
-     */
53
-    _handleClick() {
54
-        const { dispatch, participant } = this.props;
55
-
56
-        sendAnalytics(createRemoteVideoMenuButtonEvent(
57
-            'mute.button',
58
-            {
59
-                'participant_id': participant.id
60
-            }));
61
-
62
-        dispatch(openDialog(MuteRemoteParticipantDialog, { participant }));
63
-    }
64
-
65
-    /**
66
-     * Renders the item disabled if the participant is muted.
67
-     *
68
-     * @inheritdoc
69
-     */
70
-    _isDisabled() {
71
-        return this._isMuted();
72
-    }
73
-
74
-    /**
75
-     * Returns true if the participant is muted, false otherwise.
76
-     *
77
-     * @returns {boolean}
78
-     */
79
-    _isMuted() {
80
-        const { _audioTrack } = this.props;
81
-
82
-        return !_audioTrack || _audioTrack.muted;
83
-    }
84
-
85
-    /**
86
-     * Renders the item toggled if the participant is muted.
87
-     *
88
-     * @inheritdoc
89
-     */
90
-    _isToggled() {
91
-        return this._isMuted();
92
-    }
93
-}
7
+import AbstractMuteButton, { _mapStateToProps } from '../AbstractMuteButton';
94
 
8
 
95
 /**
9
 /**
96
- * Function that maps parts of Redux state tree into component props.
97
- *
98
- * @param {Object} state - Redux state.
99
- * @param {Object} ownProps - Properties of component.
100
- * @private
101
- * @returns {{
102
- *      _audioTrack: Track
103
- *  }}
10
+ * We don't need any further implementation for this on mobile, but we keep it
11
+ * here for clarity and consistency with web. Once web uses the
12
+ * {@code AbstractButton} base class, we can remove all these and just use
13
+ * the {@code AbstractMuteButton} as {@MuteButton}.
104
  */
14
  */
105
-function _mapStateToProps(state, ownProps) {
106
-    const tracks = state['features/base/tracks'];
107
-    const audioTrack
108
-        = getTrackByMediaTypeAndParticipant(
109
-            tracks, MEDIA_TYPE.AUDIO, ownProps.participant.id);
110
-
111
-    return {
112
-        _audioTrack: audioTrack
113
-    };
114
-}
115
-
116
-export default translate(connect(_mapStateToProps)(MuteButton));
15
+export default translate(connect(_mapStateToProps)(AbstractMuteButton));

+ 4
- 51
react/features/remote-video-menu/components/native/MuteRemoteParticipantDialog.js 查看文件

1
 // @flow
1
 // @flow
2
 
2
 
3
-import React, { Component } from 'react';
3
+import React from 'react';
4
 import { connect } from 'react-redux';
4
 import { connect } from 'react-redux';
5
 
5
 
6
-import {
7
-    createRemoteMuteConfirmedEvent,
8
-    sendAnalytics
9
-} from '../../../analytics';
10
 import { ConfirmDialog } from '../../../base/dialog';
6
 import { ConfirmDialog } from '../../../base/dialog';
11
 import { translate } from '../../../base/i18n';
7
 import { translate } from '../../../base/i18n';
12
-import { muteRemoteParticipant } from '../../../base/participants';
13
 
8
 
14
-type Props = {
15
-
16
-    /**
17
-     * The Redux dispatch function.
18
-     */
19
-    dispatch: Function,
20
-
21
-    /**
22
-     * The remote participant to be muted.
23
-     */
24
-    participant: Object,
25
-
26
-    /**
27
-     * Function to translate i18n labels.
28
-     */
29
-    t: Function
30
-};
9
+import AbstractMuteRemoteParticipantDialog
10
+    from '../AbstractMuteRemoteParticipantDialog';
31
 
11
 
32
 /**
12
 /**
33
  * Dialog to confirm a remote participant mute action.
13
  * Dialog to confirm a remote participant mute action.
34
  */
14
  */
35
-class MuteRemoteParticipantDialog extends Component<Props> {
36
-    /**
37
-     * Initializes a new {@code MuteRemoteParticipantDialog} instance.
38
-     *
39
-     * @inheritdoc
40
-     */
41
-    constructor(props: Props) {
42
-        super(props);
43
-
44
-        this._onSubmit = this._onSubmit.bind(this);
45
-    }
46
-
15
+class MuteRemoteParticipantDialog extends AbstractMuteRemoteParticipantDialog {
47
     /**
16
     /**
48
      * Implements React's {@link Component#render()}.
17
      * Implements React's {@link Component#render()}.
49
      *
18
      *
59
     }
28
     }
60
 
29
 
61
     _onSubmit: () => boolean;
30
     _onSubmit: () => boolean;
62
-
63
-    /**
64
-     * Callback for the confirm button.
65
-     *
66
-     * @private
67
-     * @returns {boolean} - True (to note that the modal should be closed).
68
-     */
69
-    _onSubmit() {
70
-        const { dispatch, participant } = this.props;
71
-
72
-        sendAnalytics(createRemoteMuteConfirmedEvent(participant.id));
73
-
74
-        dispatch(muteRemoteParticipant(participant.id));
75
-
76
-        return true;
77
-    }
78
 }
31
 }
79
 
32
 
80
 export default translate(connect()(MuteRemoteParticipantDialog));
33
 export default translate(connect()(MuteRemoteParticipantDialog));

+ 1
- 1
react/features/remote-video-menu/components/native/RemoteVideoMenu.js 查看文件

72
         const buttonProps = {
72
         const buttonProps = {
73
             afterClick: this._onCancel,
73
             afterClick: this._onCancel,
74
             showLabel: true,
74
             showLabel: true,
75
-            participant: this.props.participant,
75
+            participantID: this.props.participant.id,
76
             styles: bottomSheetItemStylesCombined
76
             styles: bottomSheetItemStylesCombined
77
         };
77
         };
78
 
78
 

+ 6
- 0
react/features/remote-video-menu/components/native/index.js 查看文件

1
 // @flow
1
 // @flow
2
 
2
 
3
+export {
4
+    default as KickRemoteParticipantDialog
5
+} from './KickRemoteParticipantDialog';
6
+export {
7
+    default as MuteRemoteParticipantDialog
8
+} from './MuteRemoteParticipantDialog';
3
 export { default as RemoteVideoMenu } from './RemoteVideoMenu';
9
 export { default as RemoteVideoMenu } from './RemoteVideoMenu';

+ 8
- 3
react/features/remote-video-menu/components/native/styles.js 查看文件

1
 // @flow
1
 // @flow
2
 
2
 
3
+import {
4
+    MD_FONT_SIZE,
5
+    MD_ITEM_HEIGHT,
6
+    MD_ITEM_MARGIN_PADDING
7
+} from '../../../base/dialog';
3
 import { ColorPalette, createStyleSheet } from '../../../base/styles';
8
 import { ColorPalette, createStyleSheet } from '../../../base/styles';
4
 
9
 
5
 export default createStyleSheet({
10
 export default createStyleSheet({
8
         borderBottomColor: ColorPalette.darkGrey,
13
         borderBottomColor: ColorPalette.darkGrey,
9
         borderBottomWidth: 1,
14
         borderBottomWidth: 1,
10
         flexDirection: 'row',
15
         flexDirection: 'row',
11
-        height: 48
16
+        height: MD_ITEM_HEIGHT
12
     },
17
     },
13
 
18
 
14
     participantNameLabel: {
19
     participantNameLabel: {
15
         color: ColorPalette.lightGrey,
20
         color: ColorPalette.lightGrey,
16
         flexShrink: 1,
21
         flexShrink: 1,
17
-        fontSize: 16,
18
-        marginLeft: 16,
22
+        fontSize: MD_FONT_SIZE,
23
+        marginLeft: MD_ITEM_MARGIN_PADDING,
19
         opacity: 0.90
24
         opacity: 0.90
20
     }
25
     }
21
 });
26
 });

+ 18
- 65
react/features/remote-video-menu/components/web/KickButton.js 查看文件

1
 /* @flow */
1
 /* @flow */
2
 
2
 
3
-import React, { Component } from 'react';
3
+import React from 'react';
4
 import { connect } from 'react-redux';
4
 import { connect } from 'react-redux';
5
 
5
 
6
-import {
7
-    createRemoteVideoMenuButtonEvent,
8
-    sendAnalytics
9
-} from '../../../analytics';
10
 import { translate } from '../../../base/i18n';
6
 import { translate } from '../../../base/i18n';
11
-import { kickParticipant } from '../../../base/participants';
12
 
7
 
13
-import RemoteVideoMenuButton from './RemoteVideoMenuButton';
14
-
15
-/**
16
- * The type of the React {@code Component} state of {@link KickButton}.
17
- */
18
-type Props = {
19
-
20
-    /**
21
-     * Invoked to signal the participant with the passed in participantID
22
-     * should be removed from the conference.
23
-     */
24
-    dispatch: Dispatch<*>,
25
-
26
-    /**
27
-     * Callback to invoke when {@code KickButton} is clicked.
28
-     */
29
-    onClick: Function,
30
-
31
-    /**
32
-     * The ID of the participant linked to the onClick callback.
33
-     */
34
-    participantID: string,
8
+import AbstractKickButton, {
9
+    type Props
10
+} from '../AbstractKickButton';
35
 
11
 
36
-    /**
37
-     * Invoked to obtain translated strings.
38
-     */
39
-    t: Function,
40
-};
12
+import RemoteVideoMenuButton from './RemoteVideoMenuButton';
41
 
13
 
42
 /**
14
 /**
43
  * Implements a React {@link Component} which displays a button for kicking out
15
  * Implements a React {@link Component} which displays a button for kicking out
44
  * a participant from the conference.
16
  * a participant from the conference.
45
  *
17
  *
46
- * @extends Component
18
+ * NOTE: At the time of writing this is a button that doesn't use the
19
+ * {@code AbstractButton} base component, but is inherited from the same
20
+ * super class ({@code AbstractKickButton} that extends {@code AbstractButton})
21
+ * for the sake of code sharing between web and mobile. Once web uses the
22
+ * {@code AbstractButton} base component, this can be fully removed.
47
  */
23
  */
48
-class KickButton extends Component<Props> {
24
+class KickButton extends AbstractKickButton {
49
     /**
25
     /**
50
-     * Initializes a new {@code KickButton} instance.
26
+     * Instantiates a new {@code Component}.
51
      *
27
      *
52
-     * @param {Object} props - The read-only React Component props with which
53
-     * the new instance is to be initialized.
28
+     * @inheritdoc
54
      */
29
      */
55
-    constructor(props) {
30
+    constructor(props: Props) {
56
         super(props);
31
         super(props);
57
 
32
 
58
-        // Bind event handlers so they are only bound once for every instance.
59
-        this._onClick = this._onClick.bind(this);
33
+        this._handleClick = this._handleClick.bind(this);
60
     }
34
     }
61
 
35
 
62
     /**
36
     /**
73
                 buttonText = { t('videothumbnail.kick') }
47
                 buttonText = { t('videothumbnail.kick') }
74
                 iconClass = 'icon-kick'
48
                 iconClass = 'icon-kick'
75
                 id = { `ejectlink_${participantID}` }
49
                 id = { `ejectlink_${participantID}` }
76
-                onClick = { this._onClick } />
50
+                // eslint-disable-next-line react/jsx-handler-names
51
+                onClick = { this._handleClick } />
77
         );
52
         );
78
     }
53
     }
79
 
54
 
80
-    _onClick: () => void;
81
-
82
-    /**
83
-     * Remove the participant with associated participantID from the conference.
84
-     *
85
-     * @private
86
-     * @returns {void}
87
-     */
88
-    _onClick() {
89
-        const { dispatch, onClick, participantID } = this.props;
90
-
91
-        sendAnalytics(createRemoteVideoMenuButtonEvent(
92
-            'kick.button',
93
-            {
94
-                'participant_id': participantID
95
-            }));
96
-
97
-        dispatch(kickParticipant(participantID));
98
-
99
-        if (onClick) {
100
-            onClick();
101
-        }
102
-    }
55
+    _handleClick: () => void
103
 }
56
 }
104
 
57
 
105
 export default translate(connect()(KickButton));
58
 export default translate(connect()(KickButton));

+ 39
- 0
react/features/remote-video-menu/components/web/KickRemoteParticipantDialog.js 查看文件

1
+// @flow
2
+
3
+import React from 'react';
4
+import { connect } from 'react-redux';
5
+
6
+import { Dialog } from '../../../base/dialog';
7
+import { translate } from '../../../base/i18n';
8
+
9
+import AbstractKickRemoteParticipantDialog
10
+    from '../AbstractKickRemoteParticipantDialog';
11
+
12
+/**
13
+ * Dialog to confirm a remote participant kick action.
14
+ */
15
+class KickRemoteParticipantDialog extends AbstractKickRemoteParticipantDialog {
16
+    /**
17
+     * Implements React's {@link Component#render()}.
18
+     *
19
+     * @inheritdoc
20
+     * @returns {ReactElement}
21
+     */
22
+    render() {
23
+        return (
24
+            <Dialog
25
+                okTitleKey = 'dialog.kickParticipantButton'
26
+                onSubmit = { this._onSubmit }
27
+                titleKey = 'dialog.kickParticipantTitle'
28
+                width = 'small'>
29
+                <div>
30
+                    { this.props.t('dialog.kickParticipantDialog') }
31
+                </div>
32
+            </Dialog>
33
+        );
34
+    }
35
+
36
+    _onSubmit: () => boolean;
37
+}
38
+
39
+export default translate(connect()(KickRemoteParticipantDialog));

+ 21
- 74
react/features/remote-video-menu/components/web/MuteButton.js 查看文件

1
 /* @flow */
1
 /* @flow */
2
 
2
 
3
-import React, { Component } from 'react';
3
+import React from 'react';
4
 import { connect } from 'react-redux';
4
 import { connect } from 'react-redux';
5
 
5
 
6
-import {
7
-    createRemoteVideoMenuButtonEvent,
8
-    sendAnalytics
9
-} from '../../../analytics';
10
 import { translate } from '../../../base/i18n';
6
 import { translate } from '../../../base/i18n';
11
-import { openDialog } from '../../../base/dialog';
12
 
7
 
13
-import RemoteVideoMenuButton from './RemoteVideoMenuButton';
14
-import MuteRemoteParticipantDialog from './MuteRemoteParticipantDialog';
15
-
16
-/**
17
- * The type of the React {@code Component} props of {@link MuteButton}.
18
- */
19
-type Props = {
20
-
21
-    /**
22
-     * Invoked to send a request for muting the participant with the passed
23
-     * in participantID.
24
-     */
25
-    dispatch: Dispatch<*>,
26
-
27
-    /**
28
-     * Whether or not the participant is currently audio muted.
29
-     */
30
-    isAudioMuted: Function,
31
-
32
-    /**
33
-     * Callback to invoke when {@code MuteButton} is clicked.
34
-     */
35
-    onClick: Function,
36
-
37
-    /**
38
-     * The ID of the participant linked to the onClick callback.
39
-     */
40
-    participantID: string,
8
+import AbstractMuteButton, {
9
+    _mapStateToProps,
10
+    type Props
11
+} from '../AbstractMuteButton';
41
 
12
 
42
-    /**
43
-     * Invoked to obtain translated strings.
44
-     */
45
-    t: Function
46
-};
13
+import RemoteVideoMenuButton from './RemoteVideoMenuButton';
47
 
14
 
48
 /**
15
 /**
49
  * Implements a React {@link Component} which displays a button for audio muting
16
  * Implements a React {@link Component} which displays a button for audio muting
50
  * a participant in the conference.
17
  * a participant in the conference.
51
  *
18
  *
52
- * @extends Component
19
+ * NOTE: At the time of writing this is a button that doesn't use the
20
+ * {@code AbstractButton} base component, but is inherited from the same
21
+ * super class ({@code AbstractMuteButton} that extends {@code AbstractButton})
22
+ * for the sake of code sharing between web and mobile. Once web uses the
23
+ * {@code AbstractButton} base component, this can be fully removed.
53
  */
24
  */
54
-class MuteButton extends Component<Props> {
25
+class MuteButton extends AbstractMuteButton {
55
     /**
26
     /**
56
-     * Initializes a new {@code MuteButton} instance.
27
+     * Instantiates a new {@code Component}.
57
      *
28
      *
58
-     * @param {Object} props - The read-only React Component props with which
59
-     * the new instance is to be initialized.
29
+     * @inheritdoc
60
      */
30
      */
61
     constructor(props: Props) {
31
     constructor(props: Props) {
62
         super(props);
32
         super(props);
63
 
33
 
64
-        // Bind event handlers so they are only bound once for every instance.
65
-        this._onClick = this._onClick.bind(this);
34
+        this._handleClick = this._handleClick.bind(this);
66
     }
35
     }
67
 
36
 
68
     /**
37
     /**
72
      * @returns {ReactElement}
41
      * @returns {ReactElement}
73
      */
42
      */
74
     render() {
43
     render() {
75
-        const { isAudioMuted, participantID, t } = this.props;
76
-        const muteConfig = isAudioMuted ? {
44
+        const { _audioTrackMuted, participantID, t } = this.props;
45
+        const muteConfig = _audioTrackMuted ? {
77
             translationKey: 'videothumbnail.muted',
46
             translationKey: 'videothumbnail.muted',
78
             muteClassName: 'mutelink disabled'
47
             muteClassName: 'mutelink disabled'
79
         } : {
48
         } : {
87
                 displayClass = { muteConfig.muteClassName }
56
                 displayClass = { muteConfig.muteClassName }
88
                 iconClass = 'icon-mic-disabled'
57
                 iconClass = 'icon-mic-disabled'
89
                 id = { `mutelink_${participantID}` }
58
                 id = { `mutelink_${participantID}` }
90
-                onClick = { this._onClick } />
59
+                // eslint-disable-next-line react/jsx-handler-names
60
+                onClick = { this._handleClick } />
91
         );
61
         );
92
     }
62
     }
93
 
63
 
94
-    _onClick: () => void;
95
-
96
-    /**
97
-     * Dispatches a request to mute the participant with the passed in
98
-     * participantID.
99
-     *
100
-     * @private
101
-     * @returns {void}
102
-     */
103
-    _onClick() {
104
-        const { dispatch, onClick, participantID } = this.props;
105
-
106
-        sendAnalytics(createRemoteVideoMenuButtonEvent(
107
-            'mute.button',
108
-            {
109
-                'participant_id': participantID
110
-            }));
111
-
112
-        dispatch(openDialog(MuteRemoteParticipantDialog, { participantID }));
113
-
114
-        if (onClick) {
115
-            onClick();
116
-        }
117
-    }
64
+    _handleClick: () => void
118
 }
65
 }
119
 
66
 
120
-export default translate(connect()(MuteButton));
67
+export default translate(connect(_mapStateToProps)(MuteButton));

+ 8
- 82
react/features/remote-video-menu/components/web/MuteRemoteParticipantDialog.js 查看文件

1
 /* @flow */
1
 /* @flow */
2
 
2
 
3
-import React, { Component } from 'react';
3
+import React from 'react';
4
 import { connect } from 'react-redux';
4
 import { connect } from 'react-redux';
5
 
5
 
6
 import { Dialog } from '../../../base/dialog';
6
 import { Dialog } from '../../../base/dialog';
7
 import { translate } from '../../../base/i18n';
7
 import { translate } from '../../../base/i18n';
8
 
8
 
9
-import {
10
-    createRemoteMuteConfirmedEvent,
11
-    sendAnalytics
12
-} from '../../../analytics';
13
-import { muteRemoteParticipant } from '../../../base/participants';
14
-
15
-/**
16
- * The type of the React {@code Component} props of
17
- * {@link MuteRemoteParticipantDialog}.
18
- */
19
-type Props = {
20
-
21
-    /**
22
-     * Invoked to send a request for muting the participant with the passed
23
-     * in participantID.
24
-     */
25
-    dispatch: Dispatch<*>,
26
-
27
-    /**
28
-     * The ID of the participant linked to the onClick callback.
29
-     */
30
-    participantID: string,
31
-
32
-    /**
33
-     * Invoked to obtain translated strings.
34
-     */
35
-    t: Function
36
-};
9
+import AbstractMuteRemoteParticipantDialog
10
+    from '../AbstractMuteRemoteParticipantDialog';
37
 
11
 
38
 /**
12
 /**
39
  * A React Component with the contents for a dialog that asks for confirmation
13
  * A React Component with the contents for a dialog that asks for confirmation
41
  *
15
  *
42
  * @extends Component
16
  * @extends Component
43
  */
17
  */
44
-class MuteRemoteParticipantDialog extends Component<Props> {
45
-    /**
46
-     * Initializes a new {@code MuteRemoteParticipantDialog} instance.
47
-     *
48
-     * @param {Object} props - The read-only properties with which the new
49
-     * instance is to be initialized.
50
-     */
51
-    constructor(props: Props) {
52
-        super(props);
53
-
54
-        // Bind event handlers so they are only bound once per instance.
55
-        this._onSubmit = this._onSubmit.bind(this);
56
-        this._renderContent = this._renderContent.bind(this);
57
-    }
58
-
18
+class MuteRemoteParticipantDialog extends AbstractMuteRemoteParticipantDialog {
59
     /**
19
     /**
60
      * Implements React's {@link Component#render()}.
20
      * Implements React's {@link Component#render()}.
61
      *
21
      *
69
                 onSubmit = { this._onSubmit }
29
                 onSubmit = { this._onSubmit }
70
                 titleKey = 'dialog.muteParticipantTitle'
30
                 titleKey = 'dialog.muteParticipantTitle'
71
                 width = 'small'>
31
                 width = 'small'>
72
-                { this._renderContent() }
32
+                <div>
33
+                    { this.props.t('dialog.muteParticipantBody') }
34
+                </div>
73
             </Dialog>
35
             </Dialog>
74
         );
36
         );
75
     }
37
     }
76
 
38
 
77
-    _onSubmit: () => void;
78
-
79
-    /**
80
-     * Handles the submit button action.
81
-     *
82
-     * @private
83
-     * @returns {boolean} - True (to note that the modal should be closed).
84
-     */
85
-    _onSubmit() {
86
-        const { dispatch, participantID } = this.props;
87
-
88
-        sendAnalytics(createRemoteMuteConfirmedEvent(participantID));
89
-
90
-        dispatch(muteRemoteParticipant(participantID));
91
-
92
-        return true;
93
-    }
94
-
95
-    _renderContent: () => React$Element<*>;
96
-
97
-    /**
98
-     * Renders the content of the dialog.
99
-     *
100
-     * @private
101
-     * @returns {Component} The React {@code Component} which is the view of the
102
-     * dialog content.
103
-     */
104
-    _renderContent() {
105
-        const { t } = this.props;
106
-
107
-        return (
108
-            <div>
109
-                { t('dialog.muteParticipantBody') }
110
-            </div>
111
-        );
112
-    }
113
-
39
+    _onSubmit: () => boolean;
114
 }
40
 }
115
 
41
 
116
 export default translate(connect()(MuteRemoteParticipantDialog));
42
 export default translate(connect()(MuteRemoteParticipantDialog));

+ 6
- 0
react/features/remote-video-menu/components/web/index.js 查看文件

1
 // @flow
1
 // @flow
2
 
2
 
3
 export { default as KickButton } from './KickButton';
3
 export { default as KickButton } from './KickButton';
4
+export {
5
+    default as KickRemoteParticipantDialog
6
+} from './KickRemoteParticipantDialog';
4
 export { default as MuteButton } from './MuteButton';
7
 export { default as MuteButton } from './MuteButton';
8
+export {
9
+    default as MuteRemoteParticipantDialog
10
+} from './MuteRemoteParticipantDialog';
5
 export {
11
 export {
6
     REMOTE_CONTROL_MENU_STATES,
12
     REMOTE_CONTROL_MENU_STATES,
7
     default as RemoteControlButton
13
     default as RemoteControlButton

Loading…
取消
儲存