Remove wrong use of memo and some requested changes
This commit is contained in:
parent
64afe08fe6
commit
10f074eb02
|
@ -1,6 +1,5 @@
|
|||
import React from 'react';
|
||||
import { View, Pressable } from 'react-native';
|
||||
import { BorderlessButton } from 'react-native-gesture-handler';
|
||||
|
||||
import { useTheme } from '../../theme';
|
||||
import { CustomIcon } from '../CustomIcon';
|
||||
|
@ -9,19 +8,22 @@ import { IFooterProps } from './interfaces';
|
|||
|
||||
const BUTTON_HIT_SLOP = { top: 15, right: 15, bottom: 15, left: 15 };
|
||||
|
||||
const Footer = React.memo(({ onSearchPressed, onBackspacePressed }: IFooterProps) => {
|
||||
const Footer = ({ onSearchPressed, onBackspacePressed }: IFooterProps): React.ReactElement => {
|
||||
const { colors } = useTheme();
|
||||
return (
|
||||
<View style={[styles.footerContainer, { backgroundColor: colors.bannerBackground }]}>
|
||||
<BorderlessButton activeOpacity={0.7} onPress={onSearchPressed} style={styles.footerButtonsContainer}>
|
||||
<Pressable
|
||||
onPress={onSearchPressed}
|
||||
hitSlop={BUTTON_HIT_SLOP}
|
||||
style={({ pressed }) => [[styles.footerButtonsContainer, { opacity: pressed ? 0.7 : 1 }]]}>
|
||||
<CustomIcon color={colors.auxiliaryTintColor} size={24} name='search' />
|
||||
</BorderlessButton>
|
||||
</Pressable>
|
||||
|
||||
<Pressable onPress={onBackspacePressed} hitSlop={BUTTON_HIT_SLOP} style={({ pressed }) => [{ opacity: pressed ? 0.7 : 1 }]}>
|
||||
<CustomIcon color={colors.auxiliaryTintColor} size={24} name='backspace' />
|
||||
</Pressable>
|
||||
</View>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
|
|
|
@ -24,7 +24,7 @@ const TabBar = ({ activeTab, tabs, goToPage }: ITabBarProps): React.ReactElement
|
|||
backgroundColor: isIOS && pressed ? colors.bannerBackground : 'transparent'
|
||||
}
|
||||
]}>
|
||||
<CustomIcon name={tab} size={24} color={colors.titleText} />
|
||||
<CustomIcon name={tab} size={24} color={activeTab === i ? colors.tintColor : colors.auxiliaryTintColor} />
|
||||
<View style={activeTab === i ? [styles.activeTabLine, { backgroundColor: colors.tintColor }] : styles.tabLine} />
|
||||
</Pressable>
|
||||
))}
|
||||
|
|
|
@ -19,7 +19,7 @@ import { IEmoji, ICustomEmojis, TFrequentlyUsedEmojiModel } from '../../definiti
|
|||
import { useAppSelector } from '../../lib/hooks';
|
||||
import { IEmojiPickerProps, EventTypes } from './interfaces';
|
||||
|
||||
const useFrequentlyUsedEmoji = () => {
|
||||
export const useFrequentlyUsedEmoji = () => {
|
||||
const [frequentlyUsed, setFrequentlyUsed] = useState<IEmoji[]>([]);
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
const getFrequentlyUsedEmojis = async () => {
|
||||
|
@ -41,146 +41,150 @@ const useFrequentlyUsedEmoji = () => {
|
|||
return { frequentlyUsed, loaded };
|
||||
};
|
||||
|
||||
const EmojiPicker = React.memo(
|
||||
({ onItemClicked, tabEmojiStyle, isEmojiKeyboard = false, searching = false, searchedEmojis = [] }: IEmojiPickerProps) => {
|
||||
const [width, setWidth] = useState(null);
|
||||
const { colors } = useTheme();
|
||||
const { frequentlyUsed, loaded } = useFrequentlyUsedEmoji();
|
||||
const EmojiPicker = ({
|
||||
onItemClicked,
|
||||
tabEmojiStyle,
|
||||
isEmojiKeyboard = false,
|
||||
searching = false,
|
||||
searchedEmojis = []
|
||||
}: IEmojiPickerProps): React.ReactElement | null => {
|
||||
const [width, setWidth] = useState(null);
|
||||
const { colors } = useTheme();
|
||||
const { frequentlyUsed, loaded } = useFrequentlyUsedEmoji();
|
||||
|
||||
const baseUrl = useAppSelector(state => state.server?.server);
|
||||
const allCustomEmojis: ICustomEmojis = useAppSelector(state => state.customEmojis);
|
||||
const customEmojis = useMemo(
|
||||
() =>
|
||||
Object.keys(allCustomEmojis)
|
||||
.filter(item => item === allCustomEmojis[item].name)
|
||||
.map(item => ({
|
||||
content: allCustomEmojis[item].name,
|
||||
extension: allCustomEmojis[item].extension,
|
||||
isCustom: true
|
||||
})),
|
||||
[allCustomEmojis]
|
||||
);
|
||||
const baseUrl = useAppSelector(state => state.server?.server);
|
||||
const allCustomEmojis: ICustomEmojis = useAppSelector(state => state.customEmojis);
|
||||
const customEmojis = useMemo(
|
||||
() =>
|
||||
Object.keys(allCustomEmojis)
|
||||
.filter(item => item === allCustomEmojis[item].name)
|
||||
.map(item => ({
|
||||
content: allCustomEmojis[item].name,
|
||||
extension: allCustomEmojis[item].extension,
|
||||
isCustom: true
|
||||
})),
|
||||
[allCustomEmojis]
|
||||
);
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
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;
|
||||
const handleEmojiSelect = (emoji: IEmoji) => {
|
||||
try {
|
||||
if (emoji.isCustom) {
|
||||
_addFrequentlyUsed({
|
||||
content: emoji.content,
|
||||
extension: emoji.extension,
|
||||
isCustom: true
|
||||
});
|
||||
onItemClicked(EventTypes.EMOJI_PRESSED, `:${emoji.content}:`);
|
||||
} else {
|
||||
emojis = emojisByCategory[category];
|
||||
const content = emoji;
|
||||
_addFrequentlyUsed({ content, isCustom: false });
|
||||
const shortname = `:${emoji}:`;
|
||||
onItemClicked(EventTypes.EMOJI_PRESSED, shortnameToUnicode(shortname), shortname);
|
||||
}
|
||||
return (
|
||||
} 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;
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
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 (
|
||||
<EmojiCategory
|
||||
emojis={emojis as IEmoji[]}
|
||||
onEmojiSelected={(emoji: IEmoji) => handleEmojiSelect(emoji)}
|
||||
style={styles.categoryContainer}
|
||||
width={width}
|
||||
baseUrl={baseUrl}
|
||||
tabLabel={label}
|
||||
tabsCount={tabsCount}
|
||||
isBottomSheet={!isEmojiKeyboard}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
if (!loaded) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const tabsCount = frequentlyUsed.length === 0 ? categories.tabs.length - 1 : categories.tabs.length;
|
||||
|
||||
return (
|
||||
<View onLayout={onLayout} style={{ flex: 1 }}>
|
||||
{searching ? (
|
||||
<EmojiCategory
|
||||
emojis={emojis as IEmoji[]}
|
||||
emojis={searchedEmojis as IEmoji[]}
|
||||
onEmojiSelected={(emoji: IEmoji) => handleEmojiSelect(emoji)}
|
||||
style={styles.categoryContainer}
|
||||
width={width}
|
||||
baseUrl={baseUrl}
|
||||
tabLabel={label}
|
||||
tabLabel={'searching'}
|
||||
tabsCount={tabsCount}
|
||||
isBottomSheet={!isEmojiKeyboard}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
if (!loaded) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const tabsCount = frequentlyUsed.length === 0 ? categories.tabs.length - 1 : categories.tabs.length;
|
||||
|
||||
return (
|
||||
<View onLayout={onLayout} style={{ flex: 1 }}>
|
||||
{searching ? (
|
||||
<EmojiCategory
|
||||
emojis={searchedEmojis as IEmoji[]}
|
||||
onEmojiSelected={(emoji: IEmoji) => handleEmojiSelect(emoji)}
|
||||
style={styles.categoryContainer}
|
||||
width={width}
|
||||
baseUrl={baseUrl}
|
||||
tabLabel={'searching'}
|
||||
tabsCount={tabsCount}
|
||||
isBottomSheet={!isEmojiKeyboard}
|
||||
/>
|
||||
) : (
|
||||
<ScrollableTabView
|
||||
renderTabBar={() => <TabBar tabEmojiStyle={tabEmojiStyle} />}
|
||||
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)
|
||||
)}
|
||||
</ScrollableTabView>
|
||||
)}
|
||||
{isEmojiKeyboard && (
|
||||
<Footer
|
||||
onSearchPressed={() => onItemClicked(EventTypes.SEARCH_PRESSED)}
|
||||
onBackspacePressed={() => onItemClicked(EventTypes.BACKSPACE_PRESSED)}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
);
|
||||
) : (
|
||||
<ScrollableTabView
|
||||
renderTabBar={() => <TabBar tabEmojiStyle={tabEmojiStyle} />}
|
||||
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)
|
||||
)}
|
||||
</ScrollableTabView>
|
||||
)}
|
||||
{isEmojiKeyboard && (
|
||||
<Footer
|
||||
onSearchPressed={() => onItemClicked(EventTypes.SEARCH_PRESSED)}
|
||||
onBackspacePressed={() => onItemClicked(EventTypes.BACKSPACE_PRESSED)}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmojiPicker;
|
||||
|
|
|
@ -13,6 +13,7 @@ import database from '../../lib/database';
|
|||
import styles from './styles';
|
||||
|
||||
const BUTTON_HIT_SLOP = { top: 15, right: 15, bottom: 15, left: 15 };
|
||||
const EMOJI_SIZE = 30;
|
||||
|
||||
interface IEmojiSearchbarProps {
|
||||
openEmoji: () => void;
|
||||
|
@ -22,12 +23,17 @@ interface IEmojiSearchbarProps {
|
|||
baseUrl: string;
|
||||
}
|
||||
|
||||
const renderEmoji = (emoji: IEmoji, size: number, baseUrl: string) => {
|
||||
const Emoji = React.memo(({ emoji, baseUrl }: { emoji: IEmoji; baseUrl: string }) => {
|
||||
const { colors } = useTheme();
|
||||
if (emoji?.name) {
|
||||
return <CustomEmoji style={{ height: size, width: size, margin: 4 }} emoji={emoji} baseUrl={baseUrl} />;
|
||||
return <CustomEmoji style={{ height: EMOJI_SIZE, width: EMOJI_SIZE, margin: 4 }} emoji={emoji} baseUrl={baseUrl} />;
|
||||
}
|
||||
return <Text style={[styles.searchedEmoji, { fontSize: size }]}>{shortnameToUnicode(`:${emoji}:`)}</Text>;
|
||||
};
|
||||
return (
|
||||
<Text style={[styles.searchedEmoji, { fontSize: EMOJI_SIZE, color: colors.backdropColor }]}>
|
||||
{shortnameToUnicode(`:${emoji}:`)}
|
||||
</Text>
|
||||
);
|
||||
});
|
||||
|
||||
const EmojiSearchbar = React.forwardRef<TextInput, IEmojiSearchbarProps>(
|
||||
({ openEmoji, onChangeText, emojis, onEmojiSelected, baseUrl }, ref) => {
|
||||
|
@ -59,14 +65,13 @@ const EmojiSearchbar = React.forwardRef<TextInput, IEmojiSearchbarProps>(
|
|||
if (!text) getFrequentlyUsedEmojis();
|
||||
};
|
||||
|
||||
const renderItem = (emoji: IEmoji) => {
|
||||
const emojiSize = 30;
|
||||
return (
|
||||
<View style={[styles.emojiContainer]}>
|
||||
<Pressable onPress={() => onEmojiSelected(emoji)}>{renderEmoji(emoji, emojiSize, baseUrl)}</Pressable>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
const renderItem = (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
|
||||
|
|
|
@ -166,7 +166,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
|||
|
||||
private typingTimeout: any;
|
||||
|
||||
private emojiSearchbarRef: any;
|
||||
private emojiSearchbarRef: React.RefObject<RNTextInput>;
|
||||
|
||||
static defaultProps = {
|
||||
message: {
|
||||
|
@ -627,7 +627,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
|||
case EventTypes.SEARCH_PRESSED:
|
||||
this.setState({ showEmojiKeyboard: false, showEmojiSearchbar: true });
|
||||
setTimeout(() => {
|
||||
this.emojiSearchbarRef.current.focus();
|
||||
this.emojiSearchbarRef.current?.focus();
|
||||
}, 400);
|
||||
break;
|
||||
default:
|
||||
|
@ -930,6 +930,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
|||
openEmoji = () => {
|
||||
logEvent(events.ROOM_OPEN_EMOJI);
|
||||
this.setState({ showEmojiKeyboard: true, showEmojiSearchbar: false });
|
||||
this.stopTrackingMention();
|
||||
};
|
||||
|
||||
recordingCallback = (recording: any) => {
|
||||
|
|
|
@ -159,8 +159,7 @@ export default StyleSheet.create({
|
|||
...sharedStyles.textRegular
|
||||
},
|
||||
searchedEmoji: {
|
||||
backgroundColor: 'transparent',
|
||||
color: '#ffffff'
|
||||
backgroundColor: 'transparent'
|
||||
},
|
||||
emojiContainer: { justifyContent: 'center', marginHorizontal: 2 },
|
||||
emojiListContainer: { height: 50, paddingHorizontal: 5, marginVertical: 5, flexGrow: 1 },
|
||||
|
@ -172,7 +171,7 @@ export default StyleSheet.create({
|
|||
alignItems: 'center'
|
||||
},
|
||||
openEmojiKeyboard: { marginHorizontal: 10, justifyContent: 'center' },
|
||||
emojiSearchbar: { padding: 10, borderRadius: 5 },
|
||||
emojiSearchbar: { paddingHorizontal: 20, borderRadius: 2, fontSize: 16 },
|
||||
textInputContainer: { justifyContent: 'center', marginBottom: 0, marginRight: 15 },
|
||||
listEmptyComponent: {
|
||||
width: '100%',
|
||||
|
|
|
@ -73,7 +73,7 @@ const ReactionPicker = React.memo(({ onEmojiSelected, message, reactionClose }:
|
|||
};
|
||||
|
||||
return (
|
||||
<View style={[styles.reactionPickerContainer]} testID='reaction-picker'>
|
||||
<View style={styles.reactionPickerContainer} testID='reaction-picker'>
|
||||
<View style={styles.searchbarContainer}>
|
||||
<FormTextInput
|
||||
autoCapitalize='none'
|
||||
|
|
|
@ -831,20 +831,19 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
|||
showReactionPicker = () => {
|
||||
const { showActionSheet, width, height } = this.props;
|
||||
const { reacting, selectedMessage } = this.state;
|
||||
showActionSheet &&
|
||||
showActionSheet({
|
||||
children: (
|
||||
<ReactionPicker
|
||||
show={reacting}
|
||||
message={selectedMessage}
|
||||
onEmojiSelected={this.onReactionPress}
|
||||
reactionClose={this.onReactionClose}
|
||||
width={width}
|
||||
height={height}
|
||||
/>
|
||||
),
|
||||
snaps: [400, '100%']
|
||||
});
|
||||
showActionSheet({
|
||||
children: (
|
||||
<ReactionPicker
|
||||
show={reacting}
|
||||
message={selectedMessage}
|
||||
onEmojiSelected={this.onReactionPress}
|
||||
reactionClose={this.onReactionClose}
|
||||
width={width}
|
||||
height={height}
|
||||
/>
|
||||
),
|
||||
snaps: [400, '100%']
|
||||
});
|
||||
};
|
||||
|
||||
onReactionInit = (message: TAnyMessageModel) => {
|
||||
|
|
Loading…
Reference in New Issue