Add searchbar for Emojis
This commit is contained in:
parent
349c56ba45
commit
8ea5755345
|
@ -47,7 +47,7 @@ const EmojiCategory = React.memo(({ baseUrl, onEmojiSelected, emojis, width, tab
|
||||||
return (
|
return (
|
||||||
<FlatList
|
<FlatList
|
||||||
// rerender FlatList in case of width changes
|
// rerender FlatList in case of width changes
|
||||||
key={`emoji-category-${numColumns}`}
|
key={`emoji-category-${width}`}
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
keyExtractor={item => (item && item.isCustom && item.content) || item}
|
keyExtractor={item => (item && item.isCustom && item.content) || item}
|
||||||
data={emojis}
|
data={emojis}
|
||||||
|
|
|
@ -7,11 +7,11 @@ import { CustomIcon } from '../CustomIcon';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { IFooterProps } from './interfaces';
|
import { IFooterProps } from './interfaces';
|
||||||
|
|
||||||
const Footer = React.memo(({ onBackspacePressed }: IFooterProps) => {
|
const Footer = React.memo(({ onSearchPressed, onBackspacePressed }: IFooterProps) => {
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
return (
|
return (
|
||||||
<View style={[styles.footerContainer, { backgroundColor: colors.bannerBackground }]}>
|
<View style={[styles.footerContainer, { backgroundColor: colors.bannerBackground }]}>
|
||||||
<BorderlessButton activeOpacity={0.7} onPress={() => console.log('Search!')} style={styles.footerButtonsContainer}>
|
<BorderlessButton activeOpacity={0.7} onPress={onSearchPressed} style={styles.footerButtonsContainer}>
|
||||||
<CustomIcon color={colors.auxiliaryTintColor} size={24} name='search' />
|
<CustomIcon color={colors.auxiliaryTintColor} size={24} name='search' />
|
||||||
</BorderlessButton>
|
</BorderlessButton>
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ const EmojiPicker = React.memo(({ onItemClicked, tabEmojiStyle }: IEmojiPickerPr
|
||||||
const content = emoji;
|
const content = emoji;
|
||||||
_addFrequentlyUsed({ content, isCustom: false });
|
_addFrequentlyUsed({ content, isCustom: false });
|
||||||
const shortname = `:${emoji}:`;
|
const shortname = `:${emoji}:`;
|
||||||
onItemClicked(EventTypes.EMOJI_PRESSED, shortnameToUnicode(shortname), shortname);
|
onItemClicked(EventTypes.EMOJI_PRESSED, shortnameToUnicode(shortname));
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e);
|
log(e);
|
||||||
|
@ -131,8 +131,6 @@ const EmojiPicker = React.memo(({ onItemClicked, tabEmojiStyle }: IEmojiPickerPr
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onBackspacePressed = () => onItemClicked(EventTypes.BACKSPACE_PRESSED);
|
|
||||||
|
|
||||||
if (!show) {
|
if (!show) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -154,7 +152,10 @@ const EmojiPicker = React.memo(({ onItemClicked, tabEmojiStyle }: IEmojiPickerPr
|
||||||
: renderCategory(tab.category, i, tab.tabLabel, tabsCount)
|
: renderCategory(tab.category, i, tab.tabLabel, tabsCount)
|
||||||
)}
|
)}
|
||||||
</ScrollableTabView>
|
</ScrollableTabView>
|
||||||
<Footer onBackspacePressed={onBackspacePressed} />
|
<Footer
|
||||||
|
onSearchPressed={() => onItemClicked(EventTypes.SEARCH_PRESSED)}
|
||||||
|
onBackspacePressed={() => onItemClicked(EventTypes.BACKSPACE_PRESSED)}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,16 +2,19 @@ import { StyleProp, TextStyle } from 'react-native';
|
||||||
|
|
||||||
export enum EventTypes {
|
export enum EventTypes {
|
||||||
EMOJI_PRESSED = 'emojiPressed',
|
EMOJI_PRESSED = 'emojiPressed',
|
||||||
BACKSPACE_PRESSED = 'backspacePressed'
|
BACKSPACE_PRESSED = 'backspacePressed',
|
||||||
|
SEARCH_PRESSED = 'searchPressed'
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IEmojiPickerProps {
|
export interface IEmojiPickerProps {
|
||||||
onItemClicked: (event: EventTypes, emoji?: string, shortname?: string) => void;
|
baseUrl: string;
|
||||||
|
onItemClicked: (event: EventTypes, emoji?: string) => void;
|
||||||
tabEmojiStyle?: StyleProp<TextStyle>;
|
tabEmojiStyle?: StyleProp<TextStyle>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IFooterProps {
|
export interface IFooterProps {
|
||||||
onBackspacePressed: () => void;
|
onBackspacePressed: () => void;
|
||||||
|
onSearchPressed: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ITabBarProps {
|
export interface ITabBarProps {
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { View, Text, TouchableOpacity, TextInput, FlatList } from 'react-native';
|
||||||
|
import { orderBy } from 'lodash';
|
||||||
|
|
||||||
|
import FormTextInput from '../TextInput/FormTextInput';
|
||||||
|
import { useTheme } from '../../theme';
|
||||||
|
import I18n from '../../i18n';
|
||||||
|
import { CustomIcon } from '../CustomIcon';
|
||||||
|
import { IEmoji } from '../../definitions';
|
||||||
|
import shortnameToUnicode from '../../lib/methods/helpers/shortnameToUnicode';
|
||||||
|
import CustomEmoji from '../EmojiPicker/CustomEmoji';
|
||||||
|
import database from '../../lib/database';
|
||||||
|
|
||||||
|
interface IEmojiSearchbarProps {
|
||||||
|
openEmoji: () => void;
|
||||||
|
onChangeText: (value: string) => void;
|
||||||
|
emojis: IEmoji[];
|
||||||
|
onEmojiSelected: (emoji: IEmoji) => void;
|
||||||
|
baseUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderEmoji = (emoji: IEmoji, size: number, baseUrl: string) => {
|
||||||
|
if (emoji.name) {
|
||||||
|
return <CustomEmoji style={{ height: size, width: size }} emoji={emoji} baseUrl={baseUrl} />;
|
||||||
|
}
|
||||||
|
return <Text style={{ fontSize: size }}>{shortnameToUnicode(`:${emoji}:`)}</Text>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const EmojiSearchbar = React.forwardRef<TextInput, IEmojiSearchbarProps>(
|
||||||
|
({ openEmoji, onChangeText, emojis, onEmojiSelected, baseUrl }, ref) => {
|
||||||
|
console.log('Emojis', emojis);
|
||||||
|
const { colors, theme } = useTheme();
|
||||||
|
const [searchText, setSearchText] = useState<string>('');
|
||||||
|
const [frequentlyUsed, setFrequentlyUsed] = useState([]);
|
||||||
|
|
||||||
|
const getFrequentlyUsedEmojis = async () => {
|
||||||
|
const db = database.active;
|
||||||
|
const frequentlyUsedRecords = await db.get('frequently_used_emojis').query().fetch();
|
||||||
|
const frequentlyUsedOrdered = orderBy(frequentlyUsedRecords, ['count'], ['desc']);
|
||||||
|
const frequentlyUsedEmojis = frequentlyUsedOrdered.map(item => {
|
||||||
|
if (item.isCustom) {
|
||||||
|
return { name: item.content, extension: item.extension };
|
||||||
|
}
|
||||||
|
return item.content;
|
||||||
|
});
|
||||||
|
// @ts-ignore
|
||||||
|
setFrequentlyUsed(frequentlyUsedEmojis);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getFrequentlyUsedEmojis();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleTextChange = (text: string) => {
|
||||||
|
setSearchText(text);
|
||||||
|
onChangeText(text);
|
||||||
|
if (!text) getFrequentlyUsedEmojis();
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderItem = (emoji: IEmoji) => {
|
||||||
|
const emojiSize = 30;
|
||||||
|
return (
|
||||||
|
<View style={{ justifyContent: 'center', marginHorizontal: 2 }}>
|
||||||
|
<TouchableOpacity
|
||||||
|
activeOpacity={0.7}
|
||||||
|
// @ts-ignore
|
||||||
|
key={emoji && emoji.isCustom ? emoji.content : emoji}
|
||||||
|
onPress={() => onEmojiSelected(emoji)}>
|
||||||
|
{renderEmoji(emoji, emojiSize, baseUrl)}
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<View style={{ height: 50, paddingHorizontal: 5 }}>
|
||||||
|
<FlatList horizontal data={searchText ? emojis : frequentlyUsed} renderItem={({ item }) => renderItem(item)} />
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
height: 50,
|
||||||
|
marginVertical: 10,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center'
|
||||||
|
}}>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={{ marginHorizontal: 10, justifyContent: 'center', height: '100%' }}
|
||||||
|
activeOpacity={0.7}
|
||||||
|
onPress={openEmoji}>
|
||||||
|
<CustomIcon name='chevron-left' size={30} color={colors.collapsibleChevron} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
<View style={{ flex: 1 }}>
|
||||||
|
<FormTextInput
|
||||||
|
inputRef={ref}
|
||||||
|
autoCapitalize='none'
|
||||||
|
autoCorrect={false}
|
||||||
|
blurOnSubmit
|
||||||
|
placeholder={I18n.t('Search_emoji')}
|
||||||
|
returnKeyType='search'
|
||||||
|
underlineColorAndroid='transparent'
|
||||||
|
onChangeText={handleTextChange}
|
||||||
|
style={{ backgroundColor: colors.passcodeButtonActive, padding: 10, borderRadius: 5 }}
|
||||||
|
containerStyle={{ height: '100%', justifyContent: 'center', marginBottom: 0, marginRight: 15 }}
|
||||||
|
value={searchText}
|
||||||
|
theme={theme}
|
||||||
|
onClearInput={() => handleTextChange('')}
|
||||||
|
iconRight={'search'}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export default EmojiSearchbar;
|
|
@ -4,5 +4,6 @@ export const MENTIONS_TRACKING_TYPE_COMMANDS = '/';
|
||||||
export const MENTIONS_TRACKING_TYPE_ROOMS = '#';
|
export const MENTIONS_TRACKING_TYPE_ROOMS = '#';
|
||||||
export const MENTIONS_TRACKING_TYPE_CANNED = '!';
|
export const MENTIONS_TRACKING_TYPE_CANNED = '!';
|
||||||
export const MENTIONS_COUNT_TO_DISPLAY = 4;
|
export const MENTIONS_COUNT_TO_DISPLAY = 4;
|
||||||
|
export const MAX_EMOJIS_TO_DISPLAY = 20;
|
||||||
|
|
||||||
export const TIMEOUT_CLOSE_EMOJI = 300;
|
export const TIMEOUT_CLOSE_EMOJI = 300;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { Alert, Keyboard, NativeModules, Text, View } from 'react-native';
|
import { Alert, Keyboard, NativeModules, Text, View, TextInput as RNTextInput } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { KeyboardAccessoryView } from 'react-native-ui-lib/keyboard';
|
import { KeyboardAccessoryView } from 'react-native-ui-lib/keyboard';
|
||||||
import ImagePicker, { Image, ImageOrVideo, Options } from 'react-native-image-crop-picker';
|
import ImagePicker, { Image, ImageOrVideo, Options } from 'react-native-image-crop-picker';
|
||||||
|
@ -34,7 +34,8 @@ import {
|
||||||
MENTIONS_TRACKING_TYPE_EMOJIS,
|
MENTIONS_TRACKING_TYPE_EMOJIS,
|
||||||
MENTIONS_TRACKING_TYPE_ROOMS,
|
MENTIONS_TRACKING_TYPE_ROOMS,
|
||||||
MENTIONS_TRACKING_TYPE_USERS,
|
MENTIONS_TRACKING_TYPE_USERS,
|
||||||
TIMEOUT_CLOSE_EMOJI
|
TIMEOUT_CLOSE_EMOJI,
|
||||||
|
MAX_EMOJIS_TO_DISPLAY
|
||||||
} from './constants';
|
} from './constants';
|
||||||
import CommandsPreview from './CommandsPreview';
|
import CommandsPreview from './CommandsPreview';
|
||||||
import { getUserSelector } from '../../selectors/login';
|
import { getUserSelector } from '../../selectors/login';
|
||||||
|
@ -60,6 +61,8 @@ import { Services } from '../../lib/services';
|
||||||
import { TSupportedThemes } from '../../theme';
|
import { TSupportedThemes } from '../../theme';
|
||||||
import { ChatsStackParamList } from '../../stacks/types';
|
import { ChatsStackParamList } from '../../stacks/types';
|
||||||
import { EventTypes } from '../EmojiPicker/interfaces';
|
import { EventTypes } from '../EmojiPicker/interfaces';
|
||||||
|
import EmojiSearchbar from './EmojiSearchbar';
|
||||||
|
import shortnameToUnicode from '../../lib/methods/helpers/shortnameToUnicode';
|
||||||
|
|
||||||
require('./EmojiKeyboard');
|
require('./EmojiKeyboard');
|
||||||
|
|
||||||
|
@ -130,6 +133,8 @@ interface IMessageBoxState {
|
||||||
tshow: boolean;
|
tshow: boolean;
|
||||||
mentionLoading: boolean;
|
mentionLoading: boolean;
|
||||||
permissionToUpload: boolean;
|
permissionToUpload: boolean;
|
||||||
|
showEmojiSearchbar: boolean;
|
||||||
|
searchedEmojis: any[];
|
||||||
}
|
}
|
||||||
|
|
||||||
class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
|
@ -161,6 +166,8 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
|
|
||||||
private typingTimeout: any;
|
private typingTimeout: any;
|
||||||
|
|
||||||
|
private emojiSearchbarRef: any;
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
message: {
|
message: {
|
||||||
id: ''
|
id: ''
|
||||||
|
@ -184,11 +191,14 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
command: {},
|
command: {},
|
||||||
tshow: this.sendThreadToChannel,
|
tshow: this.sendThreadToChannel,
|
||||||
mentionLoading: false,
|
mentionLoading: false,
|
||||||
permissionToUpload: true
|
permissionToUpload: true,
|
||||||
|
showEmojiSearchbar: false,
|
||||||
|
searchedEmojis: []
|
||||||
};
|
};
|
||||||
this.text = '';
|
this.text = '';
|
||||||
this.selection = { start: 0, end: 0 };
|
this.selection = { start: 0, end: 0 };
|
||||||
this.focused = false;
|
this.focused = false;
|
||||||
|
this.emojiSearchbarRef = React.createRef<RNTextInput>();
|
||||||
|
|
||||||
const libPickerLabels = {
|
const libPickerLabels = {
|
||||||
cropperChooseText: I18n.t('Choose'),
|
cropperChooseText: I18n.t('Choose'),
|
||||||
|
@ -327,7 +337,9 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
tshow,
|
tshow,
|
||||||
mentionLoading,
|
mentionLoading,
|
||||||
trackingType,
|
trackingType,
|
||||||
permissionToUpload
|
permissionToUpload,
|
||||||
|
showEmojiSearchbar,
|
||||||
|
searchedEmojis
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
@ -395,6 +407,12 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
if (nextProps.goToCannedResponses !== goToCannedResponses) {
|
if (nextProps.goToCannedResponses !== goToCannedResponses) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (nextState.showEmojiSearchbar !== showEmojiSearchbar) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!dequal(nextState.searchedEmojis, searchedEmojis)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -578,28 +596,40 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onItemSelected = (keyboardId: string, params: { eventType: EventTypes; emoji: string }) => {
|
onKeyboardItemSelected = (keyboardId: string, params: { eventType: EventTypes; emoji: string }) => {
|
||||||
const { eventType, emoji } = params;
|
const { eventType, emoji } = params;
|
||||||
const { text } = this;
|
const { text } = this;
|
||||||
let newText = '';
|
let newText = '';
|
||||||
// if messagebox has an active cursor
|
// if messagebox has an active cursor
|
||||||
const { start, end } = this.selection;
|
const { start, end } = this.selection;
|
||||||
const cursor = Math.max(start, end);
|
const cursor = Math.max(start, end);
|
||||||
if (eventType === EventTypes.BACKSPACE_PRESSED) {
|
let newCursor;
|
||||||
let charsToRemove = 1;
|
|
||||||
if (cursor > 1) {
|
switch (eventType) {
|
||||||
|
case EventTypes.BACKSPACE_PRESSED:
|
||||||
|
let charsToRemove = 1;
|
||||||
const emojiRegex = /\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff]/;
|
const emojiRegex = /\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff]/;
|
||||||
const lastEmoji = text.substr(cursor - 2, cursor);
|
const lastEmoji = text.substr(text.length - 2, text.length);
|
||||||
|
// Check if last character is an emoji
|
||||||
if (emojiRegex.test(lastEmoji)) charsToRemove = 2;
|
if (emojiRegex.test(lastEmoji)) charsToRemove = 2;
|
||||||
}
|
newText = text.substr(0, text.length - charsToRemove);
|
||||||
newText = text.substr(0, cursor - charsToRemove);
|
newCursor = cursor - charsToRemove;
|
||||||
this.setInput(newText, { start: cursor - charsToRemove, end: cursor - charsToRemove });
|
this.setInput(newText, { start: newCursor, end: newCursor });
|
||||||
this.setShowSend(newText !== '');
|
this.setShowSend(newText !== '');
|
||||||
} else if (eventType === EventTypes.EMOJI_PRESSED) {
|
break;
|
||||||
newText = `${text.substr(0, cursor)}${emoji}${text.substr(cursor)}`;
|
case EventTypes.EMOJI_PRESSED:
|
||||||
const newCursor = cursor + emoji.length;
|
newText = `${text.substr(0, cursor)}${emoji}${text.substr(cursor)}`;
|
||||||
this.setInput(newText, { start: newCursor, end: newCursor });
|
newCursor = cursor + emoji.length;
|
||||||
this.setShowSend(true);
|
this.setInput(newText, { start: newCursor, end: newCursor });
|
||||||
|
this.setShowSend(true);
|
||||||
|
break;
|
||||||
|
case EventTypes.SEARCH_PRESSED:
|
||||||
|
this.setState({ showEmojiKeyboard: false, showEmojiSearchbar: true }, () => {
|
||||||
|
this.emojiSearchbarRef.current.focus();
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Do nothing
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -893,7 +923,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
|
|
||||||
openEmoji = () => {
|
openEmoji = () => {
|
||||||
logEvent(events.ROOM_OPEN_EMOJI);
|
logEvent(events.ROOM_OPEN_EMOJI);
|
||||||
this.setState({ showEmojiKeyboard: true });
|
this.setState({ showEmojiKeyboard: true, showEmojiSearchbar: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
recordingCallback = (recording: any) => {
|
recordingCallback = (recording: any) => {
|
||||||
|
@ -1095,6 +1125,55 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
searchEmojis = debounce(async (keyword: any) => {
|
||||||
|
const db = database.active;
|
||||||
|
const customEmojisCollection = db.get('custom_emojis');
|
||||||
|
const likeString = sanitizeLikeString(keyword);
|
||||||
|
const whereClause = [];
|
||||||
|
if (likeString) {
|
||||||
|
whereClause.push(Q.where('name', Q.like(`${likeString}%`)));
|
||||||
|
}
|
||||||
|
let customEmojis = await customEmojisCollection.query(...whereClause).fetch();
|
||||||
|
customEmojis = customEmojis.slice(0, MAX_EMOJIS_TO_DISPLAY / 2);
|
||||||
|
const filteredEmojis = emojis.filter(emoji => emoji.indexOf(keyword) !== -1).slice(0, MAX_EMOJIS_TO_DISPLAY / 2);
|
||||||
|
const mergedEmojis = [...customEmojis, ...filteredEmojis].slice(0, MAX_EMOJIS_TO_DISPLAY);
|
||||||
|
this.setState({ searchedEmojis: mergedEmojis });
|
||||||
|
}, 300);
|
||||||
|
|
||||||
|
renderEmojiSearchbar = () => {
|
||||||
|
const { showEmojiSearchbar, searchedEmojis } = this.state;
|
||||||
|
const { baseUrl } = this.props;
|
||||||
|
const onChangeText = (value: string) => {
|
||||||
|
this.searchEmojis(value);
|
||||||
|
};
|
||||||
|
const onEmojiSelected = (emoji: any) => {
|
||||||
|
let selectedEmoji;
|
||||||
|
if (emoji.name) {
|
||||||
|
selectedEmoji = `:${emoji.name}:`;
|
||||||
|
} else {
|
||||||
|
selectedEmoji = shortnameToUnicode(`:${emoji}:`);
|
||||||
|
}
|
||||||
|
const { text } = this;
|
||||||
|
let newText = '';
|
||||||
|
const { start, end } = this.selection;
|
||||||
|
const cursor = Math.max(start, end);
|
||||||
|
newText = `${text.substr(0, cursor)}${selectedEmoji}${text.substr(cursor)}`;
|
||||||
|
const newCursor = cursor + selectedEmoji.length;
|
||||||
|
this.setInput(newText, { start: newCursor, end: newCursor });
|
||||||
|
this.setShowSend(true);
|
||||||
|
};
|
||||||
|
return showEmojiSearchbar ? (
|
||||||
|
<EmojiSearchbar
|
||||||
|
ref={this.emojiSearchbarRef}
|
||||||
|
openEmoji={this.openEmoji}
|
||||||
|
onChangeText={onChangeText}
|
||||||
|
emojis={searchedEmojis}
|
||||||
|
baseUrl={baseUrl}
|
||||||
|
onEmojiSelected={onEmojiSelected}
|
||||||
|
/>
|
||||||
|
) : null;
|
||||||
|
};
|
||||||
|
|
||||||
renderContent = () => {
|
renderContent = () => {
|
||||||
const {
|
const {
|
||||||
recording,
|
recording,
|
||||||
|
@ -1209,6 +1288,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
{recordAudio}
|
{recordAudio}
|
||||||
</View>
|
</View>
|
||||||
{this.renderSendToChannel()}
|
{this.renderSendToChannel()}
|
||||||
|
{this.renderEmojiSearchbar()}
|
||||||
</View>
|
</View>
|
||||||
{children}
|
{children}
|
||||||
</>
|
</>
|
||||||
|
@ -1236,7 +1316,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
kbComponent={showEmojiKeyboard ? 'EmojiKeyboard' : null}
|
kbComponent={showEmojiKeyboard ? 'EmojiKeyboard' : null}
|
||||||
kbInitialProps={{ theme }}
|
kbInitialProps={{ theme }}
|
||||||
onKeyboardResigned={this.onKeyboardResigned}
|
onKeyboardResigned={this.onKeyboardResigned}
|
||||||
onItemSelected={this.onItemSelected}
|
onItemSelected={this.onKeyboardItemSelected}
|
||||||
trackInteractive
|
trackInteractive
|
||||||
requiresSameParentToManageScrollView
|
requiresSameParentToManageScrollView
|
||||||
addBottomView
|
addBottomView
|
||||||
|
|
|
@ -482,6 +482,7 @@
|
||||||
"Search_Messages": "Search Messages",
|
"Search_Messages": "Search Messages",
|
||||||
"Search": "Search",
|
"Search": "Search",
|
||||||
"Search_by": "Search by",
|
"Search_by": "Search by",
|
||||||
|
"Search_emoji": "Search emoji",
|
||||||
"Search_global_users": "Search for global users",
|
"Search_global_users": "Search for global users",
|
||||||
"Search_global_users_description": "If you turn-on, you can search for any user from others companies or servers.",
|
"Search_global_users_description": "If you turn-on, you can search for any user from others companies or servers.",
|
||||||
"Seconds": "{{second}} seconds",
|
"Seconds": "{{second}} seconds",
|
||||||
|
|
Loading…
Reference in New Issue