[FIX] Remove some unnecessary re-renders on Messagebox (#1341)
This commit is contained in:
parent
66222a3f9a
commit
fcb420a773
|
@ -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 (
|
|
||||||
<TouchableOpacity
|
|
||||||
style={styles.commandPreview}
|
|
||||||
onPress={() => onPress(item)}
|
|
||||||
testID={`command-preview-item${ item.id }`}
|
|
||||||
>
|
|
||||||
{item.type === 'image'
|
|
||||||
? (
|
|
||||||
<FastImage
|
|
||||||
style={styles.commandPreviewImage}
|
|
||||||
source={{ uri: item.value }}
|
|
||||||
resizeMode={FastImage.resizeMode.cover}
|
|
||||||
onLoadStart={() => this.setState({ loading: true })}
|
|
||||||
onLoad={() => this.setState({ loading: false })}
|
|
||||||
>
|
|
||||||
{ loading ? <ActivityIndicator /> : null }
|
|
||||||
</FastImage>
|
|
||||||
)
|
|
||||||
: <CustomIcon name='file-generic' size={36} color={COLOR_PRIMARY} />
|
|
||||||
}
|
|
||||||
</TouchableOpacity>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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 (
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.commandPreview}
|
||||||
|
onPress={() => onPressCommandPreview(item)}
|
||||||
|
testID={`command-preview-item${ item.id }`}
|
||||||
|
>
|
||||||
|
{item.type === 'image'
|
||||||
|
? (
|
||||||
|
<FastImage
|
||||||
|
style={styles.commandPreviewImage}
|
||||||
|
source={{ uri: item.value }}
|
||||||
|
resizeMode={FastImage.resizeMode.cover}
|
||||||
|
onLoadStart={() => setLoading(true)}
|
||||||
|
onLoad={() => setLoading(false)}
|
||||||
|
>
|
||||||
|
{ loading ? <ActivityIndicator /> : null }
|
||||||
|
</FastImage>
|
||||||
|
)
|
||||||
|
: <CustomIcon name='file-generic' size={36} color={COLOR_PRIMARY} />
|
||||||
|
}
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Item.propTypes = {
|
||||||
|
item: PropTypes.object
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Item;
|
|
@ -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 (
|
||||||
|
<FlatList
|
||||||
|
testID='commandbox-container'
|
||||||
|
style={styles.mentionList}
|
||||||
|
data={commandPreview}
|
||||||
|
renderItem={({ item }) => <Item item={item} />}
|
||||||
|
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;
|
|
@ -0,0 +1,4 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const MessageboxContext = React.createContext();
|
||||||
|
export default MessageboxContext;
|
|
@ -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 }) => (
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.mentionItem}
|
||||||
|
onPress={() => onPress(item)}
|
||||||
|
>
|
||||||
|
<Text style={styles.fixedMentionAvatar}>{item.username}</Text>
|
||||||
|
<Text style={styles.mentionText}>{item.username === 'here' ? I18n.t('Notify_active_in_this_room') : I18n.t('Notify_all_in_this_room')}</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
|
||||||
|
FixedMentionItem.propTypes = {
|
||||||
|
item: PropTypes.object,
|
||||||
|
onPress: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FixedMentionItem;
|
|
@ -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 (
|
||||||
|
<CustomEmoji
|
||||||
|
style={styles.mentionItemCustomEmoji}
|
||||||
|
emoji={item}
|
||||||
|
baseUrl={baseUrl}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Text style={styles.mentionItemEmoji}>
|
||||||
|
{shortnameToUnicode(`:${ item }:`)}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
MentionEmoji.propTypes = {
|
||||||
|
item: PropTypes.object
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MentionEmoji;
|
|
@ -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 <FixedMentionItem item={item} onPress={onPressMention} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = (
|
||||||
|
<>
|
||||||
|
<Avatar
|
||||||
|
style={styles.avatar}
|
||||||
|
text={item.username || item.name}
|
||||||
|
size={30}
|
||||||
|
type={item.t}
|
||||||
|
baseUrl={baseUrl}
|
||||||
|
userId={user.id}
|
||||||
|
token={user.token}
|
||||||
|
/>
|
||||||
|
<Text style={styles.mentionText}>{ item.username || item.name || item }</Text>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (trackingType === MENTIONS_TRACKING_TYPE_EMOJIS) {
|
||||||
|
content = (
|
||||||
|
<>
|
||||||
|
<MentionEmoji item={item} />
|
||||||
|
<Text style={styles.mentionText}>:{ item.name || item }:</Text>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trackingType === MENTIONS_TRACKING_TYPE_COMMANDS) {
|
||||||
|
content = (
|
||||||
|
<>
|
||||||
|
<Text style={styles.slash}>/</Text>
|
||||||
|
<Text>{ item.command}</Text>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.mentionItem}
|
||||||
|
onPress={() => onPressMention(item)}
|
||||||
|
testID={testID}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
MentionItem.propTypes = {
|
||||||
|
item: PropTypes.object,
|
||||||
|
trackingType: PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MentionItem;
|
|
@ -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 (
|
||||||
|
<FlatList
|
||||||
|
testID='messagebox-container'
|
||||||
|
style={styles.mentionList}
|
||||||
|
data={mentions}
|
||||||
|
extraData={mentions}
|
||||||
|
renderItem={({ item }) => <MentionItem item={item} trackingType={trackingType} />}
|
||||||
|
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;
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { Component } from 'react';
|
import React from 'react';
|
||||||
import { View, Text, StyleSheet } from 'react-native';
|
import { View, Text, StyleSheet } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
@ -47,45 +47,38 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
class ReplyPreview extends Component {
|
const ReplyPreview = React.memo(({
|
||||||
static propTypes = {
|
message, Message_TimeFormat, baseUrl, username, useMarkdown, replying, getCustomEmoji, close
|
||||||
useMarkdown: PropTypes.bool,
|
}) => {
|
||||||
message: PropTypes.object.isRequired,
|
if (!replying) {
|
||||||
Message_TimeFormat: PropTypes.string.isRequired,
|
return null;
|
||||||
close: PropTypes.func.isRequired,
|
|
||||||
baseUrl: PropTypes.string.isRequired,
|
|
||||||
username: PropTypes.string.isRequired,
|
|
||||||
getCustomEmoji: PropTypes.func
|
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate() {
|
const time = moment(message.ts).format(Message_TimeFormat);
|
||||||
return false;
|
return (
|
||||||
}
|
<View style={styles.container}>
|
||||||
|
<View style={styles.messageContainer}>
|
||||||
close = () => {
|
<View style={styles.header}>
|
||||||
const { close } = this.props;
|
<Text style={styles.username}>{message.u.username}</Text>
|
||||||
close();
|
<Text style={styles.time}>{time}</Text>
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
message, Message_TimeFormat, baseUrl, username, useMarkdown, getCustomEmoji
|
|
||||||
} = this.props;
|
|
||||||
const time = moment(message.ts).format(Message_TimeFormat);
|
|
||||||
return (
|
|
||||||
<View style={styles.container}>
|
|
||||||
<View style={styles.messageContainer}>
|
|
||||||
<View style={styles.header}>
|
|
||||||
<Text style={styles.username}>{message.u.username}</Text>
|
|
||||||
<Text style={styles.time}>{time}</Text>
|
|
||||||
</View>
|
|
||||||
<Markdown msg={message.msg} baseUrl={baseUrl} username={username} getCustomEmoji={getCustomEmoji} numberOfLines={1} useMarkdown={useMarkdown} preview />
|
|
||||||
</View>
|
</View>
|
||||||
<CustomIcon name='cross' color={COLOR_TEXT_DESCRIPTION} size={20} style={styles.close} onPress={this.close} />
|
<Markdown msg={message.msg} baseUrl={baseUrl} username={username} getCustomEmoji={getCustomEmoji} numberOfLines={1} useMarkdown={useMarkdown} preview />
|
||||||
</View>
|
</View>
|
||||||
);
|
<CustomIcon name='cross' color={COLOR_TEXT_DESCRIPTION} size={20} style={styles.close} onPress={close} />
|
||||||
}
|
</View>
|
||||||
}
|
);
|
||||||
|
}, (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 => ({
|
const mapStateToProps = state => ({
|
||||||
useMarkdown: state.markdown.useMarkdown,
|
useMarkdown: state.markdown.useMarkdown,
|
||||||
|
|
|
@ -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;
|
|
@ -1,10 +1,9 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import {
|
import {
|
||||||
View, TextInput, FlatList, Text, TouchableOpacity, Alert, ScrollView
|
View, TextInput, Alert
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { shortnameToUnicode } from 'emoji-toolkit';
|
|
||||||
import { KeyboardAccessoryView } from 'react-native-keyboard-input';
|
import { KeyboardAccessoryView } from 'react-native-keyboard-input';
|
||||||
import ImagePicker from 'react-native-image-crop-picker';
|
import ImagePicker from 'react-native-image-crop-picker';
|
||||||
import equal from 'deep-equal';
|
import equal from 'deep-equal';
|
||||||
|
@ -16,8 +15,6 @@ import { userTyping as userTypingAction } from '../../actions/room';
|
||||||
import RocketChat from '../../lib/rocketchat';
|
import RocketChat from '../../lib/rocketchat';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import database from '../../lib/database';
|
import database from '../../lib/database';
|
||||||
import Avatar from '../Avatar';
|
|
||||||
import CustomEmoji from '../EmojiPicker/CustomEmoji';
|
|
||||||
import { emojis } from '../../emojis';
|
import { emojis } from '../../emojis';
|
||||||
import Recording from './Recording';
|
import Recording from './Recording';
|
||||||
import UploadModal from './UploadModal';
|
import UploadModal from './UploadModal';
|
||||||
|
@ -29,13 +26,16 @@ import { COLOR_TEXT_DESCRIPTION } from '../../constants/colors';
|
||||||
import LeftButtons from './LeftButtons';
|
import LeftButtons from './LeftButtons';
|
||||||
import RightButtons from './RightButtons';
|
import RightButtons from './RightButtons';
|
||||||
import { isAndroid } from '../../utils/deviceInfo';
|
import { isAndroid } from '../../utils/deviceInfo';
|
||||||
import CommandPreview from './CommandPreview';
|
|
||||||
import { canUploadFile } from '../../utils/media';
|
import { canUploadFile } from '../../utils/media';
|
||||||
|
import Mentions from './Mentions';
|
||||||
const MENTIONS_TRACKING_TYPE_USERS = '@';
|
import MessageboxContext from './Context';
|
||||||
const MENTIONS_TRACKING_TYPE_EMOJIS = ':';
|
import {
|
||||||
const MENTIONS_TRACKING_TYPE_COMMANDS = '/';
|
MENTIONS_TRACKING_TYPE_EMOJIS,
|
||||||
const MENTIONS_COUNT_TO_DISPLAY = 4;
|
MENTIONS_TRACKING_TYPE_COMMANDS,
|
||||||
|
MENTIONS_COUNT_TO_DISPLAY,
|
||||||
|
MENTIONS_TRACKING_TYPE_USERS
|
||||||
|
} from './constants';
|
||||||
|
import CommandsPreview from './CommandsPreview';
|
||||||
|
|
||||||
const imagePickerConfig = {
|
const imagePickerConfig = {
|
||||||
cropping: true,
|
cropping: true,
|
||||||
|
@ -545,7 +545,6 @@ class MessageBox extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
showUploadModal = (file) => {
|
showUploadModal = (file) => {
|
||||||
this.setState({ file: { ...file, isVisible: true } });
|
this.setState({ file: { ...file, isVisible: true } });
|
||||||
}
|
}
|
||||||
|
@ -726,175 +725,29 @@ class MessageBox extends Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
renderFixedMentionItem = item => (
|
|
||||||
<TouchableOpacity
|
|
||||||
style={styles.mentionItem}
|
|
||||||
onPress={() => this.onPressMention(item)}
|
|
||||||
>
|
|
||||||
<Text style={styles.fixedMentionAvatar}>{item.username}</Text>
|
|
||||||
<Text style={styles.mentionText}>{item.username === 'here' ? I18n.t('Notify_active_in_this_room') : I18n.t('Notify_all_in_this_room')}</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
)
|
|
||||||
|
|
||||||
renderMentionEmoji = (item) => {
|
|
||||||
const { baseUrl } = this.props;
|
|
||||||
|
|
||||||
if (item.name) {
|
|
||||||
return (
|
|
||||||
<CustomEmoji
|
|
||||||
key='mention-item-avatar'
|
|
||||||
style={styles.mentionItemCustomEmoji}
|
|
||||||
emoji={item}
|
|
||||||
baseUrl={baseUrl}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Text
|
|
||||||
key='mention-item-avatar'
|
|
||||||
style={styles.mentionItemEmoji}
|
|
||||||
>
|
|
||||||
{shortnameToUnicode(`:${ item }:`)}
|
|
||||||
</Text>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (
|
|
||||||
<TouchableOpacity
|
|
||||||
style={styles.mentionItem}
|
|
||||||
onPress={() => this.onPressMention(item)}
|
|
||||||
testID={testID}
|
|
||||||
>
|
|
||||||
|
|
||||||
{(() => {
|
|
||||||
switch (trackingType) {
|
|
||||||
case MENTIONS_TRACKING_TYPE_EMOJIS:
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{this.renderMentionEmoji(item)}
|
|
||||||
<Text key='mention-item-name' style={styles.mentionText}>:{ item.name || item }:</Text>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
case MENTIONS_TRACKING_TYPE_COMMANDS:
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Text key='mention-item-command' style={styles.slash}>/</Text>
|
|
||||||
<Text key='mention-item-param'>{ item.command}</Text>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Avatar
|
|
||||||
key='mention-item-avatar'
|
|
||||||
style={styles.avatar}
|
|
||||||
text={item.username || item.name}
|
|
||||||
size={30}
|
|
||||||
type={item.t}
|
|
||||||
baseUrl={baseUrl}
|
|
||||||
userId={user.id}
|
|
||||||
token={user.token}
|
|
||||||
/>
|
|
||||||
<Text key='mention-item-name' style={styles.mentionText}>{ item.username || item.name || item }</Text>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
}
|
|
||||||
</TouchableOpacity>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderMentions = () => {
|
|
||||||
const { mentions, trackingType } = this.state;
|
|
||||||
if (!trackingType) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<ScrollView
|
|
||||||
testID='messagebox-container'
|
|
||||||
style={styles.scrollViewMention}
|
|
||||||
keyboardShouldPersistTaps='always'
|
|
||||||
>
|
|
||||||
<FlatList
|
|
||||||
style={styles.mentionList}
|
|
||||||
data={mentions}
|
|
||||||
extraData={mentions}
|
|
||||||
renderItem={this.renderMentionItem}
|
|
||||||
keyExtractor={item => item.id || item.username || item.command || item}
|
|
||||||
keyboardShouldPersistTaps='always'
|
|
||||||
/>
|
|
||||||
</ScrollView>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
renderCommandPreviewItem = ({ item }) => (
|
|
||||||
<CommandPreview item={item} onPress={this.onPressCommandPreview} />
|
|
||||||
);
|
|
||||||
|
|
||||||
renderCommandPreview = () => {
|
|
||||||
const { commandPreview, showCommandPreview } = this.state;
|
|
||||||
if (!showCommandPreview) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<View key='commandbox-container' testID='commandbox-container'>
|
|
||||||
<FlatList
|
|
||||||
style={styles.mentionList}
|
|
||||||
data={commandPreview}
|
|
||||||
renderItem={this.renderCommandPreviewItem}
|
|
||||||
keyExtractor={item => item.id}
|
|
||||||
keyboardShouldPersistTaps='always'
|
|
||||||
horizontal
|
|
||||||
showsHorizontalScrollIndicator={false}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderReplyPreview = () => {
|
|
||||||
const {
|
|
||||||
message, replying, replyCancel, user, getCustomEmoji
|
|
||||||
} = this.props;
|
|
||||||
if (!replying) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return <ReplyPreview key='reply-preview' message={message} close={replyCancel} username={user.username} getCustomEmoji={getCustomEmoji} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
renderContent = () => {
|
renderContent = () => {
|
||||||
const { recording, showEmojiKeyboard, showSend } = this.state;
|
const {
|
||||||
const { editing } = this.props;
|
recording, showEmojiKeyboard, showSend, mentions, trackingType, commandPreview, showCommandPreview
|
||||||
|
} = this.state;
|
||||||
|
const {
|
||||||
|
editing, message, replying, replyCancel, user, getCustomEmoji
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
if (recording) {
|
if (recording) {
|
||||||
return (<Recording onFinish={this.finishAudioMessage} />);
|
return <Recording onFinish={this.finishAudioMessage} />;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{this.renderCommandPreview()}
|
<CommandsPreview commandPreview={commandPreview} showCommandPreview={showCommandPreview} />
|
||||||
{this.renderMentions()}
|
<Mentions mentions={mentions} trackingType={trackingType} />
|
||||||
<View style={styles.composer} key='messagebox'>
|
<View style={styles.composer}>
|
||||||
{this.renderReplyPreview()}
|
<ReplyPreview
|
||||||
|
message={message}
|
||||||
|
close={replyCancel}
|
||||||
|
username={user.username}
|
||||||
|
replying={replying}
|
||||||
|
getCustomEmoji={getCustomEmoji}
|
||||||
|
/>
|
||||||
<View
|
<View
|
||||||
style={[styles.textArea, editing && styles.editing]}
|
style={[styles.textArea, editing && styles.editing]}
|
||||||
testID='messagebox'
|
testID='messagebox'
|
||||||
|
@ -936,8 +789,16 @@ class MessageBox extends Component {
|
||||||
render() {
|
render() {
|
||||||
console.count(`${ this.constructor.name }.render calls`);
|
console.count(`${ this.constructor.name }.render calls`);
|
||||||
const { showEmojiKeyboard, file } = this.state;
|
const { showEmojiKeyboard, file } = this.state;
|
||||||
|
const { user, baseUrl } = this.props;
|
||||||
return (
|
return (
|
||||||
<>
|
<MessageboxContext.Provider
|
||||||
|
value={{
|
||||||
|
user,
|
||||||
|
baseUrl,
|
||||||
|
onPressMention: this.onPressMention,
|
||||||
|
onPressCommandPreview: this.onPressCommandPreview
|
||||||
|
}}
|
||||||
|
>
|
||||||
<KeyboardAccessoryView
|
<KeyboardAccessoryView
|
||||||
renderContent={this.renderContent}
|
renderContent={this.renderContent}
|
||||||
kbInputRef={this.component}
|
kbInputRef={this.component}
|
||||||
|
@ -955,7 +816,7 @@ class MessageBox extends Component {
|
||||||
close={() => this.setState({ file: {} })}
|
close={() => this.setState({ file: {} })}
|
||||||
submit={this.sendMediaMessage}
|
submit={this.sendMediaMessage}
|
||||||
/>
|
/>
|
||||||
</>
|
</MessageboxContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue