diff --git a/app/containers/ActionSheet/ActionSheet.tsx b/app/containers/ActionSheet/ActionSheet.tsx index 94065dd84..e520d482c 100644 --- a/app/containers/ActionSheet/ActionSheet.tsx +++ b/app/containers/ActionSheet/ActionSheet.tsx @@ -140,8 +140,8 @@ const ActionSheet = React.memo( style={{ ...styles.container, ...bottomSheet }} backgroundStyle={{ backgroundColor: colors.focusedBackground }} onChange={index => index === -1 && onClose()} - // We need this to allow horizontal swipe gestures inside bottom sheet like in reaction picker - enableContentPanningGesture={data?.enableContentPanningGesture ?? true} + activeOffsetY={[-1, 1]} + failOffsetX={[-5, 5]} {...androidTablet} > diff --git a/app/containers/EmojiPicker/EmojiCategory.tsx b/app/containers/EmojiPicker/EmojiCategory.tsx index fc8188982..0b00239a3 100644 --- a/app/containers/EmojiPicker/EmojiCategory.tsx +++ b/app/containers/EmojiPicker/EmojiCategory.tsx @@ -1,5 +1,7 @@ import React from 'react'; -import { FlatList, Text, TouchableOpacity } from 'react-native'; +import { Text, TouchableOpacity } from 'react-native'; +import { BottomSheetFlatList } from '@gorhom/bottom-sheet'; +import { FlatList as GHFlatList } from 'react-native-gesture-handler'; import shortnameToUnicode from '../../lib/methods/helpers/shortnameToUnicode'; import styles from './styles'; @@ -10,7 +12,7 @@ import { IEmoji, IEmojiCategory } from '../../definitions/IEmoji'; const MAX_EMOJI_SIZE = 50; const renderEmoji = (emoji: IEmoji, size: number, baseUrl: string) => { - if (emoji && emoji.isCustom) { + if (emoji?.isCustom || emoji?.name) { return ( { ); }; -const EmojiCategory = React.memo(({ baseUrl, onEmojiSelected, emojis, width, tabsCount, ...props }: IEmojiCategory) => { - const emojiSize = width ? Math.min(width / tabsCount, MAX_EMOJI_SIZE) : MAX_EMOJI_SIZE; - const numColumns = Math.trunc(width ? width / emojiSize : tabsCount); - const renderItem = (emoji: IEmoji) => ( - onEmojiSelected(emoji)} - testID={`reaction-picker-${emoji && emoji.isCustom ? emoji.content : emoji}`}> - {renderEmoji(emoji, emojiSize, baseUrl)} - - ); +const EmojiCategory = React.memo( + ({ baseUrl, onEmojiSelected, emojis, width, tabsCount, isBottomSheet, ...props }: IEmojiCategory) => { + const emojiSize = width ? Math.min(width / tabsCount, MAX_EMOJI_SIZE) : MAX_EMOJI_SIZE; + const numColumns = Math.trunc(width ? width / emojiSize : tabsCount); - if (!width) { - return null; + const FlatList = isBottomSheet ? BottomSheetFlatList : GHFlatList; + + const renderItem = (emoji: IEmoji) => ( + onEmojiSelected(emoji)} + testID={`reaction-picker-${emoji && emoji.isCustom ? emoji.content : emoji}`} + > + {renderEmoji(emoji, emojiSize, baseUrl)} + + ); + + if (!width) { + return null; + } + + return ( + (item && item.isCustom && item.content) || item} + data={emojis} + extraData={{ baseUrl, onEmojiSelected, width, ...props }} + renderItem={({ item }) => renderItem(item)} + numColumns={numColumns} + initialNumToRender={45} + removeClippedSubviews + {...scrollPersistTaps} + keyboardDismissMode={'none'} + /> + ); } - - return ( - (item && item.isCustom && item.content) || item} - data={emojis} - extraData={{ baseUrl, onEmojiSelected, width, ...props }} - renderItem={({ item }) => renderItem(item)} - numColumns={numColumns} - initialNumToRender={45} - removeClippedSubviews - {...scrollPersistTaps} - keyboardDismissMode={'none'} - /> - ); -}); +); export default EmojiCategory; diff --git a/app/containers/EmojiPicker/TabBar.tsx b/app/containers/EmojiPicker/TabBar.tsx index 51945015b..b80385eaa 100644 --- a/app/containers/EmojiPicker/TabBar.tsx +++ b/app/containers/EmojiPicker/TabBar.tsx @@ -14,19 +14,11 @@ const TabBar = React.memo(({ activeTab, tabs, goToPage, tabEmojiStyle }: ITabBar { - if (goToPage) { - goToPage(i); - } - }} + onPress={() => goToPage?.(i)} style={styles.tab} testID={`reaction-picker-${tab}`}> {tab} - {activeTab === i ? ( - - ) : ( - - )} + ))} diff --git a/app/containers/EmojiPicker/index.tsx b/app/containers/EmojiPicker/index.tsx index 5125de0e6..4cc48ba82 100644 --- a/app/containers/EmojiPicker/index.tsx +++ b/app/containers/EmojiPicker/index.tsx @@ -19,147 +19,164 @@ import { IEmoji, ICustomEmojis, TFrequentlyUsedEmojiModel } from '../../definiti import { useAppSelector } from '../../lib/hooks'; import { IEmojiPickerProps, EventTypes } from './interfaces'; -const EmojiPicker = React.memo(({ onItemClicked, tabEmojiStyle, isEmojiKeyboard = false }: IEmojiPickerProps) => { - const [frequentlyUsed, setFrequentlyUsed] = useState([]); - const [show, setShow] = useState(false); - const [width, setWidth] = useState(null); - const { colors } = useTheme(); +const EmojiPicker = React.memo( + ({ onItemClicked, tabEmojiStyle, isEmojiKeyboard = false, searching = false, searchedEmojis = [] }: IEmojiPickerProps) => { + const [frequentlyUsed, setFrequentlyUsed] = useState([]); + const [show, setShow] = useState(false); + const [width, setWidth] = useState(null); + const { colors } = useTheme(); - const allCustomEmojis: ICustomEmojis = useAppSelector(state => state.customEmojis); - const baseUrl = useAppSelector(state=>state.server?.server) - const customEmojis = Object.keys(allCustomEmojis) - .filter(item => item === allCustomEmojis[item].name) - .map(item => ({ - content: allCustomEmojis[item].name, - extension: allCustomEmojis[item].extension, - isCustom: true - })); + const baseUrl = useAppSelector(state => state.server?.server); + const allCustomEmojis: ICustomEmojis = useAppSelector(state => state.customEmojis); + const customEmojis = Object.keys(allCustomEmojis) + .filter(item => item === allCustomEmojis[item].name) + .map(item => ({ + content: allCustomEmojis[item].name, + extension: allCustomEmojis[item].extension, + isCustom: true + })); - useEffect(() => { - const init = async () => { - await updateFrequentlyUsed(); - setShow(true); + useEffect(() => { + const init = async () => { + await updateFrequentlyUsed(); + setShow(true); + }; + init(); + }, []); + + const handleEmojiSelect = (emoji: IEmoji) => { + try { + if (emoji.isCustom) { + _addFrequentlyUsed({ + content: emoji.content, + extension: emoji.extension, + isCustom: true + }); + onItemClicked(EventTypes.EMOJI_PRESSED, `:${emoji.content}:`); + } else { + const content = emoji; + _addFrequentlyUsed({ content, isCustom: false }); + const shortname = `:${emoji}:`; + onItemClicked(EventTypes.EMOJI_PRESSED, shortnameToUnicode(shortname), shortname); + } + } catch (e) { + log(e); + } }; - init(); - }, []); - const handleEmojiSelect = (emoji: IEmoji) => { - try { - if (emoji.isCustom) { - _addFrequentlyUsed({ - content: emoji.content, - extension: emoji.extension, - isCustom: true - }); - onItemClicked(EventTypes.EMOJI_PRESSED, `:${emoji.content}:`); - } else { - const content = emoji; - _addFrequentlyUsed({ content, isCustom: false }); - const shortname = `:${emoji}:`; - onItemClicked(EventTypes.EMOJI_PRESSED, shortnameToUnicode(shortname)); + const _addFrequentlyUsed = protectedFunction(async (emoji: IEmoji) => { + const db = database.active; + const freqEmojiCollection = db.get('frequently_used_emojis'); + let freqEmojiRecord: TFrequentlyUsedEmojiModel; + try { + freqEmojiRecord = await freqEmojiCollection.find(emoji.content); + } catch (error) { + // Do nothing } - } catch (e) { - log(e); - } - }; - const _addFrequentlyUsed = protectedFunction(async (emoji: IEmoji) => { - const db = database.active; - const freqEmojiCollection = db.get('frequently_used_emojis'); - let freqEmojiRecord: TFrequentlyUsedEmojiModel; - try { - freqEmojiRecord = await freqEmojiCollection.find(emoji.content); - } catch (error) { - // Do nothing - } - - await db.write(async () => { - if (freqEmojiRecord) { - await freqEmojiRecord.update(f => { - if (f.count) { - f.count += 1; - } - }); - } else { - await freqEmojiCollection.create(f => { - f._raw = sanitizedRaw({ id: emoji.content }, freqEmojiCollection.schema); - Object.assign(f, emoji); - f.count = 1; - }); - } + await db.write(async () => { + if (freqEmojiRecord) { + await freqEmojiRecord.update(f => { + if (f.count) { + f.count += 1; + } + }); + } else { + await freqEmojiCollection.create(f => { + f._raw = sanitizedRaw({ id: emoji.content }, freqEmojiCollection.schema); + Object.assign(f, emoji); + f.count = 1; + }); + } + }); }); - }); - const updateFrequentlyUsed = async () => { - const db = database.active; - const frequentlyUsedRecords = await db.get('frequently_used_emojis').query().fetch(); - const frequentlyUsedOrdered = orderBy(frequentlyUsedRecords, ['count'], ['desc']); - const frequentlyUsedEmojis = frequentlyUsedOrdered.map(item => { - if (item.isCustom) { - return { content: item.content, extension: item.extension, isCustom: item.isCustom }; + const updateFrequentlyUsed = async () => { + const db = database.active; + const frequentlyUsedRecords = await db.get('frequently_used_emojis').query().fetch(); + const frequentlyUsedOrdered = orderBy(frequentlyUsedRecords, ['count'], ['desc']); + const frequentlyUsedEmojis = frequentlyUsedOrdered.map(item => { + if (item.isCustom) { + return { content: item.content, extension: item.extension, isCustom: item.isCustom }; + } + return shortnameToUnicode(`${item.content}`); + }) as IEmoji[]; + setFrequentlyUsed(frequentlyUsedEmojis); + }; + + const onLayout = ({ + nativeEvent: { + layout: { width } } - return shortnameToUnicode(`${item.content}`); - }) as IEmoji[]; - setFrequentlyUsed(frequentlyUsedEmojis); - }; + }: any) => setWidth(width); - const onLayout = ({ - nativeEvent: { - layout: { width } - } - }: any) => setWidth(width); - - const renderCategory = (category: keyof typeof emojisByCategory, i: number, label: string, tabsCount: number) => { - let emojis = []; - if (i === 0) { - emojis = frequentlyUsed; - } else if (i === 1) { - emojis = customEmojis; - } else { - emojis = emojisByCategory[category]; - } - return ( - handleEmojiSelect(emoji)} - style={styles.categoryContainer} - width={width} - baseUrl={baseUrl} - tabLabel={label} - tabsCount={tabsCount} - /> - ); - }; - - if (!show) { - return null; - } - - const tabsCount = frequentlyUsed.length === 0 ? categories.tabs.length - 1 : categories.tabs.length; - - return ( - - } - contentProps={{ - keyboardShouldPersistTaps: 'always', - keyboardDismissMode: 'none' - }} - style={{ backgroundColor: colors.focusedBackground }}> - {categories.tabs.map((tab: any, i) => - i === 0 && frequentlyUsed.length === 0 - ? null // when no frequentlyUsed don't show the tab - : renderCategory(tab.category, i, tab.tabLabel, tabsCount) - )} - - {isEmojiKeyboard && ( -