diff options
-rw-r--r-- | webapp/actions/post_actions.jsx | 8 | ||||
-rw-r--r-- | webapp/components/create_post.jsx | 33 | ||||
-rw-r--r-- | webapp/components/post_view/components/post_list.jsx | 40 | ||||
-rw-r--r-- | webapp/components/post_view/post_view_controller.jsx | 1 | ||||
-rw-r--r-- | webapp/components/suggestion/suggestion_box.jsx | 8 | ||||
-rw-r--r-- | webapp/components/suggestion/suggestion_list.jsx | 18 | ||||
-rw-r--r-- | webapp/stores/post_store.jsx | 34 | ||||
-rw-r--r-- | webapp/utils/constants.jsx | 1 |
8 files changed, 87 insertions, 56 deletions
diff --git a/webapp/actions/post_actions.jsx b/webapp/actions/post_actions.jsx index 969c764f1..d55a0d578 100644 --- a/webapp/actions/post_actions.jsx +++ b/webapp/actions/post_actions.jsx @@ -499,3 +499,11 @@ export function performSearch(terms, isMentionSearch, success, error) { } ); } + +export function storePostDraft(channelId, draft) { + AppDispatcher.handleViewAction({ + type: ActionTypes.POST_DRAFT_CHANGED, + channelId, + draft + }); +} diff --git a/webapp/components/create_post.jsx b/webapp/components/create_post.jsx index 6e59b88b1..59c12e059 100644 --- a/webapp/components/create_post.jsx +++ b/webapp/components/create_post.jsx @@ -71,10 +71,11 @@ export default class CreatePost extends React.Component { PostStore.clearDraftUploads(); - const draft = PostStore.getCurrentDraft(); + const channelId = ChannelStore.getCurrentId(); + const draft = PostStore.getPostDraft(channelId); this.state = { - channelId: ChannelStore.getCurrentId(), + channelId, message: draft.message, uploadsInProgress: draft.uploadsInProgress, fileInfos: draft.fileInfos, @@ -136,7 +137,7 @@ export default class CreatePost extends React.Component { const isReaction = REACTION_PATTERN.exec(post.message); if (post.message.indexOf('/') === 0) { - PostStore.storeDraft(this.state.channelId, null); + PostActions.storePostDraft(this.state.channelId, null); this.setState({message: '', postError: null, fileInfos: [], enableSendButton: false}); const args = {}; @@ -241,7 +242,7 @@ export default class CreatePost extends React.Component { PostActions.removeReaction(this.state.channelId, postId, emojiName); } - PostStore.storeCurrentDraft(null); + PostActions.storePostDraft(this.state.channelId, null); } focusTextbox(keepFocus = false) { @@ -271,9 +272,9 @@ export default class CreatePost extends React.Component { enableSendButton }); - const draft = PostStore.getCurrentDraft(); + const draft = PostStore.getPostDraft(this.state.channelId); draft.message = message; - PostStore.storeCurrentDraft(draft); + PostActions.storePostDraft(this.state.channelId, draft); } handleFileUploadChange() { @@ -281,10 +282,10 @@ export default class CreatePost extends React.Component { } handleUploadStart(clientIds, channelId) { - const draft = PostStore.getDraft(channelId); + const draft = PostStore.getPostDraft(channelId); draft.uploadsInProgress = draft.uploadsInProgress.concat(clientIds); - PostStore.storeDraft(channelId, draft); + PostActions.storePostDraft(channelId, draft); this.setState({uploadsInProgress: draft.uploadsInProgress}); @@ -294,7 +295,7 @@ export default class CreatePost extends React.Component { } handleFileUploadComplete(fileInfos, clientIds, channelId) { - const draft = PostStore.getDraft(channelId); + const draft = PostStore.getPostDraft(channelId); // remove each finished file from uploads for (let i = 0; i < clientIds.length; i++) { @@ -306,7 +307,7 @@ export default class CreatePost extends React.Component { } draft.fileInfos = draft.fileInfos.concat(fileInfos); - PostStore.storeDraft(channelId, draft); + PostActions.storePostDraft(channelId, draft); if (channelId === this.state.channelId) { this.setState({ @@ -325,14 +326,14 @@ export default class CreatePost extends React.Component { } if (clientId !== -1) { - const draft = PostStore.getDraft(channelId); + const draft = PostStore.getPostDraft(channelId); const index = draft.uploadsInProgress.indexOf(clientId); if (index !== -1) { draft.uploadsInProgress.splice(index, 1); } - PostStore.storeDraft(channelId, draft); + PostActions.storePostDraft(channelId, draft); if (channelId === this.state.channelId) { this.setState({uploadsInProgress: draft.uploadsInProgress}); @@ -362,10 +363,10 @@ export default class CreatePost extends React.Component { fileInfos.splice(index, 1); } - const draft = PostStore.getCurrentDraft(); + const draft = PostStore.getPostDraft(this.state.channelId); draft.fileInfos = fileInfos; draft.uploadsInProgress = uploadsInProgress; - PostStore.storeCurrentDraft(draft); + PostActions.storePostDraft(this.state.channelId, draft); const enableSendButton = this.handleEnableSendButton(this.state.message, fileInfos); this.setState({fileInfos, uploadsInProgress, enableSendButton}); @@ -432,7 +433,7 @@ export default class CreatePost extends React.Component { onChange() { const channelId = ChannelStore.getCurrentId(); if (this.state.channelId !== channelId) { - const draft = PostStore.getCurrentDraft(); + const draft = PostStore.getPostDraft(channelId); this.setState({channelId, message: draft.message, submitting: false, serverError: null, postError: null, fileInfos: draft.fileInfos, uploadsInProgress: draft.uploadsInProgress}); } @@ -453,7 +454,7 @@ export default class CreatePost extends React.Component { return this.state.fileInfos.length + this.state.uploadsInProgress.length; } - const draft = PostStore.getDraft(channelId); + const draft = PostStore.getPostDraft(channelId); return draft.fileInfos.length + draft.uploadsInProgress.length; } diff --git a/webapp/components/post_view/components/post_list.jsx b/webapp/components/post_view/components/post_list.jsx index 0d1244c55..f79cbec19 100644 --- a/webapp/components/post_view/components/post_list.jsx +++ b/webapp/components/post_view/components/post_list.jsx @@ -21,6 +21,7 @@ import * as ChannelActions from 'actions/channel_actions.jsx'; import Constants from 'utils/constants.jsx'; const ScrollTypes = Constants.ScrollTypes; +import PostStore from 'stores/post_store.jsx'; import PreferenceStore from 'stores/preference_store.jsx'; import {FormattedDate, FormattedMessage} from 'react-intl'; @@ -96,6 +97,11 @@ export default class PostList extends React.Component { }, 0); } this.setState({unViewedCount}); + + if (this.props.channelId !== nextProps.channelId) { + PostStore.removePostDraftChangeListener(this.props.channelId, this.handlePostDraftChange); + PostStore.addPostDraftChangeListener(nextProps.channelId, this.handlePostDraftChange); + } } handleKeyDown(e) { @@ -527,6 +533,16 @@ export default class PostList extends React.Component { window.addEventListener('resize', this.handleResize); window.addEventListener('keydown', this.handleKeyDown); + + PostStore.addPostDraftChangeListener(this.props.channelId, this.handlePostDraftChange); + } + + handlePostDraftChange = (draft) => { + // this.state.draft isn't used anywhere, but this will cause an update to the scroll position + // without causing two updates to trigger when something else changes + this.setState({ + draft + }); } componentWillUnmount() { @@ -534,6 +550,8 @@ export default class PostList extends React.Component { window.removeEventListener('resize', this.handleResize); window.removeEventListener('keydown', this.handleKeyDown); this.scrollStopAction.cancel(); + + PostStore.removePostDraftChangeListener(this.handlePostDraftChange); } componentDidUpdate() { @@ -545,13 +563,6 @@ export default class PostList extends React.Component { } render() { - if (this.props.postList == null) { - return <div/>; - } - - const posts = this.props.postList.posts; - const order = this.props.postList.order; - // Create intro message or top loadmore link let moreMessagesTop; if (this.props.showMoreMessagesTop) { @@ -588,11 +599,17 @@ export default class PostList extends React.Component { } // Create post elements - const postElements = this.createPosts(posts, order); - + let postElements = null; let topPostCreateAt = 0; - if (this.state.topPostId && this.props.postList.posts[this.state.topPostId]) { - topPostCreateAt = this.props.postList.posts[this.state.topPostId].create_at; + if (this.props.postList) { + const posts = this.props.postList.posts; + const order = this.props.postList.order; + + postElements = this.createPosts(posts, order); + + if (this.state.topPostId && this.props.postList.posts[this.state.topPostId]) { + topPostCreateAt = this.props.postList.posts[this.state.topPostId].create_at; + } } return ( @@ -642,6 +659,7 @@ PostList.propTypes = { postList: PropTypes.object, profiles: PropTypes.object, channel: PropTypes.object, + channelId: PropTypes.string.isRequired, currentUser: PropTypes.object, scrollPostId: PropTypes.string, scrollType: PropTypes.number, diff --git a/webapp/components/post_view/post_view_controller.jsx b/webapp/components/post_view/post_view_controller.jsx index 2d4afb7d7..12112ac10 100644 --- a/webapp/components/post_view/post_view_controller.jsx +++ b/webapp/components/post_view/post_view_controller.jsx @@ -363,6 +363,7 @@ export default class PostViewController extends React.Component { <PostList postList={this.state.postList} profiles={this.state.profiles} + channelId={this.state.channel.id} channel={this.state.channel} currentUser={this.state.currentUser} showMoreMessagesTop={!this.state.atTop} diff --git a/webapp/components/suggestion/suggestion_box.jsx b/webapp/components/suggestion/suggestion_box.jsx index c70e8d5ae..1915b22b7 100644 --- a/webapp/components/suggestion/suggestion_box.jsx +++ b/webapp/components/suggestion/suggestion_box.jsx @@ -37,12 +37,10 @@ export default class SuggestionBox extends React.Component { } componentDidMount() { - SuggestionStore.addCompleteWordListener(this.suggestionId, this.handleCompleteWord); SuggestionStore.addPretextChangedListener(this.suggestionId, this.handlePretextChanged); } componentWillUnmount() { - SuggestionStore.removeCompleteWordListener(this.suggestionId, this.handleCompleteWord); SuggestionStore.removePretextChangedListener(this.suggestionId, this.handlePretextChanged); SuggestionStore.unregisterSuggestionBox(this.suggestionId); @@ -161,6 +159,8 @@ export default class SuggestionBox extends React.Component { provider.handleCompleteWord(term, matchedPretext); } } + + GlobalActions.emitCompleteWordSuggestion(this.suggestionId); } handleKeyDown(e) { @@ -172,7 +172,7 @@ export default class SuggestionBox extends React.Component { GlobalActions.emitSelectNextSuggestion(this.suggestionId); e.preventDefault(); } else if (e.which === KeyCodes.ENTER || e.which === KeyCodes.TAB) { - GlobalActions.emitCompleteWordSuggestion(this.suggestionId); + this.handleCompleteWord(SuggestionStore.getSelection(this.suggestionId), SuggestionStore.getSelectedMatchedPretext(this.suggestionId)); this.props.onKeyDown(e); e.preventDefault(); } else if (e.which === KeyCodes.ESCAPE) { @@ -212,6 +212,7 @@ export default class SuggestionBox extends React.Component { // Don't pass props used by SuggestionBox Reflect.deleteProperty(props, 'providers'); + Reflect.deleteProperty(props, 'onChange'); // We use onInput instead of onChange on the actual input Reflect.deleteProperty(props, 'onItemSelected'); const childProps = { @@ -260,6 +261,7 @@ export default class SuggestionBox extends React.Component { suggestionId={this.suggestionId} location={listStyle} renderDividers={renderDividers} + onCompleteWord={this.handleCompleteWord} /> </div> ); diff --git a/webapp/components/suggestion/suggestion_list.jsx b/webapp/components/suggestion/suggestion_list.jsx index bc2245077..59f0d02f8 100644 --- a/webapp/components/suggestion/suggestion_list.jsx +++ b/webapp/components/suggestion/suggestion_list.jsx @@ -2,20 +2,19 @@ // See License.txt for license information. import $ from 'jquery'; +import PropTypes from 'prop-types'; +import React from 'react'; import ReactDOM from 'react-dom'; -import * as GlobalActions from 'actions/global_actions.jsx'; -import SuggestionStore from 'stores/suggestion_store.jsx'; import {FormattedMessage} from 'react-intl'; -import PropTypes from 'prop-types'; - -import React from 'react'; +import SuggestionStore from 'stores/suggestion_store.jsx'; export default class SuggestionList extends React.Component { static propTypes = { suggestionId: PropTypes.string.isRequired, location: PropTypes.string, - renderDividers: PropTypes.bool + renderDividers: PropTypes.bool, + onCompleteWord: PropTypes.func.isRequired }; static defaultProps = { @@ -29,7 +28,6 @@ export default class SuggestionList extends React.Component { this.getContent = this.getContent.bind(this); - this.handleItemClick = this.handleItemClick.bind(this); this.handleSuggestionsChanged = this.handleSuggestionsChanged.bind(this); this.scrollToItem = this.scrollToItem.bind(this); @@ -67,10 +65,6 @@ export default class SuggestionList extends React.Component { return $(ReactDOM.findDOMNode(this.refs.content)); } - handleItemClick(term, matchedPretext) { - GlobalActions.emitCompleteWordSuggestion(this.props.suggestionId, term, matchedPretext); - } - handleSuggestionsChanged() { this.setState(this.getStateFromStores()); } @@ -145,7 +139,7 @@ export default class SuggestionList extends React.Component { term={term} matchedPretext={this.state.matchedPretext[i]} isSelection={isSelection} - onClick={this.handleItemClick} + onClick={this.props.onCompleteWord} /> ); } diff --git a/webapp/stores/post_store.jsx b/webapp/stores/post_store.jsx index 5a1dc5cf8..770e232ca 100644 --- a/webapp/stores/post_store.jsx +++ b/webapp/stores/post_store.jsx @@ -4,7 +4,6 @@ import AppDispatcher from '../dispatcher/app_dispatcher.jsx'; import EventEmitter from 'events'; -import ChannelStore from 'stores/channel_store.jsx'; import BrowserStore from 'stores/browser_store.jsx'; import UserStore from 'stores/user_store.jsx'; @@ -17,6 +16,7 @@ const EDIT_POST_EVENT = 'edit_post'; const POSTS_VIEW_JUMP_EVENT = 'post_list_jump'; const SELECTED_POST_CHANGE_EVENT = 'selected_post_change'; const POST_PINNED_CHANGE_EVENT = 'post_pinned_change'; +const POST_DRAFT_CHANGE_EVENT = 'post_draft_change'; class PostStoreClass extends EventEmitter { constructor() { @@ -75,6 +75,18 @@ class PostStoreClass extends EventEmitter { this.removeListener(POSTS_VIEW_JUMP_EVENT, callback); } + emitPostDraftChange(channelId) { + this.emit(POST_DRAFT_CHANGE_EVENT + channelId, this.getPostDraft(channelId)); + } + + addPostDraftChangeListener(channelId, callback) { + this.on(POST_DRAFT_CHANGE_EVENT + channelId, callback); + } + + removePostDraftChangeListener(channelId, callback) { + this.removeListener(POST_DRAFT_CHANGE_EVENT + channelId, callback); + } + jumpPostsViewToBottom() { this.emitPostsViewJump(Constants.PostsViewJumpTypes.BOTTOM, null); } @@ -585,21 +597,11 @@ class PostStoreClass extends EventEmitter { return draft; } - storeCurrentDraft(draft) { - var channelId = ChannelStore.getCurrentId(); + storePostDraft(channelId, draft) { BrowserStore.setGlobalItem('draft_' + channelId, draft); } - getCurrentDraft() { - var channelId = ChannelStore.getCurrentId(); - return this.getDraft(channelId); - } - - storeDraft(channelId, draft) { - BrowserStore.setGlobalItem('draft_' + channelId, draft); - } - - getDraft(channelId) { + getPostDraft(channelId) { return this.normalizeDraft(BrowserStore.getGlobalItem('draft_' + channelId)); } @@ -700,7 +702,7 @@ PostStore.dispatchToken = AppDispatcher.register((payload) => { break; case ActionTypes.CREATE_POST: PostStore.storePendingPost(action.post); - PostStore.storeDraft(action.post.channel_id, null); + PostStore.storePostDraft(action.post.channel_id, null); PostStore.jumpPostsViewToBottom(); break; case ActionTypes.CREATE_COMMENT: @@ -723,6 +725,10 @@ PostStore.dispatchToken = AppDispatcher.register((payload) => { case ActionTypes.RECEIVED_POST_UNPINNED: PostStore.emitPostPinnedChange(); break; + case ActionTypes.POST_DRAFT_CHANGED: + PostStore.storePostDraft(action.channelId, action.draft); + PostStore.emitPostDraftChange(action.channelId); + break; default: } }); diff --git a/webapp/utils/constants.jsx b/webapp/utils/constants.jsx index 619d37a74..8ab3fc15e 100644 --- a/webapp/utils/constants.jsx +++ b/webapp/utils/constants.jsx @@ -92,6 +92,7 @@ export const ActionTypes = keyMirror({ RECEIVED_ADD_MENTION: null, RECEIVED_POST_PINNED: null, RECEIVED_POST_UNPINNED: null, + POST_DRAFT_CHANGED: null, RECEIVED_PROFILES: null, RECEIVED_PROFILES_IN_TEAM: null, |