This commit is contained in:
Danish 2022-08-20 20:02:13 +05:30
parent d9fa16977e
commit 83f0edefcc
8 changed files with 67 additions and 63 deletions

View File

@ -35,12 +35,11 @@ const EmojiCategory = ({ baseUrl, onEmojiSelected, emojis, width, tabsCount }: I
const numColumns = Math.trunc(width ? width / emojiSize : tabsCount);
const { colors } = useTheme();
const renderItem = (emoji: IEmoji) => (
const renderItem = (emoji: IEmoji | string) => (
<Pressable
// @ts-ignore
key={emoji && emoji.isCustom ? emoji.content : emoji}
key={typeof emoji === 'string' ? emoji : emoji.content}
onPress={() => onEmojiSelected(emoji)}
testID={`reaction-picker-${emoji && emoji.isCustom ? emoji.content : emoji}`}
testID={`reaction-picker-${typeof emoji === 'string' ? emoji : emoji.content}`}
android_ripple={{ color: colors.bannerBackground, borderless: true, radius: emojiSize / 2 }}
style={({ pressed }: { pressed: boolean }) => ({
backgroundColor: isIOS && pressed ? colors.bannerBackground : 'transparent'

View File

@ -2813,3 +2813,5 @@ export const emojis = [
'flag_tc',
'flag_mf'
];
export const DEFAULT_EMOJIS = ['clap', '+1', 'heart_eyes', 'grinning', 'thinking_face', 'smiley'];

View File

@ -2,6 +2,7 @@ import React, { useMemo, useState } from 'react';
import { View } from 'react-native';
import ScrollableTabView from 'react-native-scrollable-tab-view';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import { shallowEqual } from 'react-redux';
import TabBar from './TabBar';
import EmojiCategory from './EmojiCategory';
@ -10,7 +11,6 @@ import styles from './styles';
import categories from './categories';
import database from '../../lib/database';
import { emojisByCategory } from './emojis';
import protectedFunction from '../../lib/methods/helpers/protectedFunction';
import shortnameToUnicode from '../../lib/methods/helpers/shortnameToUnicode';
import log from '../../lib/methods/helpers/log';
import { useTheme } from '../../theme';
@ -31,20 +31,21 @@ const EmojiPicker = ({
const { frequentlyUsed, loaded } = useFrequentlyUsedEmoji();
const baseUrl = useAppSelector(state => state.server?.server);
const allCustomEmojis: ICustomEmojis = useAppSelector(state => state.customEmojis);
const allCustomEmojis: ICustomEmojis = useAppSelector(state => state.customEmojis, shallowEqual);
const customEmojis = useMemo(
() =>
Object.keys(allCustomEmojis)
.filter(item => item === allCustomEmojis[item].name)
.map(item => ({
content: allCustomEmojis[item].name,
name: allCustomEmojis[item].name,
extension: allCustomEmojis[item].extension,
isCustom: true
})),
[allCustomEmojis]
);
const _addFrequentlyUsed = protectedFunction(async (emoji: IEmoji) => {
const addFrequentlyUsed = async (emoji: IEmoji) => {
const db = database.active;
const freqEmojiCollection = db.get('frequently_used_emojis');
let freqEmojiRecord: TFrequentlyUsedEmojiModel;
@ -69,21 +70,22 @@ const EmojiPicker = ({
});
}
});
});
};
const handleEmojiSelect = (emoji: IEmoji) => {
const handleEmojiSelect = (emoji: IEmoji | string) => {
try {
if (emoji.isCustom) {
_addFrequentlyUsed({
if (typeof emoji === 'string') {
addFrequentlyUsed({ content: emoji, name: emoji, isCustom: false });
const shortname = `:${emoji}:`;
onItemClicked(EventTypes.EMOJI_PRESSED, shortnameToUnicode(shortname), shortname);
} else {
addFrequentlyUsed({
content: emoji.content,
name: emoji.content,
extension: emoji.extension,
isCustom: true
});
onItemClicked(EventTypes.EMOJI_PRESSED, `:${emoji.content}:`);
} else {
_addFrequentlyUsed({ content: emoji, isCustom: false });
const shortname = `:${emoji}:`;
onItemClicked(EventTypes.EMOJI_PRESSED, shortnameToUnicode(shortname), shortname);
}
} catch (e) {
log(e);
@ -107,8 +109,8 @@ const EmojiPicker = ({
}
return (
<EmojiCategory
emojis={emojis as IEmoji[]}
onEmojiSelected={(emoji: IEmoji) => handleEmojiSelect(emoji)}
emojis={emojis}
onEmojiSelected={(emoji: IEmoji | string) => handleEmojiSelect(emoji)}
style={styles.categoryContainer}
width={width}
baseUrl={baseUrl}
@ -128,8 +130,8 @@ const EmojiPicker = ({
<View onLayout={onLayout} style={{ flex: 1 }}>
{searching ? (
<EmojiCategory
emojis={searchedEmojis as IEmoji[]}
onEmojiSelected={(emoji: IEmoji) => handleEmojiSelect(emoji)}
emojis={searchedEmojis}
onEmojiSelected={(emoji: IEmoji | string) => handleEmojiSelect(emoji)}
style={styles.categoryContainer}
width={width}
baseUrl={baseUrl}

View File

@ -11,6 +11,7 @@ import { useDimensions } from '../../dimensions';
import sharedStyles from '../../views/Styles';
import { TAnyMessageModel, TFrequentlyUsedEmojiModel } from '../../definitions';
import Touch from '../Touch';
import { DEFAULT_EMOJIS } from '../EmojiPicker/emojis';
type TItem = TFrequentlyUsedEmojiModel | string;
@ -69,8 +70,6 @@ const keyExtractor = (item: TItem) => {
return (emojiModel.id ? emojiModel.content : item) as string;
};
const DEFAULT_EMOJIS = ['clap', '+1', 'heart_eyes', 'grinning', 'thinking_face', 'smiley'];
const HeaderItem = ({ item, onReaction, server, theme }: THeaderItem) => {
const emojiModel = item as TFrequentlyUsedEmojiModel;
const emoji = (emojiModel.id ? emojiModel.content : item) as string;

View File

@ -6,22 +6,19 @@ import { Provider } from 'react-redux';
import store from '../../lib/store';
import EmojiPicker from '../EmojiPicker';
import styles from './styles';
import {useTheme} from '../../theme'
import { useTheme } from '../../theme';
import { EventTypes } from '../EmojiPicker/interfaces';
const EmojiKeyboard = () => {
const { colors } = useTheme();
const onItemClicked = (eventType: EventTypes, emoji: string | undefined) => {
KeyboardRegistry.onItemSelected('EmojiKeyboard', { eventType, emoji });
};
const {colors} = useTheme()
return (
<Provider store={store}>
<View
style={[styles.emojiKeyboardContainer, { borderTopColor: colors.borderColor }]}
testID='messagebox-keyboard-emoji'
>
<View style={[styles.emojiKeyboardContainer, { borderTopColor: colors.borderColor }]} testID='messagebox-keyboard-emoji'>
<EmojiPicker onItemClicked={onItemClicked} isEmojiKeyboard={true} />
</View>
</Provider>

View File

@ -10,40 +10,52 @@ import shortnameToUnicode from '../../lib/methods/helpers/shortnameToUnicode';
import CustomEmoji from '../EmojiPicker/CustomEmoji';
import styles from './styles';
import useFrequentlyUsedEmoji from '../EmojiPicker/frequentlyUsedEmojis';
import { DEFAULT_EMOJIS } from '../EmojiPicker/emojis';
const BUTTON_HIT_SLOP = { top: 15, right: 15, bottom: 15, left: 15 };
const BUTTON_HIT_SLOP = { top: 4, right: 4, bottom: 4, left: 4 };
const EMOJI_SIZE = 30;
const DEFAULT_EMOJIS = ['clap', '+1', 'heart_eyes', 'grinning', 'thinking_face', 'smiley'];
interface IEmojiSearchBarProps {
openEmoji: () => void;
onChangeText: (value: string) => void;
emojis: IEmoji[];
onEmojiSelected: (emoji: IEmoji) => void;
emojis: (IEmoji | string)[];
onEmojiSelected: (emoji: IEmoji | string) => void;
baseUrl: string;
}
interface ICustomEmoji {
name: string;
extension: string;
interface IListItem {
emoji: IEmoji | string;
onEmojiSelected: (emoji: IEmoji | string) => void;
baseUrl: string;
}
const Emoji = React.memo(({ emoji, baseUrl }: { emoji: IEmoji; baseUrl: string }) => {
const Emoji = ({ emoji, baseUrl }: { emoji: IEmoji | string; baseUrl: string }): React.ReactElement => {
const { colors } = useTheme();
if (emoji.isCustom || emoji.name) {
return <CustomEmoji style={{ height: EMOJI_SIZE, width: EMOJI_SIZE, margin: 4 }} emoji={emoji} baseUrl={baseUrl} />;
if (typeof emoji === 'string') {
return (
<Text style={[styles.searchedEmoji, { fontSize: EMOJI_SIZE, color: colors.backdropColor }]}>
{shortnameToUnicode(`:${emoji}:`)}
</Text>
);
}
return <CustomEmoji style={{ height: EMOJI_SIZE, width: EMOJI_SIZE, margin: 4 }} emoji={emoji} baseUrl={baseUrl} />;
};
const ListItem = ({ emoji, onEmojiSelected, baseUrl }: IListItem): React.ReactElement => {
const key = typeof emoji === 'string' ? emoji : emoji?.name || emoji?.content;
return (
<Text style={[styles.searchedEmoji, { fontSize: EMOJI_SIZE, color: colors.backdropColor }]}>
{shortnameToUnicode(`:${emoji}:`)}
</Text>
<View style={[styles.emojiContainer]} key={key}>
<Pressable onPress={() => onEmojiSelected(emoji)}>
<Emoji emoji={emoji} baseUrl={baseUrl} />
</Pressable>
</View>
);
});
};
const EmojiSearchBar = React.forwardRef<TextInput, IEmojiSearchBarProps>(
({ openEmoji, onChangeText, emojis, onEmojiSelected, baseUrl }, ref) => {
const { colors } = useTheme();
const [searchText, setSearchText] = useState<string>('');
const [frequentlyUsedEmojis, setFrequentlyUsed] = useState<(string | ICustomEmoji)[]>();
const [frequentlyUsedEmojis, setFrequentlyUsed] = useState<(string | IEmoji)[]>();
const { frequentlyUsed, loaded } = useFrequentlyUsedEmoji();
useEffect(() => {
@ -63,20 +75,12 @@ const EmojiSearchBar = React.forwardRef<TextInput, IEmojiSearchBarProps>(
onChangeText(text);
};
const ListItem = React.memo(({ emoji }: { emoji: IEmoji }) => (
<View style={[styles.emojiContainer]}>
<Pressable onPress={() => onEmojiSelected(emoji)}>
<Emoji emoji={emoji} baseUrl={baseUrl} />
</Pressable>
</View>
));
return (
<View style={{ borderTopWidth: 1, borderTopColor: colors.borderColor, backgroundColor: colors.backgroundColor }}>
<FlatList
horizontal
data={searchText ? emojis : frequentlyUsedEmojis}
renderItem={({ item }) => <ListItem emoji={item as IEmoji} />}
renderItem={({ item }) => <ListItem emoji={item} onEmojiSelected={onEmojiSelected} baseUrl={baseUrl} />}
showsHorizontalScrollIndicator={false}
ListEmptyComponent={() => (
<View style={styles.listEmptyComponent}>
@ -84,7 +88,7 @@ const EmojiSearchBar = React.forwardRef<TextInput, IEmojiSearchBarProps>(
</View>
)}
// @ts-ignore
keyExtractor={item => item?.name || item}
keyExtractor={item => item?.content || item?.name || item}
contentContainerStyle={styles.emojiListContainer}
keyboardShouldPersistTaps='always'
/>

View File

@ -1,5 +1,5 @@
import React, { Component } from 'react';
import { Alert, Keyboard, NativeModules, Text, View, TextInput as RNTextInput, BackHandler } from 'react-native';
import { Alert, Keyboard, NativeModules, Text, View, BackHandler } from 'react-native';
import { connect } from 'react-redux';
import { KeyboardAccessoryView } from 'react-native-ui-lib/keyboard';
import ImagePicker, { Image, ImageOrVideo, Options } from 'react-native-image-crop-picker';
@ -166,7 +166,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
private typingTimeout: any;
private emojiSearchbarRef: React.RefObject<RNTextInput>;
private emojiSearchbarRef: any;
static defaultProps = {
message: {
@ -198,7 +198,6 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
this.text = '';
this.selection = { start: 0, end: 0 };
this.focused = false;
this.emojiSearchbarRef = React.createRef<RNTextInput>();
const libPickerLabels = {
cropperChooseText: I18n.t('Choose'),
@ -630,7 +629,9 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
case EventTypes.SEARCH_PRESSED:
this.setState({ showEmojiKeyboard: false, showEmojiSearchbar: true });
setTimeout(() => {
this.emojiSearchbarRef.current?.focus();
if (this.emojiSearchbarRef && this.emojiSearchbarRef.focus) {
this.emojiSearchbarRef.focus();
}
}, 400);
break;
default:
@ -1161,8 +1162,8 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
const onEmojiSelected = (emoji: any) => {
let selectedEmoji;
if (emoji.name) {
selectedEmoji = `:${emoji.name}:`;
if (emoji.name || emoji.content) {
selectedEmoji = `:${emoji.name || emoji.content}:`;
} else {
selectedEmoji = shortnameToUnicode(`:${emoji}:`);
}
@ -1177,7 +1178,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
};
return showEmojiSearchbar ? (
<EmojiSearchbar
ref={this.emojiSearchbarRef}
ref={ref => (this.emojiSearchbarRef = ref)}
openEmoji={this.openEmoji}
onChangeText={onChangeText}
emojis={searchedEmojis}

View File

@ -5,7 +5,7 @@ import { ImageStyle } from 'react-native-fast-image';
export interface IEmoji {
content: string;
name: string;
extension: string;
extension?: string;
isCustom: boolean;
count?: number;
}
@ -30,8 +30,8 @@ export interface ICustomEmojiModel {
export interface IEmojiCategory {
baseUrl: string;
emojis: IEmoji[];
onEmojiSelected: (emoji: IEmoji) => void;
emojis: (IEmoji | string)[];
onEmojiSelected: (emoji: IEmoji | string) => void;
width: number | null;
style: StyleProp<ImageStyle>;
tabLabel: string;