Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate EmojiPickerMenu to FlashList #31479

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
943a2d3
initial migration to flashlist
TMisiukiewicz Dec 15, 2023
3fa6a58
display emojis correctly with flashlist
TMisiukiewicz Dec 15, 2023
2928ac0
fix displaying headers
TMisiukiewicz Dec 15, 2023
9a0b422
sticky headers in emoji picker
TMisiukiewicz Dec 15, 2023
0fbc495
correctly display changing skin tone
TMisiukiewicz Dec 15, 2023
6a6e929
scroll to offset
TMisiukiewicz Dec 15, 2023
32504b6
scroll to offset
TMisiukiewicz Dec 15, 2023
5dc95a5
display headers on native correctly
TMisiukiewicz Dec 15, 2023
9b4207d
display scrollbar
TMisiukiewicz Dec 15, 2023
79ac9ba
update getItemType
TMisiukiewicz Dec 15, 2023
927a4f7
create separate file for getItemType
TMisiukiewicz Dec 15, 2023
cfd8066
update getItemType and list style
TMisiukiewicz Dec 15, 2023
3c5d0e4
code review updates
TMisiukiewicz Dec 15, 2023
18ea4ed
slice flags for operating system in asset
TMisiukiewicz Dec 15, 2023
2e69a4f
extract updatePreferredSkinTone function
TMisiukiewicz Dec 15, 2023
3a62259
create BaseEmojiPickerMenu
TMisiukiewicz Dec 15, 2023
50ebdee
update BaseEmojiPickerMenu
TMisiukiewicz Dec 15, 2023
0054ff6
code review updates
TMisiukiewicz Dec 15, 2023
57b6f88
code review updates
TMisiukiewicz Dec 15, 2023
d5445c0
refactor EmojiPickerMenu for web
TMisiukiewicz Dec 15, 2023
5bf55b9
implement useArrowKeyFocusManager
TMisiukiewicz Dec 15, 2023
0162599
Revert "implement useArrowKeyFocusManager"
TMisiukiewicz Dec 15, 2023
16e12fd
simplify implementation across platforms for emoji picker menu
TMisiukiewicz Dec 15, 2023
4e60e58
get empty list component out of render loop
TMisiukiewicz Dec 15, 2023
f418753
code review updates
TMisiukiewicz Dec 15, 2023
5063bab
Merge branch 'main' into feat/emojipicker-flashlist-migration
TMisiukiewicz Dec 18, 2023
cfd49dc
code review updates
TMisiukiewicz Dec 18, 2023
46a24a6
Merge remote-tracking branch 'upstream/main' into feat/emojipicker-fl…
TMisiukiewicz Dec 18, 2023
46177ef
add estimatedListSize to emoji picker menu
TMisiukiewicz Dec 18, 2023
7c774f9
update getEmojiPickerListHeight
TMisiukiewicz Dec 18, 2023
6b555d0
Merge branch 'main' into feat/emojipicker-flashlist-migration
TMisiukiewicz Dec 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions assets/emojis/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import getOperatingSystem from '@libs/getOperatingSystem';
import CONST from '@src/CONST';
import emojis from './common';
import enEmojis from './en';
import esEmojis from './es';
Expand Down Expand Up @@ -31,5 +33,21 @@ const localeEmojis = {
es: esEmojis,
} as const;

export {emojiNameTable, emojiCodeTableWithSkinTones, localeEmojis};
export {skinTones, categoryFrequentlyUsed, default} from './common';
// On windows, flag emojis are not supported
const emojisForOperatingSystem =
getOperatingSystem() === CONST.OS.WINDOWS
? emojis.slice(
0,
emojis.findIndex((emoji) => {
if (!('header' in emoji)) {
return;
}

return emoji.header && emoji.code === 'flags';
}),
)
: emojis;
Comment on lines +36 to +49
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TMisiukiewicz This was implemented during the time you have worked on this PR but we have added the support for flag emojis on Windows in this issues and pr #31717

There was this issue raised here #33593, if you could comment on it so I can assign you and you could make a follow up to remove this Windows specific code. thanks!


export default emojisForOperatingSystem;
export {emojiNameTable, emojiCodeTableWithSkinTones, localeEmojis, emojisForOperatingSystem};
export {skinTones, categoryFrequentlyUsed} from './common';
5 changes: 5 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -943,6 +943,11 @@ const CONST = {
IOS_CAMERAROLL_ACCESS_ERROR: 'Access to photo library was denied',
ADD_PAYMENT_MENU_POSITION_Y: 226,
ADD_PAYMENT_MENU_POSITION_X: 356,
EMOJI_PICKER_ITEM_TYPES: {
HEADER: 'header',
EMOJI: 'emoji',
SPACER: 'spacer',
},
EMOJI_PICKER_SIZE: {
WIDTH: 320,
HEIGHT: 416,
Expand Down
161 changes: 161 additions & 0 deletions src/components/EmojiPicker/EmojiPickerMenu/BaseEmojiPickerMenu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import {FlashList} from '@shopify/flash-list';
import PropTypes from 'prop-types';
import React, {useMemo} from 'react';
import {StyleSheet, Text, View} from 'react-native';
import CategoryShortcutBar from '@components/EmojiPicker/CategoryShortcutBar';
import EmojiSkinToneList from '@components/EmojiPicker/EmojiSkinToneList';
import refPropTypes from '@components/refPropTypes';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import stylePropTypes from '@styles/stylePropTypes';
import CONST from '@src/CONST';

const emojiPropTypes = {
/** The code of the item */
code: PropTypes.string.isRequired,
roryabraham marked this conversation as resolved.
Show resolved Hide resolved

/** Whether the item is a header or not */
header: PropTypes.bool,

/** Whether the item is a spacer or not */
spacer: PropTypes.bool,

/** Types of an emoji - e.g. different skin types */
types: PropTypes.arrayOf(PropTypes.string),
TMisiukiewicz marked this conversation as resolved.
Show resolved Hide resolved
};

const propTypes = {
/** Indicates if the emoji list is filtered or not */
isFiltered: PropTypes.bool.isRequired,

/** Array of header emojis */
headerEmojis: PropTypes.arrayOf(PropTypes.shape(emojiPropTypes)).isRequired,

/** Function to scroll to a specific header in the emoji list */
scrollToHeader: PropTypes.func.isRequired,

/** Style to be applied to the list wrapper */
listWrapperStyle: stylePropTypes,

/** Reference to the emoji list */
forwardedRef: refPropTypes,

/** The data for the emoji list */
data: PropTypes.arrayOf(PropTypes.shape(emojiPropTypes)).isRequired,

/** Function to render each item in the list */
renderItem: PropTypes.func.isRequired,

/** Extra data to be passed to the list for re-rendering */
// eslint-disable-next-line react/forbid-prop-types
extraData: PropTypes.any,

/** Array of indices for the sticky headers */
stickyHeaderIndices: PropTypes.arrayOf(PropTypes.number),

/** Whether the list should always bounce vertically */
alwaysBounceVertical: PropTypes.bool,
};

const defaultProps = {
listWrapperStyle: [],
forwardedRef: () => {},
extraData: [],
stickyHeaderIndices: [],
alwaysBounceVertical: false,
};

/**
* Improves FlashList's recycling when there are different types of items
* @param {Object} item
* @returns {String}
*/
const getItemType = (item) => {
// item is undefined only when list is empty
if (!item) {
return;
}

if (item.name) {
return CONST.EMOJI_PICKER_ITEM_TYPES.EMOJI;
}
if (item.header) {
return CONST.EMOJI_PICKER_ITEM_TYPES.HEADER;
}

return CONST.EMOJI_PICKER_ITEM_TYPES.SPACER;
};

/**
* Return a unique key for each emoji item
*
* @param {Object} item
* @param {Number} index
* @returns {String}
*/
const keyExtractor = (item, index) => `emoji_picker_${item.code}_${index}`;

/**
* Renders the list empty component
* @returns {React.Component}
*/
function ListEmptyComponent() {
const styles = useThemeStyles();
const {translate} = useLocalize();

return <Text style={[styles.textLabel, styles.colorMuted]}>{translate('common.noResultsFound')}</Text>;
}

function BaseEmojiPickerMenu({headerEmojis, scrollToHeader, isFiltered, listWrapperStyle, forwardedRef, data, renderItem, stickyHeaderIndices, extraData, alwaysBounceVertical}) {
const styles = useThemeStyles();
const {windowWidth, isSmallScreenWidth} = useWindowDimensions();

const flattenListWrapperStyle = useMemo(() => StyleSheet.flatten(listWrapperStyle), [listWrapperStyle]);

return (
<>
{!isFiltered && (
<CategoryShortcutBar
headerEmojis={headerEmojis}
onPress={scrollToHeader}
/>
)}
<View style={listWrapperStyle}>
<FlashList
ref={forwardedRef}
keyboardShouldPersistTaps="handled"
data={data}
renderItem={renderItem}
keyExtractor={keyExtractor}
numColumns={CONST.EMOJI_NUM_PER_ROW}
stickyHeaderIndices={stickyHeaderIndices}
ListEmptyComponent={ListEmptyComponent}
alwaysBounceVertical={alwaysBounceVertical}
estimatedItemSize={CONST.EMOJI_PICKER_ITEM_HEIGHT}
estimatedListSize={{height: flattenListWrapperStyle.height, width: isSmallScreenWidth ? windowWidth : CONST.EMOJI_PICKER_SIZE.WIDTH}}
contentContainerStyle={styles.ph4}
extraData={extraData}
getItemType={getItemType}
/>
</View>
<EmojiSkinToneList />
</>
);
}

BaseEmojiPickerMenu.propTypes = propTypes;
BaseEmojiPickerMenu.defaultProps = defaultProps;
BaseEmojiPickerMenu.displayName = 'BaseEmojiPickerMenu';

const BaseEmojiPickerMenuWithRef = React.forwardRef((props, ref) => (
<BaseEmojiPickerMenu
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
));

BaseEmojiPickerMenuWithRef.displayName = 'BaseEmojiPickerMenuWithRef';

export default BaseEmojiPickerMenuWithRef;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import PropTypes from 'prop-types';

const emojiPickerMenuPropTypes = {
/** Function to add the selected emoji to the main compose text input */
onEmojiSelected: PropTypes.func.isRequired,
};

export default emojiPickerMenuPropTypes;
Loading
Loading