Performance improvements on Home to Channel (#6106)

This commit is contained in:
Daniel Espino García 2022-03-31 10:01:22 +02:00 committed by GitHub
parent 9069048aa7
commit 328f029a93
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 117 additions and 46 deletions

View file

@ -152,7 +152,9 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
};
});
const emptyList: UserProfile[] = [];
const emptyProfileList: UserProfile[] = [];
const empytSectionList: UserMentionSections = [];
const emptyGroupList: Group[] = [];
const AtMention = ({
channelId,
@ -170,20 +172,20 @@ const AtMention = ({
const theme = useTheme();
const style = getStyleFromTheme(theme);
const [sections, setSections] = useState<UserMentionSections>([]);
const [usersInChannel, setUsersInChannel] = useState<UserProfile[]>([]);
const [usersOutOfChannel, setUsersOutOfChannel] = useState<UserProfile[]>([]);
const [groups, setGroups] = useState<Group[]>([]);
const [sections, setSections] = useState<UserMentionSections>(empytSectionList);
const [usersInChannel, setUsersInChannel] = useState<UserProfile[]>(emptyProfileList);
const [usersOutOfChannel, setUsersOutOfChannel] = useState<UserProfile[]>(emptyProfileList);
const [groups, setGroups] = useState<Group[]>(emptyGroupList);
const [loading, setLoading] = useState(false);
const [noResultsTerm, setNoResultsTerm] = useState<string|null>(null);
const [localCursorPosition, setLocalCursorPosition] = useState(cursorPosition); // To avoid errors due to delay between value changes and cursor position changes.
const runSearch = useMemo(() => debounce(async (sUrl: string, term: string, cId?: string) => {
setLoading(true);
const {users: receivedUsers, error} = await searchUsers(sUrl, term, cId);
if (!error) {
setUsersInChannel(receivedUsers!.users);
setUsersOutOfChannel(receivedUsers!.out_of_channel || emptyList);
const {users: receivedUsers} = await searchUsers(sUrl, term, cId);
if (receivedUsers) {
setUsersInChannel(receivedUsers.users.length ? receivedUsers.users : emptyProfileList);
setUsersOutOfChannel(receivedUsers.out_of_channel?.length ? receivedUsers.out_of_channel : emptyProfileList);
}
setLoading(false);
}, 200), []);
@ -195,9 +197,9 @@ const AtMention = ({
const matchTerm = getMatchTermForAtMention(value.substring(0, localCursorPosition), isSearch);
const resetState = () => {
setUsersInChannel(emptyList);
setUsersOutOfChannel(emptyList);
setSections([]);
setUsersInChannel(emptyProfileList);
setUsersOutOfChannel(emptyProfileList);
setSections(empytSectionList);
runSearch.cancel();
};
@ -222,7 +224,7 @@ const AtMention = ({
onShowingChange(false);
setNoResultsTerm(mention);
setSections([]);
setSections(empytSectionList);
}, [value, localCursorPosition, isSearch]);
const renderSpecialMentions = useCallback((item: SpecialMention) => {
@ -286,12 +288,12 @@ const AtMention = ({
useEffect(() => {
if (useGroupMentions) {
getGroupsForAutocomplete(serverUrl, channelId || '').then((res) => {
setGroups(res);
setGroups(res.length ? res : emptyGroupList);
}).catch(() => {
setGroups([]);
setGroups(emptyGroupList);
});
} else {
setGroups([]);
setGroups(emptyGroupList);
}
}, [channelId, useGroupMentions]);
@ -318,7 +320,7 @@ const AtMention = ({
if (!loading && !nSections && noResultsTerm == null) {
setNoResultsTerm(matchTerm);
}
setSections(newSections);
setSections(nSections ? newSections : empytSectionList);
onShowingChange(Boolean(nSections));
}, [usersInChannel, usersOutOfChannel, teamMembers, groups, loading]);

View file

@ -161,6 +161,9 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
};
});
const emptySections: Array<SectionListData<Channel>> = [];
const emptyChannels: Channel[] = [];
const ChannelMention = ({
cursorPosition,
isSearch,
@ -175,8 +178,8 @@ const ChannelMention = ({
const theme = useTheme();
const style = getStyleFromTheme(theme);
const [sections, setSections] = useState<Array<SectionListData<Channel>>>([]);
const [channels, setChannels] = useState<Channel[]>([]);
const [sections, setSections] = useState<Array<SectionListData<Channel>>>(emptySections);
const [channels, setChannels] = useState<Channel[]>(emptyChannels);
const [loading, setLoading] = useState(false);
const [noResultsTerm, setNoResultsTerm] = useState<string|null>(null);
const [localCursorPosition, setLocalCursorPosition] = useState(cursorPosition); // To avoid errors due to delay between value changes and cursor position changes.
@ -187,17 +190,17 @@ const ChannelMention = ({
const runSearch = useMemo(() => debounce(async (sUrl: string, term: string) => {
setLoading(true);
const {channels: receivedChannels, error} = await searchChannels(sUrl, term);
if (!error) {
setChannels(receivedChannels!);
const {channels: receivedChannels} = await searchChannels(sUrl, term);
if (receivedChannels) {
setChannels(receivedChannels.length ? receivedChannels : emptyChannels);
}
setLoading(false);
}, 200), []);
const matchTerm = getMatchTermForChannelMention(value.substring(0, localCursorPosition), isSearch);
const resetState = () => {
setChannels([]);
setSections([]);
setChannels(emptyChannels);
setSections(emptySections);
runSearch.cancel();
};
@ -235,7 +238,7 @@ const ChannelMention = ({
onShowingChange(false);
setNoResultsTerm(mention);
setSections([]);
setSections(emptySections);
}, [value, localCursorPosition, isSearch]);
const renderItem = useCallback(({item}) => {
@ -286,7 +289,7 @@ const ChannelMention = ({
if (!loading && !nSections && noResultsTerm == null) {
setNoResultsTerm(matchTerm);
}
setSections(newSections);
setSections(newSections.length ? newSections : emptySections);
onShowingChange(Boolean(nSections));
}, [channels, myMembers, loading]);

View file

@ -208,5 +208,5 @@ const Header = ({
);
};
export default Header;
export default React.memo(Header);

View file

@ -3,6 +3,7 @@
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
import withObservables from '@nozbe/with-observables';
import React from 'react';
import {combineLatest, of as of$} from 'rxjs';
import {switchMap} from 'rxjs/operators';
@ -42,4 +43,4 @@ const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
};
});
export default withDatabase(enhanced(DraftHandler));
export default React.memo(withDatabase(enhanced(DraftHandler)));

View file

@ -3,6 +3,7 @@
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
import withObservables from '@nozbe/with-observables';
import React from 'react';
import {combineLatest, of as of$, from as from$} from 'rxjs';
import {switchMap} from 'rxjs/operators';
@ -85,4 +86,4 @@ const enhanced = withObservables([], (ownProps: WithDatabaseArgs & OwnProps) =>
};
});
export default withDatabase(enhanced(PostDraft));
export default React.memo(withDatabase(enhanced(PostDraft)));

View file

@ -3,6 +3,7 @@
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
import withObservables from '@nozbe/with-observables';
import React from 'react';
import {of as of$} from 'rxjs';
import {switchMap} from 'rxjs/operators';
@ -59,4 +60,4 @@ const enhanced = withObservables([], ({database, channelId, rootId}: WithDatabas
};
});
export default withDatabase(enhanced(PostInput));
export default React.memo(withDatabase(enhanced(PostInput)));

View file

@ -3,6 +3,7 @@
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
import withObservables from '@nozbe/with-observables';
import React from 'react';
import {combineLatest, of as of$} from 'rxjs';
import {switchMap} from 'rxjs/operators';
@ -35,4 +36,4 @@ const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
};
});
export default withDatabase(enhanced(QuickActions));
export default React.memo(withDatabase(enhanced(QuickActions)));

View file

@ -41,7 +41,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
};
});
export default function Typing({
function Typing({
channelId,
rootId,
}: Props) {
@ -155,3 +155,5 @@ export default function Typing({
</Animated.View>
);
}
export default React.memo(Typing);

View file

@ -68,7 +68,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
};
});
export default function Uploads({
function Uploads({
currentUserId,
files,
uploadFileError,
@ -174,3 +174,5 @@ export default function Uploads({
</GalleryInit>
);
}
export default React.memo(Uploads);

View file

@ -3,6 +3,7 @@
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
import withObservables from '@nozbe/with-observables';
import React from 'react';
import {combineLatest, from as from$, of as of$} from 'rxjs';
import {map, switchMap} from 'rxjs/operators';
@ -54,4 +55,4 @@ const withCombinedPosts = withObservables(['postId'], ({database, postId}: WithD
};
});
export default withDatabase(withCombinedPosts(CombinedUserActivity));
export default React.memo(withDatabase(withCombinedPosts(CombinedUserActivity)));

View file

@ -68,6 +68,8 @@ const Reaction = ({count, emojiName, highlight, onPress, onLongPress, theme}: Re
onPress(emojiName, highlight);
}, [highlight]);
const fontStyle = useMemo(() => [styles.count, (highlight && styles.countHighlight)], [styles, highlight]);
return (
<TouchableOpacity
onPress={handlePress}
@ -86,7 +88,7 @@ const Reaction = ({count, emojiName, highlight, onPress, onLongPress, theme}: Re
<View style={styles.countContainer}>
<AnimatedNumbers
includeComma={false}
fontStyle={[styles.count, (highlight && styles.countHighlight)]}
fontStyle={fontStyle}
animateToNumber={count}
animationDuration={450}
/>
@ -95,4 +97,4 @@ const Reaction = ({count, emojiName, highlight, onPress, onLongPress, theme}: Re
);
};
export default Reaction;
export default React.memo(Reaction);

View file

@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useCallback, useEffect, useRef, useState} from 'react';
import React, {useCallback, useRef, useState} from 'react';
import {useIntl} from 'react-intl';
import {Keyboard, TouchableOpacity, View} from 'react-native';
@ -11,6 +11,7 @@ import {Screens} from '@constants';
import {MAX_ALLOWED_REACTIONS} from '@constants/emoji';
import {useServerUrl} from '@context/server';
import {useIsTablet} from '@hooks/device';
import useDidUpdate from '@hooks/did_update';
import {bottomSheetModalOptions, showModal, showModalOverCurrentContext} from '@screens/navigation';
import {getEmojiFirstAlias} from '@utils/emoji/helpers';
import {preventDoubleTap} from '@utils/tap';
@ -66,7 +67,7 @@ const Reactions = ({currentUserId, canAddReaction, canRemoveReaction, disabled,
const [sortedReactions, setSortedReactions] = useState(new Set(reactions.map((r) => getEmojiFirstAlias(r.emojiName))));
const styles = getStyleSheet(theme);
useEffect(() => {
useDidUpdate(() => {
// This helps keep the reactions in the same position at all times until unmounted
const rs = reactions.map((r) => getEmojiFirstAlias(r.emojiName));
const sorted = new Set([...sortedReactions]);

View file

@ -3,6 +3,7 @@
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
import withObservables from '@nozbe/with-observables';
import React from 'react';
import {from as from$, of as of$} from 'rxjs';
import {switchMap} from 'rxjs/operators';
@ -141,4 +142,4 @@ const withPost = withObservables(
};
});
export default withDatabase(withSystem(withPost(Post)));
export default React.memo(withDatabase(withSystem(withPost(Post))));

View file

@ -4,6 +4,7 @@
import {Q} from '@nozbe/watermelondb';
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
import withObservables from '@nozbe/with-observables';
import React from 'react';
import {AppStateStatus} from 'react-native';
import {of as of$} from 'rxjs';
import {switchMap} from 'rxjs/operators';
@ -47,4 +48,4 @@ const enhanced = withObservables(['channelId', 'forceQueryAfterAppState'], ({dat
};
});
export default withDatabase(enhanced(ChannelPostList));
export default React.memo(withDatabase(enhanced(ChannelPostList)));

63
package-lock.json generated
View file

@ -154,6 +154,7 @@
"mock-async-storage": "2.2.0",
"nock": "13.2.4",
"patch-package": "6.4.7",
"react-devtools-core": "4.24.3",
"react-native-svg-transformer": "1.0.0",
"react-test-renderer": "17.0.2",
"tough-cookie": "4.0.0",
@ -19518,9 +19519,10 @@
}
},
"node_modules/react-devtools-core": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-4.19.1.tgz",
"integrity": "sha512-2wJiGffPWK0KggBjVwnTaAk+Z3MSxKInHmdzPTrBh1mAarexsa93Kw+WMX88+XjN+TtYgAiLe9xeTqcO5FfJTw==",
"version": "4.24.3",
"resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-4.24.3.tgz",
"integrity": "sha512-+htKZxLxDN14jhRG3+IXRiJqNSGHUiPYrMtv9e7qlZxcbKeJjVs+C/hd8kZF5rydp3faBwFN6ZpTaZnLA3/ZGA==",
"dev": true,
"dependencies": {
"shell-quote": "^1.6.1",
"ws": "^7"
@ -20224,6 +20226,35 @@
"node": ">= 10"
}
},
"node_modules/react-native/node_modules/react-devtools-core": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-4.19.1.tgz",
"integrity": "sha512-2wJiGffPWK0KggBjVwnTaAk+Z3MSxKInHmdzPTrBh1mAarexsa93Kw+WMX88+XjN+TtYgAiLe9xeTqcO5FfJTw==",
"dependencies": {
"shell-quote": "^1.6.1",
"ws": "^7"
}
},
"node_modules/react-native/node_modules/react-devtools-core/node_modules/ws": {
"version": "7.5.7",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz",
"integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==",
"engines": {
"node": ">=8.3.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/react-native/node_modules/react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
@ -24181,6 +24212,7 @@
"version": "7.5.5",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz",
"integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==",
"dev": true,
"engines": {
"node": ">=8.3.0"
},
@ -39314,9 +39346,10 @@
}
},
"react-devtools-core": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-4.19.1.tgz",
"integrity": "sha512-2wJiGffPWK0KggBjVwnTaAk+Z3MSxKInHmdzPTrBh1mAarexsa93Kw+WMX88+XjN+TtYgAiLe9xeTqcO5FfJTw==",
"version": "4.24.3",
"resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-4.24.3.tgz",
"integrity": "sha512-+htKZxLxDN14jhRG3+IXRiJqNSGHUiPYrMtv9e7qlZxcbKeJjVs+C/hd8kZF5rydp3faBwFN6ZpTaZnLA3/ZGA==",
"dev": true,
"requires": {
"shell-quote": "^1.6.1",
"ws": "^7"
@ -39458,6 +39491,23 @@
"react-is": "^17.0.1"
}
},
"react-devtools-core": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-4.19.1.tgz",
"integrity": "sha512-2wJiGffPWK0KggBjVwnTaAk+Z3MSxKInHmdzPTrBh1mAarexsa93Kw+WMX88+XjN+TtYgAiLe9xeTqcO5FfJTw==",
"requires": {
"shell-quote": "^1.6.1",
"ws": "^7"
},
"dependencies": {
"ws": {
"version": "7.5.7",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz",
"integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==",
"requires": {}
}
}
},
"react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
@ -43031,6 +43081,7 @@
"version": "7.5.5",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz",
"integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==",
"dev": true,
"requires": {}
},
"xcode": {

View file

@ -151,6 +151,7 @@
"mock-async-storage": "2.2.0",
"nock": "13.2.4",
"patch-package": "6.4.7",
"react-devtools-core": "4.24.3",
"react-native-svg-transformer": "1.0.0",
"react-test-renderer": "17.0.2",
"tough-cookie": "4.0.0",