verdnatura-chat/app/containers/MessageBox/EmojiSearchbar.tsx

144 lines
4.7 KiB
TypeScript
Raw Permalink Normal View History

2022-08-25 21:25:58 +00:00
import React, { useState } from 'react';
import { View, Text, Pressable, TextInput, FlatList } from 'react-native';
2022-06-27 19:32:34 +00:00
import { FormTextInput } from '../TextInput/FormTextInput';
2022-06-27 19:32:34 +00:00
import { useTheme } from '../../theme';
import I18n from '../../i18n';
import { CustomIcon } from '../CustomIcon';
import { IEmoji } from '../../definitions';
import shortnameToUnicode from '../../lib/methods/helpers/shortnameToUnicode';
import CustomEmoji from '../EmojiPicker/CustomEmoji';
2022-06-27 21:30:21 +00:00
import styles from './styles';
2022-08-25 21:25:58 +00:00
import { useFrequentlyUsedEmoji, addFrequentlyUsed } from '../EmojiPicker/frequentlyUsedEmojis';
2022-08-20 14:32:13 +00:00
import { DEFAULT_EMOJIS } from '../EmojiPicker/emojis';
2022-06-27 19:32:34 +00:00
2022-08-20 14:32:13 +00:00
const BUTTON_HIT_SLOP = { top: 4, right: 4, bottom: 4, left: 4 };
const EMOJI_SIZE = 30;
2022-07-29 13:14:54 +00:00
interface IEmojiSearchBarProps {
2022-06-27 19:32:34 +00:00
openEmoji: () => void;
onChangeText: (value: string) => void;
2022-08-20 14:32:13 +00:00
emojis: (IEmoji | string)[];
onEmojiSelected: (emoji: IEmoji | string) => void;
2022-06-27 19:32:34 +00:00
baseUrl: string;
}
2022-08-20 14:32:13 +00:00
interface IListItem {
emoji: IEmoji | string;
onEmojiSelected: (emoji: IEmoji | string) => void;
baseUrl: string;
}
2022-08-20 14:32:13 +00:00
const Emoji = ({ emoji, baseUrl }: { emoji: IEmoji | string; baseUrl: string }): React.ReactElement => {
const { colors } = useTheme();
2022-08-20 14:32:13 +00:00
if (typeof emoji === 'string') {
return (
<Text style={[styles.searchedEmoji, { fontSize: EMOJI_SIZE, color: colors.backdropColor }]}>
{shortnameToUnicode(`:${emoji}:`)}
</Text>
);
2022-06-27 19:32:34 +00:00
}
2022-08-25 21:25:58 +00:00
return (
<CustomEmoji
style={[styles.emojiSearchCustomEmoji, { height: EMOJI_SIZE, width: EMOJI_SIZE }]}
emoji={emoji}
baseUrl={baseUrl}
/>
);
2022-08-20 14:32:13 +00:00
};
const ListItem = ({ emoji, onEmojiSelected, baseUrl }: IListItem): React.ReactElement => {
const key = typeof emoji === 'string' ? emoji : emoji?.name || emoji?.content;
2022-08-25 21:25:58 +00:00
const onPress = () => {
onEmojiSelected(emoji);
if (typeof emoji === 'string') {
addFrequentlyUsed({ content: emoji, name: emoji, isCustom: false });
} else {
addFrequentlyUsed({
content: emoji?.content || emoji?.name,
name: emoji?.name,
extension: emoji.extension,
isCustom: true
});
}
};
return (
2022-08-24 19:09:54 +00:00
<View style={[styles.emojiContainer]} key={key} testID={`searched-emoji-${key}`}>
2022-08-25 21:25:58 +00:00
<Pressable onPress={onPress}>
2022-08-20 14:32:13 +00:00
<Emoji emoji={emoji} baseUrl={baseUrl} />
</Pressable>
</View>
);
2022-08-20 14:32:13 +00:00
};
2022-06-27 19:32:34 +00:00
2022-07-29 13:14:54 +00:00
const EmojiSearchBar = React.forwardRef<TextInput, IEmojiSearchBarProps>(
2022-06-27 19:32:34 +00:00
({ openEmoji, onChangeText, emojis, onEmojiSelected, baseUrl }, ref) => {
const { colors } = useTheme();
2022-06-27 19:32:34 +00:00
const [searchText, setSearchText] = useState<string>('');
2022-08-25 21:25:58 +00:00
const { frequentlyUsed } = useFrequentlyUsedEmoji();
2022-06-27 19:32:34 +00:00
2022-08-25 21:25:58 +00:00
const frequentlyUsedWithDefaultEmojis = frequentlyUsed
.filter(emoji => {
if (typeof emoji === 'string') return !DEFAULT_EMOJIS.includes(emoji);
return !DEFAULT_EMOJIS.includes(emoji.name);
})
.concat(DEFAULT_EMOJIS);
2022-06-27 19:32:34 +00:00
const handleTextChange = (text: string) => {
setSearchText(text);
onChangeText(text);
};
return (
2022-08-25 21:25:58 +00:00
<View
style={[styles.emojiSearchViewContainer, { borderTopColor: colors.borderColor, backgroundColor: colors.backgroundColor }]}
>
2022-06-27 21:30:21 +00:00
<FlatList
horizontal
2022-08-25 21:25:58 +00:00
data={searchText ? emojis : frequentlyUsedWithDefaultEmojis}
2022-08-20 14:32:13 +00:00
renderItem={({ item }) => <ListItem emoji={item} onEmojiSelected={onEmojiSelected} baseUrl={baseUrl} />}
2022-06-27 21:30:21 +00:00
showsHorizontalScrollIndicator={false}
2022-06-28 14:41:52 +00:00
ListEmptyComponent={() => (
2022-08-24 19:09:54 +00:00
<View style={styles.listEmptyComponent} testID='no-results-found'>
2022-06-28 14:41:52 +00:00
<Text style={{ color: colors.auxiliaryText }}>{I18n.t('No_results_found')}</Text>
</View>
)}
// @ts-ignore
2022-08-20 14:32:13 +00:00
keyExtractor={item => item?.content || item?.name || item}
2022-06-27 21:30:21 +00:00
contentContainerStyle={styles.emojiListContainer}
keyboardShouldPersistTaps='always'
/>
<View style={styles.emojiSearchbarContainer}>
<Pressable
style={({ pressed }: { pressed: boolean }) => [styles.openEmojiKeyboard, { opacity: pressed ? 0.7 : 1 }]}
onPress={openEmoji}
hitSlop={BUTTON_HIT_SLOP}
2022-08-24 19:09:54 +00:00
testID='openback-emoji-keyboard'
>
2022-06-27 19:32:34 +00:00
<CustomIcon name='chevron-left' size={30} color={colors.collapsibleChevron} />
</Pressable>
2022-08-25 21:25:58 +00:00
<View style={styles.emojiSearchInput}>
2022-06-27 19:32:34 +00:00
<FormTextInput
inputRef={ref}
autoCapitalize='none'
autoCorrect={false}
blurOnSubmit
placeholder={I18n.t('Search_emoji')}
returnKeyType='search'
underlineColorAndroid='transparent'
onChangeText={handleTextChange}
2022-06-27 21:30:21 +00:00
style={[styles.emojiSearchbar, { backgroundColor: colors.passcodeButtonActive }]}
containerStyle={styles.textInputContainer}
2022-06-27 19:32:34 +00:00
value={searchText}
onClearInput={() => handleTextChange('')}
iconRight={'search'}
2022-08-24 19:09:54 +00:00
testID='emoji-searchbar-input'
2022-06-27 19:32:34 +00:00
/>
</View>
</View>
</View>
);
}
);
2022-07-29 13:14:54 +00:00
export default EmojiSearchBar;