diff options
Diffstat (limited to 'web')
28 files changed, 685 insertions, 899 deletions
diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx index ffe7cbb5d..e46b2ccd7 100644 --- a/web/react/components/channel_header.jsx +++ b/web/react/components/channel_header.jsx @@ -27,6 +27,7 @@ const ActionTypes = Constants.ActionTypes; const Popover = ReactBootstrap.Popover; const OverlayTrigger = ReactBootstrap.OverlayTrigger; +const Tooltip = ReactBootstrap.Tooltip; export default class ChannelHeader extends React.Component { constructor(props) { @@ -121,6 +122,7 @@ export default class ChannelHeader extends React.Component { } const channel = this.state.channel; + const recentMentionsTooltip = <Tooltip id='recentMentionsTooltip'>{'Recent Mentions'}</Tooltip>; const popoverContent = ( <Popover id='hader-popover' @@ -382,31 +384,19 @@ export default class ChannelHeader extends React.Component { <th className='search-bar__container'><NavbarSearchBox /></th> <th> <div className='dropdown channel-header__links'> - <a - href='#' - className='dropdown-toggle theme' - type='button' - id='channel_header_right_dropdown' - data-toggle='dropdown' - aria-expanded='true' - > - <span dangerouslySetInnerHTML={{__html: Constants.MENU_ICON}} /> - </a> - <ul - className='dropdown-menu dropdown-menu-right' - role='menu' - aria-labelledby='channel_header_right_dropdown' + <OverlayTrigger + delayShow={400} + placement='bottom' + overlay={recentMentionsTooltip} > - <li role='presentation'> - <a - role='menuitem' - href='#' - onClick={this.searchMentions} - > - {'Recent Mentions'} - </a> - </li> - </ul> + <a + href='#' + type='button' + onClick={this.searchMentions} + > + {'@'} + </a> + </OverlayTrigger> </div> </th> </tr> diff --git a/web/react/components/create_comment.jsx b/web/react/components/create_comment.jsx index 058594165..22a659ed5 100644 --- a/web/react/components/create_comment.jsx +++ b/web/react/components/create_comment.jsx @@ -194,7 +194,8 @@ export default class CreateComment extends React.Component { title: 'Comment', message: lastPost.message, postId: lastPost.id, - channelId: lastPost.channel_id + channelId: lastPost.channel_id, + comments: PostStore.getCommentCount(lastPost) }); } } diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx index 5a69c9bfb..d823a54f1 100644 --- a/web/react/components/create_post.jsx +++ b/web/react/components/create_post.jsx @@ -372,7 +372,8 @@ export default class CreatePost extends React.Component { title: type, message: lastPost.message, postId: lastPost.id, - channelId: lastPost.channel_id + channelId: lastPost.channel_id, + comments: PostStore.getCommentCount(lastPost) }); } } @@ -382,8 +383,8 @@ export default class CreatePost extends React.Component { screens.push( <div> <h4>{'Sending Messages'}</h4> - <p>{'Type here to write a message.'}</p> - <p>{'Click the attachment button to upload an image or a file.'}</p> + <p>{'Type here to write a message and press '}<strong>{'Enter'}</strong>{' to post it.'}</p> + <p>{'Click the '}<strong>{'Attachment'}</strong>{' button to upload an image or a file.'}</p> </div> ); diff --git a/web/react/components/delete_post_modal.jsx b/web/react/components/delete_post_modal.jsx index f3bead1c2..e0489856f 100644 --- a/web/react/components/delete_post_modal.jsx +++ b/web/react/components/delete_post_modal.jsx @@ -3,7 +3,8 @@ var Client = require('../utils/client.jsx'); var PostStore = require('../stores/post_store.jsx'); -var BrowserStore = require('../stores/browser_store.jsx'); +var ModalStore = require('../stores/modal_store.jsx'); +var Modal = ReactBootstrap.Modal; var Utils = require('../utils/utils.jsx'); var AsyncClient = require('../utils/async_client.jsx'); var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); @@ -15,18 +16,40 @@ export default class DeletePostModal extends React.Component { super(props); this.handleDelete = this.handleDelete.bind(this); + this.handleToggle = this.handleToggle.bind(this); + this.handleHide = this.handleHide.bind(this); this.onListenerChange = this.onListenerChange.bind(this); - this.onShow = this.onShow.bind(this); - this.state = {title: '', postId: '', channelId: '', selectedList: PostStore.getSelectedPost(), comments: 0}; + this.selectedList = null; + + this.state = { + show: true, + post: null, + commentCount: 0, + error: '' + }; + } + + componentDidMount() { + ModalStore.addModalListener(ActionTypes.TOGGLE_DELETE_POST_MODAL, this.handleToggle); + PostStore.addSelectedPostChangeListener(this.onListenerChange); + } + + componentWillUnmount() { + PostStore.removeSelectedPostChangeListener(this.onListenerChange); + ModalStore.removeModalListener(ActionTypes.TOGGLE_DELETE_POST_MODAL, this.handleToggle); } + handleDelete() { - Client.deletePost(this.state.channelId, this.state.postId, - function deleteSuccess() { - var selectedList = this.state.selectedList; + Client.deletePost( + this.state.post.channel_id, + this.state.post.id, + () => { + var selectedList = this.selectedList; + if (selectedList && selectedList.order && selectedList.order.length > 0) { var selectedPost = selectedList.posts[selectedList.order[0]]; - if ((selectedPost.id === this.state.postId && this.state.title === 'Post') || selectedPost.root_id === this.state.postId) { + if ((selectedPost.id === this.state.post.id && !this.state.root_id) || selectedPost.root_id === this.state.post.id) { AppDispatcher.handleServerAction({ type: ActionTypes.RECIEVED_SEARCH, results: null @@ -36,7 +59,7 @@ export default class DeletePostModal extends React.Component { type: ActionTypes.RECIEVED_POST_SELECTED, results: null }); - } else if (selectedPost.id === this.state.postId && this.state.title === 'Comment') { + } else if (selectedPost.id === this.state.post.id && this.state.root_id) { if (selectedPost.root_id && selectedPost.root_id.length > 0 && selectedList.posts[selectedPost.root_id]) { selectedList.order = [selectedPost.root_id]; delete selectedList.posts[selectedPost.id]; @@ -53,98 +76,96 @@ export default class DeletePostModal extends React.Component { } } } - PostStore.removePost(this.state.postId, this.state.channelId); - AsyncClient.getPosts(this.state.channelId); - }.bind(this), - function deleteFailed(err) { + + PostStore.removePost(this.state.post.id, this.state.post.channel_id); + AsyncClient.getPosts(this.state.post.channel_id); + }, + (err) => { AsyncClient.dispatchError(err, 'deletePost'); } ); + + this.handleHide(); } - onShow(e) { - var newState = {}; - if (BrowserStore.getItem('edit_state_transfer')) { - newState = BrowserStore.getItem('edit_state_transfer'); - BrowserStore.removeItem('edit_state_transfer'); - } else { - var button = e.relatedTarget; - newState = {title: $(button).attr('data-title'), channelId: $(button).attr('data-channelid'), postId: $(button).attr('data-postid'), comments: $(button).attr('data-comments')}; - } - this.setState(newState); - } - componentDidMount() { - $(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', this.onShow); - PostStore.addSelectedPostChangeListener(this.onListenerChange); + + handleToggle(value, args) { + this.setState({ + show: value, + post: args.post, + commentCount: args.commentCount, + error: '' + }); } - componentWillUnmount() { - PostStore.removeSelectedPostChangeListener(this.onListenerChange); + + handleHide() { + this.setState({show: false}); } + onListenerChange() { var newList = PostStore.getSelectedPost(); - if (!Utils.areObjectsEqual(this.state.selectedList, newList)) { - this.setState({selectedList: newList}); + if (!Utils.areObjectsEqual(this.selectedList, newList)) { + this.selectedList = newList; } } + render() { + if (!this.state.post) { + return null; + } + var error = null; if (this.state.error) { error = <div className='form-group has-error'><label className='control-label'>{this.state.error}</label></div>; } var commentWarning = ''; - if (this.state.comments > 0) { - commentWarning = 'This post has ' + this.state.comments + ' comment(s) on it.'; + if (this.state.commentCount > 0) { + commentWarning = 'This post has ' + this.state.commentCount + ' comment(s) on it.'; } + const postTerm = Utils.getPostTerm(this.state.post); + return ( - <div - className='modal fade' - id='delete_post' - ref='modal' - role='dialog' - tabIndex='-1' - aria-hidden='true' + <Modal + show={this.state.show} + onHide={this.handleHide} > - <div className='modal-dialog modal-push-down'> - <div className='modal-content'> - <div className='modal-header'> - <button - type='button' - className='close' - data-dismiss='modal' - aria-label='Close' - > - <span aria-hidden='true'>×</span> - </button> - <h4 className='modal-title'>Confirm {this.state.title} Delete</h4> - </div> - <div className='modal-body'> - Are you sure you want to delete the {this.state.title.toLowerCase()}? - <br/> - <br/> + <Modal.Header closeButton={true}> + {`Confirm ${postTerm} Delete`} + </Modal.Header> + <Modal.Body> + {`Are you sure you want to delete this ${postTerm.toLowerCase()}?`} + <br /> + <br /> {commentWarning} - </div> - {error} - <div className='modal-footer'> + {error} + </Modal.Body> + <Modal.Footer> <button type='button' className='btn btn-default' - data-dismiss='modal' + onClick={this.handleHide} > - Cancel + {'Cancel'} </button> <button type='button' className='btn btn-danger' - data-dismiss='modal' onClick={this.handleDelete} > - Delete + {'Delete'} </button> - </div> - </div> - </div> - </div> + </Modal.Footer> + </Modal> ); } + + static show(post, commentCount) { + AppDispatcher.handleViewAction({ + type: ActionTypes.TOGGLE_DELETE_POST_MODAL, + value: true, + post, + commentCount: commentCount || 0 + }); + } } diff --git a/web/react/components/edit_post_modal.jsx b/web/react/components/edit_post_modal.jsx index ef32baa7d..c75da75c9 100644 --- a/web/react/components/edit_post_modal.jsx +++ b/web/react/components/edit_post_modal.jsx @@ -3,6 +3,7 @@ var Client = require('../utils/client.jsx'); var AsyncClient = require('../utils/async_client.jsx'); +var DeletePostModal = require('./delete_post_modal.jsx'); var Textbox = require('./textbox.jsx'); var BrowserStore = require('../stores/browser_store.jsx'); var PostStore = require('../stores/post_store.jsx'); @@ -34,7 +35,7 @@ export default class EditPostModal extends React.Component { delete tempState.editText; BrowserStore.setItem('edit_state_transfer', tempState); $('#edit_post').modal('hide'); - $('#delete_post').modal('show'); + DeletePostModal.show(PostStore.getPost(this.state.channel_id, this.state.post_id), this.state.comments); return; } diff --git a/web/react/components/file_preview.jsx b/web/react/components/file_preview.jsx index df5deb8bc..b8c86ed67 100644 --- a/web/react/components/file_preview.jsx +++ b/web/react/components/file_preview.jsx @@ -8,9 +8,14 @@ export default class FilePreview extends React.Component { super(props); this.handleRemove = this.handleRemove.bind(this); + } - this.state = {}; + componentDidUpdate() { + if (this.props.uploadsInProgress.length > 0) { + ReactDOM.findDOMNode(this.refs[this.props.uploadsInProgress[0]]).scrollIntoView(); + } } + handleRemove(e) { var previewDiv = e.target.parentNode.parentNode; @@ -20,9 +25,10 @@ export default class FilePreview extends React.Component { this.props.onRemove(previewDiv.getAttribute('data-client-id')); } } + render() { var previews = []; - this.props.files.forEach(function setupPreview(fullFilename) { + this.props.files.forEach((fullFilename) => { var filename = fullFilename; var originalFilename = filename; var filenameSplit = filename.split('.'); @@ -72,11 +78,12 @@ export default class FilePreview extends React.Component { </div> ); } - }.bind(this)); + }); - this.props.uploadsInProgress.forEach(function addUploadsInProgress(clientId) { + this.props.uploadsInProgress.forEach((clientId) => { previews.push( <div + ref={clientId} key={clientId} className='preview-div' data-client-id={clientId} @@ -93,7 +100,7 @@ export default class FilePreview extends React.Component { </a> </div> ); - }.bind(this)); + }); return ( <div className='preview-container'> @@ -104,8 +111,8 @@ export default class FilePreview extends React.Component { } FilePreview.defaultProps = { - files: null, - uploadsInProgress: null + files: [], + uploadsInProgress: [] }; FilePreview.propTypes = { onRemove: React.PropTypes.func.isRequired, diff --git a/web/react/components/post.jsx b/web/react/components/post.jsx index 2b9586345..7e4af07c4 100644 --- a/web/react/components/post.jsx +++ b/web/react/components/post.jsx @@ -13,8 +13,6 @@ var AsyncClient = require('../utils/async_client.jsx'); var ActionTypes = Constants.ActionTypes; var utils = require('../utils/utils.jsx'); -var PostInfo = require('./post_info.jsx'); - export default class Post extends React.Component { constructor(props) { super(props); @@ -141,6 +139,8 @@ export default class Post extends React.Component { var postType = ''; if (type !== 'Post') { postType = 'post--comment'; + } else if (commentCount > 0) { + postType = 'post--root'; } var currentUserCss = ''; @@ -170,14 +170,11 @@ export default class Post extends React.Component { } profilePic = ( - <div className='post-profile-img__container'> - <img - className='post-profile-img' - src={src} - height='36' - width='36' - /> - </div> + <img + src={src} + height='36' + width='36' + /> ); } @@ -187,32 +184,26 @@ export default class Post extends React.Component { id={'post_' + post.id} className={'post ' + sameUserClass + ' ' + rootUser + ' ' + postType + ' ' + currentUserCss} > - {profilePic} <div className='post__content'> - <PostHeader - ref='header' - post={post} - sameRoot={this.props.sameRoot} - commentCount={commentCount} - handleCommentClick={this.handleCommentClick} - isLastComment={this.props.isLastComment} - /> - <PostBody - post={post} - sameRoot={this.props.sameRoot} - parentPost={parentPost} - posts={posts} - handleCommentClick={this.handleCommentClick} - retryPost={this.retryPost} - /> - <PostInfo - ref='info' - post={post} - sameRoot={this.props.sameRoot} - commentCount={commentCount} - handleCommentClick={this.handleCommentClick} - allowReply='true' - /> + <div className='post__img'>{profilePic}</div> + <div> + <PostHeader + ref='header' + post={post} + sameRoot={this.props.sameRoot} + commentCount={commentCount} + handleCommentClick={this.handleCommentClick} + isLastComment={this.props.isLastComment} + /> + <PostBody + post={post} + sameRoot={this.props.sameRoot} + parentPost={parentPost} + posts={posts} + handleCommentClick={this.handleCommentClick} + retryPost={this.retryPost} + /> + </div> </div> </div> </div> diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx index 975ac64dc..e1c057775 100644 --- a/web/react/components/post_body.jsx +++ b/web/react/components/post_body.jsx @@ -257,7 +257,7 @@ export default class PostBody extends React.Component { } return ( - <div className='post-comment'> + <div> <h4> <span className='video-type'>{header}</span> <span className='video-title'><a href={link}>{this.state.youtubeTitle}</a></span> @@ -329,7 +329,7 @@ export default class PostBody extends React.Component { } comment = ( - <p className='post-link'> + <div className='post__link'> <span> {'Commented on '}{name}{apostrophe}{' message: '} <a @@ -339,15 +339,13 @@ export default class PostBody extends React.Component { {message} </a> </span> - </p> + </div> ); - - postClass += ' post-comment'; } let loading; if (post.state === Constants.POST_FAILED) { - postClass += ' post-fail'; + postClass += ' post--fail'; loading = ( <a className='theme post-retry pull-right' @@ -379,25 +377,27 @@ export default class PostBody extends React.Component { } return ( - <div className='post-body'> + <div> {comment} - <div - key={`${post.id}_message`} - id={`${post.id}_message`} - className={postClass} - > - {loading} - <span - ref='message_span' - onClick={TextFormatting.handleClick} - dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.state.message)}} + <div className='post__body'> + <div + key={`${post.id}_message`} + id={`${post.id}_message`} + className={postClass} + > + {loading} + <span + ref='message_span' + onClick={TextFormatting.handleClick} + dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.state.message)}} + /> + </div> + <PostBodyAdditionalContent + post={this.state.post} /> + {fileAttachmentHolder} + {this.embed} </div> - <PostBodyAdditionalContent - post={this.state.post} - /> - {fileAttachmentHolder} - {this.embed} </div> ); } diff --git a/web/react/components/post_header.jsx b/web/react/components/post_header.jsx index 45e60c767..e8b162fc2 100644 --- a/web/react/components/post_header.jsx +++ b/web/react/components/post_header.jsx @@ -26,14 +26,14 @@ export default class PostHeader extends React.Component { ); } - botIndicator = <li className='post-header-col post-header__name bot-indicator'>{'BOT'}</li>; + botIndicator = <li className='col col__name bot-indicator'>{'BOT'}</li>; } return ( - <ul className='post-header post-header-post'> - <li className='post-header-col post-header__name'><strong>{userProfile}</strong></li> + <ul className='post__header'> + <li className='col col__name'>{userProfile}</li> {botIndicator} - <li className='post-info--hidden'> + <li className='col'> <PostInfo post={post} commentCount={this.props.commentCount} diff --git a/web/react/components/post_info.jsx b/web/react/components/post_info.jsx index a01d842e5..5446fca7a 100644 --- a/web/react/components/post_info.jsx +++ b/web/react/components/post_info.jsx @@ -1,6 +1,7 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. +var DeletePostModal = require('./delete_post_modal.jsx'); var UserStore = require('../stores/user_store.jsx'); var utils = require('../utils/utils.jsx'); var TimeSince = require('./time_since.jsx'); @@ -50,7 +51,7 @@ export default class PostInfo extends React.Component { data-channelid={post.channel_id} data-comments={dataComments} > - Edit + {'Edit'} </a> </li> ); @@ -65,31 +66,9 @@ export default class PostInfo extends React.Component { <a href='#' role='menuitem' - data-toggle='modal' - data-target='#delete_post' - data-title={type} - data-postid={post.id} - data-channelid={post.channel_id} - data-comments={dataComments} - > - Delete - </a> - </li> - ); - } - - if (this.props.allowReply === 'true') { - dropdownContents.push( - <li - key='replyLink' - role='presentation' - > - <a - className='reply-link theme' - href='#' - onClick={this.props.handleCommentClick} + onClick={() => DeletePostModal.show(post, dataComments)} > - Reply + {'Delete'} </a> </li> ); @@ -103,7 +82,7 @@ export default class PostInfo extends React.Component { <div> <a href='#' - className='dropdown-toggle theme' + className='dropdown-toggle post__dropdown theme' type='button' data-toggle='dropdown' aria-expanded='false' @@ -120,23 +99,27 @@ export default class PostInfo extends React.Component { render() { var post = this.props.post; var comments = ''; - var lastCommentClass = ' comment-icon__container__hide'; - if (this.props.isLastComment) { - lastCommentClass = ' comment-icon__container__show'; + var showCommentClass = ''; + var commentCountText = this.props.commentCount; + + if (this.props.commentCount >= 1) { + showCommentClass = ' icon--show'; + } else { + commentCountText = ''; } - if (this.props.commentCount >= 1 && post.state !== Constants.POST_FAILED && post.state !== Constants.POST_LOADING && post.state !== Constants.POST_DELETED) { + if (post.state !== Constants.POST_FAILED && post.state !== Constants.POST_LOADING && post.state !== Constants.POST_DELETED) { comments = ( <a href='#' - className={'comment-icon__container theme' + lastCommentClass} + className={'comment-icon__container' + showCommentClass} onClick={this.props.handleCommentClick} > <span className='comment-icon' dangerouslySetInnerHTML={{__html: Constants.COMMENT_ICON}} /> - {this.props.commentCount} + {commentCountText} </a> ); } @@ -144,17 +127,17 @@ export default class PostInfo extends React.Component { var dropdown = this.createDropdown(); return ( - <ul className='post-header post-info'> - <li className='post-header-col'> + <ul className='post__header post__header--info'> + <li className='col'> <TimeSince eventTime={post.create_at} /> </li> - <li className='post-header-col post-header__reply'> + <li className='col col__reply'> + {comments} <div className='dropdown'> {dropdown} </div> - {comments} </li> </ul> ); diff --git a/web/react/components/rhs_comment.jsx b/web/react/components/rhs_comment.jsx index 58cc1cac7..a55bf0039 100644 --- a/web/react/components/rhs_comment.jsx +++ b/web/react/components/rhs_comment.jsx @@ -8,6 +8,7 @@ var UserStore = require('../stores/user_store.jsx'); var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); var Utils = require('../utils/utils.jsx'); var Constants = require('../utils/constants.jsx'); +var DeletePostModal = require('./delete_post_modal.jsx'); var FileAttachmentList = require('./file_attachment_list.jsx'); var Client = require('../utils/client.jsx'); var AsyncClient = require('../utils/async_client.jsx'); @@ -114,12 +115,7 @@ export default class RhsComment extends React.Component { <a href='#' role='menuitem' - data-toggle='modal' - data-target='#delete_post' - data-title='Comment' - data-postid={post.id} - data-channelid={post.channel_id} - data-comments={0} + onClick={() => DeletePostModal.show(post, 0)} > {'Delete'} </a> @@ -135,7 +131,7 @@ export default class RhsComment extends React.Component { <div className='dropdown'> <a href='#' - className='dropdown-toggle theme' + className='post__dropdown dropdown-toggle' type='button' data-toggle='dropdown' aria-expanded='false' @@ -197,38 +193,39 @@ export default class RhsComment extends React.Component { return ( <div className={'post ' + currentUserCss}> - <div className='post-profile-img__container'> - <img - className='post-profile-img' - src={'/api/v1/users/' + post.user_id + '/image?time=' + timestamp + '&' + Utils.getSessionIndex()} - height='36' - width='36' - /> - </div> <div className='post__content'> - <ul className='post-header'> - <li className='post-header-col'> - <strong><UserProfile userId={post.user_id} /></strong> - </li> - <li className='post-header-col'> - <time className='post-profile-time'> - {Utils.displayCommentDateTime(post.create_at)} - </time> - </li> - <li className='post-header-col post-header__reply'> - {dropdown} - </li> - </ul> - <div className='post-body'> - <div className={postClass}> - {loading} - <div - ref='message_holder' - onClick={TextFormatting.handleClick} - dangerouslySetInnerHTML={{__html: TextFormatting.formatText(post.message)}} - /> + <div className='post__img'> + <img + src={'/api/v1/users/' + post.user_id + '/image?time=' + timestamp + '&' + Utils.getSessionIndex()} + height='36' + width='36' + /> + </div> + <div> + <ul className='post__header'> + <li className='col__name'> + <strong><UserProfile userId={post.user_id} /></strong> + </li> + <li className='col'> + <time className='post__time'> + {Utils.displayCommentDateTime(post.create_at)} + </time> + </li> + <li className='col col__reply'> + {dropdown} + </li> + </ul> + <div className='post__body'> + <div className={postClass}> + {loading} + <div + ref='message_holder' + onClick={TextFormatting.handleClick} + dangerouslySetInnerHTML={{__html: TextFormatting.formatText(post.message)}} + /> + </div> + {fileAttachment} </div> - {fileAttachment} </div> </div> </div> diff --git a/web/react/components/rhs_root_post.jsx b/web/react/components/rhs_root_post.jsx index 69de5d523..358bf8440 100644 --- a/web/react/components/rhs_root_post.jsx +++ b/web/react/components/rhs_root_post.jsx @@ -6,6 +6,7 @@ var UserProfile = require('./user_profile.jsx'); var UserStore = require('../stores/user_store.jsx'); var TextFormatting = require('../utils/text_formatting.jsx'); var utils = require('../utils/utils.jsx'); +var DeletePostModal = require('./delete_post_modal.jsx'); var FileAttachmentList = require('./file_attachment_list.jsx'); var twemoji = require('twemoji'); var Constants = require('../utils/constants.jsx'); @@ -65,7 +66,7 @@ export default class RhsRootPost extends React.Component { ownerOptions = ( <div> <a href='#' - className='dropdown-toggle theme' + className='post__dropdown dropdown-toggle' type='button' data-toggle='dropdown' aria-expanded='false' @@ -86,21 +87,16 @@ export default class RhsRootPost extends React.Component { data-postid={post.id} data-channelid={post.channel_id} > - Edit + {'Edit'} </a> </li> <li role='presentation'> <a href='#' role='menuitem' - data-toggle='modal' - data-target='#delete_post' - data-title={type} - data-postid={post.id} - data-channelid={post.channel_id} - data-comments={this.props.commentCount} + onClick={() => DeletePostModal.show(post, this.props.commentCount)} > - Delete + {'Delete'} </a> </li> </ul> @@ -133,7 +129,7 @@ export default class RhsRootPost extends React.Component { ); } - botIndicator = <li className='post-header-col post-header__name bot-indicator'>{'BOT'}</li>; + botIndicator = <li className='col col__name bot-indicator'>{'BOT'}</li>; } let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp + '&' + utils.getSessionIndex(); @@ -144,47 +140,47 @@ export default class RhsRootPost extends React.Component { } const profilePic = ( - <div className='post-profile-img__container'> - <img - className='post-profile-img' - src={src} - height='36' - width='36' - /> - </div> + <img + className='post-profile-img' + src={src} + height='36' + width='36' + /> ); return ( <div className={'post post--root ' + currentUserCss}> <div className='post-right-channel__name'>{channelName}</div> - <div className='post-profile-img__container'> - {profilePic} - </div> <div className='post__content'> - <ul className='post-header'> - <li className='post-header-col'><strong>{userProfile}</strong></li> - {botIndicator} - <li className='post-header-col'> - <time className='post-profile-time'> - {utils.displayCommentDateTime(post.create_at)} - </time> - </li> - <li className='post-header-col post-header__reply'> - <div className='dropdown'> - {ownerOptions} - </div> - </li> - </ul> - <div className='post-body'> - <div - ref='message_holder' - onClick={TextFormatting.handleClick} - dangerouslySetInnerHTML={{__html: TextFormatting.formatText(post.message)}} - /> - <PostBodyAdditionalContent - post={post} - /> - {fileAttachment} + <div className='post__img'> + {profilePic} + </div> + <div> + <ul className='post__header'> + <li className='col__name'>{userProfile}</li> + {botIndicator} + <li className='col'> + <time className='post__time'> + {utils.displayCommentDateTime(post.create_at)} + </time> + </li> + <li className='col col__reply'> + <div className='dropdown'> + {ownerOptions} + </div> + </li> + </ul> + <div className='post__body'> + <div + ref='message_holder' + onClick={TextFormatting.handleClick} + dangerouslySetInnerHTML={{__html: TextFormatting.formatText(post.message)}} + /> + <PostBodyAdditionalContent + post={post} + /> + {fileAttachment} + </div> </div> </div> <hr /> diff --git a/web/react/components/search_results_item.jsx b/web/react/components/search_results_item.jsx index a8bd4db2c..52766a8a0 100644 --- a/web/react/components/search_results_item.jsx +++ b/web/react/components/search_results_item.jsx @@ -74,29 +74,30 @@ export default class SearchResultsItem extends React.Component { onClick={this.handleClick} > <div className='search-channel__name'>{channelName}</div> - <div className='post-profile-img__container'> - <img - className='post-profile-img' - src={'/api/v1/users/' + this.props.post.user_id + '/image?time=' + timestamp + '&' + utils.getSessionIndex()} - height='36' - width='36' - /> - </div> <div className='post__content'> - <ul className='post-header'> - <li className='post-header-col'><strong><UserProfile userId={this.props.post.user_id} /></strong></li> - <li className='post-header-col'> - <time className='search-item-time'> - {utils.displayDate(this.props.post.create_at) + ' ' + utils.displayTime(this.props.post.create_at)} - </time> - </li> - </ul> - <div className='search-item-snippet'> - <span - onClick={this.handleClick} - dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.props.post.message, formattingOptions)}} + <div className='post__img'> + <img + src={'/api/v1/users/' + this.props.post.user_id + '/image?time=' + timestamp + '&' + utils.getSessionIndex()} + height='36' + width='36' /> </div> + <div> + <ul className='post__header'> + <li className='col__name'><strong><UserProfile userId={this.props.post.user_id} /></strong></li> + <li className='col'> + <time className='search-item-time'> + {utils.displayDate(this.props.post.create_at) + ' ' + utils.displayTime(this.props.post.create_at)} + </time> + </li> + </ul> + <div className='search-item-snippet'> + <span + onClick={this.handleClick} + dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.props.post.message, formattingOptions)}} + /> + </div> + </div> </div> </div> ); diff --git a/web/react/components/time_since.jsx b/web/react/components/time_since.jsx index c37739b9c..212beb080 100644 --- a/web/react/components/time_since.jsx +++ b/web/react/components/time_since.jsx @@ -34,7 +34,7 @@ export default class TimeSince extends React.Component { placement='top' overlay={tooltip} > - <time className='post-profile-time'> + <time className='post__time'> {Utils.displayDateTime(this.props.eventTime)} </time> </OverlayTrigger> diff --git a/web/react/components/user_settings/custom_theme_chooser.jsx b/web/react/components/user_settings/custom_theme_chooser.jsx index 3dbed72c3..3d0a2b548 100644 --- a/web/react/components/user_settings/custom_theme_chooser.jsx +++ b/web/react/components/user_settings/custom_theme_chooser.jsx @@ -14,7 +14,10 @@ export default class CustomThemeChooser extends React.Component { this.state = {}; } componentDidMount() { - $('.color-picker').colorpicker().on('changeColor', this.onPickerChange); + $('.color-picker').colorpicker({ + format: 'hex' + }); + $('.color-picker').on('changeColor', this.onPickerChange); } onPickerChange(e) { const theme = this.props.theme; diff --git a/web/react/stores/modal_store.jsx b/web/react/stores/modal_store.jsx index dc65d48da..809f83a59 100644 --- a/web/react/stores/modal_store.jsx +++ b/web/react/stores/modal_store.jsx @@ -27,12 +27,14 @@ class ModalStoreClass extends EventEmitter { } handleEventPayload(payload) { - const action = payload.action; + // toggle event handlers should accept a boolean show/hide value and can accept a map of arguments + const {type, value, ...args} = payload.action; - switch (action.type) { + switch (type) { case ActionTypes.TOGGLE_IMPORT_THEME_MODAL: case ActionTypes.TOGGLE_INVITE_MEMBER_MODAL: - this.emit(action.type, action.value); + case ActionTypes.TOGGLE_DELETE_POST_MODAL: + this.emit(type, value, args); break; } } diff --git a/web/react/stores/post_store.jsx b/web/react/stores/post_store.jsx index 0fe253310..a564a2435 100644 --- a/web/react/stores/post_store.jsx +++ b/web/react/stores/post_store.jsx @@ -40,6 +40,7 @@ class PostStoreClass extends EventEmitter { this.storePosts = this.storePosts.bind(this); this.pStorePosts = this.pStorePosts.bind(this); this.getPosts = this.getPosts.bind(this); + this.getPost = this.getPost.bind(this); this.storePost = this.storePost.bind(this); this.pStorePost = this.pStorePost.bind(this); this.removePost = this.removePost.bind(this); @@ -68,6 +69,7 @@ class PostStoreClass extends EventEmitter { this.storeLatestUpdate = this.storeLatestUpdate.bind(this); this.getLatestUpdate = this.getLatestUpdate.bind(this); this.getCurrentUsersLatestPost = this.getCurrentUsersLatestPost.bind(this); + this.getCommentCount = this.getCommentCount.bind(this); } emitChange() { this.emit(CHANGE_EVENT); @@ -193,6 +195,9 @@ class PostStoreClass extends EventEmitter { getPosts(channelId) { return BrowserStore.getItem('posts_' + channelId); } + getPost(channelId, postId) { + return this.getPosts(channelId).posts[postId]; + } getCurrentUsersLatestPost(channelId, rootId) { const userId = UserStore.getCurrentId(); var postList = makePostListNonNull(this.getPosts(channelId)); @@ -402,6 +407,20 @@ class PostStoreClass extends EventEmitter { getLatestUpdate(channelId) { return BrowserStore.getItem('latest_post_' + channelId, 0); } + getCommentCount(post) { + const posts = this.getPosts(post.channel_id).posts; + + let commentCount = 0; + for (let id in posts) { + if (posts.hasOwnProperty(id)) { + if (posts[id].root_id === post.id) { + commentCount += 1; + } + } + } + + return commentCount; + } } var PostStore = new PostStoreClass(); diff --git a/web/react/utils/channel_intro_mssages.jsx b/web/react/utils/channel_intro_mssages.jsx index 11b69e28f..28a458e3f 100644 --- a/web/react/utils/channel_intro_mssages.jsx +++ b/web/react/utils/channel_intro_mssages.jsx @@ -205,6 +205,7 @@ export function createStandardIntroMessage(channel, showInviteModal) { <i className='fa fa-pencil'></i>{'Set a header'} </a> <a + className='intro-links' href='#' onClick={showInviteModal} > diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx index 4fef64f18..80c0cf0ee 100644 --- a/web/react/utils/constants.jsx +++ b/web/react/utils/constants.jsx @@ -42,7 +42,8 @@ module.exports = { SHOW_SEARCH: null, TOGGLE_IMPORT_THEME_MODAL: null, - TOGGLE_INVITE_MEMBER_MODAL: null + TOGGLE_INVITE_MEMBER_MODAL: null, + TOGGLE_DELETE_POST_MODAL: null }), PayloadSources: keyMirror({ diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx index 77b3ecb57..bf40d8c65 100644 --- a/web/react/utils/utils.jsx +++ b/web/react/utils/utils.jsx @@ -538,12 +538,11 @@ export function applyTheme(theme) { if (theme.sidebarText) { changeCss('.sidebar--left .nav-pills__container li>a, .sidebar--right, .settings-modal .nav-pills>li a, .sidebar--menu', 'color:' + changeOpacity(theme.sidebarText, 0.6), 1); - changeCss('@media(max-width: 768px){.settings-modal .settings-table .nav>li>a', 'color:' + theme.sidebarText, 1); + changeCss('@media(max-width: 960px){.settings-modal .settings-table .nav>li>a', 'color:' + theme.sidebarText, 1); changeCss('.sidebar--left .nav-pills__container li>h4, .sidebar--left .add-channel-btn', 'color:' + changeOpacity(theme.sidebarText, 0.6), 1); changeCss('.sidebar--left .add-channel-btn:hover, .sidebar--left .add-channel-btn:focus', 'color:' + theme.sidebarText, 1); - changeCss('.sidebar--left, .sidebar--right .sidebar--right__header', 'border-color:' + changeOpacity(theme.sidebarText, 0.2), 1); changeCss('.sidebar--left .status path', 'fill:' + changeOpacity(theme.sidebarText, 0.5), 1); - changeCss('@media(max-width: 768px){.settings-modal .settings-table .nav>li>a', 'border-color:' + changeOpacity(theme.sidebarText, 0.2), 2); + changeCss('@media(max-width: 960px){.settings-modal .settings-table .nav>li>a', 'border-color:' + changeOpacity(theme.sidebarText, 0.2), 2); } if (theme.sidebarUnreadText) { @@ -552,7 +551,7 @@ export function applyTheme(theme) { if (theme.sidebarTextHoverBg) { changeCss('.sidebar--left .nav-pills__container li>a:hover, .sidebar--left .nav-pills__container li>a:focus, .settings-modal .nav-pills>li:hover a, .settings-modal .nav-pills>li:focus a', 'background:' + theme.sidebarTextHoverBg, 1); - changeCss('@media(max-width: 768px){.settings-modal .settings-table .nav>li:hover a', 'background:' + theme.sidebarTextHoverBg, 1); + changeCss('@media(max-width: 960px){.settings-modal .settings-table .nav>li:hover a', 'background:' + theme.sidebarTextHoverBg, 1); } if (theme.sidebarTextActiveBorder) { @@ -570,7 +569,7 @@ export function applyTheme(theme) { changeCss('.sidebar--left .team__header, .sidebar--menu .team__header', 'background:' + theme.sidebarHeaderBg, 1); changeCss('.modal .modal-header', 'background:' + theme.sidebarHeaderBg, 1); changeCss('#navbar .navbar-default', 'background:' + theme.sidebarHeaderBg, 1); - changeCss('@media(max-width: 768px){.search-bar__container', 'background:' + theme.sidebarHeaderBg, 1); + changeCss('@media(max-width: 960px){.search-bar__container', 'background:' + theme.sidebarHeaderBg, 1); changeCss('.attachment .attachment__container', 'border-left-color:' + theme.sidebarHeaderBg, 1); } @@ -581,7 +580,7 @@ export function applyTheme(theme) { changeCss('.modal .modal-header .modal-title, .modal .modal-header .modal-title .name, .modal .modal-header button.close', 'color:' + theme.sidebarHeaderTextColor, 1); changeCss('#navbar .navbar-default .navbar-brand .heading', 'color:' + theme.sidebarHeaderTextColor, 1); changeCss('#navbar .navbar-default .navbar-toggle .icon-bar, ', 'background:' + theme.sidebarHeaderTextColor, 1); - changeCss('@media(max-width: 768px){.search-bar__container', 'color:' + theme.sidebarHeaderTextColor, 2); + changeCss('@media(max-width: 960px){.search-bar__container', 'color:' + theme.sidebarHeaderTextColor, 2); } if (theme.onlineIndicator) { @@ -614,17 +613,16 @@ export function applyTheme(theme) { } if (theme.centerChannelColor) { + changeCss('.sidebar--left, .sidebar--right .sidebar--right__header', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1); changeCss('.app__content, .post-create__container .post-create-body .btn-file, .post-create__container .post-create-footer .msg-typing, .command-name, .modal .modal-content, .dropdown-menu, .popover, .mentions-name, .tip-overlay', 'color:' + theme.centerChannelColor, 1); changeCss('#post-create', 'color:' + theme.centerChannelColor, 2); - changeCss('.channel-header__links a', 'fill:' + changeOpacity(theme.centerChannelColor, 0.9), 1); - changeCss('.channel-header__links a:hover, .channel-header__links a:active', 'fill:' + theme.centerChannelColor, 2); changeCss('.mentions--top, .command-box', 'box-shadow:' + changeOpacity(theme.centerChannelColor, 0.2) + ' 1px -3px 12px', 3); changeCss('.mentions--top, .command-box', '-webkit-box-shadow:' + changeOpacity(theme.centerChannelColor, 0.2) + ' 1px -3px 12px', 2); changeCss('.mentions--top, .command-box', '-moz-box-shadow:' + changeOpacity(theme.centerChannelColor, 0.2) + ' 1px -3px 12px', 1); changeCss('.dropdown-menu, .popover ', 'box-shadow:' + changeOpacity(theme.centerChannelColor, 0.1) + ' 0px 6px 12px', 3); changeCss('.dropdown-menu, .popover ', '-webkit-box-shadow:' + changeOpacity(theme.centerChannelColor, 0.1) + ' 0px 6px 12px', 2); changeCss('.dropdown-menu, .popover ', '-moz-box-shadow:' + changeOpacity(theme.centerChannelColor, 0.1) + ' 0px 6px 12px', 1); - changeCss('.post-body hr, .loading-screen .loading__content .round, .tutorial__circles .circle', 'background:' + theme.centerChannelColor, 1); + changeCss('.post__body hr, .loading-screen .loading__content .round, .tutorial__circles .circle', 'background:' + theme.centerChannelColor, 1); changeCss('.channel-header .heading', 'color:' + theme.centerChannelColor, 1); changeCss('.markdown__table tbody tr:nth-child(2n)', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1); changeCss('.channel-header__info>div.dropdown .header-dropdown__icon', 'color:' + changeOpacity(theme.centerChannelColor, 0.8), 1); @@ -642,25 +640,23 @@ export function applyTheme(theme) { changeCss('.post-image__column .post-image__details', 'color:' + theme.centerChannelColor, 2); changeCss('.post-image__column a, .post-image__column a:hover, .post-image__column a:focus', 'color:' + theme.centerChannelColor, 1); changeCss('.search-bar__container .search__form .search-bar, .form-control', 'color:' + theme.centerChannelColor, 2); - changeCss('@media(max-width: 768px){.search-bar__container .search__form .search-bar', 'background:' + changeOpacity(theme.centerChannelColor, 0.2) + '; color: inherit;', 1); + changeCss('@media(max-width: 960px){.search-bar__container .search__form .search-bar', 'background:' + changeOpacity(theme.centerChannelColor, 0.2) + '; color: inherit;', 1); changeCss('.input-group-addon, .search-bar__container .search__form, .form-control', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1); - changeCss('.form-control:focus, .channel-header__links', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.3), 1); + changeCss('.form-control:focus', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.3), 1); changeCss('.attachment .attachment__content', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.3), 1); changeCss('.channel-intro .channel-intro__content, .webhooks__container', 'background:' + changeOpacity(theme.centerChannelColor, 0.05), 1); changeCss('.date-separator .separator__text', 'color:' + theme.centerChannelColor, 2); changeCss('.date-separator .separator__hr, .modal-footer, .modal .custom-textarea, .post-right__container .post.post--root hr, .search-item-container', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1); changeCss('.modal .custom-textarea:focus', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.3), 1); changeCss('.channel-intro, .settings-modal .settings-table .settings-content .divider-dark, hr, .settings-modal .settings-table .settings-links', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1); - changeCss('.post.current--user .post-body, .post.post--comment.other--root.current--user .post-comment, pre', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1); - changeCss('.post.current--user .post-body, .post.post--comment.other--root.current--user .post-comment, .post.post--comment.other--root .post-comment, .post.same--root .post-body, .modal .more-table tbody>tr td, .member-div:first-child, .member-div, .access-history__table .access__report, .activity-log__table', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.1), 2); - changeCss('@media(max-width: 1440px){.post.same--root', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.07), 2); - changeCss('@media(max-width: 1440px){.post.same--root', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.07), 2); + changeCss('.post.current--user .post__body, .post.post--comment.other--root.current--user .post-comment, pre', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1); + changeCss('.post.current--user .post__body, .post.post--comment.other--root.current--user .post-comment, .post.same--root.post--comment .post__body, .modal .more-table tbody>tr td, .member-div:first-child, .member-div, .access-history__table .access__report, .activity-log__table', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.1), 2); changeCss('@media(max-width: 1800px){.inner__wrap.move--left .post.post--comment.same--root', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.07), 2); - changeCss('.post:hover, .channel-header__links a:hover, .channel-header__links a:active, .modal .more-table tbody>tr:hover td, .settings-modal .settings-table .settings-content .section-min:hover', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1); + changeCss('.post:hover, .modal .more-table tbody>tr:hover td, .settings-modal .settings-table .settings-content .section-min:hover', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1); changeCss('.date-separator.hovered--before:after, .date-separator.hovered--after:before, .new-separator.hovered--after:before, .new-separator.hovered--before:after', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1); changeCss('.command-name:hover, .mentions-name:hover, .mentions-focus, .dropdown-menu>li>a:focus, .dropdown-menu>li>a:hover, .bot-indicator', 'background:' + changeOpacity(theme.centerChannelColor, 0.15), 1); changeCss('code', 'background:' + changeOpacity(theme.centerChannelColor, 0.1), 1); - changeCss('.post.current--user:hover .post-body ', 'background: none;', 1); + changeCss('.post.current--user:hover .post__body ', 'background: none;', 1); changeCss('.sidebar--right', 'color:' + theme.centerChannelColor, 2); } @@ -1232,3 +1228,12 @@ export function getChannelTerm(channelType) { return channelTerm; } + +export function getPostTerm(post) { + let postTerm = 'Post'; + if (post.root_id) { + postTerm = 'Comment'; + } + + return postTerm; +} diff --git a/web/sass-files/sass/partials/_base.scss b/web/sass-files/sass/partials/_base.scss index ad4a65c00..c5b1a7425 100644 --- a/web/sass-files/sass/partials/_base.scss +++ b/web/sass-files/sass/partials/_base.scss @@ -85,6 +85,7 @@ img { a { word-break: break-word; color: $primary-color; + cursor: pointer; } a:focus, a:hover { diff --git a/web/sass-files/sass/partials/_headers.scss b/web/sass-files/sass/partials/_headers.scss index 69bc56841..51625afe0 100644 --- a/web/sass-files/sass/partials/_headers.scss +++ b/web/sass-files/sass/partials/_headers.scss @@ -54,9 +54,9 @@ } .channel-intro { - padding-bottom:5px; - margin: 0 1em 35px; - max-width: 850px; + margin: 0 auto 35px; + padding: 0 1em 5px; + max-width: 1000px; border-bottom: 1px solid lightgrey; .intro-links { margin: 0 1.5em 10px 0; @@ -216,7 +216,6 @@ font-size: 14px; line-height: 56px; #member_popover { - margin-right: 5px; width: 45px; color: #999; cursor: pointer; @@ -292,20 +291,21 @@ } .channel-header__links { - height: 32px; - width: 32px; - @include border-radius(50px); - border: 1px solid #ccc; + height: 30px; + width: 24px; + line-height: 26px; margin-right: 10px; + font-size: 22px; > a { - @include border-radius(50px); - height: 100%; - display: block; + color: inherit; + text-decoration: none; + @include opacity(0.6); @include single-transition(all, 0.1s, ease-in); - } - svg { - vertical-align: top; - margin: 7px 0 0 0px; - fill: inherit; + &:hover { + @include opacity(1); + } + &:focus { + color: inherit; + } } } diff --git a/web/sass-files/sass/partials/_markdown.scss b/web/sass-files/sass/partials/_markdown.scss index 8b0a32704..ea5ccd2d2 100644 --- a/web/sass-files/sass/partials/_markdown.scss +++ b/web/sass-files/sass/partials/_markdown.scss @@ -16,7 +16,7 @@ } } -.post-body { +.post__body { hr { height: 4px; padding: 0; diff --git a/web/sass-files/sass/partials/_post.scss b/web/sass-files/sass/partials/_post.scss index fad6f5074..743a76693 100644 --- a/web/sass-files/sass/partials/_post.scss +++ b/web/sass-files/sass/partials/_post.scss @@ -47,23 +47,23 @@ body.ios { .textarea-wrapper { position:relative; - .textbox-preview-area { - position: absolute; - z-index: 2; - top: 0; - left: 0; - box-shadow: none; - } + .textbox-preview-area { + position: absolute; + z-index: 2; + top: 0; + left: 0; + box-shadow: none; + } .textbox-preview-link, .textbox-help-link { - position: absolute; - z-index: 3; - bottom: -23px; + position: absolute; + z-index: 3; + bottom: -23px; font-size: 13px; - cursor: pointer; - } - .textbox-preview-link { - right: 45px; - } + cursor: pointer; + } + .textbox-preview-link { + right: 45px; + } .textbox-help-link { right: 0; } @@ -206,6 +206,7 @@ body.ios { @include flex(1 1 auto); position: relative; overflow-y: hidden; + .post-list-holder-by-time { background: #fff; overflow-y: scroll; @@ -224,31 +225,7 @@ body.ios { display: inline; } } - .post-list__table { - display: table; - table-layout: fixed; - width: 100%; - min-height: 100%; - height: 100%; - .post-list__content { - display: table-cell; - vertical-align: bottom; - div { - &:last-child { - .post { - .post-header { - .post-header-col.post-header__reply { - .dropdown-menu { - top: auto; - bottom: 25px; - } - } - } - } - } - } - } - } + .more-messages-text { margin-top: 2px; margin-bottom: 5px; @@ -269,13 +246,13 @@ body.ios { .post-create__container { form { width: 100%; - padding: 0 1em; - margin: 0; + padding: 0.5em 14px 0; + margin: 0 auto; + max-width: 1028px; } .post-create-body { - max-width: 850px; - padding: 0 0 2px; position: relative; + padding: 0 0 2px; .post-body__cell { vertical-align: top; position: relative; @@ -355,95 +332,271 @@ body.ios { } } +.post-list__table { + display: table; + table-layout: fixed; + width: 100%; + min-height: 100%; + height: 100%; + .post-list__content { + display: table-cell; + vertical-align: bottom; + > div:last-child { + .dropdown-menu { + top: auto; + bottom: 25px; + } + } + } +} + .post { word-wrap: break-word; - padding: 8px 1em; + padding: 8px 1em 0; position: relative; max-width: 100%; @include legacy-pie-clearfix; + &:hover { - .post-header .post-header-col.post-header__reply { - .dropdown, .comment-icon__container { - @include opacity(1); - } + .dropdown, .comment-icon__container { + visibility: visible; + } + } + + ul { + margin: 0; + padding: 0; + list-style: none; + } + + p { + margin: 0 0 1em; + line-height: 1.6em; + font-size: 0.97em; + white-space: pre-wrap; + + &:last-child { + margin-bottom: 0.5em; } - background: #f5f5f5; + } - &.current--user { - .post-body { - @include border-radius(4px); - background: rgba(#000, 0.05); + + span { + p:last-child { + margin-bottom: 0.5em; } } - &.post--comment { - &.other--root { - .post-comment { - border-left: 4px solid #EEE; - padding: 4px 0 6px 10px; - margin: 0 0 0 30px; - @include border-radius(0 4px 4px 0); + + &.post--root { + + .comment-icon__container { + visibility: visible; + } + + } + + &.same--root { + + &.same--user { + padding: 0 1em; + + &:hover { + + .post__time { + + &:before { + @include opacity(0.5); + } + + } + } - .post-body { - background: transparent; + + .post__header { margin: 0; - padding: 1px 0px; + height: 0; + + .col__name { + display: none; + } + + .col__reply { + top: 6px; + } + + } + + .post__time { + top: 24px; } - &.current--user { - .post-comment { - background: #f5f5f5; + + .post__time { + display: inline-block; + font: normal normal normal 14px/1 FontAwesome; + font-size: inherit; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-size: 0; + position: absolute; + top: -3px; + left: 17px; + width: 30px; + height: 30px; + line-height: 37px; + + &:before { + @include opacity(0); + content: "\f017"; + content: "\f017"; + font-size: 19px; } + } + } - &.same--root { - .post-body { - @include border-radius(0 4px 4px 0); + + &.post--comment { + + .post__link { + display: none; } - .post-body { - border-left: 4px solid #EEE; - width: 570px; - margin-left: 30px; - padding-left: 10px; - .post-link { + + .post__img { + img { display: none; } } + + } + + &.post--comment { + .post__body { + border-left: 4px solid #ddd; + } } + } - &.same--root { + + &.other--root { + .comment-icon__container { - @include opacity(0); + + &.icon--show { + visibility: visible; + } + } - div.post-profile-img__container { - height: 1px; - .post-profile-img { - visibility: hidden; + + &.post--comment { + + .post__header { + + .col__reply { + top: 53px; + } + } + + } + + } + + .post__content { + margin: 0 auto; + position: relative; + max-width: 1000px; + display: table; + width: 100%; + + > div { + display: table-cell; + vertical-align: top; } - .post__content { - padding: 0; + + } + + .post__header { + margin-bottom: 2px; + + li { + display: inline-block; } - &.same--user { - .post__content { - padding-left: 46px; + + .col__name { + margin-right: 7px; + font-weight: 600; + } + + .col__reply { + position: absolute; + right: 0; + top: 30px; + width: 70px; + } + + } + + .post__img { + width: 46px; + img { + width: 36px; + height: 36px; + vertical-align: inherit; + @include border-radius(50px); + } + } + + .dropdown { + display: inline-block; + visibility: hidden; + top: -1px; + float: right; + + .dropdown-menu { + right: 0; + left: auto; + min-width: 130px; + padding: 2px 0; + + li { + display: block; + } - .post-header-post { - visibility: hidden; + + a { + padding: 5px 15px; } + } - } - p { - margin: 0 0 1em; - line-height: 1.6em; - font-size: 0.97em; - white-space: pre-wrap; + } - span { - p:last-child { - margin-bottom: 0.5em; + .post__dropdown { + &:after { + content: '[...]'; + top: -1px; + position: relative; } } + .post__body { + word-wrap: break-word; + padding: 0.2em 0.5em 0em; + @include legacy-pie-clearfix; + width: calc(100% - 80px); + } + + .post__link { + margin: 2px 0 5px; + font-size: 13px; + } + + .post__time { + font-size: 13px; + @include opacity(0.6); + } + .post-loading-gif { height:10px; width:10px; @@ -459,8 +612,15 @@ body.ios { } .comment-icon__container { - margin-left: 7px; + margin-right: 7px; fill: $primary-color; + display: inline-block; + visibility: hidden; + + &.icon--visible { + visibility: visible; + } + .comment-icon { display: inline-block; top: 3px; @@ -468,132 +628,13 @@ body.ios { margin-right: 3px; fill: inherit; } + path { fill: inherit; } + } - > div { - &.post-profile-img__container { - float: left; - .post-profile-img { - width: 36px; - height: 36px; - margin-right: 10px; - vertical-align: inherit; - @include border-radius(50px); - } - } - &.post__content { - padding-left: 46px; - max-width: 100%; - @include legacy-pie-clearfix; - } - } - .post-image__columns { - @include legacy-pie-clearfix; - padding-bottom: 5px; - } - .post-info--hidden { - display: none; - } - .post-body { - position: relative; - z-index: 1; - max-width: 100%; - width: 600px; - float: left; - word-wrap: break-word; - padding: 0.3em 0.5em 0.1em; - margin: -0.3em 0 0; - .post-link { - @include clearfix; - text-overflow: ellipsis; - white-space: nowrap; - cursor: pointer; - } - .post-body--code { - font-size: .97em; - position:relative; - .post-body--code__language { - position: absolute; - right: 0; - background: #fff; - cursor: default; - padding: 0.3em 0.5em 0.1em; - border-bottom-left-radius: 4px; - @include opacity(.3); - } - &.tex .katex-display { - text-align: left; - } - code { - white-space: pre; - } - } - } - .create-reply-form-wrap { - width: 100%; - margin-top: 5px; - form { - padding: 0; - } - .comment-btn { - display: none; - } - } - .post-header { - position: relative; - list-style-type: none; - margin: 0 0 1px; - padding-left: 0px; - &.post-header-post { - position: relative; - z-index: 1; - width: 200px; - text-align: right; - float: left; - padding-right: 15px; - } - &.post-info { - .post-profile-time { - vertical-align: top; - width: 150px; - max-width: 220px; - overflow: hidden; - display: block; - white-space: nowrap; - text-overflow: ellipsis; - margin: 0 0 0 50px; - } - } - .post-header-col { - vertical-align: top; - display: inline-block; - margin-right: 10px; - &.post-header__reply { - position: relative; - top: -1px; - min-width: 70px; - .dropdown-menu { - right: 0; - left: auto; - } - .dropdown { - min-width: 18px; - display: inline-block; - @include opacity(0); - } - .dropdown-toggle:after { - content: '[...]'; - } - } - } - .post-profile-time { - @include opacity(0.5); - } - } - .post-comment { - } + .web-embed-data { padding: 2px 0 0 10px; background: #f9f9f9; @@ -621,22 +662,6 @@ body.ios { display: none; } } - .msg-typing { - margin-top: 2px; - margin-left: 5px; - color: #a8adb7; - } - .post-grey { - color:lightgrey; - } - .post-reply { - clear: both; - width: 100%; - word-break: break-word; - .btn-file { - width: 40px; - } - } } .bot-indicator { diff --git a/web/sass-files/sass/partials/_post_right.scss b/web/sass-files/sass/partials/_post_right.scss index 54c3bcdf8..582b72c90 100644 --- a/web/sass-files/sass/partials/_post_right.scss +++ b/web/sass-files/sass/partials/_post_right.scss @@ -9,28 +9,31 @@ .post-right-comments-container { position: relative; - padding: 0; + padding: 0.7em 0 0; } .post { + &.post--root { - padding: 1em 1em 0; - margin: 0 0 1em; - width: 100%; - hr { - border-color: #DDD; - margin: 1em 0 0 0; - } + padding-bottom: 0; } - .post-header { - .post-profile-time { - width: 200px; - display: inline-block; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + + .post__header { + + .col__reply { + top: 0; } + } + + .post__body { + width: 100%; + } + + } + + hr { + margin-bottom: 0; } .post-create__container { diff --git a/web/sass-files/sass/partials/_responsive.scss b/web/sass-files/sass/partials/_responsive.scss index aad991035..368027cbf 100644 --- a/web/sass-files/sass/partials/_responsive.scss +++ b/web/sass-files/sass/partials/_responsive.scss @@ -8,96 +8,10 @@ } } } - .post { - &.post--comment { - &.same--root { - margin-left: 104px; - padding-left: 10px; - border-left: 4px solid #EEE; - div.post-profile-img__container { - .post-profile-img { - display: none; - } - } - .post__content { - width: 825px; - margin-left: 0; - } - .post-body { - width: 736px; - border: none; - margin: 3px 0 0; - } - } - &.other--root { - .post-comment { - margin-left: 0; - } - } - &.same--root { - margin-top: 10px; - margin-bottom: 10px; - } - } - .post-body { - float: none; - width: 750px; - margin: 3px 0 0; - } - .post__content { - width: 920px; - } - .post-header { - &.post-header-post { - z-index: inherit; - width: auto; - float: none; - text-align: left; - padding-right: 0; - } - &.post-info { - display: none; - float: none; - } - .post-header-col { - &.post-header__reply { - float: right; - text-align: right; - .reply-link { - margin-right: 0; - } - .dropdown { - margin-left: 10px; - } - } - } - .post-info--hidden { - display: inline; - .post-info { - display: inline; - .tooltip { - margin-top: -25px; - margin-left: 40px; - } - .post-profile-time { - margin: 0; - } - } - } - } - } } } } @media screen and (max-width: 1440px) { - .post-create__container { - .post-create-body { - max-width: 810px; - } - } - .channel-intro { - max-width: 810px; - } .date-separator, .new-separator { &.hovered--comment { &:before, &:after { @@ -105,105 +19,6 @@ } } } - .post { - &.same--root.same--user { - .post-header-post { - visibility: hidden; - width: 100%; - position: relative; - top: -5px; - .post-header-col.post-header__name { - display: none; - } - } - .post-body { - top: -15px; - margin-bottom: -10px; - } - &:hover .post-header-post { - visibility: visible; - } - } - - &.post--comment { - &.other--root { - .post-comment { - margin-left: 0; - } - } - &.same--root { - margin-top: 5px; - margin-bottom: 5px; - margin-left: 104px; - padding-left: 10px; - border-left: 4px solid #EEE; - div.post-profile-img__container { - .post-profile-img { - display: none; - } - } - .post-body { - margin-left: 0; - border-left: 0; - } - &.same--user { - .post__content { - margin-left: 0; - padding-left: 0; - } - } - } - .post__content { - width: 810px; - } - .post-body { - width: 736px; - border: none; - margin: 3px 0 0; - } - } - .post__content { - width: 880px; - } - .post-header { - &.post-header-post { - z-index: inherit; - width: auto; - float: none; - text-align: left; - padding-right: 0; - } - &.post-info { - display: none; - float: none; - } - .post-header-col { - &.post-header__reply { - float: right; - margin: 0; - } - } - .post-info--hidden { - display: inline; - .post-info { - display: inline; - .tooltip { - margin-top: -25px; - margin-left: 40px; - } - .post-profile-time { - width: auto; - margin: 0; - } - } - } - } - .post-body { - margin: 3px 0 0; - float: none; - width: 750px; - } - } } @media screen and (max-width: 1140px) { @@ -232,79 +47,38 @@ } } } - .post { - .post__content { - width: 100%; - } - .post-header { - .post-header-col { - &.post-header__reply { - .reply-link { - margin-right: 0; - } - .dropdown { - margin-left: 10px; - } - } - } - } - } } @media screen and (max-width: 960px) { - .post { - .post-header .post-header-col.post-header__reply { - .comment-icon__container__hide { - display: none; - } - .dropdown { - @include opacity(1); - } - .comment-icon__container__show { - @include opacity(1); - } - } - } + .signup-team__container { font-size: 1em; } + .post { - .post-header { - .post-header-col { - &.post-header__reply { - text-align: right; - display: inline-block; - } + + .dropdown, .comment-icon__container { + visibility: visible; + } + + .post__img { + width: 40px; + + img { + width: 32px; + height: 32px; } } - } -} -@media (min-width: 992px){ - .modal-lg { - width: 700px; } -} -@media screen and (min-width: 768px) { - .second-bar { - display: none; + .post-image__column { + width: 200px; + height: 95px; } -} -@media screen and (max-height: 640px) { - .signup-team__container { - padding: 30px 0; - margin-bottom: 30px; - font-size: 0.9em; - .signup-team__name { - font-size: 2em; - } - } -} -@media screen and (max-width: 768px) { .textarea-wrapper { - .textbox-preview-link { + .textbox-preview-link, .textbox-help-link { display: none; } } @@ -385,35 +159,6 @@ } } } - .post { - &:hover { - background: none; - .post-header .post-header-col.post-header__reply { - .dropdown-toggle:after { - content: '...'; - } - } - } - &.post--comment { - &.same--root { - margin-left: 25px; - } - &.other--root { - margin-left: 0; - &:hover { - background: none; - } - } - } - .post-header .post-header-col.post-header__reply { - .dropdown-toggle:after { - content: '...'; - } - } - &.same--root.same--user .post__content{ - padding-left: 0; - } - } .signup-team__container { padding: 30px 0; margin-bottom: 30px; @@ -860,24 +605,6 @@ } } - .post { - .post-profile-img__container { - display: none; - } - &.post--comment { - &.other--root { - .post-comment { - margin-left: 11px; - } - } - } - > div { - &.post__content { - padding: 0; - } - } - } - .post-comments { padding: 9px 21px 10px 10px !important; } @@ -958,3 +685,27 @@ } } } + + +@media (min-width: 992px){ + .modal-lg { + width: 700px; + } +} + +@media screen and (min-width: 768px) { + .second-bar { + display: none; + } +} + +@media screen and (max-height: 640px) { + .signup-team__container { + padding: 30px 0; + margin-bottom: 30px; + font-size: 0.9em; + .signup-team__name { + font-size: 2em; + } + } +}
\ No newline at end of file diff --git a/web/sass-files/sass/partials/_search.scss b/web/sass-files/sass/partials/_search.scss index bedf35376..27b55f214 100644 --- a/web/sass-files/sass/partials/_search.scss +++ b/web/sass-files/sass/partials/_search.scss @@ -1,5 +1,5 @@ #channel-header .search-bar__container { - padding: 0 8px 0 0; + padding: 0 8px 0 3px; } .search-bar__container { padding: 12px 8px 0 0; diff --git a/web/sass-files/sass/partials/_sidebar--right.scss b/web/sass-files/sass/partials/_sidebar--right.scss index 2527eef28..43162831d 100644 --- a/web/sass-files/sass/partials/_sidebar--right.scss +++ b/web/sass-files/sass/partials/_sidebar--right.scss @@ -36,20 +36,6 @@ height: calc(100% - 56px); @include border-radius(2px 0 0 0); } - .post { - .post-header { - .post-header-col { - &.post-header__reply { - min-width: 30px; - text-align: right; - float: right; - } - } - } - .post-body { - margin: 3px 0 0; - } - } .sidebar__overlay { width: 100%; height: 100%; |