diff --git a/app/components/emoji/emoji.js b/app/components/emoji/emoji.js
index 2b0510658..48fa36557 100644
--- a/app/components/emoji/emoji.js
+++ b/app/components/emoji/emoji.js
@@ -38,6 +38,7 @@ export default class Emoji extends React.PureComponent {
}
componentWillMount() {
+ this.mounted = true;
if (this.state.imageUrl && this.state.isCustomEmoji) {
this.updateImageHeight(this.state.imageUrl);
}
@@ -59,6 +60,10 @@ export default class Emoji extends React.PureComponent {
}
}
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
getImageUrl = (props = this.props) => {
const emojiName = props.emojiName;
@@ -83,10 +88,12 @@ export default class Emoji extends React.PureComponent {
updateImageHeight = (imageUrl) => {
Image.getSize(imageUrl, (originalWidth, originalHeight) => {
- this.setState({
- originalWidth,
- originalHeight
- });
+ if (this.mounted) {
+ this.setState({
+ originalWidth,
+ originalHeight
+ });
+ }
});
}
diff --git a/app/components/post_list/post_list.js b/app/components/post_list/post_list.js
index 2da5651db..c34c8720c 100644
--- a/app/components/post_list/post_list.js
+++ b/app/components/post_list/post_list.js
@@ -4,6 +4,7 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {
+ StyleSheet,
View
} from 'react-native';
import FlatList from 'app/components/inverted_flat_list';
@@ -183,7 +184,14 @@ export default class PostList extends PureComponent {
theme={theme}
getItem={this.getItem}
getItemCount={this.getItemCount}
+ contentContainerStyle={styles.postListContent}
/>
);
}
}
+
+const styles = StyleSheet.create({
+ postListContent: {
+ paddingTop: 5
+ }
+});
diff --git a/app/components/post_textbox/components/attachment_button.js b/app/components/post_textbox/components/attachment_button.js
new file mode 100644
index 000000000..8e219bd72
--- /dev/null
+++ b/app/components/post_textbox/components/attachment_button.js
@@ -0,0 +1,177 @@
+import React, {PureComponent} from 'react';
+import PropTypes from 'prop-types';
+import {
+ Platform,
+ StyleSheet,
+ TouchableOpacity
+} from 'react-native';
+import Icon from 'react-native-vector-icons/Ionicons';
+import ImagePicker from 'react-native-image-picker';
+
+import {changeOpacity} from 'app/utils/theme';
+
+export default class AttachmentButton extends PureComponent {
+ static propTypes = {
+ blurTextBox: PropTypes.func.isRequired,
+ navigator: PropTypes.object.isRequired,
+ theme: PropTypes.object.isRequired,
+ uploadFiles: PropTypes.func.isRequired
+ };
+
+ attachFileFromCamera = () => {
+ const options = {
+ quality: 0.7,
+ noData: true,
+ storageOptions: {
+ cameraRoll: true,
+ waitUntilSaved: true
+ }
+ };
+
+ ImagePicker.launchCamera(options, (response) => {
+ if (response.error || response.didCancel) {
+ return;
+ }
+
+ this.uploadFiles([response]);
+ });
+ };
+
+ attachFileFromLibrary = () => {
+ const options = {
+ quality: 0.7,
+ noData: true
+ };
+
+ if (Platform.OS === 'ios') {
+ options.mediaType = 'mixed';
+ }
+
+ ImagePicker.launchImageLibrary(options, (response) => {
+ if (response.error || response.didCancel) {
+ return;
+ }
+
+ this.uploadFiles([response]);
+ });
+ };
+
+ attachVideoFromLibraryAndroid = () => {
+ const options = {
+ quality: 0.7,
+ mediaType: 'video',
+ noData: true
+ };
+
+ ImagePicker.launchImageLibrary(options, (response) => {
+ if (response.error || response.didCancel) {
+ return;
+ }
+
+ this.uploadFiles([response]);
+ });
+ }
+
+ uploadFiles = (images) => {
+ this.props.uploadFiles(images);
+ };
+
+ handleFileAttachmentOption = (action) => {
+ this.props.navigator.dismissModal({
+ animationType: 'none'
+ });
+
+ // Have to wait to launch the library attachment action.
+ // If we call the action after dismissModal with no delay then the
+ // Wix navigator will dismiss the library attachment modal as well.
+ setTimeout(() => {
+ if (typeof action === 'function') {
+ action();
+ }
+ }, 100);
+ }
+
+ showFileAttachmentOptions = () => {
+ this.props.blurTextBox();
+ const options = {
+ items: [{
+ action: () => this.handleFileAttachmentOption(this.attachFileFromCamera),
+ text: {
+ id: 'mobile.file_upload.camera',
+ defaultMessage: 'Take Photo or Video'
+ },
+ icon: 'camera'
+ }, {
+ action: () => this.handleFileAttachmentOption(this.attachFileFromLibrary),
+ text: {
+ id: 'mobile.file_upload.library',
+ defaultMessage: 'Photo Library'
+ },
+ icon: 'photo'
+ }]
+ };
+
+ if (Platform.OS === 'android') {
+ options.items.push({
+ action: () => this.handleFileAttachmentOption(this.attachVideoFromLibraryAndroid),
+ text: {
+ id: 'mobile.file_upload.video',
+ defaultMessage: 'Video Library'
+ },
+ icon: 'file-video-o'
+ });
+ }
+
+ this.props.navigator.showModal({
+ screen: 'OptionsModal',
+ title: '',
+ animationType: 'none',
+ passProps: {
+ items: options.items
+ },
+ navigatorStyle: {
+ navBarHidden: true,
+ statusBarHidden: false,
+ statusBarHideWithNavBar: false,
+ screenBackgroundColor: 'transparent',
+ modalPresentationStyle: 'overCurrentContext'
+ }
+ });
+ };
+
+ render() {
+ const {theme} = this.props;
+
+ return (
+
+
+
+ );
+ }
+}
+
+const style = StyleSheet.create({
+ attachIcon: {
+ marginTop: Platform.select({
+ ios: 2,
+ android: 0
+ })
+ },
+ buttonContainer: {
+ height: Platform.select({
+ ios: 34,
+ android: 36
+ }),
+ width: 45,
+ alignItems: 'center',
+ justifyContent: 'center'
+ }
+});
diff --git a/app/components/post_textbox/components/typing/index.js b/app/components/post_textbox/components/typing/index.js
new file mode 100644
index 000000000..220559627
--- /dev/null
+++ b/app/components/post_textbox/components/typing/index.js
@@ -0,0 +1,19 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import {connect} from 'react-redux';
+
+import {getUsersTyping} from 'mattermost-redux/selectors/entities/typing';
+
+import {getTheme} from 'app/selectors/preferences';
+
+import Typing from './typing';
+
+function mapStateToProps(state) {
+ return {
+ theme: getTheme(state),
+ typing: getUsersTyping(state)
+ };
+}
+
+export default connect(mapStateToProps)(Typing);
diff --git a/app/components/post_textbox/components/typing/typing.js b/app/components/post_textbox/components/typing/typing.js
new file mode 100644
index 000000000..0586b34fa
--- /dev/null
+++ b/app/components/post_textbox/components/typing/typing.js
@@ -0,0 +1,102 @@
+import React, {PureComponent} from 'react';
+import PropTypes from 'prop-types';
+import {
+ Animated,
+ Text
+} from 'react-native';
+
+import FormattedText from 'app/components/formatted_text';
+import {makeStyleSheetFromTheme} from 'app/utils/theme';
+
+const {View: AnimatedView} = Animated;
+
+export default class Typing extends PureComponent {
+ static propTypes = {
+ theme: PropTypes.object.isRequired,
+ typing: PropTypes.array.isRequired
+ };
+
+ state = {
+ typingHeight: new Animated.Value(0)
+ }
+
+ componentWillReceiveProps(nextProps) {
+ if (nextProps.typing.length && !this.props.typing.length) {
+ this.animateTyping(true);
+ } else if (!nextProps.typing.length) {
+ this.animateTyping();
+ }
+ }
+
+ animateTyping = (show = false) => {
+ const height = show ? 20 : 0;
+
+ Animated.timing(this.state.typingHeight, {
+ toValue: height,
+ duration: 200
+ }).start();
+ }
+
+ renderTyping = () => {
+ const {typing} = this.props;
+ const nextTyping = [...typing];
+ const numUsers = nextTyping.length;
+
+ switch (numUsers) {
+ case 0:
+ return null;
+ case 1:
+ return (
+
+ );
+ default: {
+ const last = nextTyping.pop();
+ return (
+
+ );
+ }
+ }
+ };
+
+ render() {
+ const style = getStyleSheet(this.props.theme);
+
+ return (
+
+
+ {this.renderTyping()}
+
+
+ );
+ }
+}
+
+const getStyleSheet = makeStyleSheetFromTheme((theme) => {
+ return {
+ typing: {
+ paddingLeft: 10,
+ paddingTop: 3,
+ fontSize: 11,
+ marginBottom: 5,
+ color: theme.centerChannelColor,
+ backgroundColor: 'transparent'
+ }
+ };
+});
diff --git a/app/components/post_textbox/index.js b/app/components/post_textbox/index.js
index 26f0ee94c..6dd89ff82 100644
--- a/app/components/post_textbox/index.js
+++ b/app/components/post_textbox/index.js
@@ -3,27 +3,32 @@
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
+
import {createPost} from 'mattermost-redux/actions/posts';
import {userTyping} from 'mattermost-redux/actions/websocket';
-
-import {addReactionToLatestPost} from 'app/actions/views/emoji';
-import {handleClearFiles, handleRemoveLastFile, handleUploadFiles} from 'app/actions/views/file_upload';
-import {getTheme} from 'app/selectors/preferences';
import {canUploadFilesOnMobile} from 'mattermost-redux/selectors/entities/general';
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
-import {getUsersTyping} from 'mattermost-redux/selectors/entities/typing';
+
+import {addReactionToLatestPost} from 'app/actions/views/emoji';
+import {handlePostDraftChanged} from 'app/actions/views/channel';
+import {handleClearFiles, handleRemoveLastFile, handleUploadFiles} from 'app/actions/views/file_upload';
+import {handleCommentDraftChanged} from 'app/actions/views/thread';
+import {getTheme} from 'app/selectors/preferences';
+import {getCurrentChannelDraft, getThreadDraft} from 'app/selectors/views';
import PostTextbox from './post_textbox';
function mapStateToProps(state, ownProps) {
+ const currentDraft = ownProps.rootId ? getThreadDraft(state, ownProps.rootId) : getCurrentChannelDraft(state);
+
return {
- ...ownProps,
canUploadFiles: canUploadFilesOnMobile(state),
channelIsLoading: state.views.channel.loading,
currentUserId: getCurrentUserId(state),
- typing: getUsersTyping(state),
+ files: currentDraft.files,
theme: getTheme(state),
- uploadFileRequestStatus: state.requests.files.uploadFiles.status
+ uploadFileRequestStatus: state.requests.files.uploadFiles.status,
+ value: currentDraft.draft
};
}
@@ -33,6 +38,8 @@ function mapDispatchToProps(dispatch) {
addReactionToLatestPost,
createPost,
handleClearFiles,
+ handleCommentDraftChanged,
+ handlePostDraftChanged,
handleRemoveLastFile,
handleUploadFiles,
userTyping
diff --git a/app/components/post_textbox/post_textbox.js b/app/components/post_textbox/post_textbox.js
index ce9a0e0e8..04358f15b 100644
--- a/app/components/post_textbox/post_textbox.js
+++ b/app/components/post_textbox/post_textbox.js
@@ -13,17 +13,17 @@ import {
TouchableOpacity,
View
} from 'react-native';
-import Icon from 'react-native-vector-icons/Ionicons';
-import ImagePicker from 'react-native-image-picker';
import {injectIntl, intlShape} from 'react-intl';
import {RequestStatus} from 'mattermost-redux/constants';
import Autocomplete from 'app/components/autocomplete';
import FileUploadPreview from 'app/components/file_upload_preview';
-import FormattedText from 'app/components/formatted_text';
import PaperPlane from 'app/components/paper_plane';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
+import AttachmentButton from './components/attachment_button';
+import Typing from './components/typing';
+
const INITIAL_HEIGHT = Platform.OS === 'ios' ? 34 : 36;
const MAX_CONTENT_HEIGHT = 100;
const MAX_MESSAGE_LENGTH = 4000;
@@ -34,6 +34,8 @@ class PostTextbox extends PureComponent {
actions: PropTypes.shape({
addReactionToLatestPost: PropTypes.func.isRequired,
createPost: PropTypes.func.isRequired,
+ handleCommentDraftChanged: PropTypes.func.isRequired,
+ handlePostDraftChanged: PropTypes.func.isRequired,
handleClearFiles: PropTypes.func.isRequired,
handleRemoveLastFile: PropTypes.func.isRequired,
handleUploadFiles: PropTypes.func.isRequired,
@@ -46,10 +48,8 @@ class PostTextbox extends PureComponent {
files: PropTypes.array,
intl: intlShape.isRequired,
navigator: PropTypes.object,
- onChangeText: PropTypes.func.isRequired,
rootId: PropTypes.string,
theme: PropTypes.object.isRequired,
- typing: PropTypes.array.isRequired,
uploadFileRequestStatus: PropTypes.string.isRequired,
value: PropTypes.string.isRequired
};
@@ -61,14 +61,10 @@ class PostTextbox extends PureComponent {
value: ''
};
- constructor(props) {
- super(props);
-
- this.state = {
- contentHeight: INITIAL_HEIGHT,
- inputWidth: null
- };
- }
+ state = {
+ contentHeight: INITIAL_HEIGHT,
+ inputWidth: null
+ };
componentDidMount() {
if (Platform.OS === 'android') {
@@ -197,6 +193,24 @@ class PostTextbox extends PureComponent {
});
};
+ 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);
@@ -205,13 +219,12 @@ class PostTextbox extends PureComponent {
handleTextChange = (text) => {
const {
- onChangeText,
+ actions,
channelId,
- rootId,
- actions
+ rootId
} = this.props;
- onChangeText(text);
+ this.changeDraft(text);
actions.userTyping(channelId, rootId);
};
@@ -256,160 +269,6 @@ class PostTextbox extends PureComponent {
this.autocomplete = c;
};
- attachFileFromCamera = () => {
- const options = {
- quality: 0.7,
- noData: true,
- storageOptions: {
- cameraRoll: true,
- waitUntilSaved: true
- }
- };
-
- ImagePicker.launchCamera(options, (response) => {
- if (response.error || response.didCancel) {
- return;
- }
-
- this.uploadFiles([response]);
- });
- };
-
- attachFileFromLibrary = () => {
- const options = {
- quality: 0.7,
- noData: true
- };
-
- if (Platform.OS === 'ios') {
- options.mediaType = 'mixed';
- }
-
- ImagePicker.launchImageLibrary(options, (response) => {
- if (response.error || response.didCancel) {
- return;
- }
-
- this.uploadFiles([response]);
- });
- };
-
- attachVideoFromLibraryAndroid = () => {
- const options = {
- quality: 0.7,
- mediaType: 'video',
- noData: true
- };
-
- ImagePicker.launchImageLibrary(options, (response) => {
- if (response.error || response.didCancel) {
- return;
- }
-
- this.uploadFiles([response]);
- });
- }
-
- uploadFiles = (images) => {
- this.props.actions.handleUploadFiles(images, this.props.rootId);
- };
-
- handleFileAttachmentOption = (action) => {
- this.props.navigator.dismissModal({
- animationType: 'none'
- });
-
- // Have to wait to launch the library attachment action.
- // If we call the action after dismissModal with no delay then the
- // Wix navigator will dismiss the library attachment modal as well.
- setTimeout(() => {
- if (typeof action === 'function') {
- action();
- }
- }, 100);
- }
-
- showFileAttachmentOptions = () => {
- this.blur();
- const options = {
- items: [{
- action: () => this.handleFileAttachmentOption(this.attachFileFromCamera),
- text: {
- id: 'mobile.file_upload.camera',
- defaultMessage: 'Take Photo or Video'
- },
- icon: 'camera'
- }, {
- action: () => this.handleFileAttachmentOption(this.attachFileFromLibrary),
- text: {
- id: 'mobile.file_upload.library',
- defaultMessage: 'Photo Library'
- },
- icon: 'photo'
- }]
- };
-
- if (Platform.OS === 'android') {
- options.items.push({
- action: () => this.handleFileAttachmentOption(this.attachVideoFromLibraryAndroid),
- text: {
- id: 'mobile.file_upload.video',
- defaultMessage: 'Video Library'
- },
- icon: 'file-video-o'
- });
- }
-
- this.props.navigator.showModal({
- screen: 'OptionsModal',
- title: '',
- animationType: 'none',
- passProps: {
- items: options.items
- },
- navigatorStyle: {
- navBarHidden: true,
- statusBarHidden: false,
- statusBarHideWithNavBar: false,
- screenBackgroundColor: 'transparent',
- modalPresentationStyle: 'overCurrentContext'
- }
- });
- };
-
- renderTyping = () => {
- const {typing} = this.props;
- const numUsers = typing.length;
-
- switch (numUsers) {
- case 0:
- return null;
- case 1:
- return (
-
- );
- default: {
- const last = typing.pop();
- return (
-
- );
- }
- }
- };
-
renderDisabledSendButton = () => {
const {theme} = this.props;
const style = getStyleSheet(theme);
@@ -470,21 +329,16 @@ class PostTextbox extends PureComponent {
placeholder = {id: 'create_post.write', defaultMessage: 'Write a message...'};
}
- let fileUpload = null;
+ let attachmentButton = null;
const inputContainerStyle = [style.inputContainer];
if (canUploadFiles) {
- fileUpload = (
-
-
-
+ attachmentButton = (
+
);
} else {
inputContainerStyle.push(style.inputContainerWithoutFileUpload);
@@ -498,15 +352,7 @@ class PostTextbox extends PureComponent {
>
{textValue + ' '}
-
-
- {this.renderTyping()}
-
-
+
- {fileUpload}
+ {attachmentButton}
{
return {
- buttonContainer: {
- height: Platform.select({
- ios: 34,
- android: 36
- }),
- width: 45,
- alignItems: 'center',
- justifyContent: 'center'
- },
disableButton: {
backgroundColor: changeOpacity(theme.buttonBg, 0.3)
},
@@ -594,12 +431,6 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
borderTopWidth: 1,
borderTopColor: changeOpacity(theme.centerChannelColor, 0.20)
},
- attachIcon: {
- marginTop: Platform.select({
- ios: 2,
- android: 0
- })
- },
sendButton: {
backgroundColor: theme.buttonBg,
borderRadius: 18,
@@ -619,13 +450,6 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
width: 29
}
})
- },
- typing: {
- paddingLeft: 10,
- fontSize: 11,
- marginBottom: 5,
- color: theme.centerChannelColor,
- backgroundColor: 'transparent'
}
};
});
diff --git a/app/screens/channel/channel.js b/app/screens/channel/channel.js
index 2ce355ffe..f21850477 100644
--- a/app/screens/channel/channel.js
+++ b/app/screens/channel/channel.js
@@ -21,10 +21,10 @@ import PostListRetry from 'app/components/post_list_retry';
import StatusBar from 'app/components/status_bar';
import {wrapWithPreventDoubleTap} from 'app/utils/tap';
import {makeStyleSheetFromTheme} from 'app/utils/theme';
+import PostTextbox from 'app/components/post_textbox';
import ChannelDrawerButton from './channel_drawer_button';
import ChannelPostList from './channel_post_list';
-import ChannelPostTextbox from './channel_post_textbox';
import ChannelSearchButton from './channel_search_button';
import ChannelTitle from './channel_title';
@@ -92,7 +92,7 @@ class Channel extends PureComponent {
};
blurPostTextBox = () => {
- this.postTextbox.getWrappedInstance().blur();
+ this.postTextbox.getWrappedInstance().getWrappedInstance().blur();
};
goToChannelInfo = wrapWithPreventDoubleTap(() => {
@@ -219,8 +219,9 @@ class Channel extends PureComponent {
navigator={navigator}
/>
-
diff --git a/app/screens/channel/channel_post_textbox/channel_post_textbox.js b/app/screens/channel/channel_post_textbox/channel_post_textbox.js
deleted file mode 100644
index 294447f0b..000000000
--- a/app/screens/channel/channel_post_textbox/channel_post_textbox.js
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import PropTypes from 'prop-types';
-import React from 'react';
-
-import PostTextbox from 'app/components/post_textbox';
-
-export default class ChannelPostTextbox extends React.PureComponent {
- static propTypes = {
- channelId: PropTypes.string.isRequired,
- draft: PropTypes.object.isRequired,
- navigator: PropTypes.object.isRequired,
- actions: PropTypes.shape({
- handlePostDraftChanged: PropTypes.func.isRequired
- }).isRequired
- };
-
- handleDraftChanged = (value) => {
- this.props.actions.handlePostDraftChanged(this.props.channelId, value);
- };
-
- blur = () => {
- this.refs.postTextbox.getWrappedInstance().getWrappedInstance().blur();
- };
-
- render() {
- return (
-
- );
- }
-}
diff --git a/app/screens/channel/channel_post_textbox/index.js b/app/screens/channel/channel_post_textbox/index.js
deleted file mode 100644
index 1f2b6af2f..000000000
--- a/app/screens/channel/channel_post_textbox/index.js
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {bindActionCreators} from 'redux';
-import {connect} from 'react-redux';
-
-import {
- handlePostDraftChanged
-} from 'app/actions/views/channel';
-
-import ChannelPostTextbox from './channel_post_textbox';
-
-function mapStateToProps(state, ownProps) {
- return {
- draft: state.views.channel.drafts[ownProps.channelId] || {},
- ...ownProps
- };
-}
-
-function mapDispatchToProps(dispatch) {
- return {
- actions: bindActionCreators({
- handlePostDraftChanged
- }, dispatch)
- };
-}
-
-export default connect(mapStateToProps, mapDispatchToProps, null, {withRef: true})(ChannelPostTextbox);
diff --git a/app/screens/thread/index.js b/app/screens/thread/index.js
index f91deab2d..72d19fa77 100644
--- a/app/screens/thread/index.js
+++ b/app/screens/thread/index.js
@@ -4,7 +4,6 @@
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
-import {handleCommentDraftChanged} from 'app/actions/views/thread';
import {getStatusBarHeight} from 'app/selectors/device';
import {getTheme} from 'app/selectors/preferences';
@@ -21,15 +20,12 @@ function makeMapStateToProps() {
return function mapStateToProps(state, ownProps) {
const posts = getPostsForThread(state, ownProps);
- const threadDraft = state.views.thread.drafts[ownProps.rootId];
return {
...ownProps,
channelId: ownProps.channelId,
myMember: getMyCurrentChannelMembership(state),
rootId: ownProps.rootId,
- draft: threadDraft.draft,
- files: threadDraft.files,
posts,
statusBarHeight: getStatusBarHeight(state),
theme: getTheme(state)
@@ -40,7 +36,6 @@ function makeMapStateToProps() {
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
- handleCommentDraftChanged,
selectPost
}, dispatch)
};
diff --git a/app/screens/thread/thread.js b/app/screens/thread/thread.js
index e6b9c20f7..7642038ae 100644
--- a/app/screens/thread/thread.js
+++ b/app/screens/thread/thread.js
@@ -13,15 +13,12 @@ import {makeStyleSheetFromTheme} from 'app/utils/theme';
export default class Thread extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
- handleCommentDraftChanged: PropTypes.func.isRequired,
selectPost: PropTypes.func.isRequired
}).isRequired,
channelId: PropTypes.string.isRequired,
navigator: PropTypes.object,
myMember: PropTypes.object.isRequired,
- files: PropTypes.array,
rootId: PropTypes.string.isRequired,
- draft: PropTypes.string.isRequired,
theme: PropTypes.object.isRequired,
posts: PropTypes.array.isRequired,
statusBarHeight: PropTypes.number
@@ -39,15 +36,9 @@ export default class Thread extends PureComponent {
this.props.actions.selectPost('');
}
- handleDraftChanged = (value) => {
- this.props.actions.handleCommentDraftChanged(this.props.rootId, value);
- };
-
render() {
const {
channelId,
- draft,
- files,
myMember,
navigator,
posts,
@@ -78,10 +69,7 @@ export default class Thread extends PureComponent {
/>
diff --git a/app/selectors/views.js b/app/selectors/views.js
new file mode 100644
index 000000000..124049104
--- /dev/null
+++ b/app/selectors/views.js
@@ -0,0 +1,23 @@
+import {createSelector} from 'reselect';
+
+import {getCurrentChannel} from 'mattermost-redux/selectors/entities/channels';
+
+function getChannelDrafts(state) {
+ return state.views.channel.drafts;
+}
+
+function getThreadDrafts(state) {
+ return state.views.thread.drafts;
+}
+
+export const getCurrentChannelDraft = createSelector(
+ getChannelDrafts,
+ getCurrentChannel,
+ (drafts, currentChannel) => drafts[currentChannel.id]
+);
+
+export const getThreadDraft = createSelector(
+ getThreadDrafts,
+ (state, rootId) => rootId,
+ (drafts, rootId) => drafts[rootId]
+);