2018-01-30 19:48:26 +00:00
|
|
|
import React, { Component } from 'react';
|
2019-11-25 20:01:17 +00:00
|
|
|
import { View } from 'react-native';
|
2018-01-16 18:48:05 +00:00
|
|
|
import ScrollableTabView from 'react-native-scrollable-tab-view';
|
2021-02-26 16:01:45 +00:00
|
|
|
import { dequal } from 'dequal';
|
2019-09-16 20:26:32 +00:00
|
|
|
import { connect } from 'react-redux';
|
|
|
|
import orderBy from 'lodash/orderBy';
|
|
|
|
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
|
2018-12-21 10:55:35 +00:00
|
|
|
|
2018-01-16 18:48:05 +00:00
|
|
|
import TabBar from './TabBar';
|
|
|
|
import EmojiCategory from './EmojiCategory';
|
|
|
|
import styles from './styles';
|
|
|
|
import categories from './categories';
|
2019-09-16 20:26:32 +00:00
|
|
|
import database from '../../lib/database';
|
2018-01-30 19:48:26 +00:00
|
|
|
import { emojisByCategory } from '../../emojis';
|
2018-05-18 17:55:08 +00:00
|
|
|
import protectedFunction from '../../lib/methods/helpers/protectedFunction';
|
2019-12-11 19:00:38 +00:00
|
|
|
import shortnameToUnicode from '../../utils/shortnameToUnicode';
|
2019-09-16 20:26:32 +00:00
|
|
|
import log from '../../utils/log';
|
2019-12-04 16:39:53 +00:00
|
|
|
import { themes } from '../../constants/colors';
|
|
|
|
import { withTheme } from '../../theme';
|
2022-02-17 15:27:01 +00:00
|
|
|
import { IEmoji } from '../../definitions/IEmoji';
|
2018-01-16 18:48:05 +00:00
|
|
|
|
2018-01-30 19:48:26 +00:00
|
|
|
const scrollProps = {
|
2018-02-08 14:08:50 +00:00
|
|
|
keyboardShouldPersistTaps: 'always',
|
|
|
|
keyboardDismissMode: 'none'
|
2018-01-30 19:48:26 +00:00
|
|
|
};
|
2018-01-16 18:48:05 +00:00
|
|
|
|
2021-09-13 20:41:05 +00:00
|
|
|
interface IEmojiPickerProps {
|
|
|
|
isMessageContainsOnlyEmoji: boolean;
|
|
|
|
getCustomEmoji?: Function;
|
|
|
|
baseUrl: string;
|
|
|
|
customEmojis?: any;
|
|
|
|
style: object;
|
|
|
|
theme?: string;
|
2021-12-03 19:27:57 +00:00
|
|
|
onEmojiSelected?: ((emoji: any) => void) | ((keyboardId: any, params?: any) => void);
|
2021-09-13 20:41:05 +00:00
|
|
|
tabEmojiStyle?: object;
|
|
|
|
}
|
2018-01-16 18:48:05 +00:00
|
|
|
|
2021-09-13 20:41:05 +00:00
|
|
|
interface IEmojiPickerState {
|
2022-02-01 13:39:09 +00:00
|
|
|
frequentlyUsed: (string | { content?: string; extension?: string; isCustom: boolean })[];
|
2021-09-13 20:41:05 +00:00
|
|
|
customEmojis: any;
|
|
|
|
show: boolean;
|
|
|
|
width: number | null;
|
|
|
|
}
|
|
|
|
|
|
|
|
class EmojiPicker extends Component<IEmojiPickerProps, IEmojiPickerState> {
|
|
|
|
constructor(props: IEmojiPickerProps) {
|
2018-01-16 18:48:05 +00:00
|
|
|
super(props);
|
2019-09-16 20:26:32 +00:00
|
|
|
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
|
|
|
|
}));
|
2018-01-16 18:48:05 +00:00
|
|
|
this.state = {
|
|
|
|
frequentlyUsed: [],
|
2019-09-16 20:26:32 +00:00
|
|
|
customEmojis,
|
2019-11-25 20:01:17 +00:00
|
|
|
show: false,
|
|
|
|
width: null
|
2018-01-16 18:48:05 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-09-16 20:26:32 +00:00
|
|
|
async componentDidMount() {
|
|
|
|
await this.updateFrequentlyUsed();
|
|
|
|
this.setState({ show: true });
|
2018-12-21 10:55:35 +00:00
|
|
|
}
|
|
|
|
|
2021-09-13 20:41:05 +00:00
|
|
|
shouldComponentUpdate(nextProps: any, nextState: any) {
|
2019-11-25 20:01:17 +00:00
|
|
|
const { frequentlyUsed, show, width } = this.state;
|
2019-12-04 16:39:53 +00:00
|
|
|
const { theme } = this.props;
|
|
|
|
if (nextProps.theme !== theme) {
|
|
|
|
return true;
|
|
|
|
}
|
2018-12-21 10:55:35 +00:00
|
|
|
if (nextState.show !== show) {
|
|
|
|
return true;
|
|
|
|
}
|
2019-11-25 20:01:17 +00:00
|
|
|
if (nextState.width !== width) {
|
2018-12-21 10:55:35 +00:00
|
|
|
return true;
|
|
|
|
}
|
2021-02-26 16:01:45 +00:00
|
|
|
if (!dequal(nextState.frequentlyUsed, frequentlyUsed)) {
|
2018-12-21 10:55:35 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2018-02-08 14:08:50 +00:00
|
|
|
}
|
2018-09-25 19:28:42 +00:00
|
|
|
|
2021-09-13 20:41:05 +00:00
|
|
|
onEmojiSelected = (emoji: IEmoji) => {
|
2019-09-16 20:26:32 +00:00
|
|
|
try {
|
|
|
|
const { onEmojiSelected } = this.props;
|
|
|
|
if (emoji.isCustom) {
|
|
|
|
this._addFrequentlyUsed({
|
2021-09-13 20:41:05 +00:00
|
|
|
content: emoji.content,
|
|
|
|
extension: emoji.extension,
|
|
|
|
isCustom: true
|
2019-09-16 20:26:32 +00:00
|
|
|
});
|
2021-09-13 20:41:05 +00:00
|
|
|
onEmojiSelected!(`:${emoji.content}:`);
|
2019-09-16 20:26:32 +00:00
|
|
|
} else {
|
|
|
|
const content = emoji;
|
|
|
|
this._addFrequentlyUsed({ content, isCustom: false });
|
2021-09-13 20:41:05 +00:00
|
|
|
const shortname = `:${emoji}:`;
|
|
|
|
onEmojiSelected!(shortnameToUnicode(shortname), shortname);
|
2019-09-16 20:26:32 +00:00
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
log(e);
|
2018-01-16 18:48:05 +00:00
|
|
|
}
|
2021-09-13 20:41:05 +00:00
|
|
|
};
|
2018-03-02 21:31:44 +00:00
|
|
|
|
2021-09-13 20:41:05 +00:00
|
|
|
_addFrequentlyUsed = protectedFunction(async (emoji: IEmoji) => {
|
2019-09-16 20:26:32 +00:00
|
|
|
const db = database.active;
|
2021-02-26 16:25:51 +00:00
|
|
|
const freqEmojiCollection = db.get('frequently_used_emojis');
|
2021-09-13 20:41:05 +00:00
|
|
|
let freqEmojiRecord: any;
|
2019-10-08 12:36:15 +00:00
|
|
|
try {
|
|
|
|
freqEmojiRecord = await freqEmojiCollection.find(emoji.content);
|
|
|
|
} catch (error) {
|
|
|
|
// Do nothing
|
|
|
|
}
|
|
|
|
|
2022-02-01 13:39:09 +00:00
|
|
|
await db.write(async () => {
|
2019-10-08 12:36:15 +00:00
|
|
|
if (freqEmojiRecord) {
|
2021-09-13 20:41:05 +00:00
|
|
|
await freqEmojiRecord.update((f: any) => {
|
2019-09-16 20:26:32 +00:00
|
|
|
f.count += 1;
|
|
|
|
});
|
2019-10-08 12:36:15 +00:00
|
|
|
} else {
|
2021-09-13 20:41:05 +00:00
|
|
|
await freqEmojiCollection.create((f: any) => {
|
2019-10-08 12:36:15 +00:00
|
|
|
f._raw = sanitizedRaw({ id: emoji.content }, freqEmojiCollection.schema);
|
|
|
|
Object.assign(f, emoji);
|
|
|
|
f.count = 1;
|
|
|
|
});
|
2019-09-16 20:26:32 +00:00
|
|
|
}
|
2018-01-16 18:48:05 +00:00
|
|
|
});
|
2021-09-13 20:41:05 +00:00
|
|
|
});
|
2018-09-25 19:28:42 +00:00
|
|
|
|
2021-09-13 20:41:05 +00:00
|
|
|
updateFrequentlyUsed = async () => {
|
2019-09-16 20:26:32 +00:00
|
|
|
const db = database.active;
|
2021-02-26 16:25:51 +00:00
|
|
|
const frequentlyUsedRecords = await db.get('frequently_used_emojis').query().fetch();
|
2022-02-01 13:39:09 +00:00
|
|
|
const frequentlyUsedOrdered = orderBy(frequentlyUsedRecords, ['count'], ['desc']);
|
|
|
|
const frequentlyUsed = frequentlyUsedOrdered.map(item => {
|
2018-01-16 18:48:05 +00:00
|
|
|
if (item.isCustom) {
|
2019-09-16 20:26:32 +00:00
|
|
|
return { content: item.content, extension: item.extension, isCustom: item.isCustom };
|
2018-01-16 18:48:05 +00:00
|
|
|
}
|
2021-09-13 20:41:05 +00:00
|
|
|
return shortnameToUnicode(`${item.content}`);
|
2018-01-16 18:48:05 +00:00
|
|
|
});
|
|
|
|
this.setState({ frequentlyUsed });
|
2021-09-13 20:41:05 +00:00
|
|
|
};
|
2018-01-16 18:48:05 +00:00
|
|
|
|
2021-09-13 20:41:05 +00:00
|
|
|
onLayout = ({
|
|
|
|
nativeEvent: {
|
|
|
|
layout: { width }
|
|
|
|
}
|
|
|
|
}: any) => this.setState({ width });
|
2019-11-25 20:01:17 +00:00
|
|
|
|
2021-09-13 20:41:05 +00:00
|
|
|
renderCategory(category: any, i: number, label: string) {
|
2019-11-25 20:01:17 +00:00
|
|
|
const { frequentlyUsed, customEmojis, width } = this.state;
|
|
|
|
const { baseUrl } = this.props;
|
2018-09-25 19:28:42 +00:00
|
|
|
|
2018-01-16 18:48:05 +00:00
|
|
|
let emojis = [];
|
|
|
|
if (i === 0) {
|
2018-09-25 19:28:42 +00:00
|
|
|
emojis = frequentlyUsed;
|
2018-01-16 18:48:05 +00:00
|
|
|
} else if (i === 1) {
|
2018-09-25 19:28:42 +00:00
|
|
|
emojis = customEmojis;
|
2018-01-16 18:48:05 +00:00
|
|
|
} else {
|
|
|
|
emojis = emojisByCategory[category];
|
|
|
|
}
|
|
|
|
return (
|
2018-01-30 19:48:26 +00:00
|
|
|
<EmojiCategory
|
|
|
|
emojis={emojis}
|
2021-09-13 20:41:05 +00:00
|
|
|
onEmojiSelected={(emoji: IEmoji) => this.onEmojiSelected(emoji)}
|
2018-01-30 19:48:26 +00:00
|
|
|
style={styles.categoryContainer}
|
2021-09-13 20:41:05 +00:00
|
|
|
width={width!}
|
2018-09-25 19:28:42 +00:00
|
|
|
baseUrl={baseUrl}
|
2019-11-25 20:01:17 +00:00
|
|
|
tabLabel={label}
|
2018-01-30 19:48:26 +00:00
|
|
|
/>
|
2018-01-16 18:48:05 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
2019-06-27 16:16:05 +00:00
|
|
|
const { show, frequentlyUsed } = this.state;
|
2019-12-04 16:39:53 +00:00
|
|
|
const { tabEmojiStyle, theme } = this.props;
|
2018-09-25 19:28:42 +00:00
|
|
|
|
|
|
|
if (!show) {
|
2018-01-30 19:48:26 +00:00
|
|
|
return null;
|
|
|
|
}
|
2018-01-16 18:48:05 +00:00
|
|
|
return (
|
2019-11-25 20:01:17 +00:00
|
|
|
<View onLayout={this.onLayout} style={{ flex: 1 }}>
|
|
|
|
<ScrollableTabView
|
2019-12-04 16:39:53 +00:00
|
|
|
renderTabBar={() => <TabBar tabEmojiStyle={tabEmojiStyle} theme={theme} />}
|
2021-09-13 20:41:05 +00:00
|
|
|
/* @ts-ignore*/
|
2019-11-25 20:01:17 +00:00
|
|
|
contentProps={scrollProps}
|
2021-09-13 20:41:05 +00:00
|
|
|
style={{ backgroundColor: themes[theme!].focusedBackground }}>
|
|
|
|
{categories.tabs.map((tab, i) =>
|
|
|
|
i === 0 && frequentlyUsed.length === 0
|
|
|
|
? null // when no frequentlyUsed don't show the tab
|
|
|
|
: this.renderCategory(tab.category, i, tab.tabLabel)
|
|
|
|
)}
|
2019-11-25 20:01:17 +00:00
|
|
|
</ScrollableTabView>
|
|
|
|
</View>
|
2018-01-16 18:48:05 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2019-09-16 20:26:32 +00:00
|
|
|
|
2021-09-13 20:41:05 +00:00
|
|
|
const mapStateToProps = (state: any) => ({
|
2019-09-16 20:26:32 +00:00
|
|
|
customEmojis: state.customEmojis
|
|
|
|
});
|
|
|
|
|
2021-12-03 19:27:57 +00:00
|
|
|
// TODO - remove this as any, at the new PR to fix the HOC erros
|
|
|
|
export default connect(mapStateToProps)(withTheme(EmojiPicker)) as any;
|