diff --git a/app/components/autocomplete/at_mention/at_mention.js b/app/components/autocomplete/at_mention/at_mention.js
index 85f606880..fc4284aca 100644
--- a/app/components/autocomplete/at_mention/at_mention.js
+++ b/app/components/autocomplete/at_mention/at_mention.js
@@ -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);
diff --git a/app/components/autocomplete/at_mention/index.js b/app/components/autocomplete/at_mention/index.js
index f6b5c1435..767797b61 100644
--- a/app/components/autocomplete/at_mention/index.js
+++ b/app/components/autocomplete/at_mention/index.js
@@ -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)
};
}
diff --git a/app/components/autocomplete/channel_mention/channel_mention.js b/app/components/autocomplete/channel_mention/channel_mention.js
index af898187c..ad6eb0605 100644
--- a/app/components/autocomplete/channel_mention/channel_mention.js
+++ b/app/components/autocomplete/channel_mention/channel_mention.js
@@ -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);
diff --git a/app/components/autocomplete/channel_mention/index.js b/app/components/autocomplete/channel_mention/index.js
index 54c3740b8..273aaaead 100644
--- a/app/components/autocomplete/channel_mention/index.js
+++ b/app/components/autocomplete/channel_mention/index.js
@@ -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)
};
diff --git a/app/components/autocomplete/emoji_suggestion/emoji_suggestion.js b/app/components/autocomplete/emoji_suggestion/emoji_suggestion.js
index 33896b903..d4bf71806 100644
--- a/app/components/autocomplete/emoji_suggestion/emoji_suggestion.js
+++ b/app/components/autocomplete/emoji_suggestion/emoji_suggestion.js
@@ -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);
diff --git a/app/components/autocomplete/emoji_suggestion/index.js b/app/components/autocomplete/emoji_suggestion/index.js
index 81ea1d342..e4fd2e6bf 100644
--- a/app/components/autocomplete/emoji_suggestion/index.js
+++ b/app/components/autocomplete/emoji_suggestion/index.js
@@ -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)
};
}
diff --git a/app/components/autocomplete/index.js b/app/components/autocomplete/index.js
index 96001bdfb..1b2877272 100644
--- a/app/components/autocomplete/index.js
+++ b/app/components/autocomplete/index.js
@@ -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 = {
diff --git a/app/components/post_textbox/components/paper_clip_icon.js b/app/components/post_textbox/components/paper_clip_icon.js
index 4d494051a..aa988e383 100644
--- a/app/components/post_textbox/components/paper_clip_icon.js
+++ b/app/components/post_textbox/components/paper_clip_icon.js
@@ -16,7 +16,7 @@ export default class PaperClipIcon extends Component {
if (this.root) {
this.root.setNativeProps(nativeProps);
}
- }
+ };
render() {
return (
diff --git a/app/components/post_textbox/post_textbox.js b/app/components/post_textbox/post_textbox.js
index 857527700..83c5f255b 100644
--- a/app/components/post_textbox/post_textbox.js
+++ b/app/components/post_textbox/post_textbox.js
@@ -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,8 +72,54 @@ class PostTextbox extends PureComponent {
}
componentWillReceiveProps(nextProps) {
+ if (nextProps.channelId !== this.props.channelId || nextProps.rootId !== this.props.rootId) {
+ this.setState({value: nextProps.value});
+ }
+ }
+
+ componentWillUnmount() {
+ if (Platform.OS === 'android') {
+ Keyboard.removeListener('keyboardDidHide', this.handleAndroidKeyboard);
+ BackHandler.removeEventListener('hardwareBackPress', this.handleAndroidBack);
+ }
+ }
+
+ attachAutocomplete = (c) => {
+ this.autocomplete = c;
+ };
+
+ blur = () => {
+ this.refs.input.blur();
+ };
+
+ canSend = () => {
+ const {files, uploadFileRequestStatus} = this.props;
+ const {value} = this.state;
+ const valueLength = value.trim().length;
+
+ if (files.length) {
+ return valueLength <= MAX_MESSAGE_LENGTH && uploadFileRequestStatus !== RequestStatus.STARTED && files.filter((f) => !f.failed).length > 0;
+ }
+
+ return valueLength > 0 && valueLength <= MAX_MESSAGE_LENGTH;
+ };
+
+ changeDraft = (text) => {
+ const {
+ actions,
+ channelId,
+ rootId
+ } = this.props;
+
+ if (rootId) {
+ actions.handleCommentDraftChanged(rootId, text);
+ } else {
+ actions.handlePostDraftChanged(channelId, text);
+ }
+ };
+
+ checkMessageLength = (value) => {
const {intl} = this.props;
- const {value} = nextProps;
const valueLength = value.trim().length;
if (valueLength > MAX_MESSAGE_LENGTH) {
@@ -95,28 +137,6 @@ class PostTextbox extends PureComponent {
})
);
}
- }
-
- componentWillUnmount() {
- if (Platform.OS === 'android') {
- Keyboard.removeListener('keyboardDidHide', this.handleAndroidKeyboard);
- BackHandler.removeEventListener('hardwareBackPress', this.handleAndroidBack);
- }
- }
-
- blur = () => {
- this.refs.input.blur();
- };
-
- canSend = () => {
- const {files, uploadFileRequestStatus, value} = this.props;
- const valueLength = value.trim().length;
-
- if (files.length) {
- return valueLength <= MAX_MESSAGE_LENGTH && uploadFileRequestStatus !== RequestStatus.STARTED && files.filter((f) => !f.failed).length > 0;
- }
-
- return valueLength > 0 && valueLength <= MAX_MESSAGE_LENGTH;
};
handleAndroidKeyboard = () => {
@@ -132,12 +152,47 @@ class PostTextbox extends PureComponent {
return false;
};
+ handleContentSizeChange = (event) => {
+ let contentHeight = event.nativeEvent.layout.height;
+ if (contentHeight < INITIAL_HEIGHT) {
+ contentHeight = INITIAL_HEIGHT;
+ }
+
+ this.setState({
+ contentHeight
+ });
+ };
+
+ handleEndEditing = (e) => {
+ if (e && e.nativeEvent) {
+ this.changeDraft(e.nativeEvent.text || '');
+ }
+ };
+
+ handleInputSizeChange = (event) => {
+ this.setState({
+ inputWidth: event.nativeEvent.layout.width
+ });
+ };
+
+ handlePostDraftSelectionChanged = (event) => {
+ const cursorPosition = event.nativeEvent.selection.end;
+ if (this.props.rootId) {
+ this.props.actions.handleCommentDraftSelectionChanged(this.props.rootId, cursorPosition);
+ } else {
+ this.props.actions.handlePostDraftSelectionChanged(this.props.channelId, cursorPosition);
+ }
+
+ this.autocomplete.handleSelectionChange(event);
+ };
+
handleSendMessage = () => {
if (!this.canSend()) {
return;
}
- const {files, value} = this.props;
+ const {files} = this.props;
+ const {value} = this.state;
const isReactionMatch = value.match(IS_REACTION_REGEX);
if (isReactionMatch) {
@@ -171,8 +226,85 @@ class PostTextbox extends PureComponent {
}
};
+ handleSubmit = () => {
+ // Workaround for android as the multiline is not working
+ if (Platform.OS === 'android') {
+ if (this.timeout) {
+ clearTimeout(this.timeout);
+ }
+ this.timeout = setTimeout(() => {
+ let {value: msg} = this.props;
+ msg += '\n';
+ this.handleTextChange(msg);
+ }, 10);
+ }
+ };
+
+ handleTextChange = (value) => {
+ const {
+ actions,
+ channelId,
+ rootId
+ } = this.props;
+
+ this.checkMessageLength(value);
+ this.setState({value});
+
+ if (value) {
+ actions.userTyping(channelId, rootId);
+ }
+ };
+
+ handleUploadFiles = (images) => {
+ this.props.actions.handleUploadFiles(images, this.props.rootId);
+ };
+
+ renderDisabledSendButton = () => {
+ const {theme} = this.props;
+ const style = getStyleSheet(theme);
+
+ return (
+
+
+
+
+
+ );
+ };
+
+ renderSendButton = () => {
+ const {theme, uploadFileRequestStatus} = this.props;
+ const style = getStyleSheet(theme);
+
+ if (uploadFileRequestStatus === RequestStatus.STARTED) {
+ return this.renderDisabledSendButton();
+ } else if (this.canSend()) {
+ return (
+
+
+
+
+
+ );
+ }
+
+ return null;
+ };
+
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 = {
@@ -185,6 +317,7 @@ class PostTextbox extends PureComponent {
actions.createPost(post, postFiles);
this.handleTextChange('');
+ this.changeDraft('');
if (postFiles.length) {
actions.handleClearFiles(channelId, rootId);
}
@@ -205,139 +338,25 @@ class PostTextbox extends PureComponent {
this.setState(nextState, callback);
};
- handleUploadFiles = (images) => {
- this.props.actions.handleUploadFiles(images, this.props.rootId);
- };
-
- changeDraft = (text) => {
- const {
- actions,
- channelId,
- rootId
- } = this.props;
-
- if (rootId) {
- actions.handleCommentDraftChanged(rootId, text);
- } else {
- actions.handlePostDraftChanged(channelId, text);
- }
- }
-
sendReaction = (emoji) => {
const {actions, rootId} = this.props;
actions.addReactionToLatestPost(emoji, rootId);
this.handleTextChange('');
- }
-
- handleTextChange = (text) => {
- const {
- actions,
- channelId,
- rootId
- } = this.props;
-
- this.changeDraft(text);
- actions.userTyping(channelId, rootId);
+ this.changeDraft('');
};
- handleContentSizeChange = (event) => {
- let contentHeight = event.nativeEvent.layout.height;
- if (contentHeight < INITIAL_HEIGHT) {
- contentHeight = INITIAL_HEIGHT;
- }
-
- this.setState({
- contentHeight
- });
- };
-
- handleInputSizeChange = (event) => {
- this.setState({
- inputWidth: event.nativeEvent.layout.width
- });
- };
-
- handleSubmit = () => {
- // Workaround for android as the multiline is not working
- if (Platform.OS === 'android') {
- if (this.timeout) {
- clearTimeout(this.timeout);
- }
- this.timeout = setTimeout(() => {
- let {value: msg} = this.props;
- msg += '\n';
- this.handleTextChange(msg);
- }, 10);
- }
- };
-
- attachAutocomplete = (c) => {
- this.autocomplete = c;
- };
-
- renderDisabledSendButton = () => {
- const {theme} = this.props;
- const style = getStyleSheet(theme);
-
- return (
-
-
-
- );
- }
-
- renderSendButton = () => {
- const {theme, uploadFileRequestStatus} = this.props;
- const style = getStyleSheet(theme);
-
- if (uploadFileRequestStatus === RequestStatus.STARTED) {
- return this.renderDisabledSendButton();
- } else if (this.canSend()) {
- return (
-
-
-
- );
- }
-
- return null;
- }
-
- handlePostDraftSelectionChanged = (event) => {
- const cursorPosition = event.nativeEvent.selection.end;
- if (this.props.rootId) {
- this.props.actions.handleCommentDraftSelectionChanged(this.props.rootId, cursorPosition);
- } else {
- this.props.actions.handlePostDraftSelectionChanged(this.props.channelId, cursorPosition);
- }
-
- this.autocomplete.handleSelectionChange(event);
- }
-
render() {
const {
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) {
@@ -378,8 +397,8 @@ class PostTextbox extends PureComponent {
/>
{attachmentButton}
@@ -392,13 +411,14 @@ class PostTextbox extends PureComponent {
placeholder={intl.formatMessage(placeholder)}
placeholderTextColor={changeOpacity('#000', 0.5)}
multiline={true}
- numberOfLines={10}
+ numberOfLines={5}
blurOnSubmit={false}
underlineColorAndroid='transparent'
style={[style.input, {height: textInputHeight}]}
onSubmitEditing={this.handleSubmit}
onLayout={this.handleInputSizeChange}
keyboardType={this.state.keyboardType}
+ onEndEditing={this.handleEndEditing}
/>
{this.renderSendButton()}
@@ -435,8 +455,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
flex: 1,
flexDirection: 'row',
backgroundColor: '#fff',
- alignItems: 'flex-end',
- marginRight: 10
+ alignItems: 'flex-end'
},
inputContainerWithoutFileUpload: {
marginLeft: 10
@@ -449,6 +468,9 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
borderTopWidth: 1,
borderTopColor: changeOpacity(theme.centerChannelColor, 0.20)
},
+ sendButtonContainer: {
+ paddingRight: 10
+ },
sendButton: {
backgroundColor: theme.buttonBg,
borderRadius: 18,
diff --git a/app/screens/search/search.js b/app/screens/search/search.js
index 28fe618d2..5781f88f7 100644
--- a/app/screens/search/search.js
+++ b/app/screens/search/search.js
@@ -603,6 +603,7 @@ class Search extends PureComponent {
ref={this.attachAutocomplete}
onChangeText={this.handleTextChanged}
isSearch={true}
+ value={value}
/>
{previewComponent}