[IMPROVE] migrate the emoji component
This commit is contained in:
parent
66a4627fec
commit
f5eee1cbaf
|
@ -1,13 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import FastImage from '@rocket.chat/react-native-fast-image';
|
import FastImage from '@rocket.chat/react-native-fast-image';
|
||||||
|
import {TEmoji} from "./index";
|
||||||
|
|
||||||
interface ICustomEmoji {
|
interface ICustomEmoji {
|
||||||
baseUrl: string,
|
baseUrl: string,
|
||||||
emoji: {
|
emoji: TEmoji,
|
||||||
content: any;
|
|
||||||
name: string;
|
|
||||||
extension: any;
|
|
||||||
},
|
|
||||||
style: any
|
style: any
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,8 @@ interface IEmoji {
|
||||||
onEmojiSelected({}: any): void;
|
onEmojiSelected({}: any): void;
|
||||||
emojisPerRow: number;
|
emojisPerRow: number;
|
||||||
width: number;
|
width: number;
|
||||||
|
style: any;
|
||||||
|
tabLabel: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderEmoji = (emoji: any, size: number, baseUrl: string) => {
|
const renderEmoji = (emoji: any, size: number, baseUrl: string) => {
|
||||||
|
@ -27,7 +29,7 @@ const renderEmoji = (emoji: any, size: number, baseUrl: string) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
class EmojiCategory extends React.Component<IEmoji> {
|
class EmojiCategory extends React.Component<Partial<IEmoji>> {
|
||||||
|
|
||||||
renderItem(emoji: any) {
|
renderItem(emoji: any) {
|
||||||
const { baseUrl, onEmojiSelected } = this.props;
|
const { baseUrl, onEmojiSelected } = this.props;
|
||||||
|
@ -35,10 +37,10 @@ class EmojiCategory extends React.Component<IEmoji> {
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
activeOpacity={0.7}
|
activeOpacity={0.7}
|
||||||
key={emoji && emoji.isCustom ? emoji.content : emoji}
|
key={emoji && emoji.isCustom ? emoji.content : emoji}
|
||||||
onPress={() => onEmojiSelected(emoji)}
|
onPress={() => onEmojiSelected!(emoji)}
|
||||||
testID={`reaction-picker-${ emoji && emoji.isCustom ? emoji.content : emoji }`}
|
testID={`reaction-picker-${ emoji && emoji.isCustom ? emoji.content : emoji }`}
|
||||||
>
|
>
|
||||||
{renderEmoji(emoji, EMOJI_SIZE, baseUrl)}
|
{renderEmoji(emoji, EMOJI_SIZE, baseUrl!)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ interface ITabBar {
|
||||||
theme: string
|
theme: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class TabBar extends React.Component<ITabBar> {
|
export default class TabBar extends React.Component<Partial<ITabBar>> {
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps: any) {
|
shouldComponentUpdate(nextProps: any) {
|
||||||
const { activeTab, theme } = this.props;
|
const { activeTab, theme } = this.props;
|
||||||
|
@ -29,16 +29,16 @@ export default class TabBar extends React.Component<ITabBar> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.tabsContainer}>
|
<View style={styles.tabsContainer}>
|
||||||
{tabs.map((tab, i) => (
|
{tabs!.map((tab, i) => (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
activeOpacity={0.7}
|
activeOpacity={0.7}
|
||||||
key={tab}
|
key={tab}
|
||||||
onPress={() => goToPage(i)}
|
onPress={() => goToPage!(i)}
|
||||||
style={styles.tab}
|
style={styles.tab}
|
||||||
testID={`reaction-picker-${ tab }`}
|
testID={`reaction-picker-${ tab }`}
|
||||||
>
|
>
|
||||||
<Text style={[styles.tabEmoji, tabEmojiStyle]}>{tab}</Text>
|
<Text style={[styles.tabEmoji, tabEmojiStyle]}>{tab}</Text>
|
||||||
{activeTab === i ? <View style={[styles.activeTabLine, { backgroundColor: themes[theme].tintColor }]} /> : <View style={styles.tabLine} />}
|
{activeTab === i ? <View style={[styles.activeTabLine, { backgroundColor: themes[theme!].tintColor }]} /> : <View style={styles.tabLine} />}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import ScrollableTabView from 'react-native-scrollable-tab-view';
|
import ScrollableTabView from 'react-native-scrollable-tab-view';
|
||||||
import { dequal } from 'dequal';
|
import { dequal } from 'dequal';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
@ -18,22 +17,30 @@ import shortnameToUnicode from '../../utils/shortnameToUnicode';
|
||||||
import log from '../../utils/log';
|
import log from '../../utils/log';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
|
import {IEmoji} from "../markdown/Emoji";
|
||||||
|
|
||||||
const scrollProps = {
|
const scrollProps = {
|
||||||
keyboardShouldPersistTaps: 'always',
|
keyboardShouldPersistTaps: 'always',
|
||||||
keyboardDismissMode: 'none'
|
keyboardDismissMode: 'none'
|
||||||
};
|
};
|
||||||
|
|
||||||
class EmojiPicker extends Component {
|
export type TEmoji = {
|
||||||
static propTypes = {
|
content: any;
|
||||||
baseUrl: PropTypes.string.isRequired,
|
name: string;
|
||||||
customEmojis: PropTypes.object,
|
extension: any;
|
||||||
onEmojiSelected: PropTypes.func,
|
isCustom: boolean;
|
||||||
tabEmojiStyle: PropTypes.object,
|
}
|
||||||
theme: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props) {
|
interface IEmojiPickerState {
|
||||||
|
frequentlyUsed: [];
|
||||||
|
customEmojis: any;
|
||||||
|
show: boolean;
|
||||||
|
width: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
class EmojiPicker extends Component<IEmoji, IEmojiPickerState> {
|
||||||
|
|
||||||
|
constructor(props: IEmoji) {
|
||||||
super(props);
|
super(props);
|
||||||
const customEmojis = Object.keys(props.customEmojis)
|
const customEmojis = Object.keys(props.customEmojis)
|
||||||
.filter(item => item === props.customEmojis[item].name)
|
.filter(item => item === props.customEmojis[item].name)
|
||||||
|
@ -55,7 +62,7 @@ class EmojiPicker extends Component {
|
||||||
this.setState({ show: true });
|
this.setState({ show: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps, nextState) {
|
shouldComponentUpdate(nextProps: any, nextState: any) {
|
||||||
const { frequentlyUsed, show, width } = this.state;
|
const { frequentlyUsed, show, width } = this.state;
|
||||||
const { theme } = this.props;
|
const { theme } = this.props;
|
||||||
if (nextProps.theme !== theme) {
|
if (nextProps.theme !== theme) {
|
||||||
|
@ -73,19 +80,19 @@ class EmojiPicker extends Component {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onEmojiSelected = (emoji) => {
|
onEmojiSelected = (emoji: TEmoji) => {
|
||||||
try {
|
try {
|
||||||
const { onEmojiSelected } = this.props;
|
const { onEmojiSelected } = this.props;
|
||||||
if (emoji.isCustom) {
|
if (emoji.isCustom) {
|
||||||
this._addFrequentlyUsed({
|
this._addFrequentlyUsed({
|
||||||
content: emoji.content, extension: emoji.extension, isCustom: true
|
content: emoji.content, extension: emoji.extension, isCustom: true
|
||||||
});
|
});
|
||||||
onEmojiSelected(`:${ emoji.content }:`);
|
onEmojiSelected!(`:${ emoji.content }:`);
|
||||||
} else {
|
} else {
|
||||||
const content = emoji;
|
const content = emoji;
|
||||||
this._addFrequentlyUsed({ content, isCustom: false });
|
this._addFrequentlyUsed({ content, isCustom: false });
|
||||||
const shortname = `:${ emoji }:`;
|
const shortname = `:${ emoji }:`;
|
||||||
onEmojiSelected(shortnameToUnicode(shortname), shortname);
|
onEmojiSelected!(shortnameToUnicode(shortname), shortname);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e);
|
log(e);
|
||||||
|
@ -93,10 +100,10 @@ class EmojiPicker extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line react/sort-comp
|
// eslint-disable-next-line react/sort-comp
|
||||||
_addFrequentlyUsed = protectedFunction(async(emoji) => {
|
_addFrequentlyUsed = protectedFunction(async(emoji: TEmoji) => {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
const freqEmojiCollection = db.get('frequently_used_emojis');
|
const freqEmojiCollection = db.get('frequently_used_emojis');
|
||||||
let freqEmojiRecord;
|
let freqEmojiRecord: any;
|
||||||
try {
|
try {
|
||||||
freqEmojiRecord = await freqEmojiCollection.find(emoji.content);
|
freqEmojiRecord = await freqEmojiCollection.find(emoji.content);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -105,11 +112,11 @@ class EmojiPicker extends Component {
|
||||||
|
|
||||||
await db.action(async() => {
|
await db.action(async() => {
|
||||||
if (freqEmojiRecord) {
|
if (freqEmojiRecord) {
|
||||||
await freqEmojiRecord.update((f) => {
|
await freqEmojiRecord.update((f: any) => {
|
||||||
f.count += 1;
|
f.count += 1;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await freqEmojiCollection.create((f) => {
|
await freqEmojiCollection.create((f: any) => {
|
||||||
f._raw = sanitizedRaw({ id: emoji.content }, freqEmojiCollection.schema);
|
f._raw = sanitizedRaw({ id: emoji.content }, freqEmojiCollection.schema);
|
||||||
Object.assign(f, emoji);
|
Object.assign(f, emoji);
|
||||||
f.count = 1;
|
f.count = 1;
|
||||||
|
@ -121,8 +128,8 @@ class EmojiPicker extends Component {
|
||||||
updateFrequentlyUsed = async() => {
|
updateFrequentlyUsed = async() => {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
const frequentlyUsedRecords = await db.get('frequently_used_emojis').query().fetch();
|
const frequentlyUsedRecords = await db.get('frequently_used_emojis').query().fetch();
|
||||||
let frequentlyUsed = orderBy(frequentlyUsedRecords, ['count'], ['desc']);
|
let frequentlyUsed: any = orderBy(frequentlyUsedRecords, ['count'], ['desc']);
|
||||||
frequentlyUsed = frequentlyUsed.map((item) => {
|
frequentlyUsed = frequentlyUsed.map((item: TEmoji) => {
|
||||||
if (item.isCustom) {
|
if (item.isCustom) {
|
||||||
return { content: item.content, extension: item.extension, isCustom: item.isCustom };
|
return { content: item.content, extension: item.extension, isCustom: item.isCustom };
|
||||||
}
|
}
|
||||||
|
@ -131,9 +138,9 @@ class EmojiPicker extends Component {
|
||||||
this.setState({ frequentlyUsed });
|
this.setState({ frequentlyUsed });
|
||||||
}
|
}
|
||||||
|
|
||||||
onLayout = ({ nativeEvent: { layout: { width } } }) => this.setState({ width });
|
onLayout = ({ nativeEvent: { layout: { width } } }: any) => this.setState({ width });
|
||||||
|
|
||||||
renderCategory(category, i, label) {
|
renderCategory(category: any, i: number, label: string) {
|
||||||
const { frequentlyUsed, customEmojis, width } = this.state;
|
const { frequentlyUsed, customEmojis, width } = this.state;
|
||||||
const { baseUrl } = this.props;
|
const { baseUrl } = this.props;
|
||||||
|
|
||||||
|
@ -150,7 +157,7 @@ class EmojiPicker extends Component {
|
||||||
emojis={emojis}
|
emojis={emojis}
|
||||||
onEmojiSelected={emoji => this.onEmojiSelected(emoji)}
|
onEmojiSelected={emoji => this.onEmojiSelected(emoji)}
|
||||||
style={styles.categoryContainer}
|
style={styles.categoryContainer}
|
||||||
width={width}
|
width={width!}
|
||||||
baseUrl={baseUrl}
|
baseUrl={baseUrl}
|
||||||
tabLabel={label}
|
tabLabel={label}
|
||||||
/>
|
/>
|
||||||
|
@ -168,8 +175,9 @@ class EmojiPicker extends Component {
|
||||||
<View onLayout={this.onLayout} style={{ flex: 1 }}>
|
<View onLayout={this.onLayout} style={{ flex: 1 }}>
|
||||||
<ScrollableTabView
|
<ScrollableTabView
|
||||||
renderTabBar={() => <TabBar tabEmojiStyle={tabEmojiStyle} theme={theme} />}
|
renderTabBar={() => <TabBar tabEmojiStyle={tabEmojiStyle} theme={theme} />}
|
||||||
|
/*@ts-ignore*/
|
||||||
contentProps={scrollProps}
|
contentProps={scrollProps}
|
||||||
style={{ backgroundColor: themes[theme].focusedBackground }}
|
style={{ backgroundColor: themes[theme!].focusedBackground }}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
categories.tabs.map((tab, i) => (
|
categories.tabs.map((tab, i) => (
|
||||||
|
@ -184,7 +192,7 @@ class EmojiPicker extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state: IEmojiPickerState) => ({
|
||||||
customEmojis: state.customEmojis
|
customEmojis: state.customEmojis
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -7,14 +7,16 @@ import { themes } from '../../constants/colors';
|
||||||
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
interface IEmoji {
|
export interface IEmoji {
|
||||||
literal: string;
|
literal: string;
|
||||||
isMessageContainsOnlyEmoji: boolean;
|
isMessageContainsOnlyEmoji: boolean;
|
||||||
getCustomEmoji?({}: any): string;
|
getCustomEmoji?: Function;
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
customEmojis?: boolean;
|
customEmojis?: any;
|
||||||
style: object;
|
style: object;
|
||||||
theme?: string;
|
theme?: string;
|
||||||
|
onEmojiSelected?: Function;
|
||||||
|
tabEmojiStyle?: object;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Emoji = React.memo(({
|
const Emoji = React.memo(({
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export const emojisByCategory = {
|
export const emojisByCategory: any = {
|
||||||
people: [
|
people: [
|
||||||
'grinning',
|
'grinning',
|
||||||
'grimacing',
|
'grimacing',
|
||||||
|
|
Loading…
Reference in New Issue