Tweaks
This commit is contained in:
parent
d9fa16977e
commit
83f0edefcc
|
@ -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'
|
||||
|
|
|
@ -2813,3 +2813,5 @@ export const emojis = [
|
|||
'flag_tc',
|
||||
'flag_mf'
|
||||
];
|
||||
|
||||
export const DEFAULT_EMOJIS = ['clap', '+1', 'heart_eyes', 'grinning', 'thinking_face', 'smiley'];
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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'
|
||||
/>
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue