diff --git a/app/containers/EmojiPicker/EmojiCategory.tsx b/app/containers/EmojiPicker/EmojiCategory.tsx
index d8a20b0d3..f5fed80de 100644
--- a/app/containers/EmojiPicker/EmojiCategory.tsx
+++ b/app/containers/EmojiPicker/EmojiCategory.tsx
@@ -7,7 +7,7 @@ import CustomEmoji from './CustomEmoji';
import scrollPersistTaps from '../../lib/methods/helpers/scrollPersistTaps';
import { IEmoji, IEmojiCategory } from '../../definitions/IEmoji';
-const EMOJI_SIZE = 50;
+const MAX_EMOJI_SIZE = 50;
const renderEmoji = (emoji: IEmoji, size: number, baseUrl: string) => {
if (emoji && emoji.isCustom) {
@@ -26,7 +26,9 @@ const renderEmoji = (emoji: IEmoji, size: number, baseUrl: string) => {
);
};
-const EmojiCategory = React.memo(({ baseUrl, onEmojiSelected, emojis, width, ...props }: IEmojiCategory) => {
+const EmojiCategory = React.memo(({ baseUrl, onEmojiSelected, emojis, width, tabsCount, ...props }: IEmojiCategory) => {
+ const emojiSize = width ? Math.min(width / tabsCount, MAX_EMOJI_SIZE) : MAX_EMOJI_SIZE;
+ const numColumns = Math.trunc(width ? width / emojiSize : tabsCount);
const renderItem = (emoji: IEmoji) => (
onEmojiSelected(emoji)}
testID={`reaction-picker-${emoji && emoji.isCustom ? emoji.content : emoji}`}>
- {renderEmoji(emoji, EMOJI_SIZE, baseUrl)}
+ {renderEmoji(emoji, emojiSize, baseUrl)}
);
@@ -42,14 +44,10 @@ const EmojiCategory = React.memo(({ baseUrl, onEmojiSelected, emojis, 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}
diff --git a/app/containers/EmojiPicker/Footer.tsx b/app/containers/EmojiPicker/Footer.tsx
new file mode 100644
index 000000000..1d0a139c7
--- /dev/null
+++ b/app/containers/EmojiPicker/Footer.tsx
@@ -0,0 +1,25 @@
+import React from 'react';
+import { View, TouchableOpacity } from 'react-native';
+import { BorderlessButton } from 'react-native-gesture-handler';
+
+import { useTheme } from '../../theme';
+import { CustomIcon } from '../CustomIcon';
+import styles from './styles';
+import { IFooterProps } from './interfaces';
+
+const Footer = React.memo(({ onBackspacePressed }: IFooterProps) => {
+ const { colors } = useTheme();
+ return (
+
+ console.log('Search!')} style={styles.footerButtonsContainer}>
+
+
+
+
+
+
+
+ );
+});
+
+export default Footer;
diff --git a/app/containers/EmojiPicker/TabBar.tsx b/app/containers/EmojiPicker/TabBar.tsx
index 887e93c81..51945015b 100644
--- a/app/containers/EmojiPicker/TabBar.tsx
+++ b/app/containers/EmojiPicker/TabBar.tsx
@@ -1,15 +1,9 @@
import React from 'react';
-import { StyleProp, Text, TextStyle, TouchableOpacity, View } from 'react-native';
+import { Text, TouchableOpacity, View } from 'react-native';
import styles from './styles';
import { useTheme } from '../../theme';
-
-interface ITabBarProps {
- goToPage?: (page: number) => void;
- activeTab?: number;
- tabs?: string[];
- tabEmojiStyle: StyleProp;
-}
+import { ITabBarProps } from './interfaces';
const TabBar = React.memo(({ activeTab, tabs, goToPage, tabEmojiStyle }: ITabBarProps) => {
const { colors } = useTheme();
diff --git a/app/containers/EmojiPicker/index.tsx b/app/containers/EmojiPicker/index.tsx
index a456149de..2e563c927 100644
--- a/app/containers/EmojiPicker/index.tsx
+++ b/app/containers/EmojiPicker/index.tsx
@@ -1,11 +1,12 @@
import React, { useEffect, useState } from 'react';
-import { StyleProp, TextStyle, View } from 'react-native';
+import { View } from 'react-native';
import ScrollableTabView from 'react-native-scrollable-tab-view';
import orderBy from 'lodash/orderBy';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import TabBar from './TabBar';
import EmojiCategory from './EmojiCategory';
+import Footer from './Footer';
import styles from './styles';
import categories from './categories';
import database from '../../lib/database';
@@ -16,13 +17,9 @@ import log from '../../lib/methods/helpers/log';
import { useTheme } from '../../theme';
import { IEmoji, ICustomEmojis, TFrequentlyUsedEmojiModel } from '../../definitions';
import { useAppSelector } from '../../lib/hooks';
+import { IEmojiPickerProps, EventTypes } from './interfaces';
-interface IEmojiPickerProps {
- onEmojiSelected: (emoji: string, shortname?: string) => void;
- tabEmojiStyle?: StyleProp;
-}
-
-const EmojiPicker = React.memo(({ onEmojiSelected, tabEmojiStyle }: IEmojiPickerProps) => {
+const EmojiPicker = React.memo(({ onItemClicked, tabEmojiStyle }: IEmojiPickerProps) => {
const [frequentlyUsed, setFrequentlyUsed] = useState([]);
const [show, setShow] = useState(false);
const [width, setWidth] = useState(null);
@@ -54,12 +51,12 @@ const EmojiPicker = React.memo(({ onEmojiSelected, tabEmojiStyle }: IEmojiPicker
extension: emoji.extension,
isCustom: true
});
- onEmojiSelected(`:${emoji.content}:`);
+ onItemClicked(EventTypes.EMOJI_PRESSED, `:${emoji.content}:`);
} else {
const content = emoji;
_addFrequentlyUsed({ content, isCustom: false });
const shortname = `:${emoji}:`;
- onEmojiSelected(shortnameToUnicode(shortname), shortname);
+ onItemClicked(EventTypes.EMOJI_PRESSED, shortnameToUnicode(shortname), shortname);
}
} catch (e) {
log(e);
@@ -112,7 +109,7 @@ const EmojiPicker = React.memo(({ onEmojiSelected, tabEmojiStyle }: IEmojiPicker
}
}: any) => setWidth(width);
- const renderCategory = (category: keyof typeof emojisByCategory, i: number, label: string) => {
+ const renderCategory = (category: keyof typeof emojisByCategory, i: number, label: string, tabsCount: number) => {
let emojis = [];
if (i === 0) {
emojis = frequentlyUsed;
@@ -129,13 +126,19 @@ const EmojiPicker = React.memo(({ onEmojiSelected, tabEmojiStyle }: IEmojiPicker
width={width}
baseUrl={baseUrl}
tabLabel={label}
+ tabsCount={tabsCount}
/>
);
};
+ const onBackspacePressed = () => onItemClicked(EventTypes.BACKSPACE_PRESSED);
+
if (!show) {
return null;
}
+
+ const tabsCount = frequentlyUsed.length === 0 ? categories.tabs.length - 1 : categories.tabs.length;
+
return (
i === 0 && frequentlyUsed.length === 0
? null // when no frequentlyUsed don't show the tab
- : renderCategory(tab.category, i, tab.tabLabel)
+ : renderCategory(tab.category, i, tab.tabLabel, tabsCount)
)}
+
);
});
diff --git a/app/containers/EmojiPicker/interfaces.ts b/app/containers/EmojiPicker/interfaces.ts
new file mode 100644
index 000000000..db7b4cb1f
--- /dev/null
+++ b/app/containers/EmojiPicker/interfaces.ts
@@ -0,0 +1,22 @@
+import { StyleProp, TextStyle } from 'react-native';
+
+export enum EventTypes {
+ EMOJI_PRESSED = 'emojiPressed',
+ BACKSPACE_PRESSED = 'backspacePressed'
+}
+
+export interface IEmojiPickerProps {
+ onItemClicked: (event: EventTypes, emoji?: string, shortname?: string) => void;
+ tabEmojiStyle?: StyleProp;
+}
+
+export interface IFooterProps {
+ onBackspacePressed: () => void;
+}
+
+export interface ITabBarProps {
+ goToPage?: (page: number) => void;
+ activeTab?: number;
+ tabs?: string[];
+ tabEmojiStyle: StyleProp;
+}
diff --git a/app/containers/EmojiPicker/styles.ts b/app/containers/EmojiPicker/styles.ts
index ca1263275..755da695e 100644
--- a/app/containers/EmojiPicker/styles.ts
+++ b/app/containers/EmojiPicker/styles.ts
@@ -54,5 +54,18 @@ export default StyleSheet.create({
},
customCategoryEmoji: {
margin: 8
+ },
+ footerContainer: {
+ height: 45,
+ paddingHorizontal: 12,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center'
+ },
+ footerButtonsContainer: {
+ height: 30,
+ width: 30,
+ justifyContent: 'center',
+ alignItems: 'center'
}
});
diff --git a/app/containers/MessageBox/EmojiKeyboard.tsx b/app/containers/MessageBox/EmojiKeyboard.tsx
index 5ddcbf9a2..cbee8543b 100644
--- a/app/containers/MessageBox/EmojiKeyboard.tsx
+++ b/app/containers/MessageBox/EmojiKeyboard.tsx
@@ -6,21 +6,23 @@ import { Provider } from 'react-redux';
import store from '../../lib/store';
import EmojiPicker from '../EmojiPicker';
import styles from './styles';
-import { themes } from '../../lib/constants';
-import { TSupportedThemes } from '../../theme';
+import {useTheme} from '../../theme'
+import { EventTypes } from '../EmojiPicker/interfaces';
-const EmojiKeyboard = ({ theme }: { theme: TSupportedThemes }) => {
- const onEmojiSelected = (emoji: string) => {
- KeyboardRegistry.onItemSelected('EmojiKeyboard', { emoji });
+const EmojiKeyboard = () => {
+ const onItemClicked = (eventType: EventTypes, emoji: string | undefined) => {
+ KeyboardRegistry.onItemSelected('EmojiKeyboard', { eventType, emoji });
};
+ const {colors} = useTheme()
+
return (
-
+
);
diff --git a/app/containers/MessageBox/index.tsx b/app/containers/MessageBox/index.tsx
index 067dbb502..cbca62c0a 100644
--- a/app/containers/MessageBox/index.tsx
+++ b/app/containers/MessageBox/index.tsx
@@ -59,6 +59,7 @@ import { hasPermission, debounce, isAndroid, isIOS, isTablet, compareServerVersi
import { Services } from '../../lib/services';
import { TSupportedThemes } from '../../theme';
import { ChatsStackParamList } from '../../stacks/types';
+import { EventTypes } from '../EmojiPicker/interfaces';
require('./EmojiKeyboard');
@@ -577,18 +578,29 @@ class MessageBox extends Component {
}
};
- onEmojiSelected = (keyboardId: string, params: { emoji: string }) => {
+ onItemSelected = (keyboardId: string, params: { eventType: EventTypes; emoji: string }) => {
+ const { eventType, emoji } = params;
const { text } = this;
- const { emoji } = params;
let newText = '';
-
// if messagebox has an active cursor
const { start, end } = this.selection;
const cursor = Math.max(start, end);
- newText = `${text.substr(0, cursor)}${emoji}${text.substr(cursor)}`;
- const newCursor = cursor + emoji.length;
- this.setInput(newText, { start: newCursor, end: newCursor });
- this.setShowSend(true);
+ if (eventType === EventTypes.BACKSPACE_PRESSED) {
+ let charsToRemove = 1;
+ if (cursor > 1) {
+ const emojiRegex = /\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff]/;
+ const lastEmoji = text.substr(cursor - 2, cursor);
+ if (emojiRegex.test(lastEmoji)) charsToRemove = 2;
+ }
+ newText = text.substr(0, cursor - charsToRemove);
+ this.setInput(newText, { start: cursor - charsToRemove, end: cursor - charsToRemove });
+ this.setShowSend(newText !== '');
+ } else if (eventType === EventTypes.EMOJI_PRESSED) {
+ newText = `${text.substr(0, cursor)}${emoji}${text.substr(cursor)}`;
+ const newCursor = cursor + emoji.length;
+ this.setInput(newText, { start: newCursor, end: newCursor });
+ this.setShowSend(true);
+ }
};
getPermalink = async (message: any) => {
@@ -1224,7 +1236,7 @@ class MessageBox extends Component {
kbComponent={showEmojiKeyboard ? 'EmojiKeyboard' : null}
kbInitialProps={{ theme }}
onKeyboardResigned={this.onKeyboardResigned}
- onItemSelected={this.onEmojiSelected}
+ onItemSelected={this.onItemSelected}
trackInteractive
requiresSameParentToManageScrollView
addBottomView
diff --git a/app/definitions/IEmoji.ts b/app/definitions/IEmoji.ts
index 04972d8bc..0dcf298bf 100644
--- a/app/definitions/IEmoji.ts
+++ b/app/definitions/IEmoji.ts
@@ -35,6 +35,7 @@ export interface IEmojiCategory {
width: number | null;
style: StyleProp;
tabLabel: string;
+ tabsCount: number;
}
export type TGetCustomEmoji = (name: string) => any;
diff --git a/app/views/RoomView/ReactionPicker.tsx b/app/views/RoomView/ReactionPicker.tsx
index 955fda42b..19c028180 100644
--- a/app/views/RoomView/ReactionPicker.tsx
+++ b/app/views/RoomView/ReactionPicker.tsx
@@ -9,6 +9,7 @@ import { themes } from '../../lib/constants';
import { TSupportedThemes, withTheme } from '../../theme';
import styles from './styles';
import { IApplicationState } from '../../definitions';
+import { EventTypes } from '../../containers/EmojiPicker/interfaces';
const margin = isAndroid ? 40 : 20;
const maxSize = 400;
@@ -30,12 +31,13 @@ class ReactionPicker extends React.Component {
return nextProps.show !== show || width !== nextProps.width || height !== nextProps.height;
}
- onEmojiSelected = (emoji: string, shortname?: string) => {
+ onEmojiSelected = (_eventType: EventTypes, emoji?: string, shortname?: string) => {
// standard emojis: `emoji` is unicode and `shortname` is :joy:
// custom emojis: only `emoji` is returned with shortname type (:joy:)
// to set reactions, we need shortname type
const { onEmojiSelected, message } = this.props;
if (message) {
+ // @ts-ignore
onEmojiSelected(shortname || emoji, message.id);
}
};
@@ -71,7 +73,7 @@ class ReactionPicker extends React.Component {
]}
testID='reaction-picker'
>
-
+
) : null;