Skip to content

Commit

Permalink
Merge pull request #31057 from adhorodyski/refactor/28902/flashlist-m…
Browse files Browse the repository at this point in the history
…igration

refactor(28902): migrate BaseAutoCompleteSuggestions from flatlist to flashlist
  • Loading branch information
roryabraham authored Nov 21, 2023
2 parents d15f6dc + d437910 commit 06fcabf
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 48 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, {useEffect, useRef} from 'react';
// We take FlatList from this package to properly handle the scrolling of AutoCompleteSuggestions in chats since one scroll is nested inside another
import {FlatList} from 'react-native-gesture-handler';
import {FlashList} from '@shopify/flash-list';
import React, {useCallback, useEffect, useRef} from 'react';
// We take ScrollView from this package to properly handle the scrolling of AutoCompleteSuggestions in chats since one scroll is nested inside another
import {ScrollView} from 'react-native-gesture-handler';
import Animated, {Easing, FadeOutDown, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
import * as StyleUtils from '@styles/StyleUtils';
Expand Down Expand Up @@ -28,7 +29,16 @@ const measureHeightOfSuggestionRows = (numRows, isSuggestionPickerLarge) => {
return numRows * CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT;
};

function BaseAutoCompleteSuggestions(props) {
function BaseAutoCompleteSuggestions({
highlightedSuggestionIndex,
onSelect,
renderSuggestionMenuItem,
suggestions,
accessibilityLabelExtractor,
keyExtractor,
isSuggestionPickerLarge,
forwardedRef,
}) {
const styles = useThemeStyles();
const rowHeight = useSharedValue(0);
const scrollRef = useRef(null);
Expand All @@ -39,70 +49,56 @@ function BaseAutoCompleteSuggestions(props) {
* @param {Number} params.index
* @returns {JSX.Element}
*/
const renderSuggestionMenuItem = ({item, index}) => (
<PressableWithFeedback
style={({hovered}) => StyleUtils.getAutoCompleteSuggestionItemStyle(props.highlightedSuggestionIndex, CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT, hovered, index)}
hoverDimmingValue={1}
onMouseDown={(e) => e.preventDefault()}
onPress={() => props.onSelect(index)}
onLongPress={() => {}}
accessibilityLabel={props.accessibilityLabelExtractor(item, index)}
>
{props.renderSuggestionMenuItem(item, index)}
</PressableWithFeedback>
const renderItem = useCallback(
({item, index}) => (
<PressableWithFeedback
style={({hovered}) => StyleUtils.getAutoCompleteSuggestionItemStyle(highlightedSuggestionIndex, CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT, hovered, index)}
hoverDimmingValue={1}
onMouseDown={(e) => e.preventDefault()}
onPress={() => onSelect(index)}
onLongPress={() => {}}
accessibilityLabel={accessibilityLabelExtractor(item, index)}
>
{renderSuggestionMenuItem(item, index)}
</PressableWithFeedback>
),
[highlightedSuggestionIndex, renderSuggestionMenuItem, onSelect, accessibilityLabelExtractor],
);

/**
* This function is used to compute the layout of any given item in our list. Since we know that each item will have the exact same height, this is a performance optimization
* so that the heights can be determined before the options are rendered. Otherwise, the heights are determined when each option is rendering and it causes a lot of overhead on large
* lists.
*
* Also, `scrollToIndex` should be used in conjunction with `getItemLayout`, otherwise there is no way to know the location of offscreen indices or handle failures.
*
* @param {Array} data - This is the same as the data we pass into the component
* @param {Number} index the current item's index in the set of data
*
* @returns {Object}
*/
const getItemLayout = (data, index) => ({
length: CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT,
offset: index * CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT,
index,
});

const innerHeight = CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT * props.suggestions.length;
const innerHeight = CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT * suggestions.length;
const animatedStyles = useAnimatedStyle(() => StyleUtils.getAutoCompleteSuggestionContainerStyle(rowHeight.value));

useEffect(() => {
rowHeight.value = withTiming(measureHeightOfSuggestionRows(props.suggestions.length, props.isSuggestionPickerLarge), {
rowHeight.value = withTiming(measureHeightOfSuggestionRows(suggestions.length, isSuggestionPickerLarge), {
duration: 100,
easing: Easing.inOut(Easing.ease),
});
}, [props.suggestions.length, props.isSuggestionPickerLarge, rowHeight]);
}, [suggestions.length, isSuggestionPickerLarge, rowHeight]);

useEffect(() => {
if (!scrollRef.current) {
return;
}
scrollRef.current.scrollToIndex({index: props.highlightedSuggestionIndex, animated: true});
}, [props.highlightedSuggestionIndex]);
scrollRef.current.scrollToIndex({index: highlightedSuggestionIndex, animated: true});
}, [highlightedSuggestionIndex]);

return (
<Animated.View
ref={props.forwardedRef}
ref={forwardedRef}
style={[styles.autoCompleteSuggestionsContainer, animatedStyles]}
exiting={FadeOutDown.duration(100).easing(Easing.inOut(Easing.ease))}
>
<FlatList
<FlashList
estimatedItemSize={CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT}
ref={scrollRef}
keyboardShouldPersistTaps="handled"
data={props.suggestions}
renderItem={renderSuggestionMenuItem}
keyExtractor={props.keyExtractor}
data={suggestions}
renderItem={renderItem}
renderScrollComponent={ScrollView}
keyExtractor={keyExtractor}
removeClippedSubviews={false}
showsVerticalScrollIndicator={innerHeight > rowHeight.value}
style={{flex: 1}}
getItemLayout={getItemLayout}
extraData={highlightedSuggestionIndex}
/>
</Animated.View>
);
Expand Down
8 changes: 6 additions & 2 deletions src/pages/home/report/ReportActionCompose/SuggestionEmoji.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,10 @@ function SuggestionEmoji({

const getSuggestions = useCallback(() => suggestionValues.suggestedEmojis, [suggestionValues]);

const resetEmojiSuggestions = useCallback(() => {
setSuggestionValues((prevState) => ({...prevState, suggestedEmojis: []}));
}, []);

useImperativeHandle(
forwardedRef,
() => ({
Expand All @@ -220,11 +224,11 @@ function SuggestionEmoji({

return (
<EmojiSuggestions
onClose={() => setSuggestionValues((prevState) => ({...prevState, suggestedEmojis: []}))}
onClose={resetEmojiSuggestions}
highlightedEmojiIndex={highlightedEmojiIndex}
emojis={suggestionValues.suggestedEmojis}
comment={value}
updateComment={(newComment) => setValue(newComment)}
updateComment={setValue}
colonIndex={suggestionValues.colonIndex}
prefix={value.slice(suggestionValues.colonIndex + 1, selection.start)}
onSelect={insertSelectedEmoji}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ function SuggestionMention({
highlightedMentionIndex={highlightedMentionIndex}
mentions={suggestionValues.suggestedMentions}
comment={value}
updateComment={(newComment) => setValue(newComment)}
updateComment={setValue}
colonIndex={suggestionValues.colonIndex}
prefix={suggestionValues.mentionPrefix}
onSelect={insertSelectedMention}
Expand Down
1 change: 1 addition & 0 deletions src/styles/StyleUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1041,6 +1041,7 @@ function getAutoCompleteSuggestionContainerStyle(itemsHeight: number): ViewStyle
overflow: 'hidden',
top: -(height + CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTER_PADDING + borderWidth),
height,
minHeight: CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT,
};
}

Expand Down

0 comments on commit 06fcabf

Please sign in to comment.