diff --git a/app/containers/EmojiPicker/EmojiCategory.tsx b/app/containers/EmojiPicker/EmojiCategory.tsx index 46d5af685..d8a20b0d3 100644 --- a/app/containers/EmojiPicker/EmojiCategory.tsx +++ b/app/containers/EmojiPicker/EmojiCategory.tsx @@ -26,50 +26,42 @@ const renderEmoji = (emoji: IEmoji, size: number, baseUrl: string) => { ); }; -class EmojiCategory extends React.Component { - renderItem(emoji: IEmoji) { - const { baseUrl, onEmojiSelected } = this.props; - return ( - onEmojiSelected(emoji)} - testID={`reaction-picker-${emoji && emoji.isCustom ? emoji.content : emoji}`} - > - {renderEmoji(emoji, EMOJI_SIZE, baseUrl)} - - ); +const EmojiCategory = React.memo(({ baseUrl, onEmojiSelected, emojis, width, ...props }: IEmojiCategory) => { + const renderItem = (emoji: IEmoji) => ( + onEmojiSelected(emoji)} + testID={`reaction-picker-${emoji && emoji.isCustom ? emoji.content : emoji}`}> + {renderEmoji(emoji, EMOJI_SIZE, baseUrl)} + + ); + + if (!width) { + return null; } - render() { - const { emojis, width } = this.props; + const numColumns = Math.trunc(width / EMOJI_SIZE); + const marginHorizontal = (width - numColumns * EMOJI_SIZE) / 2; - if (!width) { - return null; - } - - const numColumns = Math.trunc(width / EMOJI_SIZE); - const marginHorizontal = (width - numColumns * EMOJI_SIZE) / 2; - - return ( - (item && item.isCustom && item.content) || item} - data={emojis} - extraData={this.props} - renderItem={({ item }) => this.renderItem(item)} - numColumns={numColumns} - initialNumToRender={45} - removeClippedSubviews - {...scrollPersistTaps} - keyboardDismissMode={'none'} - /> - ); - } -} + return ( + (item && item.isCustom && item.content) || item} + data={emojis} + extraData={{ baseUrl, onEmojiSelected, width, ...props }} + renderItem={({ item }) => renderItem(item)} + numColumns={numColumns} + initialNumToRender={45} + removeClippedSubviews + {...scrollPersistTaps} + keyboardDismissMode={'none'} + /> + ); +}); export default EmojiCategory; diff --git a/app/containers/EmojiPicker/TabBar.tsx b/app/containers/EmojiPicker/TabBar.tsx index c26b41722..887e93c81 100644 --- a/app/containers/EmojiPicker/TabBar.tsx +++ b/app/containers/EmojiPicker/TabBar.tsx @@ -2,55 +2,41 @@ import React from 'react'; import { StyleProp, Text, TextStyle, TouchableOpacity, View } from 'react-native'; import styles from './styles'; -import { themes } from '../../lib/constants'; -import { TSupportedThemes } from '../../theme'; +import { useTheme } from '../../theme'; interface ITabBarProps { goToPage?: (page: number) => void; activeTab?: number; tabs?: string[]; tabEmojiStyle: StyleProp; - theme: TSupportedThemes; } -export default class TabBar extends React.Component { - shouldComponentUpdate(nextProps: ITabBarProps) { - const { activeTab, theme } = this.props; - if (nextProps.activeTab !== activeTab) { - return true; - } - if (nextProps.theme !== theme) { - return true; - } - return false; - } +const TabBar = React.memo(({ activeTab, tabs, goToPage, tabEmojiStyle }: ITabBarProps) => { + const { colors } = useTheme(); - render() { - const { tabs, goToPage, tabEmojiStyle, activeTab, theme } = this.props; + return ( + + {tabs?.map((tab, i) => ( + { + if (goToPage) { + goToPage(i); + } + }} + style={styles.tab} + testID={`reaction-picker-${tab}`}> + {tab} + {activeTab === i ? ( + + ) : ( + + )} + + ))} + + ); +}); - return ( - - {tabs?.map((tab, i) => ( - { - if (goToPage) { - goToPage(i); - } - }} - style={styles.tab} - testID={`reaction-picker-${tab}`} - > - {tab} - {activeTab === i ? ( - - ) : ( - - )} - - ))} - - ); - } -} +export default TabBar; diff --git a/app/containers/EmojiPicker/index.tsx b/app/containers/EmojiPicker/index.tsx index c9cdf7059..a456149de 100644 --- a/app/containers/EmojiPicker/index.tsx +++ b/app/containers/EmojiPicker/index.tsx @@ -1,11 +1,8 @@ -import React, { Component } from 'react'; +import React, { useEffect, useState } from 'react'; import { StyleProp, TextStyle, View } from 'react-native'; import ScrollableTabView from 'react-native-scrollable-tab-view'; -import { dequal } from 'dequal'; -import { connect } from 'react-redux'; import orderBy from 'lodash/orderBy'; import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; -import { ImageStyle } from 'react-native-fast-image'; import TabBar from './TabBar'; import EmojiCategory from './EmojiCategory'; @@ -16,74 +13,43 @@ 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 { themes } from '../../lib/constants'; -import { TSupportedThemes } from '../../theme'; -import { IEmoji, TGetCustomEmoji, IApplicationState, ICustomEmojis, TFrequentlyUsedEmojiModel } from '../../definitions'; +import { useTheme } from '../../theme'; +import { IEmoji, ICustomEmojis, TFrequentlyUsedEmojiModel } from '../../definitions'; +import { useAppSelector } from '../../lib/hooks'; interface IEmojiPickerProps { - isMessageContainsOnlyEmoji?: boolean; - getCustomEmoji?: TGetCustomEmoji; - baseUrl: string; - customEmojis: ICustomEmojis; - style?: StyleProp; - theme: TSupportedThemes; onEmojiSelected: (emoji: string, shortname?: string) => void; tabEmojiStyle?: StyleProp; } -interface IEmojiPickerState { - frequentlyUsed: (string | { content?: string; extension?: string; isCustom: boolean })[]; - customEmojis: any; - show: boolean; - width: number | null; -} +const EmojiPicker = React.memo(({ onEmojiSelected, tabEmojiStyle }: IEmojiPickerProps) => { + const [frequentlyUsed, setFrequentlyUsed] = useState([]); + const [show, setShow] = useState(false); + const [width, setWidth] = useState(null); + const { colors } = useTheme(); -class EmojiPicker extends Component { - constructor(props: IEmojiPickerProps) { - super(props); - const customEmojis = Object.keys(props.customEmojis) - .filter(item => item === props.customEmojis[item].name) - .map(item => ({ - content: props.customEmojis[item].name, - extension: props.customEmojis[item].extension, - isCustom: true - })); - this.state = { - frequentlyUsed: [], - customEmojis, - show: false, - width: null + const allCustomEmojis: ICustomEmojis = useAppSelector(state => state.customEmojis); + const baseUrl = useAppSelector(state=>state.server?.server) + const customEmojis = Object.keys(allCustomEmojis) + .filter(item => item === allCustomEmojis[item].name) + .map(item => ({ + content: allCustomEmojis[item].name, + extension: allCustomEmojis[item].extension, + isCustom: true + })); + + useEffect(() => { + const init = async () => { + await updateFrequentlyUsed(); + setShow(true); }; - } + init(); + }, []); - async componentDidMount() { - await this.updateFrequentlyUsed(); - this.setState({ show: true }); - } - - shouldComponentUpdate(nextProps: IEmojiPickerProps, nextState: IEmojiPickerState) { - const { frequentlyUsed, show, width } = this.state; - const { theme } = this.props; - if (nextProps.theme !== theme) { - return true; - } - if (nextState.show !== show) { - return true; - } - if (nextState.width !== width) { - return true; - } - if (!dequal(nextState.frequentlyUsed, frequentlyUsed)) { - return true; - } - return false; - } - - onEmojiSelected = (emoji: IEmoji) => { + const handleEmojiSelect = (emoji: IEmoji) => { try { - const { onEmojiSelected } = this.props; if (emoji.isCustom) { - this._addFrequentlyUsed({ + _addFrequentlyUsed({ content: emoji.content, extension: emoji.extension, isCustom: true @@ -91,7 +57,7 @@ class EmojiPicker extends Component { onEmojiSelected(`:${emoji.content}:`); } else { const content = emoji; - this._addFrequentlyUsed({ content, isCustom: false }); + _addFrequentlyUsed({ content, isCustom: false }); const shortname = `:${emoji}:`; onEmojiSelected(shortnameToUnicode(shortname), shortname); } @@ -100,7 +66,7 @@ class EmojiPicker extends Component { } }; - _addFrequentlyUsed = protectedFunction(async (emoji: IEmoji) => { + const _addFrequentlyUsed = protectedFunction(async (emoji: IEmoji) => { const db = database.active; const freqEmojiCollection = db.get('frequently_used_emojis'); let freqEmojiRecord: TFrequentlyUsedEmojiModel; @@ -127,29 +93,26 @@ class EmojiPicker extends Component { }); }); - updateFrequentlyUsed = async () => { + const updateFrequentlyUsed = async () => { const db = database.active; const frequentlyUsedRecords = await db.get('frequently_used_emojis').query().fetch(); const frequentlyUsedOrdered = orderBy(frequentlyUsedRecords, ['count'], ['desc']); - const frequentlyUsed = frequentlyUsedOrdered.map(item => { + const frequentlyUsedEmojis = frequentlyUsedOrdered.map(item => { if (item.isCustom) { return { content: item.content, extension: item.extension, isCustom: item.isCustom }; } return shortnameToUnicode(`${item.content}`); - }); - this.setState({ frequentlyUsed }); + }) as IEmoji[]; + setFrequentlyUsed(frequentlyUsedEmojis); }; - onLayout = ({ + const onLayout = ({ nativeEvent: { layout: { width } } - }: any) => this.setState({ width }); - - renderCategory(category: keyof typeof emojisByCategory, i: number, label: string) { - const { frequentlyUsed, customEmojis, width } = this.state; - const { baseUrl } = this.props; + }: any) => setWidth(width); + const renderCategory = (category: keyof typeof emojisByCategory, i: number, label: string) => { let emojis = []; if (i === 0) { emojis = frequentlyUsed; @@ -160,47 +123,36 @@ class EmojiPicker extends Component { } return ( this.onEmojiSelected(emoji)} + emojis={emojis as IEmoji[]} + onEmojiSelected={(emoji: IEmoji) => handleEmojiSelect(emoji)} style={styles.categoryContainer} width={width} baseUrl={baseUrl} tabLabel={label} /> ); + }; + + if (!show) { + return null; } - - render() { - const { show, frequentlyUsed } = this.state; - const { tabEmojiStyle, theme } = this.props; - - if (!show) { - return null; - } - return ( - - } - contentProps={{ - keyboardShouldPersistTaps: 'always', - keyboardDismissMode: 'none' - }} - style={{ backgroundColor: themes[theme].focusedBackground }} - > - {categories.tabs.map((tab: any, i) => - i === 0 && frequentlyUsed.length === 0 - ? null // when no frequentlyUsed don't show the tab - : this.renderCategory(tab.category, i, tab.tabLabel) - )} - - - ); - } -} - -const mapStateToProps = (state: IApplicationState) => ({ - customEmojis: state.customEmojis, - baseUrl: state.share.server.server || state.server.server + return ( + + } + 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) + )} + + + ); }); -export default connect(mapStateToProps)(EmojiPicker); +export default EmojiPicker;