Refactoring and style fixes

This commit is contained in:
Danish 2022-08-26 02:55:58 +05:30
parent 8390a4c582
commit 0f51a3c5df
8 changed files with 98 additions and 80 deletions

View File

@ -3,14 +3,13 @@ import { Text, Pressable } from 'react-native';
import { FlatList } from 'react-native-gesture-handler';
import shortnameToUnicode from '../../lib/methods/helpers/shortnameToUnicode';
import styles from './styles';
import styles, { MIN_EMOJI_SIZE, MAX_EMOJI_SIZE } from './styles';
import CustomEmoji from './CustomEmoji';
import scrollPersistTaps from '../../lib/methods/helpers/scrollPersistTaps';
import { IEmoji, IEmojiCategory } from '../../definitions/IEmoji';
import { useTheme } from '../../theme';
import { isIOS } from '../../lib/methods/helpers';
const MAX_EMOJI_SIZE = 50;
import { useDimensions } from '../../dimensions';
interface IEmojiProps {
emoji: string | IEmoji;
@ -18,7 +17,7 @@ interface IEmojiProps {
baseUrl: string;
}
const Emoji = React.memo(({ emoji, size, baseUrl }: IEmojiProps) => {
const Emoji = ({ emoji, size, baseUrl }: IEmojiProps): React.ReactElement => {
if (typeof emoji === 'string')
return (
<Text style={[styles.categoryEmoji, { height: size, width: size, fontSize: size - 14 }]}>
@ -28,12 +27,14 @@ const Emoji = React.memo(({ emoji, size, baseUrl }: IEmojiProps) => {
return (
<CustomEmoji style={[styles.customCategoryEmoji, { height: size - 16, width: size - 16 }]} emoji={emoji} baseUrl={baseUrl} />
);
});
};
const EmojiCategory = ({ baseUrl, onEmojiSelected, emojis, width, tabsCount }: IEmojiCategory): React.ReactElement | null => {
const emojiSize = width ? Math.min(width / tabsCount, MAX_EMOJI_SIZE) : MAX_EMOJI_SIZE;
const numColumns = Math.trunc(width ? width / emojiSize : tabsCount);
const EmojiCategory = ({ baseUrl, onEmojiSelected, emojis, tabsCount }: IEmojiCategory): React.ReactElement | null => {
const { colors } = useTheme();
const { width } = useDimensions();
const emojiSize = Math.min(Math.max(width / tabsCount, MIN_EMOJI_SIZE), MAX_EMOJI_SIZE);
const numColumns = Math.trunc(width / emojiSize);
const marginHorizontal = (width - numColumns * emojiSize) / 2;
const renderItem = (emoji: IEmoji | string) => (
<Pressable
@ -57,14 +58,14 @@ const EmojiCategory = ({ baseUrl, onEmojiSelected, emojis, width, tabsCount }: I
<FlatList
// rerender FlatList in case of width changes
key={`emoji-category-${width}`}
// @ts-ignore
keyExtractor={item => (item?.isCustom && item.content) || item}
keyExtractor={item => (typeof item === 'string' ? item : item.content)}
data={emojis}
extraData={{ baseUrl, width }}
renderItem={({ item }) => renderItem(item)}
numColumns={numColumns}
initialNumToRender={45}
removeClippedSubviews
contentContainerStyle={{ marginHorizontal }}
{...scrollPersistTaps}
keyboardDismissMode={'none'}
/>

View File

@ -1,11 +1,12 @@
import { useEffect, useState } from 'react';
import orderBy from 'lodash/orderBy';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import database from '../../lib/database';
import shortnameToUnicode from '../../lib/methods/helpers/shortnameToUnicode';
import { IEmoji } from '../../definitions';
import { IEmoji, TFrequentlyUsedEmojiModel } from '../../definitions';
const useFrequentlyUsedEmoji = (): {
export const useFrequentlyUsedEmoji = (): {
frequentlyUsed: (string | IEmoji)[];
loaded: boolean;
} => {
@ -30,4 +31,29 @@ const useFrequentlyUsedEmoji = (): {
return { frequentlyUsed, loaded };
};
export default useFrequentlyUsedEmoji;
export const addFrequentlyUsed = async (emoji: IEmoji) => {
const db = database.active;
const freqEmojiCollection = db.get('frequently_used_emojis');
let freqEmojiRecord: TFrequentlyUsedEmojiModel;
try {
freqEmojiRecord = await freqEmojiCollection.find(emoji.content || emoji.name);
} 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 || emoji.name }, freqEmojiCollection.schema);
Object.assign(f, emoji);
f.count = 1;
});
}
});
};

View File

@ -1,7 +1,6 @@
import React, { useMemo, useState } from 'react';
import React, { useMemo } 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';
@ -9,15 +8,14 @@ import EmojiCategory from './EmojiCategory';
import Footer from './Footer';
import styles from './styles';
import categories from './categories';
import database from '../../lib/database';
import { emojisByCategory } from './emojis';
import shortnameToUnicode from '../../lib/methods/helpers/shortnameToUnicode';
import log from '../../lib/methods/helpers/log';
import { useTheme } from '../../theme';
import { IEmoji, ICustomEmojis, TFrequentlyUsedEmojiModel } from '../../definitions';
import { IEmoji, ICustomEmojis } from '../../definitions';
import { useAppSelector } from '../../lib/hooks';
import { IEmojiPickerProps, EventTypes } from './interfaces';
import useFrequentlyUsedEmoji from './frequentlyUsedEmojis';
import { useFrequentlyUsedEmoji, addFrequentlyUsed } from './frequentlyUsedEmojis';
const EmojiPicker = ({
onItemClicked,
@ -26,7 +24,6 @@ const EmojiPicker = ({
searching = false,
searchedEmojis = []
}: IEmojiPickerProps): React.ReactElement | null => {
const [width, setWidth] = useState(null);
const { colors } = useTheme();
const { frequentlyUsed, loaded } = useFrequentlyUsedEmoji();
@ -45,33 +42,6 @@ const EmojiPicker = ({
[allCustomEmojis]
);
const addFrequentlyUsed = 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;
});
}
});
};
const handleEmojiSelect = (emoji: IEmoji | string) => {
try {
if (typeof emoji === 'string') {
@ -81,7 +51,7 @@ const EmojiPicker = ({
} else {
addFrequentlyUsed({
content: emoji.content,
name: emoji.content,
name: emoji.name,
extension: emoji.extension,
isCustom: true
});
@ -92,12 +62,6 @@ const EmojiPicker = ({
}
};
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) {
@ -112,7 +76,6 @@ const EmojiPicker = ({
emojis={emojis}
onEmojiSelected={(emoji: IEmoji | string) => handleEmojiSelect(emoji)}
style={styles.categoryContainer}
width={width}
baseUrl={baseUrl}
tabLabel={label}
tabsCount={tabsCount}
@ -127,13 +90,12 @@ const EmojiPicker = ({
const tabsCount = frequentlyUsed.length === 0 ? categories.tabs.length - 1 : categories.tabs.length;
return (
<View onLayout={onLayout} style={{ flex: 1 }}>
<View style={styles.emojiPickerContainer}>
{searching ? (
<EmojiCategory
emojis={searchedEmojis}
onEmojiSelected={(emoji: IEmoji | string) => handleEmojiSelect(emoji)}
style={styles.categoryContainer}
width={width}
baseUrl={baseUrl}
tabLabel={'searching'}
tabsCount={tabsCount}

View File

@ -2,6 +2,9 @@ import { StyleSheet } from 'react-native';
import sharedStyles from '../../views/Styles';
export const MAX_EMOJI_SIZE = 50;
export const MIN_EMOJI_SIZE = 42;
export default StyleSheet.create({
container: {
flex: 1
@ -14,7 +17,8 @@ export default StyleSheet.create({
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 10
paddingVertical: 10,
width: 44
},
tabEmoji: {
fontSize: 20,
@ -66,5 +70,6 @@ export default StyleSheet.create({
width: 44,
justifyContent: 'center',
alignItems: 'center'
}
},
emojiPickerContainer: { flex: 1 }
});

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React, { useState } from 'react';
import { View, Text, Pressable, TextInput, FlatList } from 'react-native';
import { FormTextInput } from '../TextInput/FormTextInput';
@ -9,7 +9,7 @@ import { IEmoji } from '../../definitions';
import shortnameToUnicode from '../../lib/methods/helpers/shortnameToUnicode';
import CustomEmoji from '../EmojiPicker/CustomEmoji';
import styles from './styles';
import useFrequentlyUsedEmoji from '../EmojiPicker/frequentlyUsedEmojis';
import { useFrequentlyUsedEmoji, addFrequentlyUsed } from '../EmojiPicker/frequentlyUsedEmojis';
import { DEFAULT_EMOJIS } from '../EmojiPicker/emojis';
const BUTTON_HIT_SLOP = { top: 4, right: 4, bottom: 4, left: 4 };
@ -37,14 +37,33 @@ const Emoji = ({ emoji, baseUrl }: { emoji: IEmoji | string; baseUrl: string }):
</Text>
);
}
return <CustomEmoji style={{ height: EMOJI_SIZE, width: EMOJI_SIZE, margin: 4 }} emoji={emoji} baseUrl={baseUrl} />;
return (
<CustomEmoji
style={[styles.emojiSearchCustomEmoji, { height: EMOJI_SIZE, width: EMOJI_SIZE }]}
emoji={emoji}
baseUrl={baseUrl}
/>
);
};
const ListItem = ({ emoji, onEmojiSelected, baseUrl }: IListItem): React.ReactElement => {
const key = typeof emoji === 'string' ? emoji : emoji?.name || emoji?.content;
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 (
<View style={[styles.emojiContainer]} key={key} testID={`searched-emoji-${key}`}>
<Pressable onPress={() => onEmojiSelected(emoji)}>
<Pressable onPress={onPress}>
<Emoji emoji={emoji} baseUrl={baseUrl} />
</Pressable>
</View>
@ -55,20 +74,14 @@ 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 | IEmoji)[]>();
const { frequentlyUsed, loaded } = useFrequentlyUsedEmoji();
const { frequentlyUsed } = useFrequentlyUsedEmoji();
useEffect(() => {
if (loaded) {
const frequentlyUsedWithDefaultEmojis = frequentlyUsed
.filter(emoji => {
if (typeof emoji === 'string') return !DEFAULT_EMOJIS.includes(emoji);
return !DEFAULT_EMOJIS.includes(emoji.name);
})
.concat(DEFAULT_EMOJIS);
setFrequentlyUsed(frequentlyUsedWithDefaultEmojis);
}
}, [loaded]);
const handleTextChange = (text: string) => {
setSearchText(text);
@ -76,10 +89,12 @@ const EmojiSearchBar = React.forwardRef<TextInput, IEmojiSearchBarProps>(
};
return (
<View style={{ borderTopWidth: 1, borderTopColor: colors.borderColor, backgroundColor: colors.backgroundColor }}>
<View
style={[styles.emojiSearchViewContainer, { borderTopColor: colors.borderColor, backgroundColor: colors.backgroundColor }]}
>
<FlatList
horizontal
data={searchText ? emojis : frequentlyUsedEmojis}
data={searchText ? emojis : frequentlyUsedWithDefaultEmojis}
renderItem={({ item }) => <ListItem emoji={item} onEmojiSelected={onEmojiSelected} baseUrl={baseUrl} />}
showsHorizontalScrollIndicator={false}
ListEmptyComponent={() => (
@ -101,7 +116,7 @@ const EmojiSearchBar = React.forwardRef<TextInput, IEmojiSearchBarProps>(
>
<CustomIcon name='chevron-left' size={30} color={colors.collapsibleChevron} />
</Pressable>
<View style={{ flex: 1 }}>
<View style={styles.emojiSearchInput}>
<FormTextInput
inputRef={ref}
autoCapitalize='none'

View File

@ -163,6 +163,9 @@ export default StyleSheet.create({
},
emojiContainer: { justifyContent: 'center', marginHorizontal: 2 },
emojiListContainer: { height: 50, paddingHorizontal: 5, marginVertical: 5, flexGrow: 1 },
emojiSearchViewContainer: {
borderTopWidth: 1
},
emojiSearchbarContainer: {
flexDirection: 'row',
height: 50,
@ -177,5 +180,11 @@ export default StyleSheet.create({
width: '100%',
alignItems: 'center',
justifyContent: 'center'
},
emojiSearchCustomEmoji: {
margin: 4
},
emojiSearchInput: {
flex: 1
}
});

View File

@ -32,7 +32,6 @@ export interface IEmojiCategory {
baseUrl: string;
emojis: (IEmoji | string)[];
onEmojiSelected: (emoji: IEmoji | string) => void;
width: number | null;
style: StyleProp<ImageStyle>;
tabLabel: string;
tabsCount: number;

View File

@ -69,6 +69,7 @@ export default StyleSheet.create({
paddingHorizontal: 15
},
reactionPickerSearchbar: {
paddingHorizontal: 20
paddingHorizontal: 20,
minHeight: 48
}
});