Improve performance on post textbox (#1138)

This commit is contained in:
enahum 2017-11-13 18:36:24 -03:00 committed by GitHub
parent f6d829c445
commit 49899b2a00
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 115 additions and 141 deletions

View file

@ -27,16 +27,16 @@ export default class AtMention extends PureComponent {
matchTerm: PropTypes.string,
onChangeText: PropTypes.func.isRequired,
outChannel: PropTypes.array,
postDraft: PropTypes.string,
requestStatus: PropTypes.string.isRequired,
teamMembers: PropTypes.array,
theme: PropTypes.object.isRequired
theme: PropTypes.object.isRequired,
value: PropTypes.string
};
static defaultProps = {
defaultChannel: {},
postDraft: '',
isSearch: false
isSearch: false,
value: ''
};
constructor(props) {
@ -144,8 +144,8 @@ export default class AtMention extends PureComponent {
};
completeMention = (mention) => {
const {cursorPosition, isSearch, onChangeText, postDraft} = this.props;
const mentionPart = postDraft.substring(0, cursorPosition);
const {cursorPosition, isSearch, onChangeText, value} = this.props;
const mentionPart = value.substring(0, cursorPosition);
let completedDraft;
if (isSearch) {
@ -154,8 +154,8 @@ export default class AtMention extends PureComponent {
completedDraft = mentionPart.replace(AT_MENTION_REGEX, `@${mention} `);
}
if (postDraft.length > cursorPosition) {
completedDraft += postDraft.substring(cursorPosition);
if (value.length > cursorPosition) {
completedDraft += value.substring(cursorPosition);
}
onChangeText(completedDraft, true);

View file

@ -19,25 +19,10 @@ import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import AtMention from './at_mention';
function mapStateToProps(state, ownProps) {
const {cursorPosition, isSearch, rootId} = ownProps;
const {cursorPosition, isSearch} = ownProps;
const currentChannelId = getCurrentChannelId(state);
let postDraft = '';
if (isSearch) {
postDraft = state.views.search;
} else if (ownProps.rootId) {
const threadDraft = state.views.thread.drafts[rootId];
if (threadDraft) {
postDraft = threadDraft.draft;
}
} else if (currentChannelId) {
const channelDraft = state.views.channel.drafts[currentChannelId];
if (channelDraft) {
postDraft = channelDraft.draft;
}
}
const value = postDraft.substring(0, cursorPosition);
const value = ownProps.value.substring(0, cursorPosition);
const matchTerm = getMatchTermForAtMention(value, isSearch);
let teamMembers;
@ -54,14 +39,12 @@ function mapStateToProps(state, ownProps) {
currentChannelId,
currentTeamId: getCurrentTeamId(state),
defaultChannel: getDefaultChannel(state),
postDraft,
matchTerm,
teamMembers,
inChannel,
outChannel,
requestStatus: state.requests.users.autocompleteUsers.status,
theme: getTheme(state),
...ownProps
theme: getTheme(state)
};
}

View file

@ -24,16 +24,16 @@ export default class ChannelMention extends PureComponent {
myChannels: PropTypes.array,
otherChannels: PropTypes.array,
onChangeText: PropTypes.func.isRequired,
postDraft: PropTypes.string,
privateChannels: PropTypes.array,
publicChannels: PropTypes.array,
requestStatus: PropTypes.string.isRequired,
theme: PropTypes.object.isRequired
theme: PropTypes.object.isRequired,
value: PropTypes.string
};
static defaultProps = {
postDraft: '',
isSearch: false
isSearch: false,
value: ''
};
constructor(props) {
@ -116,8 +116,8 @@ export default class ChannelMention extends PureComponent {
}
completeMention = (mention) => {
const {cursorPosition, isSearch, onChangeText, postDraft} = this.props;
const mentionPart = postDraft.substring(0, cursorPosition);
const {cursorPosition, isSearch, onChangeText, value} = this.props;
const mentionPart = value.substring(0, cursorPosition);
let completedDraft;
if (isSearch) {
@ -127,8 +127,8 @@ export default class ChannelMention extends PureComponent {
completedDraft = mentionPart.replace(CHANNEL_MENTION_REGEX, `~${mention} `);
}
if (postDraft.length > cursorPosition) {
completedDraft += postDraft.substring(cursorPosition);
if (value.length > cursorPosition) {
completedDraft += value.substring(cursorPosition);
}
onChangeText(completedDraft, true);

View file

@ -5,7 +5,6 @@ import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {searchChannels} from 'mattermost-redux/actions/channels';
import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels';
import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
import {
@ -20,25 +19,9 @@ import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import ChannelMention from './channel_mention';
function mapStateToProps(state, ownProps) {
const {cursorPosition, isSearch, rootId} = ownProps;
const currentChannelId = getCurrentChannelId(state);
const {cursorPosition, isSearch} = ownProps;
let postDraft = '';
if (isSearch) {
postDraft = state.views.search;
} else if (rootId) {
const threadDraft = state.views.thread.drafts[rootId];
if (threadDraft) {
postDraft = threadDraft.draft;
}
} else if (currentChannelId) {
const channelDraft = state.views.channel.drafts[currentChannelId];
if (channelDraft) {
postDraft = channelDraft.draft;
}
}
const value = postDraft.substring(0, cursorPosition);
const value = ownProps.value.substring(0, cursorPosition);
const matchTerm = getMatchTermForChannelMention(value, isSearch);
let myChannels;
@ -61,7 +44,6 @@ function mapStateToProps(state, ownProps) {
privateChannels,
currentTeamId: getCurrentTeamId(state),
matchTerm,
postDraft,
requestStatus: state.requests.channels.getChannels.status,
theme: getTheme(state)
};

View file

@ -22,25 +22,25 @@ export default class EmojiSuggestion extends Component {
}).isRequired,
cursorPosition: PropTypes.number,
emojis: PropTypes.array.isRequired,
postDraft: PropTypes.string,
theme: PropTypes.object.isRequired,
onChangeText: PropTypes.func.isRequired,
rootId: PropTypes.string
rootId: PropTypes.string,
value: PropTypes.string
};
static defaultProps = {
defaultChannel: {},
postDraft: ''
value: ''
};
state = {
active: false,
dataSource: []
}
};
componentWillReceiveProps(nextProps) {
const regex = EMOJI_REGEX;
const match = nextProps.postDraft.substring(0, nextProps.cursorPosition).match(regex);
const match = nextProps.value.substring(0, nextProps.cursorPosition).match(regex);
if (!match || this.state.emojiComplete) {
this.setState({
@ -74,8 +74,8 @@ export default class EmojiSuggestion extends Component {
}
completeSuggestion = (emoji) => {
const {actions, cursorPosition, onChangeText, postDraft, rootId} = this.props;
const emojiPart = postDraft.substring(0, cursorPosition);
const {actions, cursorPosition, onChangeText, value, rootId} = this.props;
const emojiPart = value.substring(0, cursorPosition);
if (emojiPart.startsWith('+:')) {
actions.addReactionToLatestPost(emoji, rootId);
@ -83,8 +83,8 @@ export default class EmojiSuggestion extends Component {
} else {
let completedDraft = emojiPart.replace(EMOJI_REGEX, `:${emoji}: `);
if (postDraft.length > cursorPosition) {
completedDraft += postDraft.substring(cursorPosition);
if (value.length > cursorPosition) {
completedDraft += value.substring(cursorPosition);
}
onChangeText(completedDraft);

View file

@ -25,26 +25,11 @@ const getEmojisByName = createSelector(
}
);
function mapStateToProps(state, ownProps) {
const {currentChannelId} = state.entities.channels;
function mapStateToProps(state) {
const emojis = getEmojisByName(state);
let postDraft;
if (ownProps.rootId) {
const threadDraft = state.views.thread.drafts[ownProps.rootId];
if (threadDraft) {
postDraft = threadDraft.draft;
}
} else if (currentChannelId) {
const channelDraft = state.views.channel.drafts[currentChannelId];
if (channelDraft) {
postDraft = channelDraft.draft;
}
}
return {
emojis,
postDraft,
theme: getTheme(state)
};
}

View file

@ -17,7 +17,8 @@ export default class Autocomplete extends PureComponent {
static propTypes = {
onChangeText: PropTypes.func.isRequired,
rootId: PropTypes.string,
isSearch: PropTypes.bool
isSearch: PropTypes.bool,
value: PropTypes.string
};
static defaultProps = {

View file

@ -16,7 +16,7 @@ export default class PaperClipIcon extends Component {
if (this.root) {
this.root.setNativeProps(nativeProps);
}
}
};
render() {
return (

View file

@ -3,16 +3,7 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {
Alert,
BackHandler,
Keyboard,
Platform,
Text,
TextInput,
TouchableOpacity,
View
} from 'react-native';
import {Alert, BackHandler, Keyboard, Platform, Text, TextInput, TouchableOpacity, View} from 'react-native';
import {injectIntl, intlShape} from 'react-intl';
import {RequestStatus} from 'mattermost-redux/constants';
@ -62,11 +53,16 @@ class PostTextbox extends PureComponent {
value: ''
};
state = {
contentHeight: INITIAL_HEIGHT,
inputWidth: null,
keyboardType: 'default'
};
constructor(props) {
super(props);
this.state = {
contentHeight: INITIAL_HEIGHT,
inputWidth: null,
keyboardType: 'default',
value: props.value
};
}
componentDidMount() {
if (Platform.OS === 'android') {
@ -76,24 +72,8 @@ class PostTextbox extends PureComponent {
}
componentWillReceiveProps(nextProps) {
const {intl} = this.props;
const {value} = nextProps;
const valueLength = value.trim().length;
if (valueLength > MAX_MESSAGE_LENGTH) {
Alert.alert(
intl.formatMessage({
id: 'mobile.message_length.title',
defaultMessage: 'Message Length'
}),
intl.formatMessage({
id: 'mobile.message_length.message',
defaultMessage: 'Your current message is too long. Current character count: {max}/{count}'
}, {
max: MAX_MESSAGE_LENGTH,
count: valueLength
})
);
if (nextProps.channelId !== this.props.channelId || nextProps.rootId !== this.props.rootId) {
this.setState({value: nextProps.value});
}
}
@ -113,7 +93,8 @@ class PostTextbox extends PureComponent {
};
canSend = () => {
const {files, uploadFileRequestStatus, value} = this.props;
const {files, uploadFileRequestStatus} = this.props;
const {value} = this.state;
const valueLength = value.trim().length;
if (files.length) {
@ -137,6 +118,27 @@ class PostTextbox extends PureComponent {
}
};
checkMessageLength = (value) => {
const {intl} = this.props;
const valueLength = value.trim().length;
if (valueLength > MAX_MESSAGE_LENGTH) {
Alert.alert(
intl.formatMessage({
id: 'mobile.message_length.title',
defaultMessage: 'Message Length'
}),
intl.formatMessage({
id: 'mobile.message_length.message',
defaultMessage: 'Your current message is too long. Current character count: {max}/{count}'
}, {
max: MAX_MESSAGE_LENGTH,
count: valueLength
})
);
}
};
handleAndroidKeyboard = () => {
this.blur();
};
@ -169,6 +171,12 @@ class PostTextbox extends PureComponent {
});
};
handleEndEditing = (e) => {
if (e && e.nativeEvent) {
this.changeDraft(e.nativeEvent.text || '');
}
};
handleFocus = () => {
if (this.refs.input && Platform.OS === 'android') {
this.refs.input.setNativeProps({
@ -199,7 +207,8 @@ class PostTextbox extends PureComponent {
return;
}
const {files, value} = this.props;
const {files} = this.props;
const {value} = this.state;
const isReactionMatch = value.match(IS_REACTION_REGEX);
if (isReactionMatch) {
@ -233,15 +242,19 @@ class PostTextbox extends PureComponent {
}
};
handleTextChange = (text) => {
handleTextChange = (value) => {
const {
actions,
channelId,
rootId
} = this.props;
this.changeDraft(text);
actions.userTyping(channelId, rootId);
this.checkMessageLength(value);
this.setState({value});
if (value) {
actions.userTyping(channelId, rootId);
}
};
handleUploadFiles = (images) => {
@ -253,12 +266,14 @@ class PostTextbox extends PureComponent {
const style = getStyleSheet(theme);
return (
<View style={[style.sendButton, style.disableButton]}>
<PaperPlane
height={13}
width={15}
color={theme.buttonColor}
/>
<View style={style.sendButtonContainer}>
<View style={[style.sendButton, style.disableButton]}>
<PaperPlane
height={13}
width={15}
color={theme.buttonColor}
/>
</View>
</View>
);
};
@ -273,13 +288,15 @@ class PostTextbox extends PureComponent {
return (
<TouchableOpacity
onPress={this.handleSendMessage}
style={style.sendButton}
style={style.sendButtonContainer}
>
<PaperPlane
height={13}
width={15}
color={theme.buttonColor}
/>
<View style={style.sendButton}>
<PaperPlane
height={13}
width={15}
color={theme.buttonColor}
/>
</View>
</TouchableOpacity>
);
}
@ -288,7 +305,8 @@ class PostTextbox extends PureComponent {
};
sendMessage = () => {
const {actions, currentUserId, channelId, files, rootId, value} = this.props;
const {actions, currentUserId, channelId, files, rootId} = this.props;
const {value} = this.state;
const postFiles = files.filter((f) => !f.failed);
const post = {
@ -301,6 +319,7 @@ class PostTextbox extends PureComponent {
actions.createPost(post, postFiles);
this.handleTextChange('');
this.changeDraft('');
if (postFiles.length) {
actions.handleClearFiles(channelId, rootId);
}
@ -325,6 +344,7 @@ class PostTextbox extends PureComponent {
const {actions, rootId} = this.props;
actions.addReactionToLatestPost(emoji, rootId);
this.handleTextChange('');
this.changeDraft('');
};
render() {
@ -332,14 +352,13 @@ class PostTextbox extends PureComponent {
canUploadFiles,
channelIsLoading,
intl,
theme,
value
theme
} = this.props;
const style = getStyleSheet(theme);
const textInputHeight = Math.min(this.state.contentHeight, MAX_CONTENT_HEIGHT);
const textValue = channelIsLoading ? '' : value;
const textValue = channelIsLoading ? '' : this.state.value;
let placeholder;
if (this.props.rootId) {
@ -380,8 +399,8 @@ class PostTextbox extends PureComponent {
/>
<Autocomplete
ref={this.attachAutocomplete}
onChangeText={this.changeDraft}
rootId={this.props.rootId}
onChangeText={this.handleTextChange}
value={this.state.value}
/>
<View style={style.inputWrapper}>
{attachmentButton}
@ -402,6 +421,7 @@ class PostTextbox extends PureComponent {
keyboardType={this.state.keyboardType}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
onEndEditing={this.handleEndEditing}
/>
{this.renderSendButton()}
</View>
@ -438,8 +458,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
flex: 1,
flexDirection: 'row',
backgroundColor: '#fff',
alignItems: 'flex-end',
marginRight: 10
alignItems: 'flex-end'
},
inputContainerWithoutFileUpload: {
marginLeft: 10
@ -452,6 +471,9 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
borderTopWidth: 1,
borderTopColor: changeOpacity(theme.centerChannelColor, 0.20)
},
sendButtonContainer: {
paddingRight: 10
},
sendButton: {
backgroundColor: theme.buttonBg,
borderRadius: 18,

View file

@ -627,6 +627,7 @@ class Search extends PureComponent {
ref={this.attachAutocomplete}
onChangeText={this.handleTextChanged}
isSearch={true}
value={value}
/>
{previewComponent}
</View>