* Fix scroll perf degradation with pull to refresh enabled
* Update app/components/post_list/post_list_refresh_control.tsx
Co-authored-by: Anurag Shivarathri <anurag6713@gmail.com>
Co-authored-by: Anurag Shivarathri <anurag6713@gmail.com>
(cherry picked from commit 58d9c2e9f0)
Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
This commit is contained in:
parent
50f616f5d7
commit
962a1759a5
8 changed files with 141 additions and 58 deletions
|
|
@ -3,10 +3,7 @@
|
|||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Text,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import {Platform, Text, View} from 'react-native';
|
||||
import {injectIntl, intlShape} from 'react-intl';
|
||||
|
||||
import {displayUsername} from '@mm-redux/utils/user_utils';
|
||||
|
|
@ -386,6 +383,11 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
|||
marginHorizontal: 12,
|
||||
marginBottom: 12,
|
||||
overflow: 'hidden',
|
||||
...Platform.select({
|
||||
android: {
|
||||
scaleY: -1,
|
||||
},
|
||||
}),
|
||||
},
|
||||
displayName: {
|
||||
color: theme.centerChannelColor,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import {injectIntl, intlShape} from 'react-intl';
|
||||
import {Keyboard, View} from 'react-native';
|
||||
import {Keyboard, StyleProp, View, ViewStyle} from 'react-native';
|
||||
|
||||
import {showModalOverCurrentContext} from '@actions/navigation';
|
||||
import Markdown from '@components/markdown';
|
||||
|
|
@ -31,6 +31,7 @@ type Props = {
|
|||
testID?: string;
|
||||
theme: Theme;
|
||||
usernamesById: Record<string, string>;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
||||
|
|
@ -87,7 +88,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
|||
|
||||
const CombinedUserActivity = ({
|
||||
canDelete, currentUserId, currentUsername, intl, post,
|
||||
showJoinLeave, testID, theme, usernamesById,
|
||||
showJoinLeave, testID, theme, usernamesById, style,
|
||||
}: Props) => {
|
||||
const itemTestID = `${testID}.${post.id}`;
|
||||
const textStyles = getMarkdownTextStyles(theme);
|
||||
|
|
@ -215,6 +216,7 @@ const CombinedUserActivity = ({
|
|||
|
||||
return (
|
||||
<View
|
||||
style={style}
|
||||
testID={testID}
|
||||
>
|
||||
<TouchableWithFeedback
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import {isPostFlagged, isSystemMessage} from '@mm-redux/utils/post_utils';
|
|||
import {canDeletePost} from '@selectors/permissions';
|
||||
import {areConsecutivePosts, postUserDisplayName} from '@utils/post';
|
||||
|
||||
import type {StyleProp, ViewStyle} from 'react-native';
|
||||
import type {Post as PostType} from '@mm-redux/types/posts';
|
||||
import type {Theme} from '@mm-redux/types/preferences';
|
||||
import type {GlobalState} from '@mm-redux/types/store';
|
||||
|
|
@ -27,6 +28,7 @@ type OwnProps = {
|
|||
post?: PostType;
|
||||
previousPostId?: string;
|
||||
nextPostId?: string;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
testID: string;
|
||||
theme: Theme;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ type PostProps = {
|
|||
showPermalink: (intl: typeof intlShape, teamName: string, postId: string) => null;
|
||||
skipFlaggedHeader?: boolean;
|
||||
skipPinnedHeader?: boolean;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
teammateNameDisplay: string;
|
||||
testID?: string;
|
||||
theme: Theme
|
||||
|
|
@ -92,10 +93,10 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
|||
const Post = ({
|
||||
canDelete, enablePostUsernameOverride, highlight, highlightPinnedOrFlagged = true, intl, isConsecutivePost, isFirstReply, isFlagged, isLastReply,
|
||||
location, post, removePost, rootPostAuthor, shouldRenderReplyButton, skipFlaggedHeader, skipPinnedHeader, showAddReaction = true, showPermalink,
|
||||
teammateNameDisplay, testID, theme,
|
||||
teammateNameDisplay, testID, theme, style,
|
||||
}: PostProps) => {
|
||||
const pressDetected = useRef(false);
|
||||
const style = getStyleSheet(theme);
|
||||
const styles = getStyleSheet(theme);
|
||||
|
||||
const handlePress = preventDoubleTap(() => {
|
||||
pressDetected.current = true;
|
||||
|
|
@ -159,23 +160,23 @@ const Post = ({
|
|||
const highlightFlagged = isFlagged && !skipFlaggedHeader;
|
||||
const hightlightPinned = post.is_pinned && !skipPinnedHeader;
|
||||
const itemTestID = `${testID}.${post.id}`;
|
||||
const rightColumnStyle = [style.rightColumn, (post.root_id && isLastReply && style.rightColumnPadding)];
|
||||
const pendingPostStyle: StyleProp<ViewStyle> | undefined = isPostPendingOrFailed(post) ? style.pendingPost : undefined;
|
||||
const rightColumnStyle = [styles.rightColumn, (post.root_id && isLastReply && styles.rightColumnPadding)];
|
||||
const pendingPostStyle: StyleProp<ViewStyle> | undefined = isPostPendingOrFailed(post) ? styles.pendingPost : undefined;
|
||||
const isAutoResponder = fromAutoResponder(post);
|
||||
|
||||
let highlightedStyle: StyleProp<ViewStyle>;
|
||||
if (highlight) {
|
||||
highlightedStyle = style.highlight;
|
||||
highlightedStyle = styles.highlight;
|
||||
} else if ((highlightFlagged || hightlightPinned) && highlightPinnedOrFlagged) {
|
||||
highlightedStyle = style.highlightPinnedOrFlagged;
|
||||
highlightedStyle = styles.highlightPinnedOrFlagged;
|
||||
}
|
||||
|
||||
let header: ReactNode;
|
||||
let postAvatar: ReactNode;
|
||||
let consecutiveStyle: StyleProp<ViewStyle>;
|
||||
if (isConsecutivePost) {
|
||||
consecutiveStyle = style.consective;
|
||||
postAvatar = <View style={style.consecutivePostContainer}/>;
|
||||
consecutiveStyle = styles.consective;
|
||||
postAvatar = <View style={styles.consecutivePostContainer}/>;
|
||||
} else {
|
||||
postAvatar = isAutoResponder ? (
|
||||
<SystemAvatar theme={theme}/>
|
||||
|
|
@ -235,7 +236,7 @@ const Post = ({
|
|||
return (
|
||||
<View
|
||||
testID={testID}
|
||||
style={[style.postStyle, highlightedStyle]}
|
||||
style={[styles.postStyle, style, highlightedStyle]}
|
||||
>
|
||||
<TouchableWithFeedback
|
||||
testID={itemTestID}
|
||||
|
|
@ -254,7 +255,7 @@ const Post = ({
|
|||
skipPinnedHeader={skipPinnedHeader}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={[style.container, consecutiveStyle]}>
|
||||
<View style={[styles.container, consecutiveStyle]}>
|
||||
{postAvatar}
|
||||
<View style={rightColumnStyle}>
|
||||
{header}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {ReactElement, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState} from 'react';
|
||||
import React, {ReactElement, useCallback, useEffect, useLayoutEffect, useRef, useState} from 'react';
|
||||
import {injectIntl, intlShape} from 'react-intl';
|
||||
import {DeviceEventEmitter, FlatList, Platform, RefreshControl, StyleSheet, ViewToken} from 'react-native';
|
||||
import {DeviceEventEmitter, FlatList, NativeScrollEvent, NativeSyntheticEvent, Platform, StyleSheet, ViewToken} from 'react-native';
|
||||
|
||||
import {DeepLinkTypes, NavigationTypes} from '@constants';
|
||||
import * as Screens from '@constants/screen';
|
||||
|
|
@ -27,6 +27,7 @@ import DateSeparator from './date_separator';
|
|||
import MoreMessagesButton from './more_messages_button';
|
||||
import NewMessagesLine from './new_message_line';
|
||||
import Post from './post';
|
||||
import PostListRefreshControl from './post_list_refresh_control';
|
||||
|
||||
type PostListProps = {
|
||||
channelId?: string;
|
||||
|
|
@ -79,6 +80,13 @@ const styles = StyleSheet.create({
|
|||
postListContent: {
|
||||
paddingTop: 5,
|
||||
},
|
||||
scale: {
|
||||
...Platform.select({
|
||||
android: {
|
||||
scaleY: -1,
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
const buildExtraData = makeExtraData();
|
||||
|
|
@ -95,6 +103,7 @@ const PostList = ({
|
|||
const onScrollEndIndexListener = useRef<onScrollEndIndexListenerEvent>();
|
||||
const onViewableItemsChangedListener = useRef<ViewableItemsChangedListenerEvent>();
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
const [offsetY, setOffsetY] = useState(0);
|
||||
|
||||
const registerViewableItemsListener = useCallback((listener) => {
|
||||
onViewableItemsChangedListener.current = listener;
|
||||
|
|
@ -181,6 +190,7 @@ const PostList = ({
|
|||
theme={theme}
|
||||
moreMessages={moreNewMessages && checkForPostId}
|
||||
testID={`${testID}.new_messages_line`}
|
||||
style={styles.scale}
|
||||
/>
|
||||
);
|
||||
} else if (isDateLine(item)) {
|
||||
|
|
@ -188,6 +198,7 @@ const PostList = ({
|
|||
<DateSeparator
|
||||
date={getDateForDateLine(item)}
|
||||
theme={theme}
|
||||
style={styles.scale}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -195,6 +206,7 @@ const PostList = ({
|
|||
if (isCombinedUserActivityPost(item)) {
|
||||
const postProps = {
|
||||
postId: item,
|
||||
style: styles.scale,
|
||||
testID: `${testID}.combined_user_activity`,
|
||||
theme,
|
||||
};
|
||||
|
|
@ -232,6 +244,7 @@ const PostList = ({
|
|||
<Post
|
||||
highlight={highlightPostId === item}
|
||||
postId={item}
|
||||
style={styles.scale}
|
||||
testID={`${testID}.post`}
|
||||
{...postProps}
|
||||
/>
|
||||
|
|
@ -247,6 +260,17 @@ const PostList = ({
|
|||
});
|
||||
}, []);
|
||||
|
||||
const onScroll = useCallback((event: NativeSyntheticEvent<NativeScrollEvent>) => {
|
||||
if (Platform.OS === 'android') {
|
||||
const {y} = event.nativeEvent.contentOffset;
|
||||
if (y === 0) {
|
||||
setOffsetY(y);
|
||||
} else if (offsetY === 0 && y !== 0) {
|
||||
setOffsetY(y);
|
||||
}
|
||||
}
|
||||
}, [offsetY]);
|
||||
|
||||
useResetNativeScrollView(scrollViewNativeID, postIds);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -300,46 +324,47 @@ const PostList = ({
|
|||
}
|
||||
}, [initialIndex, highlightPostId]);
|
||||
|
||||
const refreshControl = useMemo(() => (
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
colors={[theme.onlineIndicator, theme.awayIndicator, theme.dndIndicator]}
|
||||
tintColor={theme.centerChannelColor}
|
||||
onRefresh={onRefresh}
|
||||
const list = (
|
||||
<FlatList
|
||||
contentContainerStyle={styles.postListContent}
|
||||
data={postIds}
|
||||
extraData={buildExtraData(channelId, highlightPostId, extraData, loadMorePostsVisible)}
|
||||
initialNumToRender={INITIAL_BATCH_TO_RENDER}
|
||||
key={`recyclerFor-${channelId}-${hasPostsKey}`}
|
||||
keyboardDismissMode={'interactive'}
|
||||
keyboardShouldPersistTaps={'handled'}
|
||||
keyExtractor={keyExtractor}
|
||||
ListFooterComponent={renderFooter}
|
||||
listKey={`recyclerFor-${channelId}`}
|
||||
maintainVisibleContentPosition={SCROLL_POSITION_CONFIG}
|
||||
maxToRenderPerBatch={Platform.select({android: 5})}
|
||||
nativeID={scrollViewNativeID}
|
||||
onEndReached={onLoadMoreUp}
|
||||
onEndReachedThreshold={2}
|
||||
onScroll={onScroll}
|
||||
onScrollToIndexFailed={onScrollToIndexFailed}
|
||||
onViewableItemsChanged={onViewableItemsChanged}
|
||||
ref={flatListRef}
|
||||
removeClippedSubviews={true}
|
||||
renderItem={renderItem}
|
||||
scrollEventThrottle={60}
|
||||
style={styles.flex}
|
||||
windowSize={Posts.POST_CHUNK_SIZE / 2}
|
||||
viewabilityConfig={VIEWABILITY_CONFIG}
|
||||
testID={testID}
|
||||
/>
|
||||
), [refreshing, theme]);
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<FlatList
|
||||
contentContainerStyle={styles.postListContent}
|
||||
data={postIds}
|
||||
extraData={buildExtraData(channelId, highlightPostId, extraData, loadMorePostsVisible)}
|
||||
initialNumToRender={INITIAL_BATCH_TO_RENDER}
|
||||
inverted={true}
|
||||
key={`recyclerFor-${channelId}-${hasPostsKey}`}
|
||||
keyboardDismissMode={'interactive'}
|
||||
keyboardShouldPersistTaps={'handled'}
|
||||
keyExtractor={keyExtractor}
|
||||
ListFooterComponent={renderFooter}
|
||||
listKey={`recyclerFor-${channelId}`}
|
||||
maintainVisibleContentPosition={SCROLL_POSITION_CONFIG}
|
||||
maxToRenderPerBatch={Platform.select({android: 5})}
|
||||
nativeID={scrollViewNativeID}
|
||||
onScrollToIndexFailed={onScrollToIndexFailed}
|
||||
ref={flatListRef}
|
||||
onEndReached={onLoadMoreUp}
|
||||
onEndReachedThreshold={2}
|
||||
removeClippedSubviews={true}
|
||||
renderItem={renderItem}
|
||||
scrollEventThrottle={60}
|
||||
style={styles.flex}
|
||||
windowSize={Posts.POST_CHUNK_SIZE / 2}
|
||||
viewabilityConfig={VIEWABILITY_CONFIG}
|
||||
onViewableItemsChanged={onViewableItemsChanged}
|
||||
refreshControl={refreshControl}
|
||||
testID={testID}
|
||||
/>
|
||||
<PostListRefreshControl
|
||||
enabled={offsetY === 0}
|
||||
refreshing={refreshing}
|
||||
onRefresh={onRefresh}
|
||||
theme={theme}
|
||||
>
|
||||
{list}
|
||||
</PostListRefreshControl>
|
||||
{showMoreMessagesButton &&
|
||||
<MoreMessagesButton
|
||||
channelId={channelId}
|
||||
|
|
|
|||
52
app/components/post_list/post_list_refresh_control.tsx
Normal file
52
app/components/post_list/post_list_refresh_control.tsx
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {ReactElement} from 'react';
|
||||
import {Platform, RefreshControl, StyleSheet} from 'react-native';
|
||||
|
||||
import type {Theme} from '@mm-redux/types/preferences';
|
||||
|
||||
type Props = {
|
||||
children: ReactElement;
|
||||
enabled: boolean;
|
||||
onRefresh: () => void;
|
||||
refreshing: boolean;
|
||||
theme: Theme;
|
||||
}
|
||||
|
||||
const style = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
scaleY: -1,
|
||||
},
|
||||
});
|
||||
|
||||
const PostListRefreshControl = ({children, enabled, onRefresh, refreshing, theme}: Props) => {
|
||||
const props = {
|
||||
colors: [theme.onlineIndicator, theme.awayIndicator, theme.dndIndicator],
|
||||
onRefresh,
|
||||
refreshing,
|
||||
tintColor: theme.centerChannelColor,
|
||||
};
|
||||
|
||||
if (Platform.OS === 'android') {
|
||||
return (
|
||||
<RefreshControl
|
||||
{...props}
|
||||
enabled={enabled}
|
||||
style={style.container}
|
||||
>
|
||||
{children}
|
||||
</RefreshControl>
|
||||
);
|
||||
}
|
||||
|
||||
const refreshControl = <RefreshControl {...props}/>;
|
||||
|
||||
return React.cloneElement(
|
||||
children,
|
||||
{refreshControl, inverted: true},
|
||||
);
|
||||
};
|
||||
|
||||
export default PostListRefreshControl;
|
||||
|
|
@ -693,7 +693,7 @@ SPEC CHECKSUMS:
|
|||
BVLinearGradient: e3aad03778a456d77928f594a649e96995f1c872
|
||||
DoubleConversion: cf9b38bf0b2d048436d9a82ad2abe1404f11e7de
|
||||
FBLazyVector: e686045572151edef46010a6f819ade377dfeb4b
|
||||
FBReactNativeSpec: 74185edd6e8e5cb35512747bd840f8f9ad2381ff
|
||||
FBReactNativeSpec: cef0cc6d50abc92e8cf52f140aa22b5371cfec0b
|
||||
glog: 73c2498ac6884b13ede40eda8228cb1eee9d9d62
|
||||
jail-monkey: 01cd0a75aa1034d08fd851869e6e6c3b063242d7
|
||||
libwebp: e90b9c01d99205d03b6bb8f2c8c415e5a4ef66f0
|
||||
|
|
|
|||
5
package-lock.json
generated
5
package-lock.json
generated
|
|
@ -5,7 +5,7 @@
|
|||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"version": "1.44.1",
|
||||
"version": "1.45.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache 2.0",
|
||||
"dependencies": {
|
||||
|
|
@ -62143,8 +62143,7 @@
|
|||
"version": "7.5.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.0.tgz",
|
||||
"integrity": "sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgi4vw==",
|
||||
"requires": {
|
||||
}
|
||||
"requires": {}
|
||||
},
|
||||
"xcode": {
|
||||
"version": "3.0.1",
|
||||
|
|
|
|||
Loading…
Reference in a new issue