diff options
-rw-r--r-- | webapp/components/channel_view.jsx | 15 | ||||
-rw-r--r-- | webapp/components/create_comment.jsx | 75 | ||||
-rw-r--r-- | webapp/components/create_post.jsx | 83 | ||||
-rw-r--r-- | webapp/components/emoji_picker/emoji_picker.jsx | 13 | ||||
-rw-r--r-- | webapp/components/emoji_picker/emoji_picker_overlay.jsx | 10 | ||||
-rw-r--r-- | webapp/components/file_upload.jsx | 24 | ||||
-rw-r--r-- | webapp/components/post_view/post_info/post_info.jsx | 1 | ||||
-rw-r--r-- | webapp/components/rhs_comment.jsx | 1 | ||||
-rw-r--r-- | webapp/components/rhs_root_post.jsx | 1 | ||||
-rw-r--r-- | webapp/components/rhs_thread/rhs_thread.jsx | 10 |
10 files changed, 153 insertions, 80 deletions
diff --git a/webapp/components/channel_view.jsx b/webapp/components/channel_view.jsx index 6c2157ffd..4a5ac8969 100644 --- a/webapp/components/channel_view.jsx +++ b/webapp/components/channel_view.jsx @@ -31,18 +31,22 @@ export default class ChannelView extends React.Component { this.state = this.getStateFromStores(props); } + getStateFromStores() { return { channelId: ChannelStore.getCurrentId(), tutorialStep: PreferenceStore.getInt(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), 999) }; } + isStateValid() { return this.state.channelId !== ''; } + updateState() { this.setState(this.getStateFromStores(this.props)); } + componentDidMount() { ChannelStore.addChangeListener(this.updateState); @@ -53,14 +57,17 @@ export default class ChannelView extends React.Component { $('body').addClass('browser--ie'); } } + componentWillUnmount() { ChannelStore.removeChangeListener(this.updateState); $('body').removeClass('app__body'); } + componentWillReceiveProps(nextProps) { this.setState(this.getStateFromStores(nextProps)); } + shouldComponentUpdate(nextProps, nextState) { if (!Utils.areObjectsEqual(nextProps.params, this.props.params)) { return true; @@ -72,6 +79,11 @@ export default class ChannelView extends React.Component { return false; } + + getChannelView = () => { + return this.refs.channelView; + } + render() { if (this.state.tutorialStep <= TutorialSteps.INTRO_SCREENS) { return (<TutorialView/>); @@ -79,6 +91,7 @@ export default class ChannelView extends React.Component { return ( <div + ref='channelView' id='app-content' className='app__content' > @@ -93,7 +106,7 @@ export default class ChannelView extends React.Component { className='post-create__container' id='post-create' > - <CreatePost/> + <CreatePost getChannelView={this.getChannelView}/> </div> </div> ); diff --git a/webapp/components/create_comment.jsx b/webapp/components/create_comment.jsx index 2ddc828cc..1a2bbeeae 100644 --- a/webapp/components/create_comment.jsx +++ b/webapp/components/create_comment.jsx @@ -15,7 +15,7 @@ import Textbox from './textbox.jsx'; import MsgTyping from './msg_typing.jsx'; import FileUpload from './file_upload.jsx'; import FilePreview from './file_preview.jsx'; -import EmojiPicker from './emoji_picker/emoji_picker.jsx'; +import EmojiPickerOverlay from 'components/emoji_picker/emoji_picker_overlay.jsx'; import * as Utils from 'utils/utils.jsx'; import * as UserAgent from 'utils/user_agent.jsx'; import * as GlobalActions from 'actions/global_actions.jsx'; @@ -24,7 +24,6 @@ import * as PostActions from 'actions/post_actions.jsx'; import Constants from 'utils/constants.jsx'; import {FormattedMessage} from 'react-intl'; -import {RootCloseWrapper} from 'react-overlays'; import {browserHistory} from 'react-router/es6'; const ActionTypes = Constants.ActionTypes; @@ -54,12 +53,15 @@ export default class CreateComment extends React.Component { this.removePreview = this.removePreview.bind(this); this.getFileCount = this.getFileCount.bind(this); this.getFileUploadTarget = this.getFileUploadTarget.bind(this); + this.getCreateCommentControls = this.getCreateCommentControls.bind(this); this.onPreferenceChange = this.onPreferenceChange.bind(this); this.focusTextbox = this.focusTextbox.bind(this); this.showPostDeletedModal = this.showPostDeletedModal.bind(this); this.hidePostDeletedModal = this.hidePostDeletedModal.bind(this); this.handlePostError = this.handlePostError.bind(this); this.handleEmojiClick = this.handleEmojiClick.bind(this); + this.toggleEmojiPicker = this.toggleEmojiPicker.bind(this); + this.hideEmojiPicker = this.hideEmojiPicker.bind(this); PostStore.clearCommentDraftUploads(); MessageHistoryStore.resetHistoryIndex('comment'); @@ -81,10 +83,14 @@ export default class CreateComment extends React.Component { this.lastBlurAt = 0; } - toggleEmojiPicker = () => { + toggleEmojiPicker() { this.setState({showEmojiPicker: !this.state.showEmojiPicker}); } + hideEmojiPicker() { + this.setState({showEmojiPicker: false}); + } + handleEmojiClick(emoji) { const emojiAlias = emoji.name || emoji.aliases[0]; @@ -470,6 +476,10 @@ export default class CreateComment extends React.Component { return this.refs.textbox; } + getCreateCommentControls() { + return this.refs.createCommentControls; + } + focusTextbox(keepFocus = false) { if (keepFocus || !Utils.isMobile()) { this.refs.textbox.focus(); @@ -548,15 +558,38 @@ export default class CreateComment extends React.Component { addButtonClass += ' disabled'; } + const fileUpload = ( + <FileUpload + ref='fileUpload' + getFileCount={this.getFileCount} + getTarget={this.getFileUploadTarget} + onFileUploadChange={this.handleFileUploadChange} + onUploadStart={this.handleUploadStart} + onFileUpload={this.handleFileUploadComplete} + onUploadError={this.handleUploadError} + postType='comment' + channelId={this.props.channelId} + /> + ); + let emojiPicker = null; - if (this.state.showEmojiPicker) { + if (Utils.isFeatureEnabled(Constants.PRE_RELEASE_FEATURES.EMOJI_PICKER_PREVIEW)) { emojiPicker = ( - <RootCloseWrapper onRootClose={this.toggleEmojiPicker}> - <EmojiPicker + <span> + <EmojiPickerOverlay + show={this.state.showEmojiPicker} + container={this.props.getSidebarBody} + target={this.getCreateCommentControls} + onHide={this.hideEmojiPicker} onEmojiClick={this.handleEmojiClick} - onHide={this.toggleEmojiPicker} + rightOffset={15} + topOffset={55} /> - </RootCloseWrapper> + <span + className={'fa fa-smile-o icon--emoji-picker emoji-rhs'} + onClick={this.toggleEmojiPicker} + /> + </span> ); } @@ -582,22 +615,13 @@ export default class CreateComment extends React.Component { id='reply_textbox' ref='textbox' /> - <FileUpload - ref='fileUpload' - getFileCount={this.getFileCount} - getTarget={this.getFileUploadTarget} - onFileUploadChange={this.handleFileUploadChange} - onUploadStart={this.handleUploadStart} - onFileUpload={this.handleFileUploadComplete} - onUploadError={this.handleUploadError} - postType='comment' - channelId={this.props.channelId} - onEmojiClick={this.toggleEmojiPicker} - emojiEnabled={this.state.emojiPickerEnabled} - navBarName='rhs' - /> - - {emojiPicker} + <span + ref='createCommentControls' + className='btn btn-file' + > + {fileUpload} + {emojiPicker} + </span> </div> </div> <MsgTyping @@ -629,5 +653,6 @@ export default class CreateComment extends React.Component { CreateComment.propTypes = { channelId: PropTypes.string.isRequired, rootId: PropTypes.string.isRequired, - latestPostId: PropTypes.string + latestPostId: PropTypes.string, + getSidebarBody: PropTypes.func }; diff --git a/webapp/components/create_post.jsx b/webapp/components/create_post.jsx index e7fafc514..56f392292 100644 --- a/webapp/components/create_post.jsx +++ b/webapp/components/create_post.jsx @@ -8,7 +8,7 @@ import FileUpload from './file_upload.jsx'; import FilePreview from './file_preview.jsx'; import PostDeletedModal from './post_deleted_modal.jsx'; import TutorialTip from './tutorial/tutorial_tip.jsx'; -import EmojiPicker from './emoji_picker/emoji_picker.jsx'; +import EmojiPickerOverlay from 'components/emoji_picker/emoji_picker_overlay.jsx'; import AppDispatcher from 'dispatcher/app_dispatcher.jsx'; import * as GlobalActions from 'actions/global_actions.jsx'; @@ -28,7 +28,6 @@ import ConfirmModal from './confirm_modal.jsx'; import Constants from 'utils/constants.jsx'; import {FormattedHTMLMessage, FormattedMessage} from 'react-intl'; -import {RootCloseWrapper} from 'react-overlays'; import {browserHistory} from 'react-router/es6'; const Preferences = Constants.Preferences; @@ -37,6 +36,7 @@ const ActionTypes = Constants.ActionTypes; const KeyCodes = Constants.KeyCodes; import React from 'react'; +import PropTypes from 'prop-types'; export const REACTION_PATTERN = /^(\+|-):([^:\s]+):\s*$/; export const EMOJI_PATTERN = /:[A-Za-z-_0-9]*:/g; @@ -60,6 +60,7 @@ export default class CreatePost extends React.Component { this.onPreferenceChange = this.onPreferenceChange.bind(this); this.getFileCount = this.getFileCount.bind(this); this.getFileUploadTarget = this.getFileUploadTarget.bind(this); + this.getCreatePostControls = this.getCreatePostControls.bind(this); this.handleKeyDown = this.handleKeyDown.bind(this); this.handleBlur = this.handleBlur.bind(this); this.sendMessage = this.sendMessage.bind(this); @@ -111,6 +112,10 @@ export default class CreatePost extends React.Component { this.setState({showEmojiPicker: !this.state.showEmojiPicker}); } + hideEmojiPicker = () => { + this.setState({showEmojiPicker: false}); + } + doSubmit(e) { if (e) { e.preventDefault(); @@ -500,6 +505,10 @@ export default class CreatePost extends React.Component { return this.refs.textbox; } + getCreatePostControls() { + return this.refs.createPostControls; + } + handleKeyDown(e) { if (this.state.ctrlSend && e.keyCode === KeyCodes.ENTER && e.ctrlKey === true) { this.postMsgKeyPress(e); @@ -693,23 +702,46 @@ export default class CreatePost extends React.Component { sendButtonClass += ' disabled'; } + let attachmentsDisabled = ''; + if (global.window.mm_config.EnableFileAttachments === 'false') { + attachmentsDisabled = ' post-create--attachment-disabled'; + } + + const fileUpload = ( + <FileUpload + ref='fileUpload' + getFileCount={this.getFileCount} + getTarget={this.getFileUploadTarget} + onFileUploadChange={this.handleFileUploadChange} + onUploadStart={this.handleUploadStart} + onFileUpload={this.handleFileUploadComplete} + onUploadError={this.handleUploadError} + postType='post' + channelId='' + /> + ); + let emojiPicker = null; - if (this.state.showEmojiPicker) { + if (Utils.isFeatureEnabled(Constants.PRE_RELEASE_FEATURES.EMOJI_PICKER_PREVIEW)) { emojiPicker = ( - <RootCloseWrapper onRootClose={this.toggleEmojiPicker}> - <EmojiPicker - onHide={this.toggleEmojiPicker} + <span> + <EmojiPickerOverlay + show={this.state.showEmojiPicker} + container={this.props.getChannelView} + target={this.getCreatePostControls} + onHide={this.hideEmojiPicker} onEmojiClick={this.handleEmojiClick} + rightOffset={15} + topOffset={-7} + /> + <span + className={'fa fa-smile-o icon--emoji-picker emoji-main'} + onClick={this.toggleEmojiPicker} /> - </RootCloseWrapper> + </span> ); } - let attachmentsDisabled = ''; - if (global.window.mm_config.EnableFileAttachments === 'false') { - attachmentsDisabled = ' post-create--attachment-disabled'; - } - return ( <form id='create_post' @@ -734,22 +766,13 @@ export default class CreatePost extends React.Component { id='post_textbox' ref='textbox' /> - <FileUpload - ref='fileUpload' - getFileCount={this.getFileCount} - getTarget={this.getFileUploadTarget} - onFileUploadChange={this.handleFileUploadChange} - onUploadStart={this.handleUploadStart} - onFileUpload={this.handleFileUploadComplete} - onUploadError={this.handleUploadError} - postType='post' - channelId='' - onEmojiClick={this.toggleEmojiPicker} - emojiEnabled={this.state.emojiPickerEnabled} - navBarName='main' - /> - - {emojiPicker} + <span + ref='createPostControls' + className='btn btn-file' + > + {fileUpload} + {emojiPicker} + </span> </div> <a className={sendButtonClass} @@ -785,3 +808,7 @@ export default class CreatePost extends React.Component { ); } } + +CreatePost.propTypes = { + getChannelView: PropTypes.func +}; diff --git a/webapp/components/emoji_picker/emoji_picker.jsx b/webapp/components/emoji_picker/emoji_picker.jsx index 2a5b84747..a047c1277 100644 --- a/webapp/components/emoji_picker/emoji_picker.jsx +++ b/webapp/components/emoji_picker/emoji_picker.jsx @@ -31,11 +31,18 @@ const CATEGORIES = [ export default class EmojiPicker extends React.Component { static propTypes = { style: PropTypes.object, + rightOffset: PropTypes.number, + topOffset: PropTypes.number, placement: PropTypes.oneOf(['top', 'bottom', 'left']), customEmojis: PropTypes.object, onEmojiClick: PropTypes.func.isRequired } + static defaultProps = { + rightOffset: 0, + topOffset: 0 + }; + constructor(props) { super(props); @@ -299,13 +306,17 @@ export default class EmojiPicker extends React.Component { pickerStyle = { top: this.props.style.top, bottom: this.props.style.bottom, - right: 1 + right: this.props.rightOffset }; } else { pickerStyle = this.props.style; } } + if (pickerStyle && pickerStyle.top) { + pickerStyle.top += this.props.topOffset; + } + return ( <div className='emoji-picker' diff --git a/webapp/components/emoji_picker/emoji_picker_overlay.jsx b/webapp/components/emoji_picker/emoji_picker_overlay.jsx index 09cc0a36c..0a289a242 100644 --- a/webapp/components/emoji_picker/emoji_picker_overlay.jsx +++ b/webapp/components/emoji_picker/emoji_picker_overlay.jsx @@ -13,7 +13,9 @@ export default class EmojiPickerOverlay extends React.PureComponent { container: PropTypes.func, target: PropTypes.func.isRequired, onEmojiClick: PropTypes.func.isRequired, - onHide: PropTypes.func.isRequired + onHide: PropTypes.func.isRequired, + rightOffset: PropTypes.number, + topOffset: PropTypes.number } constructor(props) { @@ -55,7 +57,11 @@ export default class EmojiPickerOverlay extends React.PureComponent { target={this.props.target} animation={false} > - <EmojiPicker onEmojiClick={this.props.onEmojiClick}/> + <EmojiPicker + onEmojiClick={this.props.onEmojiClick} + rightOffset={this.props.rightOffset} + topOffset={this.props.topOffset} + /> </Overlay> ); } diff --git a/webapp/components/file_upload.jsx b/webapp/components/file_upload.jsx index 17bb50a2b..0b32ab66e 100644 --- a/webapp/components/file_upload.jsx +++ b/webapp/components/file_upload.jsx @@ -51,7 +51,6 @@ class FileUpload extends React.Component { this.pasteUpload = this.pasteUpload.bind(this); this.keyUpload = this.keyUpload.bind(this); this.handleMaxUploadReached = this.handleMaxUploadReached.bind(this); - this.emojiClick = this.emojiClick.bind(this); this.state = { requests: {} @@ -230,10 +229,6 @@ class FileUpload extends React.Component { target.off('dragenter dragleave dragover drop dragster:enter dragster:leave dragster:over dragster:drop'); } - emojiClick() { - this.props.onEmojiClick(); - } - pasteUpload(e) { const {formatMessage} = this.props.intl; @@ -377,19 +372,8 @@ class FileUpload extends React.Component { } const channelId = this.props.channelId || ChannelStore.getCurrentId(); - const uploadsRemaining = Constants.MAX_UPLOAD_FILES - this.props.getFileCount(channelId); - let emojiSpan; - if (this.props.emojiEnabled) { - emojiSpan = ( - <span - className={'fa fa-smile-o icon--emoji-picker emoji-' + this.props.navBarName} - onClick={this.emojiClick} - /> - ); - } - let fileDiv; if (global.window.mm_config.EnableFileAttachments === 'true') { fileDiv = ( @@ -413,10 +397,9 @@ class FileUpload extends React.Component { return ( <span ref='input' - className={'btn btn-file' + (uploadsRemaining <= 0 ? ' btn-file__disabled' : '')} + className={uploadsRemaining <= 0 ? ' btn-file__disabled' : ''} > {fileDiv} - {emojiSpan} </span> ); } @@ -433,10 +416,7 @@ FileUpload.propTypes = { onFileUploadChange: PropTypes.func, onTextDrop: PropTypes.func, channelId: PropTypes.string, - postType: PropTypes.string, - onEmojiClick: PropTypes.func, - navBarName: PropTypes.string, - emojiEnabled: PropTypes.bool + postType: PropTypes.string }; export default injectIntl(FileUpload, {withRef: true}); diff --git a/webapp/components/post_view/post_info/post_info.jsx b/webapp/components/post_view/post_info/post_info.jsx index c07a58bc7..36e0ea431 100644 --- a/webapp/components/post_view/post_info/post_info.jsx +++ b/webapp/components/post_view/post_info/post_info.jsx @@ -158,6 +158,7 @@ export default class PostInfo extends React.PureComponent { target={this.getDotMenu} onHide={this.hideEmojiPicker} onEmojiClick={this.reactEmojiClick} + rightOffset={7} /> <a href='#' diff --git a/webapp/components/rhs_comment.jsx b/webapp/components/rhs_comment.jsx index 11d64f871..8fcf3edf8 100644 --- a/webapp/components/rhs_comment.jsx +++ b/webapp/components/rhs_comment.jsx @@ -335,6 +335,7 @@ export default class RhsComment extends React.Component { target={() => this.refs.dotMenu} container={this.props.getPostList} onEmojiClick={this.reactEmojiClick} + rightOffset={15} /> <a href='#' diff --git a/webapp/components/rhs_root_post.jsx b/webapp/components/rhs_root_post.jsx index 83f50b6ea..48512d591 100644 --- a/webapp/components/rhs_root_post.jsx +++ b/webapp/components/rhs_root_post.jsx @@ -228,6 +228,7 @@ export default class RhsRootPost extends React.Component { target={() => this.refs.dotMenu} container={this.props.getPostList} onEmojiClick={this.reactEmojiClick} + rightOffset={15} /> <a href='#' diff --git a/webapp/components/rhs_thread/rhs_thread.jsx b/webapp/components/rhs_thread/rhs_thread.jsx index 50f5f0aa3..bbf61af19 100644 --- a/webapp/components/rhs_thread/rhs_thread.jsx +++ b/webapp/components/rhs_thread/rhs_thread.jsx @@ -320,6 +320,10 @@ export default class RhsThread extends React.Component { return this.refs.postListContainer; } + getSidebarBody = () => { + return this.refs.sidebarbody; + } + render() { if (this.props.posts == null || this.props.selected == null) { return ( @@ -403,7 +407,10 @@ export default class RhsThread extends React.Component { } return ( - <div className='sidebar-right__body'> + <div + className='sidebar-right__body' + ref='sidebarbody' + > <FloatingTimestamp isScrolling={this.state.isScrolling} isMobile={Utils.isMobile()} @@ -460,6 +467,7 @@ export default class RhsThread extends React.Component { channelId={selected.channel_id} rootId={selected.id} latestPostId={postsLength > 0 ? postsArray[postsLength - 1].id : selected.id} + getSidebarBody={this.getSidebarBody} /> </div> </div> |