diff --git a/app/containers/MessageBox/CommandPreview.js b/app/containers/MessageBox/CommandPreview.js
deleted file mode 100644
index 51cc64e4..00000000
--- a/app/containers/MessageBox/CommandPreview.js
+++ /dev/null
@@ -1,47 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { TouchableOpacity, ActivityIndicator } from 'react-native';
-import FastImage from 'react-native-fast-image';
-
-import styles from './styles';
-import { CustomIcon } from '../../lib/Icons';
-import { COLOR_PRIMARY } from '../../constants/colors';
-
-export default class CommandPreview extends React.PureComponent {
- static propTypes = {
- onPress: PropTypes.func,
- item: PropTypes.object
- };
-
- constructor(props) {
- super(props);
- this.state = { loading: true };
- }
-
- render() {
- const { onPress, item } = this.props;
- const { loading } = this.state;
- return (
- onPress(item)}
- testID={`command-preview-item${ item.id }`}
- >
- {item.type === 'image'
- ? (
- this.setState({ loading: true })}
- onLoad={() => this.setState({ loading: false })}
- >
- { loading ? : null }
-
- )
- :
- }
-
- );
- }
-}
diff --git a/app/containers/MessageBox/CommandsPreview/Item.js b/app/containers/MessageBox/CommandsPreview/Item.js
new file mode 100644
index 00000000..580d57bd
--- /dev/null
+++ b/app/containers/MessageBox/CommandsPreview/Item.js
@@ -0,0 +1,44 @@
+import React, { useContext, useState } from 'react';
+import PropTypes from 'prop-types';
+import { TouchableOpacity, ActivityIndicator } from 'react-native';
+import FastImage from 'react-native-fast-image';
+
+import styles from '../styles';
+import { CustomIcon } from '../../../lib/Icons';
+import { COLOR_PRIMARY } from '../../../constants/colors';
+import MessageboxContext from '../Context';
+
+const Item = ({ item }) => {
+ const context = useContext(MessageboxContext);
+ const { onPressCommandPreview } = context;
+ const [loading, setLoading] = useState(true);
+
+ return (
+ onPressCommandPreview(item)}
+ testID={`command-preview-item${ item.id }`}
+ >
+ {item.type === 'image'
+ ? (
+ setLoading(true)}
+ onLoad={() => setLoading(false)}
+ >
+ { loading ? : null }
+
+ )
+ :
+ }
+
+ );
+};
+
+Item.propTypes = {
+ item: PropTypes.object
+};
+
+export default Item;
diff --git a/app/containers/MessageBox/CommandsPreview/index.js b/app/containers/MessageBox/CommandsPreview/index.js
new file mode 100644
index 00000000..cecaf7c0
--- /dev/null
+++ b/app/containers/MessageBox/CommandsPreview/index.js
@@ -0,0 +1,40 @@
+import React from 'react';
+import { FlatList } from 'react-native';
+import PropTypes from 'prop-types';
+import equal from 'deep-equal';
+
+import Item from './Item';
+import styles from '../styles';
+
+const CommandsPreview = React.memo(({ commandPreview, showCommandPreview }) => {
+ if (!showCommandPreview) {
+ return null;
+ }
+ return (
+ }
+ keyExtractor={item => item.id}
+ keyboardShouldPersistTaps='always'
+ horizontal
+ showsHorizontalScrollIndicator={false}
+ />
+ );
+}, (prevProps, nextProps) => {
+ if (prevProps.showCommandPreview !== nextProps.showCommandPreview) {
+ return false;
+ }
+ if (!equal(prevProps.commandPreview, nextProps.commandPreview)) {
+ return false;
+ }
+ return true;
+});
+
+CommandsPreview.propTypes = {
+ commandPreview: PropTypes.array,
+ showCommandPreview: PropTypes.bool
+};
+
+export default CommandsPreview;
diff --git a/app/containers/MessageBox/Context.js b/app/containers/MessageBox/Context.js
new file mode 100644
index 00000000..3ed07ad4
--- /dev/null
+++ b/app/containers/MessageBox/Context.js
@@ -0,0 +1,4 @@
+import React from 'react';
+
+const MessageboxContext = React.createContext();
+export default MessageboxContext;
diff --git a/app/containers/MessageBox/Mentions/FixedMentionItem.js b/app/containers/MessageBox/Mentions/FixedMentionItem.js
new file mode 100644
index 00000000..1b6aabbc
--- /dev/null
+++ b/app/containers/MessageBox/Mentions/FixedMentionItem.js
@@ -0,0 +1,23 @@
+import React from 'react';
+import { TouchableOpacity, Text } from 'react-native';
+import PropTypes from 'prop-types';
+
+import styles from '../styles';
+import I18n from '../../../i18n';
+
+const FixedMentionItem = ({ item, onPress }) => (
+ onPress(item)}
+ >
+ {item.username}
+ {item.username === 'here' ? I18n.t('Notify_active_in_this_room') : I18n.t('Notify_all_in_this_room')}
+
+);
+
+FixedMentionItem.propTypes = {
+ item: PropTypes.object,
+ onPress: PropTypes.func
+};
+
+export default FixedMentionItem;
diff --git a/app/containers/MessageBox/Mentions/MentionEmoji.js b/app/containers/MessageBox/Mentions/MentionEmoji.js
new file mode 100644
index 00000000..a8fe1aa3
--- /dev/null
+++ b/app/containers/MessageBox/Mentions/MentionEmoji.js
@@ -0,0 +1,34 @@
+import React, { useContext } from 'react';
+import { Text } from 'react-native';
+import PropTypes from 'prop-types';
+import { shortnameToUnicode } from 'emoji-toolkit';
+
+import styles from '../styles';
+import MessageboxContext from '../Context';
+import CustomEmoji from '../../EmojiPicker/CustomEmoji';
+
+const MentionEmoji = ({ item }) => {
+ const context = useContext(MessageboxContext);
+ const { baseUrl } = context;
+
+ if (item.name) {
+ return (
+
+ );
+ }
+ return (
+
+ {shortnameToUnicode(`:${ item }:`)}
+
+ );
+};
+
+MentionEmoji.propTypes = {
+ item: PropTypes.object
+};
+
+export default MentionEmoji;
diff --git a/app/containers/MessageBox/Mentions/MentionItem.js b/app/containers/MessageBox/Mentions/MentionItem.js
new file mode 100644
index 00000000..d685d0b2
--- /dev/null
+++ b/app/containers/MessageBox/Mentions/MentionItem.js
@@ -0,0 +1,87 @@
+import React, { useContext } from 'react';
+import { TouchableOpacity, Text } from 'react-native';
+import PropTypes from 'prop-types';
+
+import styles from '../styles';
+import Avatar from '../../Avatar';
+import MessageboxContext from '../Context';
+import FixedMentionItem from './FixedMentionItem';
+import MentionEmoji from './MentionEmoji';
+import {
+ MENTIONS_TRACKING_TYPE_EMOJIS,
+ MENTIONS_TRACKING_TYPE_COMMANDS
+} from '../constants';
+
+const MentionItem = ({
+ item, trackingType
+}) => {
+ const context = useContext(MessageboxContext);
+ const { baseUrl, user, onPressMention } = context;
+
+ const defineTestID = (type) => {
+ switch (type) {
+ case MENTIONS_TRACKING_TYPE_EMOJIS:
+ return `mention-item-${ item.name || item }`;
+ case MENTIONS_TRACKING_TYPE_COMMANDS:
+ return `mention-item-${ item.command || item }`;
+ default:
+ return `mention-item-${ item.username || item.name || item }`;
+ }
+ };
+
+ const testID = defineTestID(trackingType);
+
+ if (item.username === 'all' || item.username === 'here') {
+ return ;
+ }
+
+ let content = (
+ <>
+
+ { item.username || item.name || item }
+ >
+ );
+
+ if (trackingType === MENTIONS_TRACKING_TYPE_EMOJIS) {
+ content = (
+ <>
+
+ :{ item.name || item }:
+ >
+ );
+ }
+
+ if (trackingType === MENTIONS_TRACKING_TYPE_COMMANDS) {
+ content = (
+ <>
+ /
+ { item.command}
+ >
+ );
+ }
+
+ return (
+ onPressMention(item)}
+ testID={testID}
+ >
+ {content}
+
+ );
+};
+
+MentionItem.propTypes = {
+ item: PropTypes.object,
+ trackingType: PropTypes.string
+};
+
+export default MentionItem;
diff --git a/app/containers/MessageBox/Mentions/index.js b/app/containers/MessageBox/Mentions/index.js
new file mode 100644
index 00000000..7d369208
--- /dev/null
+++ b/app/containers/MessageBox/Mentions/index.js
@@ -0,0 +1,39 @@
+import React from 'react';
+import { FlatList } from 'react-native';
+import PropTypes from 'prop-types';
+import equal from 'deep-equal';
+
+import styles from '../styles';
+import MentionItem from './MentionItem';
+
+const Mentions = React.memo(({ mentions, trackingType }) => {
+ if (!trackingType) {
+ return null;
+ }
+ return (
+ }
+ keyExtractor={item => item.id || item.username || item.command || item}
+ keyboardShouldPersistTaps='always'
+ />
+ );
+}, (prevProps, nextProps) => {
+ if (prevProps.trackingType !== nextProps.trackingType) {
+ return false;
+ }
+ if (!equal(prevProps.mentions, nextProps.mentions)) {
+ return false;
+ }
+ return true;
+});
+
+Mentions.propTypes = {
+ mentions: PropTypes.array,
+ trackingType: PropTypes.string
+};
+
+export default Mentions;
diff --git a/app/containers/MessageBox/ReplyPreview.js b/app/containers/MessageBox/ReplyPreview.js
index 06fc074b..85894911 100644
--- a/app/containers/MessageBox/ReplyPreview.js
+++ b/app/containers/MessageBox/ReplyPreview.js
@@ -1,4 +1,4 @@
-import React, { Component } from 'react';
+import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import PropTypes from 'prop-types';
import moment from 'moment';
@@ -47,45 +47,38 @@ const styles = StyleSheet.create({
}
});
-class ReplyPreview extends Component {
- static propTypes = {
- useMarkdown: PropTypes.bool,
- message: PropTypes.object.isRequired,
- Message_TimeFormat: PropTypes.string.isRequired,
- close: PropTypes.func.isRequired,
- baseUrl: PropTypes.string.isRequired,
- username: PropTypes.string.isRequired,
- getCustomEmoji: PropTypes.func
+const ReplyPreview = React.memo(({
+ message, Message_TimeFormat, baseUrl, username, useMarkdown, replying, getCustomEmoji, close
+}) => {
+ if (!replying) {
+ return null;
}
- shouldComponentUpdate() {
- return false;
- }
-
- close = () => {
- const { close } = this.props;
- close();
- }
-
- render() {
- const {
- message, Message_TimeFormat, baseUrl, username, useMarkdown, getCustomEmoji
- } = this.props;
- const time = moment(message.ts).format(Message_TimeFormat);
- return (
-
-
-
- {message.u.username}
- {time}
-
-
+ const time = moment(message.ts).format(Message_TimeFormat);
+ return (
+
+
+
+ {message.u.username}
+ {time}
-
+
- );
- }
-}
+
+
+ );
+}, (prevProps, nextProps) => prevProps.replying === nextProps.replying);
+
+ReplyPreview.propTypes = {
+ replying: PropTypes.bool,
+ useMarkdown: PropTypes.bool,
+ message: PropTypes.object.isRequired,
+ Message_TimeFormat: PropTypes.string.isRequired,
+ close: PropTypes.func.isRequired,
+ baseUrl: PropTypes.string.isRequired,
+ username: PropTypes.string.isRequired,
+ getCustomEmoji: PropTypes.func
+};
const mapStateToProps = state => ({
useMarkdown: state.markdown.useMarkdown,
diff --git a/app/containers/MessageBox/constants.js b/app/containers/MessageBox/constants.js
new file mode 100644
index 00000000..d5ef03e2
--- /dev/null
+++ b/app/containers/MessageBox/constants.js
@@ -0,0 +1,4 @@
+export const MENTIONS_TRACKING_TYPE_USERS = '@';
+export const MENTIONS_TRACKING_TYPE_EMOJIS = ':';
+export const MENTIONS_TRACKING_TYPE_COMMANDS = '/';
+export const MENTIONS_COUNT_TO_DISPLAY = 4;
diff --git a/app/containers/MessageBox/index.js b/app/containers/MessageBox/index.js
index 273a6bb9..cb7feb55 100644
--- a/app/containers/MessageBox/index.js
+++ b/app/containers/MessageBox/index.js
@@ -1,10 +1,9 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
- View, TextInput, FlatList, Text, TouchableOpacity, Alert, ScrollView
+ View, TextInput, Alert
} from 'react-native';
import { connect } from 'react-redux';
-import { shortnameToUnicode } from 'emoji-toolkit';
import { KeyboardAccessoryView } from 'react-native-keyboard-input';
import ImagePicker from 'react-native-image-crop-picker';
import equal from 'deep-equal';
@@ -16,8 +15,6 @@ import { userTyping as userTypingAction } from '../../actions/room';
import RocketChat from '../../lib/rocketchat';
import styles from './styles';
import database from '../../lib/database';
-import Avatar from '../Avatar';
-import CustomEmoji from '../EmojiPicker/CustomEmoji';
import { emojis } from '../../emojis';
import Recording from './Recording';
import UploadModal from './UploadModal';
@@ -29,13 +26,16 @@ import { COLOR_TEXT_DESCRIPTION } from '../../constants/colors';
import LeftButtons from './LeftButtons';
import RightButtons from './RightButtons';
import { isAndroid } from '../../utils/deviceInfo';
-import CommandPreview from './CommandPreview';
import { canUploadFile } from '../../utils/media';
-
-const MENTIONS_TRACKING_TYPE_USERS = '@';
-const MENTIONS_TRACKING_TYPE_EMOJIS = ':';
-const MENTIONS_TRACKING_TYPE_COMMANDS = '/';
-const MENTIONS_COUNT_TO_DISPLAY = 4;
+import Mentions from './Mentions';
+import MessageboxContext from './Context';
+import {
+ MENTIONS_TRACKING_TYPE_EMOJIS,
+ MENTIONS_TRACKING_TYPE_COMMANDS,
+ MENTIONS_COUNT_TO_DISPLAY,
+ MENTIONS_TRACKING_TYPE_USERS
+} from './constants';
+import CommandsPreview from './CommandsPreview';
const imagePickerConfig = {
cropping: true,
@@ -545,7 +545,6 @@ class MessageBox extends Component {
}
}
-
showUploadModal = (file) => {
this.setState({ file: { ...file, isVisible: true } });
}
@@ -726,175 +725,29 @@ class MessageBox extends Component {
});
}
- renderFixedMentionItem = item => (
- this.onPressMention(item)}
- >
- {item.username}
- {item.username === 'here' ? I18n.t('Notify_active_in_this_room') : I18n.t('Notify_all_in_this_room')}
-
- )
-
- renderMentionEmoji = (item) => {
- const { baseUrl } = this.props;
-
- if (item.name) {
- return (
-
- );
- }
- return (
-
- {shortnameToUnicode(`:${ item }:`)}
-
- );
- }
-
- renderMentionItem = ({ item }) => {
- const { trackingType } = this.state;
- const { baseUrl, user } = this.props;
-
- if (item.username === 'all' || item.username === 'here') {
- return this.renderFixedMentionItem(item);
- }
- const defineTestID = (type) => {
- switch (type) {
- case MENTIONS_TRACKING_TYPE_EMOJIS:
- return `mention-item-${ item.name || item }`;
- case MENTIONS_TRACKING_TYPE_COMMANDS:
- return `mention-item-${ item.command || item }`;
- default:
- return `mention-item-${ item.username || item.name || item }`;
- }
- };
-
- const testID = defineTestID(trackingType);
-
- return (
- this.onPressMention(item)}
- testID={testID}
- >
-
- {(() => {
- switch (trackingType) {
- case MENTIONS_TRACKING_TYPE_EMOJIS:
- return (
- <>
- {this.renderMentionEmoji(item)}
- :{ item.name || item }:
- >
- );
- case MENTIONS_TRACKING_TYPE_COMMANDS:
- return (
- <>
- /
- { item.command}
- >
- );
- default:
- return (
- <>
-
- { item.username || item.name || item }
- >
- );
- }
- })()
- }
-
- );
- }
-
- renderMentions = () => {
- const { mentions, trackingType } = this.state;
- if (!trackingType) {
- return null;
- }
- return (
-
- item.id || item.username || item.command || item}
- keyboardShouldPersistTaps='always'
- />
-
- );
- };
-
- renderCommandPreviewItem = ({ item }) => (
-
- );
-
- renderCommandPreview = () => {
- const { commandPreview, showCommandPreview } = this.state;
- if (!showCommandPreview) {
- return null;
- }
- return (
-
- item.id}
- keyboardShouldPersistTaps='always'
- horizontal
- showsHorizontalScrollIndicator={false}
- />
-
- );
- }
-
- renderReplyPreview = () => {
- const {
- message, replying, replyCancel, user, getCustomEmoji
- } = this.props;
- if (!replying) {
- return null;
- }
- return ;
- };
-
renderContent = () => {
- const { recording, showEmojiKeyboard, showSend } = this.state;
- const { editing } = this.props;
+ const {
+ recording, showEmojiKeyboard, showSend, mentions, trackingType, commandPreview, showCommandPreview
+ } = this.state;
+ const {
+ editing, message, replying, replyCancel, user, getCustomEmoji
+ } = this.props;
if (recording) {
- return ();
+ return ;
}
return (
<>
- {this.renderCommandPreview()}
- {this.renderMentions()}
-
- {this.renderReplyPreview()}
+
+
+
+
+
this.setState({ file: {} })}
submit={this.sendMediaMessage}
/>
- >
+
);
}
}