Преглед изворни кода

fix(rn,ParticipantPane) optimize

Use a FlatList to avoid loading all participants at once.
master
Hristo Terezov пре 4 година
родитељ
комит
dfd53f4041

+ 7
- 7
react/features/participants-pane/actions.native.js Прегледај датотеку

@@ -29,28 +29,28 @@ export function showContextMenuReject(participant: Object) {
29 29
  * @param {string} participantID - The selected meeting participant id.
30 30
  * @returns {Function}
31 31
  */
32
-export function showConnectionStatus(participantID: String) {
32
+export function showConnectionStatus(participantID: string) {
33 33
     return openDialog(ConnectionStatusComponent, { participantID });
34 34
 }
35 35
 
36 36
 /**
37 37
  * Displays the context menu for the selected meeting participant.
38 38
  *
39
- * @param {Object} participant - The selected meeting participant.
39
+ * @param {string} participantId - The ID of the selected meeting participant.
40 40
  * @returns {Function}
41 41
  */
42
-export function showContextMenuDetails(participant: Object) {
43
-    return openDialog(RemoteVideoMenu, { participant });
42
+export function showContextMenuDetails(participantId: string) {
43
+    return openDialog(RemoteVideoMenu, { participantId });
44 44
 }
45 45
 
46 46
 /**
47 47
  * Displays the shared video menu.
48 48
  *
49
- * @param {Object} participant - The selected meeting participant.
49
+ * @param {string} participantId - The ID of the selected meeting participant.
50 50
  * @returns {Function}
51 51
  */
52
-export function showSharedVideoMenu(participant: Object) {
53
-    return openDialog(SharedVideoMenu, { participant });
52
+export function showSharedVideoMenu(participantId: string) {
53
+    return openDialog(SharedVideoMenu, { participantId });
54 54
 }
55 55
 
56 56
 /**

+ 10
- 8
react/features/participants-pane/components/native/LobbyParticipantList.js Прегледај датотеку

@@ -2,7 +2,7 @@
2 2
 
3 3
 import React, { useCallback } from 'react';
4 4
 import { useTranslation } from 'react-i18next';
5
-import { Text, View } from 'react-native';
5
+import { ScrollView, Text, View } from 'react-native';
6 6
 import { Button, withTheme } from 'react-native-paper';
7 7
 import { useDispatch, useSelector } from 'react-redux';
8 8
 
@@ -54,13 +54,15 @@ const LobbyParticipantList = ({ theme }: Props) => {
54 54
                     )
55 55
                 }
56 56
             </View>
57
-            {
58
-                participants.map(p => (
59
-                    <LobbyParticipantItem
60
-                        key = { p.id }
61
-                        participant = { p } />)
62
-                )
63
-            }
57
+            <ScrollView>
58
+                {
59
+                    participants.map(p => (
60
+                        <LobbyParticipantItem
61
+                            key = { p.id }
62
+                            participant = { p } />)
63
+                    )
64
+                }
65
+            </ScrollView>
64 66
         </View>
65 67
     );
66 68
 };

+ 88
- 29
react/features/participants-pane/components/native/MeetingParticipantItem.js Прегледај датотеку

@@ -1,9 +1,10 @@
1 1
 // @flow
2 2
 
3
-import React from 'react';
3
+import React, { PureComponent } from 'react';
4 4
 
5 5
 import { translate } from '../../../base/i18n';
6 6
 import {
7
+    getLocalParticipant,
7 8
     getParticipantByIdOrUndefined,
8 9
     getParticipantDisplayName
9 10
 } from '../../../base/participants';
@@ -12,6 +13,7 @@ import {
12 13
     isParticipantAudioMuted,
13 14
     isParticipantVideoMuted
14 15
 } from '../../../base/tracks';
16
+import { showConnectionStatus, showContextMenuDetails, showSharedVideoMenu } from '../../actions.native';
15 17
 import { MEDIA_STATE } from '../../constants';
16 18
 import type { MediaState } from '../../constants';
17 19
 import { getParticipantAudioMediaState } from '../../functions';
@@ -31,6 +33,11 @@ type Props = {
31 33
      */
32 34
     _displayName: string,
33 35
 
36
+    /**
37
+     * True if the participant is fake.
38
+     */
39
+    _isFakeParticipant: boolean,
40
+
34 41
     /**
35 42
      * True if the participant is video muted.
36 43
      */
@@ -41,6 +48,11 @@ type Props = {
41 48
      */
42 49
     _local: boolean,
43 50
 
51
+    /**
52
+     * Shared video local participant owner.
53
+     */
54
+    _localVideoOwner: boolean,
55
+
44 56
     /**
45 57
      * The participant ID.
46 58
      */
@@ -52,9 +64,9 @@ type Props = {
52 64
     _raisedHand: boolean,
53 65
 
54 66
     /**
55
-     * Callback to invoke when item is pressed.
67
+     * The redux dispatch function.
56 68
      */
57
-    onPress: Function,
69
+    dispatch: Function,
58 70
 
59 71
     /**
60 72
      * The ID of the participant.
@@ -64,30 +76,75 @@ type Props = {
64 76
 
65 77
 /**
66 78
  * Implements the MeetingParticipantItem component.
67
- *
68
- * @param {Props} props - The props of the component.
69
- * @returns {ReactElement}
70 79
  */
71
-function MeetingParticipantItem({
72
-    _audioMediaState,
73
-    _displayName,
74
-    _isVideoMuted,
75
-    _local,
76
-    _participantID,
77
-    _raisedHand,
78
-    onPress
79
-}: Props) {
80
-    return (
81
-        <ParticipantItem
82
-            audioMediaState = { _audioMediaState }
83
-            displayName = { _displayName }
84
-            isKnockingParticipant = { false }
85
-            local = { _local }
86
-            onPress = { onPress }
87
-            participantID = { _participantID }
88
-            raisedHand = { _raisedHand }
89
-            videoMediaState = { _isVideoMuted ? MEDIA_STATE.MUTED : MEDIA_STATE.UNMUTED } />
90
-    );
80
+class MeetingParticipantItem extends PureComponent<Props> {
81
+
82
+    /**
83
+     * Creates new MeetingParticipantItem instance.
84
+     *
85
+     * @param {Props} props - The props of the component.
86
+     */
87
+    constructor(props: Props) {
88
+        super(props);
89
+
90
+        this._onPress = this._onPress.bind(this);
91
+    }
92
+
93
+    _onPress: () => void;
94
+
95
+    /**
96
+     * Handles MeetingParticipantItem press events.
97
+     *
98
+     * @returns {void}
99
+     */
100
+    _onPress() {
101
+        const {
102
+            _local,
103
+            _localVideoOwner,
104
+            _isFakeParticipant,
105
+            _participantID,
106
+            dispatch
107
+        } = this.props;
108
+
109
+        if (_isFakeParticipant && _localVideoOwner) {
110
+            dispatch(showSharedVideoMenu(_participantID));
111
+        } else if (!_isFakeParticipant) {
112
+            if (_local) {
113
+                dispatch(showConnectionStatus(_participantID));
114
+            } else {
115
+                dispatch(showContextMenuDetails(_participantID));
116
+            }
117
+        } // else no-op
118
+    }
119
+
120
+    /**
121
+     * Implements React's {@link Component#render()}.
122
+     *
123
+     * @inheritdoc
124
+     * @returns {ReactElement}
125
+     */
126
+    render() {
127
+        const {
128
+            _audioMediaState,
129
+            _displayName,
130
+            _isVideoMuted,
131
+            _local,
132
+            _participantID,
133
+            _raisedHand
134
+        } = this.props;
135
+
136
+        return (
137
+            <ParticipantItem
138
+                audioMediaState = { _audioMediaState }
139
+                displayName = { _displayName }
140
+                isKnockingParticipant = { false }
141
+                local = { _local }
142
+                onPress = { this._onPress }
143
+                participantID = { _participantID }
144
+                raisedHand = { _raisedHand }
145
+                videoMediaState = { _isVideoMuted ? MEDIA_STATE.MUTED : MEDIA_STATE.UNMUTED } />
146
+        );
147
+    }
91 148
 }
92 149
 
93 150
 /**
@@ -100,19 +157,21 @@ function MeetingParticipantItem({
100 157
  */
101 158
 function mapStateToProps(state, ownProps): Object {
102 159
     const { participantID } = ownProps;
160
+    const { ownerId } = state['features/shared-video'];
161
+    const localParticipantId = getLocalParticipant(state).id;
103 162
     const participant = getParticipantByIdOrUndefined(state, participantID);
104 163
     const _isAudioMuted = isParticipantAudioMuted(participant, state);
105 164
     const isVideoMuted = isParticipantVideoMuted(participant, state);
106
-    const audioMediaState = getParticipantAudioMediaState(
107
-        participant, _isAudioMuted, state
108
-    );
165
+    const audioMediaState = getParticipantAudioMediaState(participant, _isAudioMuted, state);
109 166
 
110 167
     return {
111 168
         _audioMediaState: audioMediaState,
112 169
         _displayName: getParticipantDisplayName(state, participant?.id),
113 170
         _isAudioMuted,
171
+        _isFakeParticipant: Boolean(participant?.isFakeParticipant),
114 172
         _isVideoMuted: isVideoMuted,
115 173
         _local: Boolean(participant?.local),
174
+        _localVideoOwner: Boolean(ownerId === localParticipantId),
116 175
         _participantID: participant?.id,
117 176
         _raisedHand: Boolean(participant?.raisedHand)
118 177
     };

+ 148
- 79
react/features/participants-pane/components/native/MeetingParticipantList.js Прегледај датотеку

@@ -1,25 +1,14 @@
1 1
 // @flow
2 2
 
3
-import React, { useCallback } from 'react';
4
-import { useTranslation } from 'react-i18next';
5
-import { Text, View } from 'react-native';
3
+import React, { PureComponent } from 'react';
4
+import { FlatList, Text, View } from 'react-native';
6 5
 import { Button } from 'react-native-paper';
7
-import { useDispatch, useSelector } from 'react-redux';
8
-
9 6
 
7
+import { translate } from '../../../base/i18n';
10 8
 import { Icon, IconInviteMore } from '../../../base/icons';
11
-import {
12
-    getLocalParticipant,
13
-    getParticipantCountWithFake,
14
-    getSortedParticipants
15
-} from '../../../base/participants';
9
+import { getLocalParticipant, getParticipantCountWithFake } from '../../../base/participants';
16 10
 import { connect } from '../../../base/redux';
17 11
 import { doInvitePeople } from '../../../invite/actions.native';
18
-import {
19
-    showConnectionStatus,
20
-    showContextMenuDetails,
21
-    showSharedVideoMenu
22
-} from '../../actions.native';
23 12
 import { shouldRenderInviteButton } from '../../functions';
24 13
 
25 14
 import MeetingParticipantItem from './MeetingParticipantItem';
@@ -28,74 +17,150 @@ import styles from './styles';
28 17
 type Props = {
29 18
 
30 19
     /**
31
-     * Shared video local participant owner.
20
+     * The ID of the local participant.
21
+     */
22
+    _localParticipantId: string,
23
+
24
+    /**
25
+     * The number of participants in the conference.
26
+     */
27
+    _participantsCount: number,
28
+
29
+    /**
30
+     * Whether or not to show the invite button.
31
+     */
32
+    _showInviteButton: boolean,
33
+
34
+    /**
35
+     * The remote participants.
32 36
      */
33
-    _localVideoOwner: boolean
37
+    _sortedRemoteParticipants: Map<string, string>,
38
+
39
+    /**
40
+     * The redux dispatch function.
41
+     */
42
+    dispatch: Function,
43
+
44
+    /**
45
+     * Translation function.
46
+     */
47
+    t: Function
34 48
 }
35 49
 
36
-const MeetingParticipantList = ({ _localVideoOwner }: Props) => {
37
-    const dispatch = useDispatch();
38
-    const onInvite = useCallback(() => dispatch(doInvitePeople()), [ dispatch ]);
39
-    const participantsCount = useSelector(getParticipantCountWithFake);
40
-    const sortedParticipants = useSelector(getSortedParticipants);
41
-    const showInviteButton = useSelector(shouldRenderInviteButton);
42
-    const { t } = useTranslation();
43
-
44
-    // eslint-disable-next-line react/no-multi-comp
45
-    const renderParticipant = p => {
46
-        if (p.isFakeParticipant) {
47
-            if (_localVideoOwner) {
48
-                return (
49
-                    <MeetingParticipantItem
50
-                        key = { p.id }
51
-                        /* eslint-disable-next-line react/jsx-no-bind,no-confusing-arrow */
52
-                        onPress = { () => dispatch(showSharedVideoMenu(p)) }
53
-                        participantID = { p.id } />
54
-                );
55
-            }
56
-
57
-            return (
58
-                <MeetingParticipantItem
59
-                    key = { p.id }
60
-                    participantID = { p.id } />
61
-            );
62
-        }
50
+/**
51
+ *  The meeting participant list component.
52
+ */
53
+class MeetingParticipantList extends PureComponent<Props> {
54
+
55
+    /**
56
+     * Creates new MeetingParticipantList instance.
57
+     *
58
+     * @param {Props} props - The props of the component.
59
+     */
60
+    constructor(props: Props) {
61
+        super(props);
62
+
63
+        this._keyExtractor = this._keyExtractor.bind(this);
64
+        this._onInvite = this._onInvite.bind(this);
65
+        this._renderParticipant = this._renderParticipant.bind(this);
66
+    }
67
+
68
+    _keyExtractor: Function;
63 69
 
70
+    /**
71
+     * Returns a key for a passed item of the list.
72
+     *
73
+     * @param {string} item - The user ID.
74
+     * @returns {string} - The user ID.
75
+     */
76
+    _keyExtractor(item) {
77
+        return item;
78
+    }
79
+
80
+    _onInvite: () => void;
81
+
82
+    /**
83
+     * Handles ivite button presses.
84
+     *
85
+     * @returns {void}
86
+     */
87
+    _onInvite() {
88
+        this.props.dispatch(doInvitePeople());
89
+    }
90
+
91
+    /**
92
+     * Renders the "invite more" icon.
93
+     *
94
+     * @returns {ReactElement}
95
+     */
96
+    _renderInviteMoreIcon() {
97
+        return (
98
+            <Icon
99
+                size = { 20 }
100
+                src = { IconInviteMore } />
101
+        );
102
+    }
103
+
104
+    _renderParticipant: Object => Object;
105
+
106
+    /**
107
+     * Renders a participant.
108
+     *
109
+     * @param {Object} flatListItem - Information about the item to be rendered.
110
+     * @param {string} flatListItem.item - The ID of the participant.
111
+     * @returns {ReactElement}
112
+     */
113
+    _renderParticipant({ item/* , index, separators */ }) {
64 114
         return (
65 115
             <MeetingParticipantItem
66
-                key = { p.id }
67
-                /* eslint-disable-next-line react/jsx-no-bind,no-confusing-arrow */
68
-                onPress = { () => p.local
69
-                    ? dispatch(showConnectionStatus(p.id)) : dispatch(showContextMenuDetails(p)) }
70
-                participantID = { p.id } />
116
+                key = { item }
117
+                participantID = { item } />
71 118
         );
72
-    };
119
+    }
73 120
 
74
-    return (
75
-        <View style = { styles.meetingList }>
76
-            <Text style = { styles.meetingListDescription }>
77
-                {t('participantsPane.headings.participantsList',
78
-                    { count: participantsCount })}
79
-            </Text>
80
-            {
81
-                showInviteButton
82
-                && <Button
83
-                    children = { t('participantsPane.actions.invite') }
84
-                    /* eslint-disable-next-line react/jsx-no-bind */
85
-                    icon = { () =>
86
-                        (<Icon
87
-                            size = { 20 }
88
-                            src = { IconInviteMore } />)
89
-                    }
90
-                    labelStyle = { styles.inviteLabel }
91
-                    mode = 'contained'
92
-                    onPress = { onInvite }
93
-                    style = { styles.inviteButton } />
94
-            }
95
-            { sortedParticipants.map(renderParticipant) }
96
-        </View>
97
-    );
98
-};
121
+    /**
122
+     * Implements React's {@link Component#render()}.
123
+     *
124
+     * @inheritdoc
125
+     * @returns {ReactElement}
126
+     */
127
+    render() {
128
+        const {
129
+            _localParticipantId,
130
+            _participantsCount,
131
+            _showInviteButton,
132
+            _sortedRemoteParticipants,
133
+            t
134
+        } = this.props;
135
+
136
+        return (
137
+            <View style = { styles.meetingList }>
138
+                <Text style = { styles.meetingListDescription }>
139
+                    {t('participantsPane.headings.participantsList',
140
+                        { count: _participantsCount })}
141
+                </Text>
142
+                {
143
+                    _showInviteButton
144
+                    && <Button
145
+                        children = { t('participantsPane.actions.invite') }
146
+                        icon = { this._renderInviteMoreIcon }
147
+                        labelStyle = { styles.inviteLabel }
148
+                        mode = 'contained'
149
+                        onPress = { this._onInvite }
150
+                        style = { styles.inviteButton } />
151
+                }
152
+                <FlatList
153
+                    bounces = { false }
154
+                    data = { [ _localParticipantId, ..._sortedRemoteParticipants ] }
155
+                    horizontal = { false }
156
+                    keyExtractor = { this._keyExtractor }
157
+                    renderItem = { this._renderParticipant }
158
+                    showsHorizontalScrollIndicator = { false }
159
+                    windowSize = { 2 } />
160
+            </View>
161
+        );
162
+    }
163
+}
99 164
 
100 165
 /**
101 166
  * Maps (parts of) the redux state to the associated props for this component.
@@ -105,12 +170,16 @@ const MeetingParticipantList = ({ _localVideoOwner }: Props) => {
105 170
  * @returns {Props}
106 171
  */
107 172
 function _mapStateToProps(state): Object {
108
-    const { ownerId } = state['features/shared-video'];
109
-    const localParticipantId = getLocalParticipant(state).id;
173
+    const _participantsCount = getParticipantCountWithFake(state);
174
+    const { remoteParticipants } = state['features/filmstrip'];
175
+    const _showInviteButton = shouldRenderInviteButton(state);
110 176
 
111 177
     return {
112
-        _localVideoOwner: Boolean(ownerId === localParticipantId)
178
+        _participantsCount,
179
+        _showInviteButton,
180
+        _sortedRemoteParticipants: remoteParticipants,
181
+        _localParticipantId: getLocalParticipant(state)?.id
113 182
     };
114 183
 }
115 184
 
116
-export default connect(_mapStateToProps)(MeetingParticipantList);
185
+export default translate(connect(_mapStateToProps)(MeetingParticipantList));

+ 3
- 5
react/features/participants-pane/components/native/ParticipantsPane.js Прегледај датотеку

@@ -2,7 +2,7 @@
2 2
 
3 3
 import React, { useCallback } from 'react';
4 4
 import { useTranslation } from 'react-i18next';
5
-import { ScrollView, View } from 'react-native';
5
+import { View } from 'react-native';
6 6
 import { Button } from 'react-native-paper';
7 7
 import { useDispatch, useSelector } from 'react-redux';
8 8
 
@@ -45,10 +45,8 @@ const ParticipantsPane = () => {
45 45
             }}
46 46
             onClose = { closePane }
47 47
             style = { styles.participantsPane }>
48
-            <ScrollView>
49
-                <LobbyParticipantList />
50
-                <MeetingParticipantList />
51
-            </ScrollView>
48
+            <LobbyParticipantList />
49
+            <MeetingParticipantList />
52 50
             {
53 51
                 isLocalModerator
54 52
                 && <View style = { styles.footer }>

Loading…
Откажи
Сачувај