Browse Source

feat(native-participants-pane) rebase, resolved conflicts pt. 1

master
Calin Chitu 4 years ago
parent
commit
d62e378528

+ 17
- 0
react/features/base/participants/functions.js View File

@@ -261,6 +261,23 @@ export function haveParticipantWithScreenSharingFeature(stateful: Object | Funct
261 261
     return toState(stateful)['features/base/participants'].haveParticipantWithScreenSharingFeature;
262 262
 }
263 263
 
264
+/**
265
+ * Selectors for getting all known participant ids, with fake participants filtered
266
+ * out.
267
+ *
268
+ * @param {(Function|Object|Participant[])} stateful - The redux state
269
+ * features/base/participants, the (whole) redux state, or redux's
270
+ * {@code getState} function to be used to retrieve the state
271
+ * features/base/participants.
272
+ * @returns {Participant[]}
273
+ */
274
+export function getParticipantsById(stateful: Object | Function) {
275
+    const state = toState(stateful)['features/base/participants'];
276
+    const noFakeParticipants = state.filter(p => !p.fakeParticipants);
277
+
278
+    return noFakeParticipants.map(p => p.id);
279
+}
280
+
264 281
 /**
265 282
  * Selectors for getting all remote participants.
266 283
  *

+ 3
- 3
react/features/participants-pane/actions.native.js View File

@@ -20,11 +20,11 @@ export function showContextMenuReject(participant: Object) {
20 20
 /**
21 21
  * Displays the context menu for the selected meeting participant.
22 22
  *
23
- * @param {Object} participant - The selected meeting participant.
23
+ * @param {string} participantID - The selected meeting participant id.
24 24
  * @returns {Function}
25 25
  */
26
-export function showContextMenuDetails(participant: Object) {
27
-    return openDialog(ContextMenuMeetingParticipantDetails, { participant });
26
+export function showContextMenuDetails(participantID: String) {
27
+    return openDialog(ContextMenuMeetingParticipantDetails, { participantID });
28 28
 }
29 29
 
30 30
 /**

+ 160
- 78
react/features/participants-pane/components/native/ContextMenuMeetingParticipantDetails.js View File

@@ -4,9 +4,10 @@ import React, { useCallback } from 'react';
4 4
 import { useTranslation } from 'react-i18next';
5 5
 import { TouchableOpacity, View } from 'react-native';
6 6
 import { Divider, Text } from 'react-native-paper';
7
-import { useDispatch, useSelector } from 'react-redux';
7
+import { useDispatch } from 'react-redux';
8 8
 
9 9
 import { Avatar } from '../../../base/avatar';
10
+import { isToolbarButtonEnabled } from '../../../base/config';
10 11
 import { hideDialog, openDialog } from '../../../base/dialog/actions';
11 12
 import BottomSheet from '../../../base/dialog/components/native/BottomSheet';
12 13
 import {
@@ -15,10 +16,14 @@ import {
15 16
     IconMuteEveryoneElse, IconVideoOff
16 17
 } from '../../../base/icons';
17 18
 import {
18
-    getParticipantsById,
19
+    getParticipantByIdOrUndefined, getParticipantDisplayName,
19 20
     isLocalParticipantModerator
20
-} from '../../../base/participants';
21
-import { getIsParticipantVideoMuted } from '../../../base/tracks';
21
+} from '../../../base/participants/functions';
22
+import { connect } from '../../../base/redux';
23
+import {
24
+    isParticipantAudioMuted,
25
+    isParticipantVideoMuted
26
+} from '../../../base/tracks/functions';
22 27
 import { openChat } from '../../../chat/actions.native';
23 28
 import {
24 29
     KickRemoteParticipantDialog,
@@ -32,131 +37,179 @@ import styles from './styles';
32 37
 
33 38
 type Props = {
34 39
 
40
+    /**
41
+     * The display name of the participant.
42
+     */
43
+    _displayName: string,
44
+
45
+    /**
46
+     * True if the local participant is moderator and false otherwise.
47
+     */
48
+    _isLocalModerator: boolean,
49
+
50
+    /**
51
+     * True if the chat button is enabled and false otherwise.
52
+     */
53
+    _isChatButtonEnabled: boolean,
54
+
55
+    /**
56
+     * True if the participant is moderator and false otherwise.
57
+     */
58
+    _isParticipantModerator: boolean,
59
+
60
+    /**
61
+     * True if the participant is video muted and false otherwise.
62
+     */
63
+    _isParticipantVideoMuted: boolean,
64
+
65
+    /**
66
+     * True if the participant is audio muted and false otherwise.
67
+     */
68
+    _isParticipantAudioMuted: boolean,
69
+
35 70
     /**
36 71
      * Participant reference
37 72
      */
38
-    participant: Object
73
+    _participant: Object,
74
+
75
+    /**
76
+     * The ID of the participant.
77
+     */
78
+    participantID: string,
39 79
 };
40 80
 
41
-export const ContextMenuMeetingParticipantDetails = ({ participant: p }: Props) => {
81
+export const ContextMenuMeetingParticipantDetails = (
82
+        {
83
+            _displayName,
84
+            _isLocalModerator,
85
+            _isChatButtonEnabled,
86
+            _isParticipantVideoMuted,
87
+            _isParticipantAudioMuted,
88
+            _participant,
89
+            participantID
90
+        }: Props) => {
42 91
     const dispatch = useDispatch();
43
-    const participantsIDArr = useSelector(getParticipantsById);
44
-    const participantIsAvailable = participantsIDArr.find(partId => partId === p.id);
45 92
     const cancel = useCallback(() => dispatch(hideDialog()), [ dispatch ]);
46
-    const displayName = p.name;
47
-    const isLocalModerator = useSelector(isLocalParticipantModerator);
48
-    const isParticipantVideoMuted = useSelector(getIsParticipantVideoMuted(p));
49 93
     const kickRemoteParticipant = useCallback(() => {
50 94
         dispatch(openDialog(KickRemoteParticipantDialog, {
51
-            participantID: p.id
95
+            participantID
52 96
         }));
53
-    }, [ dispatch, p ]);
97
+    }, [ dispatch, participantID ]);
54 98
     const muteAudio = useCallback(() => {
55 99
         dispatch(openDialog(MuteRemoteParticipantDialog, {
56
-            participantID: p.id
100
+            participantID
57 101
         }));
58
-    }, [ dispatch, p ]);
102
+    }, [ dispatch, participantID ]);
59 103
     const muteEveryoneElse = useCallback(() => {
60 104
         dispatch(openDialog(MuteEveryoneDialog, {
61
-            exclude: [ p.id ]
105
+            exclude: [ participantID ]
62 106
         }));
63
-    }, [ dispatch, p ]);
107
+    }, [ dispatch, participantID ]);
64 108
     const muteVideo = useCallback(() => {
65 109
         dispatch(openDialog(MuteRemoteParticipantsVideoDialog, {
66
-            participantID: p.id
110
+            participantID
67 111
         }));
68
-    }, [ dispatch, p ]);
112
+    }, [ dispatch, participantID ]);
69 113
 
70 114
     const sendPrivateMessage = useCallback(() => {
71 115
         dispatch(hideDialog());
72
-        dispatch(openChat(p));
73
-    }, [ dispatch, p ]);
116
+        dispatch(openChat(_participant));
117
+    }, [ dispatch, _participant ]);
74 118
     const { t } = useTranslation();
75 119
 
76 120
     return (
77 121
         <BottomSheet
78 122
             addScrollViewPadding = { false }
79 123
             onCancel = { cancel }
80
-            showSlidingView = { Boolean(participantIsAvailable) }
81 124
             style = { styles.contextMenuMeetingParticipantDetails }>
82 125
             <View
83 126
                 style = { styles.contextMenuItemSectionAvatar }>
84 127
                 <Avatar
85 128
                     className = 'participant-avatar'
86
-                    participantId = { p.id }
129
+                    participantId = { participantID }
87 130
                     size = { 20 } />
88 131
                 <View style = { styles.contextMenuItemAvatarText }>
89 132
                     <Text style = { styles.contextMenuItemName }>
90
-                        { displayName }
133
+                        { _displayName }
91 134
                     </Text>
92 135
                 </View>
93 136
             </View>
94 137
             <Divider style = { styles.divider } />
95 138
             {
96
-                isLocalModerator
97
-                && <TouchableOpacity
98
-                    onPress = { muteAudio }
99
-                    style = { styles.contextMenuItem }>
100
-                    <Icon
101
-                        size = { 20 }
102
-                        src = { IconMicrophoneEmptySlash } />
103
-                    <Text style = { styles.contextMenuItemText }>
104
-                        { t('participantsPane.actions.mute') }
105
-                    </Text>
106
-                </TouchableOpacity>
139
+                _isLocalModerator && (
140
+                    <>
141
+                        {
142
+                            !_isParticipantAudioMuted
143
+                            && <TouchableOpacity
144
+                                onPress = { muteAudio }
145
+                                style = { styles.contextMenuItem }>
146
+                                <Icon
147
+                                    size = { 20 }
148
+                                    src = { IconMicrophoneEmptySlash } />
149
+                                <Text style = { styles.contextMenuItemText }>
150
+                                    { t('participantsPane.actions.mute') }
151
+                                </Text>
152
+                            </TouchableOpacity>
153
+                        }
154
+
155
+                        <TouchableOpacity
156
+                            onPress = { muteEveryoneElse }
157
+                            style = { styles.contextMenuItem }>
158
+                            <Icon
159
+                                size = { 20 }
160
+                                src = { IconMuteEveryoneElse } />
161
+                            <Text style = { styles.contextMenuItemText }>
162
+                                { t('participantsPane.actions.muteEveryoneElse') }
163
+                            </Text>
164
+                        </TouchableOpacity>
165
+                    </>
166
+                )
107 167
             }
168
+            <Divider style = { styles.divider } />
108 169
             {
109
-                isLocalModerator
110
-                && <TouchableOpacity
111
-                    onPress = { muteEveryoneElse }
112
-                    style = { styles.contextMenuItem }>
113
-                    <Icon
114
-                        size = { 20 }
115
-                        src = { IconMuteEveryoneElse } />
116
-                    <Text style = { styles.contextMenuItemText }>
117
-                        { t('participantsPane.actions.muteEveryoneElse') }
118
-                    </Text>
119
-                </TouchableOpacity>
170
+                _isLocalModerator && (
171
+                    <>
172
+                        {
173
+                            _isParticipantVideoMuted
174
+                            || <TouchableOpacity
175
+                                onPress = { muteVideo }
176
+                                style = { styles.contextMenuItemSection }>
177
+                                <Icon
178
+                                    size = { 20 }
179
+                                    src = { IconVideoOff } />
180
+                                <Text style = { styles.contextMenuItemText }>
181
+                                    { t('participantsPane.actions.stopVideo') }
182
+                                </Text>
183
+                            </TouchableOpacity>
184
+                        }
185
+
186
+                        <TouchableOpacity
187
+                            onPress = { kickRemoteParticipant }
188
+                            style = { styles.contextMenuItem }>
189
+                            <Icon
190
+                                size = { 20 }
191
+                                src = { IconCloseCircle } />
192
+                            <Text style = { styles.contextMenuItemText }>
193
+                                { t('videothumbnail.kick') }
194
+                            </Text>
195
+                        </TouchableOpacity>
196
+                    </>
197
+                )
120 198
             }
121
-            <Divider style = { styles.divider } />
122 199
             {
123
-                isLocalModerator && (
124
-                    isParticipantVideoMuted
125
-                    || <TouchableOpacity
126
-                        onPress = { muteVideo }
127
-                        style = { styles.contextMenuItemSection }>
200
+                _isChatButtonEnabled && (
201
+                    <TouchableOpacity
202
+                        onPress = { sendPrivateMessage }
203
+                        style = { styles.contextMenuItem }>
128 204
                         <Icon
129 205
                             size = { 20 }
130
-                            src = { IconVideoOff } />
206
+                            src = { IconMessage } />
131 207
                         <Text style = { styles.contextMenuItemText }>
132
-                            { t('participantsPane.actions.stopVideo') }
208
+                            { t('toolbar.accessibilityLabel.privateMessage') }
133 209
                         </Text>
134 210
                     </TouchableOpacity>
135 211
                 )
136 212
             }
137
-            {
138
-                isLocalModerator
139
-                && <TouchableOpacity
140
-                    onPress = { kickRemoteParticipant }
141
-                    style = { styles.contextMenuItem }>
142
-                    <Icon
143
-                        size = { 20 }
144
-                        src = { IconCloseCircle } />
145
-                    <Text style = { styles.contextMenuItemText }>
146
-                        { t('videothumbnail.kick') }
147
-                    </Text>
148
-                </TouchableOpacity>
149
-            }
150
-            <TouchableOpacity
151
-                onPress = { sendPrivateMessage }
152
-                style = { styles.contextMenuItem }>
153
-                <Icon
154
-                    size = { 20 }
155
-                    src = { IconMessage } />
156
-                <Text style = { styles.contextMenuItemText }>
157
-                    { t('toolbar.accessibilityLabel.privateMessage') }
158
-                </Text>
159
-            </TouchableOpacity>
160 213
             {/* We need design specs for this*/}
161 214
             {/* <TouchableOpacity*/}
162 215
             {/*    style = { styles.contextMenuItemSection }>*/}
@@ -167,7 +220,36 @@ export const ContextMenuMeetingParticipantDetails = ({ participant: p }: Props)
167 220
             {/*    <Text style = { styles.contextMenuItemText }>{ t('participantsPane.actions.networkStats') }</Text>*/}
168 221
             {/* </TouchableOpacity>*/}
169 222
             <Divider style = { styles.divider } />
170
-            <VolumeSlider participant = { p } />
223
+            <VolumeSlider participant = { _participant } />
171 224
         </BottomSheet>
172 225
     );
173 226
 };
227
+
228
+
229
+/**
230
+ * Maps (parts of) the redux state to the associated props for this component.
231
+ *
232
+ * @param {Object} state - The Redux state.
233
+ * @param {Object} ownProps - The own props of the component.
234
+ * @private
235
+ * @returns {Props}
236
+ */
237
+function _mapStateToProps(state, ownProps): Object {
238
+    const { participantID } = ownProps;
239
+    const participant = getParticipantByIdOrUndefined(state, participantID);
240
+    const _isLocalModerator = isLocalParticipantModerator(state);
241
+    const _isChatButtonEnabled = isToolbarButtonEnabled('chat', state);
242
+    const _isParticipantVideoMuted = isParticipantVideoMuted(participant, state);
243
+    const _isParticipantAudioMuted = isParticipantAudioMuted(participant, state);
244
+
245
+    return {
246
+        _displayName: getParticipantDisplayName(state, participantID),
247
+        _isLocalModerator,
248
+        _isChatButtonEnabled,
249
+        _isParticipantAudioMuted,
250
+        _isParticipantVideoMuted,
251
+        _participant: participant
252
+    };
253
+}
254
+
255
+export default connect(_mapStateToProps)(ContextMenuMeetingParticipantDetails);

+ 4
- 1
react/features/participants-pane/components/native/LobbyParticipantItem.js View File

@@ -29,10 +29,13 @@ export const LobbyParticipantItem = ({ participant: p }: Props) => {
29 29
     return (
30 30
         <ParticipantItem
31 31
             audioMediaState = { MEDIA_STATE.NONE }
32
+            displayName = { p.name }
32 33
             isKnockingParticipant = { true }
33
-            name = { p.name }
34
+            local = { p.local }
34 35
             onPress = { openContextMenuReject }
35 36
             participant = { p }
37
+            participantID = { p.id }
38
+            raisedHand = { p.raisedHand }
36 39
             videoMediaState = { MEDIA_STATE.NONE }>
37 40
             <Button
38 41
                 children = { t('lobby.admit') }

+ 97
- 18
react/features/participants-pane/components/native/MeetingParticipantItem.js View File

@@ -1,14 +1,19 @@
1 1
 // @flow
2 2
 
3
-import React, { useCallback } from 'react';
4
-import { useSelector, useDispatch } from 'react-redux';
3
+import React from 'react';
5 4
 
5
+import { translate } from '../../../base/i18n';
6 6
 import {
7
-    getIsParticipantAudioMuted,
8
-    getIsParticipantVideoMuted
7
+    getParticipantByIdOrUndefined,
8
+    getParticipantDisplayName
9
+} from '../../../base/participants';
10
+import { connect } from '../../../base/redux';
11
+import {
12
+    isParticipantAudioMuted,
13
+    isParticipantVideoMuted
9 14
 } from '../../../base/tracks';
10
-import { showContextMenuDetails } from '../../actions.native';
11 15
 import { MEDIA_STATE } from '../../constants';
16
+import type { MediaState } from '../../constants';
12 17
 import { getParticipantAudioMediaState } from '../../functions';
13 18
 
14 19
 import ParticipantItem from './ParticipantItem';
@@ -17,26 +22,100 @@ import ParticipantItem from './ParticipantItem';
17 22
 type Props = {
18 23
 
19 24
     /**
20
-     * Participant reference
25
+     * Media state for audio.
26
+     */
27
+    _audioMediaState: MediaState,
28
+
29
+    /**
30
+     * The display name of the participant.
31
+     */
32
+    _displayName: string,
33
+
34
+    /**
35
+     * True if the participant is video muted.
36
+     */
37
+    _isVideoMuted: boolean,
38
+
39
+    /**
40
+     * True if the participant is the local participant.
41
+     */
42
+    _local: boolean,
43
+
44
+    /**
45
+     * The participant ID.
46
+     */
47
+    _participantID: string,
48
+
49
+    /**
50
+     * True if the participant have raised hand.
51
+     */
52
+    _raisedHand: boolean,
53
+
54
+    /**
55
+     * Callback to invoke when item is pressed.
56
+     */
57
+    onPress: Function,
58
+
59
+    /**
60
+     * The ID of the participant.
21 61
      */
22
-    participant: Object
62
+    participantID: ?string
23 63
 };
24 64
 
25
-export const MeetingParticipantItem = ({ participant: p }: Props) => {
26
-    const dispatch = useDispatch();
27
-    const isAudioMuted = useSelector(getIsParticipantAudioMuted(p));
28
-    const isVideoMuted = useSelector(getIsParticipantVideoMuted(p));
29
-    const audioMediaState = useSelector(getParticipantAudioMediaState(p, isAudioMuted));
30
-    const openContextMenuDetails = useCallback(() => !p.local && dispatch(showContextMenuDetails(p), [ dispatch ]));
65
+const MeetingParticipantItem = (
66
+        {
67
+            _audioMediaState,
68
+            _displayName,
69
+            _isVideoMuted,
70
+            _local,
71
+            _participantID,
72
+            _raisedHand,
73
+            onPress
74
+        }: Props) => {
75
+    const showParticipantDetails = !_local && onPress;
31 76
 
32 77
     return (
33 78
         <ParticipantItem
34
-            audioMediaState = { audioMediaState }
79
+            audioMediaState = { _audioMediaState }
80
+            displayName = { _displayName }
35 81
             isKnockingParticipant = { false }
36
-            name = { p.name }
37
-            onPress = { openContextMenuDetails }
38
-            participant = { p }
39
-            videoMediaState = { isVideoMuted ? MEDIA_STATE.MUTED : MEDIA_STATE.UNMUTED } />
82
+            local = { _local }
83
+            onPress = { showParticipantDetails }
84
+            participantID = { _participantID }
85
+            raisedHand = { _raisedHand }
86
+            videoMediaState = { _isVideoMuted ? MEDIA_STATE.MUTED : MEDIA_STATE.UNMUTED } />
40 87
     );
41 88
 };
42 89
 
90
+/**
91
+ * Maps (parts of) the redux state to the associated props for this component.
92
+ *
93
+ * @param {Object} state - The Redux state.
94
+ * @param {Object} ownProps - The own props of the component.
95
+ * @private
96
+ * @returns {Props}
97
+ */
98
+function mapStateToProps(state, ownProps): Object {
99
+    const { participantID } = ownProps;
100
+    const participant = getParticipantByIdOrUndefined(state, participantID);
101
+    const _isAudioMuted = isParticipantAudioMuted(participant, state);
102
+    const isVideoMuted = isParticipantVideoMuted(participant, state);
103
+    const audioMediaState = getParticipantAudioMediaState(
104
+        participant, _isAudioMuted, state
105
+    );
106
+
107
+    return {
108
+        _audioMediaState: audioMediaState,
109
+        _displayName: getParticipantDisplayName(state, participant?.id),
110
+        _isAudioMuted,
111
+        _isVideoMuted: isVideoMuted,
112
+        _local: Boolean(participant?.local),
113
+        _participantID: participant?.id,
114
+        _raisedHand: Boolean(participant?.raisedHand)
115
+    };
116
+}
117
+
118
+
119
+export default translate(connect(mapStateToProps)(MeetingParticipantItem));
120
+
121
+

+ 30
- 11
react/features/participants-pane/components/native/MeetingParticipantList.js View File

@@ -7,25 +7,49 @@ import { Button } from 'react-native-paper';
7 7
 import { useDispatch, useSelector } from 'react-redux';
8 8
 
9 9
 import { Icon, IconInviteMore } from '../../../base/icons';
10
-import { getParticipants } from '../../../base/participants';
10
+import {
11
+    getLocalParticipant,
12
+    getParticipantCountWithFake,
13
+    getRemoteParticipants
14
+} from '../../../base/participants';
11 15
 import { doInvitePeople } from '../../../invite/actions.native';
16
+import { showContextMenuDetails } from '../../actions.native';
12 17
 import { shouldRenderInviteButton } from '../../functions';
13 18
 
14
-import { MeetingParticipantItem } from './MeetingParticipantItem';
19
+import MeetingParticipantItem from './MeetingParticipantItem';
15 20
 import styles from './styles';
16 21
 
17 22
 export const MeetingParticipantList = () => {
18 23
     const dispatch = useDispatch();
24
+    const items = [];
25
+    const localParticipant = useSelector(getLocalParticipant);
19 26
     const onInvite = useCallback(() => dispatch(doInvitePeople()), [ dispatch ]);
27
+    const participants = useSelector(getRemoteParticipants);
28
+    const participantsCount = useSelector(getParticipantCountWithFake);
20 29
     const showInviteButton = useSelector(shouldRenderInviteButton);
21
-    const participants = useSelector(getParticipants);
30
+
22 31
     const { t } = useTranslation();
23 32
 
33
+    // eslint-disable-next-line react/no-multi-comp
34
+    const renderParticipant = id => (
35
+        <MeetingParticipantItem
36
+            key = { id }
37
+            /* eslint-disable-next-line react/jsx-no-bind */
38
+            onPress = { () => dispatch(showContextMenuDetails(id)) }
39
+            participantID = { id } />
40
+    );
41
+
42
+    localParticipant && items.push(renderParticipant(localParticipant?.id));
43
+
44
+    participants.forEach(p => {
45
+        items.push(renderParticipant(p?.id));
46
+    });
47
+
24 48
     return (
25 49
         <View style = { styles.meetingList }>
26 50
             <Text style = { styles.meetingListDescription }>
27 51
                 {t('participantsPane.headings.participantsList',
28
-                    { count: participants.length })}
52
+                    { count: participantsCount })}
29 53
             </Text>
30 54
             {
31 55
                 showInviteButton
@@ -42,13 +66,8 @@ export const MeetingParticipantList = () => {
42 66
                     onPress = { onInvite }
43 67
                     style = { styles.inviteButton } />
44 68
             }
45
-            {
46
-                participants.map(p => (
47
-                    <MeetingParticipantItem
48
-                        key = { p.id }
49
-                        participant = { p } />)
50
-                )
51
-            }
69
+            { items }
52 70
         </View>
53 71
     );
54 72
 };
73
+

+ 22
- 13
react/features/participants-pane/components/native/ParticipantItem.js View File

@@ -5,10 +5,8 @@ import type { Node } from 'react';
5 5
 import { useTranslation } from 'react-i18next';
6 6
 import { TouchableOpacity, View } from 'react-native';
7 7
 import { Text } from 'react-native-paper';
8
-import { useSelector } from 'react-redux';
9 8
 
10 9
 import { Avatar } from '../../../base/avatar';
11
-import { getParticipantDisplayNameWithId } from '../../../base/participants';
12 10
 import { MEDIA_STATE, type MediaState, AudioStateIcons, VideoStateIcons } from '../../constants';
13 11
 
14 12
 import { RaisedHandIndicator } from './RaisedHandIndicator';
@@ -26,15 +24,20 @@ type Props = {
26 24
      */
27 25
     children?: Node,
28 26
 
27
+    /**
28
+     * The name of the participant. Used for showing lobby names.
29
+     */
30
+    displayName: string,
31
+
29 32
     /**
30 33
      * Is the participant waiting?
31 34
      */
32 35
     isKnockingParticipant: boolean,
33 36
 
34 37
     /**
35
-     * The name of the participant. Used for showing lobby names.
38
+     * True if the participant is local.
36 39
      */
37
-    name?: string,
40
+    local: boolean,
38 41
 
39 42
     /**
40 43
      * Callback to be invoked on pressing the participant item.
@@ -42,9 +45,14 @@ type Props = {
42 45
     onPress?: Function,
43 46
 
44 47
     /**
45
-     * Participant reference
48
+     * The ID of the participant.
49
+     */
50
+    participantID: string,
51
+
52
+    /**
53
+     * True if the participant have raised hand.
46 54
      */
47
-    participant: Object,
55
+    raisedHand: boolean,
48 56
 
49 57
     /**
50 58
      * Media state for video
@@ -59,15 +67,16 @@ type Props = {
59 67
  */
60 68
 function ParticipantItem({
61 69
     children,
70
+    displayName,
62 71
     isKnockingParticipant,
63
-    name,
72
+    local,
64 73
     onPress,
65
-    participant: p,
74
+    participantID,
75
+    raisedHand,
66 76
     audioMediaState = MEDIA_STATE.NONE,
67 77
     videoMediaState = MEDIA_STATE.NONE
68 78
 }: Props) {
69 79
 
70
-    const displayName = name || useSelector(getParticipantDisplayNameWithId(p.id));
71 80
     const { t } = useTranslation();
72 81
 
73 82
     return (
@@ -77,19 +86,19 @@ function ParticipantItem({
77 86
                 style = { styles.participantContent }>
78 87
                 <Avatar
79 88
                     className = 'participant-avatar'
80
-                    participantId = { p.id }
89
+                    participantId = { participantID }
81 90
                     size = { 32 } />
82 91
                 <View style = { styles.participantNameContainer }>
83 92
                     <Text style = { styles.participantName }>
84 93
                         { displayName }
85 94
                     </Text>
86
-                    { p.local ? <Text style = { styles.isLocal }>({t('chat.you')})</Text> : null }
95
+                    { local ? <Text style = { styles.isLocal }>({t('chat.you')})</Text> : null }
87 96
                 </View>
88 97
                 {
89 98
                     !isKnockingParticipant
90 99
                     && <>
91 100
                         {
92
-                            p.raisedHand && <RaisedHandIndicator />
101
+                            raisedHand && <RaisedHandIndicator />
93 102
                         }
94 103
                         <View style = { styles.participantStatesContainer }>
95 104
                             <View style = { styles.participantStateVideo }>{VideoStateIcons[videoMediaState]}</View>
@@ -98,7 +107,7 @@ function ParticipantItem({
98 107
                     </>
99 108
                 }
100 109
             </TouchableOpacity>
101
-            { !p.local && children }
110
+            { !local && children }
102 111
         </View>
103 112
     );
104 113
 }

+ 11
- 11
react/features/participants-pane/components/web/MeetingParticipantContextMenu.js View File

@@ -2,9 +2,9 @@
2 2
 
3 3
 import React, { Component } from 'react';
4 4
 
5
-import { isToolbarButtonEnabled } from '../../base/config/functions.web';
6
-import { openDialog } from '../../base/dialog';
7
-import { translate } from '../../base/i18n';
5
+import { isToolbarButtonEnabled } from '../../../base/config/functions.web';
6
+import { openDialog } from '../../../base/dialog';
7
+import { translate } from '../../../base/i18n';
8 8
 import {
9 9
     IconCloseCircle,
10 10
     IconCrown,
@@ -12,18 +12,18 @@ import {
12 12
     IconMicDisabled,
13 13
     IconMuteEveryoneElse,
14 14
     IconVideoOff
15
-} from '../../base/icons';
15
+} from '../../../base/icons';
16 16
 import {
17 17
     getParticipantByIdOrUndefined,
18 18
     isLocalParticipantModerator,
19 19
     isParticipantModerator
20
-} from '../../base/participants';
21
-import { connect } from '../../base/redux';
22
-import { isParticipantAudioMuted, isParticipantVideoMuted } from '../../base/tracks';
23
-import { openChat } from '../../chat/actions';
24
-import { GrantModeratorDialog, KickRemoteParticipantDialog, MuteEveryoneDialog } from '../../video-menu';
25
-import MuteRemoteParticipantsVideoDialog from '../../video-menu/components/web/MuteRemoteParticipantsVideoDialog';
26
-import { getComputedOuterHeight } from '../functions';
20
+} from '../../../base/participants';
21
+import { connect } from '../../../base/redux';
22
+import { isParticipantAudioMuted, isParticipantVideoMuted } from '../../../base/tracks';
23
+import { openChat } from '../../../chat/actions';
24
+import { GrantModeratorDialog, KickRemoteParticipantDialog, MuteEveryoneDialog } from '../../../video-menu';
25
+import MuteRemoteParticipantsVideoDialog from '../../../video-menu/components/web/MuteRemoteParticipantsVideoDialog';
26
+import { getComputedOuterHeight } from '../../functions';
27 27
 
28 28
 import {
29 29
     ContextMenu,

+ 6
- 6
react/features/participants-pane/components/web/MeetingParticipantItem.js View File

@@ -2,14 +2,14 @@
2 2
 
3 3
 import React from 'react';
4 4
 
5
-import { getParticipantByIdOrUndefined, getParticipantDisplayName } from '../../base/participants';
6
-import { connect } from '../../base/redux';
7
-import { isParticipantAudioMuted, isParticipantVideoMuted } from '../../base/tracks';
8
-import { ACTION_TRIGGER, MEDIA_STATE, type MediaState } from '../constants';
9
-import { getParticipantAudioMediaState, getQuickActionButtonType } from '../functions';
5
+import { getParticipantByIdOrUndefined, getParticipantDisplayName } from '../../../base/participants';
6
+import { connect } from '../../../base/redux';
7
+import { isParticipantAudioMuted, isParticipantVideoMuted } from '../../../base/tracks';
8
+import { ACTION_TRIGGER, MEDIA_STATE, type MediaState } from '../../constants';
9
+import { getParticipantAudioMediaState, getQuickActionButtonType } from '../../functions';
10
+import ParticipantQuickAction from '../ParticipantQuickAction';
10 11
 
11 12
 import ParticipantItem from './ParticipantItem';
12
-import ParticipantQuickAction from './ParticipantQuickAction';
13 13
 import { ParticipantActionEllipsis } from './styled';
14 14
 
15 15
 type Props = {

+ 4
- 4
react/features/participants-pane/components/web/MeetingParticipantList.js View File

@@ -4,14 +4,14 @@ import React, { useCallback, useRef, useState } from 'react';
4 4
 import { useTranslation } from 'react-i18next';
5 5
 import { useSelector, useDispatch } from 'react-redux';
6 6
 
7
-import { openDialog } from '../../base/dialog';
7
+import { openDialog } from '../../../base/dialog';
8 8
 import {
9 9
     getLocalParticipant,
10 10
     getParticipantCountWithFake,
11 11
     getRemoteParticipants
12
-} from '../../base/participants';
13
-import MuteRemoteParticipantDialog from '../../video-menu/components/web/MuteRemoteParticipantDialog';
14
-import { findStyledAncestor, shouldRenderInviteButton } from '../functions';
12
+} from '../../../base/participants';
13
+import MuteRemoteParticipantDialog from '../../../video-menu/components/web/MuteRemoteParticipantDialog';
14
+import { findStyledAncestor, shouldRenderInviteButton } from '../../functions';
15 15
 
16 16
 import { InviteButton } from './InviteButton';
17 17
 import MeetingParticipantContextMenu from './MeetingParticipantContextMenu';

+ 3
- 3
react/features/participants-pane/components/web/ParticipantItem.js View File

@@ -2,15 +2,15 @@
2 2
 
3 3
 import React, { type Node } from 'react';
4 4
 
5
-import { Avatar } from '../../base/avatar';
5
+import { Avatar } from '../../../base/avatar';
6 6
 import {
7 7
     Icon,
8 8
     IconCameraEmpty,
9 9
     IconCameraEmptyDisabled,
10 10
     IconMicrophoneEmpty,
11 11
     IconMicrophoneEmptySlash
12
-} from '../../base/icons';
13
-import { ACTION_TRIGGER, MEDIA_STATE, type ActionTrigger, type MediaState } from '../constants';
12
+} from '../../../base/icons';
13
+import { ACTION_TRIGGER, MEDIA_STATE, type ActionTrigger, type MediaState } from '../../constants';
14 14
 
15 15
 import { RaisedHandIndicator } from './RaisedHandIndicator';
16 16
 import {

+ 10
- 10
react/features/participants-pane/components/web/ParticipantsPane.js View File

@@ -3,19 +3,19 @@
3 3
 import React, { Component } from 'react';
4 4
 import { ThemeProvider } from 'styled-components';
5 5
 
6
-import { openDialog } from '../../base/dialog';
7
-import { translate } from '../../base/i18n';
6
+import { openDialog } from '../../../base/dialog';
7
+import { translate } from '../../../base/i18n';
8 8
 import {
9 9
     getParticipantCount,
10 10
     isLocalParticipantModerator
11
-} from '../../base/participants';
12
-import { connect } from '../../base/redux';
13
-import { MuteEveryoneDialog } from '../../video-menu/components/';
14
-import { close } from '../actions';
15
-import { classList, findStyledAncestor, getParticipantsPaneOpen } from '../functions';
16
-import theme from '../theme.json';
17
-
18
-import { FooterContextMenu } from './FooterContextMenu';
11
+} from '../../../base/participants';
12
+import { connect } from '../../../base/redux';
13
+import { MuteEveryoneDialog } from '../../../video-menu/components/';
14
+import { close } from '../../actions';
15
+import { classList, findStyledAncestor, getParticipantsPaneOpen } from '../../functions';
16
+import theme from '../../theme.json';
17
+import { FooterContextMenu } from '../FooterContextMenu';
18
+
19 19
 import { LobbyParticipantList } from './LobbyParticipantList';
20 20
 import { MeetingParticipantList } from './MeetingParticipantList';
21 21
 import {

+ 1
- 1
react/features/participants-pane/components/web/index.js View File

@@ -3,5 +3,5 @@ export * from './LobbyParticipantItem';
3 3
 export * from './LobbyParticipantList';
4 4
 export * from './MeetingParticipantList';
5 5
 export { default as ParticipantsPane } from './ParticipantsPane';
6
-export * from './ParticipantsPaneButton';
6
+export * from '../ParticipantsPaneButton';
7 7
 export * from './RaisedHandIndicator';

+ 14
- 7
react/features/video-menu/components/native/VolumeSlider.js View File

@@ -5,8 +5,8 @@ import React, { PureComponent } from 'react';
5 5
 import { Slider, View } from 'react-native';
6 6
 import { withTheme } from 'react-native-paper';
7 7
 
8
-import { translate } from '../../../base/i18n';
9 8
 import { Icon, IconVolumeEmpty } from '../../../base/icons';
9
+import { getParticipantByIdOrUndefined } from '../../../base/participants';
10 10
 import { connect } from '../../../base/redux';
11 11
 import { setVolume } from '../../../participants-pane/actions.native';
12 12
 import { VOLUME_SLIDER_SCALE } from '../../constants';
@@ -19,6 +19,11 @@ import styles from './styles';
19 19
  */
20 20
 type Props = {
21 21
 
22
+    /**
23
+     * Participant reference
24
+     */
25
+    _participant: Object,
26
+
22 27
     /**
23 28
      * Whether the participant enters the conference silent.
24 29
      */
@@ -35,9 +40,9 @@ type Props = {
35 40
     dispatch: Function,
36 41
 
37 42
     /**
38
-     * Participant reference
43
+     * The ID of the participant.
39 44
      */
40
-    participant: Object,
45
+    participantID: string,
41 46
 
42 47
     /**
43 48
      * Theme used for styles.
@@ -126,8 +131,8 @@ class VolumeSlider extends PureComponent<Props, State> {
126 131
      * @returns {void}
127 132
      */
128 133
     _onVolumeChange(volumeLevel) {
129
-        const { dispatch, participant } = this.props;
130
-        const { id } = participant;
134
+        const { dispatch, _participant } = this.props;
135
+        const { id } = _participant;
131 136
 
132 137
         dispatch(setVolume(id, volumeLevel));
133 138
     }
@@ -142,16 +147,18 @@ class VolumeSlider extends PureComponent<Props, State> {
142 147
  * @returns {Props}
143 148
  */
144 149
 function mapStateToProps(state, ownProps): Object {
145
-    const { participant } = ownProps;
150
+    const { participantID } = ownProps;
151
+    const participant = getParticipantByIdOrUndefined(state, participantID);
146 152
     const { id, local } = participant;
147 153
     const { participantsVolume } = state['features/participants-pane'];
148 154
     const { startSilent } = state['features/base/config'];
149 155
 
150 156
     return {
157
+        _participant: participant,
151 158
         _startSilent: Boolean(startSilent),
152 159
         _volume: local ? undefined : participantsVolume[id]
153 160
     };
154 161
 }
155 162
 
156
-export default translate(connect(mapStateToProps)(withTheme(VolumeSlider)));
163
+export default connect(mapStateToProps)(withTheme(VolumeSlider));
157 164
 

Loading…
Cancel
Save