summaryrefslogtreecommitdiffstats
path: root/web/react
diff options
context:
space:
mode:
Diffstat (limited to 'web/react')
-rw-r--r--web/react/.eslintignore2
-rw-r--r--web/react/components/channel_header.jsx38
-rw-r--r--web/react/components/create_post.jsx4
-rw-r--r--web/react/components/file_preview.jsx21
-rw-r--r--web/react/components/navbar.jsx17
-rw-r--r--web/react/components/post.jsx61
-rw-r--r--web/react/components/post_body.jsx44
-rw-r--r--web/react/components/post_header.jsx8
-rw-r--r--web/react/components/post_info.jsx28
-rw-r--r--web/react/components/rhs_comment.jsx63
-rw-r--r--web/react/components/rhs_root_post.jsx74
-rw-r--r--web/react/components/search_results.jsx19
-rw-r--r--web/react/components/search_results_item.jsx41
-rw-r--r--web/react/components/sidebar_right.jsx27
-rw-r--r--web/react/components/time_since.jsx2
-rw-r--r--web/react/components/user_settings/custom_theme_chooser.jsx5
-rw-r--r--web/react/stores/search_store.jsx20
-rw-r--r--web/react/utils/channel_intro_mssages.jsx1
-rw-r--r--web/react/utils/constants.jsx2
-rw-r--r--web/react/utils/utils.jsx34
20 files changed, 286 insertions, 225 deletions
diff --git a/web/react/.eslintignore b/web/react/.eslintignore
index 5e8e7e0b6..25a7df331 100644
--- a/web/react/.eslintignore
+++ b/web/react/.eslintignore
@@ -1 +1,3 @@
**/*.json
+components/toggle_modal_button.jsx
+stores/modal_store.jsx
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_post.jsx b/web/react/components/create_post.jsx
index 6f25ef608..d823a54f1 100644
--- a/web/react/components/create_post.jsx
+++ b/web/react/components/create_post.jsx
@@ -383,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/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/navbar.jsx b/web/react/components/navbar.jsx
index 1fcfabccd..845c33f3b 100644
--- a/web/react/components/navbar.jsx
+++ b/web/react/components/navbar.jsx
@@ -32,6 +32,7 @@ export default class Navbar extends React.Component {
this.onChange = this.onChange.bind(this);
this.handleLeave = this.handleLeave.bind(this);
+ this.showSearch = this.showSearch.bind(this);
this.createCollapseButtons = this.createCollapseButtons.bind(this);
this.createDropdown = this.createDropdown.bind(this);
@@ -100,6 +101,11 @@ export default class Navbar extends React.Component {
$('.inner__wrap').toggleClass('move--left-small');
$('.sidebar--menu').toggleClass('move--left');
}
+ showSearch() {
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.SHOW_SEARCH
+ });
+ }
onChange() {
this.setState(this.getStateFromStores());
$('#navbar .navbar-brand .description').popover({placement: 'bottom', trigger: 'click', html: true});
@@ -411,6 +417,16 @@ export default class Navbar extends React.Component {
var collapseButtons = this.createCollapseButtons(currentId);
+ const searchButton = (
+ <button
+ type='button'
+ className='navbar-toggle pull-right'
+ onClick={this.showSearch}
+ >
+ <span className='glyphicon glyphicon-search icon--white' />
+ </button>
+ );
+
var channelMenuDropdown = this.createDropdown(channel, channelTitle, isAdmin, isDirect, popoverContent);
return (
@@ -422,6 +438,7 @@ export default class Navbar extends React.Component {
<div className='container-fluid theme'>
<div className='navbar-header'>
{collapseButtons}
+ {searchButton}
{channelMenuDropdown}
</div>
</div>
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 fffa5b19a..322834f07 100644
--- a/web/react/components/post_info.jsx
+++ b/web/react/components/post_info.jsx
@@ -81,7 +81,7 @@ export default class PostInfo extends React.Component {
role='presentation'
>
<a
- className='reply-link theme'
+ className='reply-link visible-xs theme'
href='#'
onClick={this.props.handleCommentClick}
>
@@ -99,7 +99,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'
@@ -116,23 +116,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>
);
}
@@ -140,17 +144,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 c16f9ff0e..a55bf0039 100644
--- a/web/react/components/rhs_comment.jsx
+++ b/web/react/components/rhs_comment.jsx
@@ -131,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'
@@ -193,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 84fdc014a..358bf8440 100644
--- a/web/react/components/rhs_root_post.jsx
+++ b/web/react/components/rhs_root_post.jsx
@@ -66,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'
@@ -129,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();
@@ -140,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.jsx b/web/react/components/search_results.jsx
index f4d8647db..491995087 100644
--- a/web/react/components/search_results.jsx
+++ b/web/react/components/search_results.jsx
@@ -80,14 +80,27 @@ export default class SearchResults extends React.Component {
var ctls = null;
- if (noResults) {
+ if (!searchTerm && noResults) {
+ ctls = (
+ <div className='sidebar--right__subheader'>
+ <ul>
+ <li>
+ {'Use '}<b>{'"quotation marks"'}</b>{' to search for phrases'}
+ </li>
+ <li>
+ {'Use '}<b>{'from:'}</b>{' to find posts from specific users and '}<b>{'in:'}</b>{' to find posts in specific channels'}
+ </li>
+ </ul>
+ </div>
+ );
+ } else if (noResults) {
ctls =
(
<div className='sidebar--right__subheader'>
<h4>{'NO RESULTS'}</h4>
<ul>
- <li>If you're searching a partial phrase (ex. searching "rea", looking for "reach" or "reaction"), append a * to your search term</li>
- <li>Due to the volume of results, two letter searches and common words like "this", "a" and "is" won't appear in search results</li>
+ <li>{'If you\'re searching a partial phrase (ex. searching "rea", looking for "reach" or "reaction"), append a * to your search term'}</li>
+ <li>{'Due to the volume of results, two letter searches and common words like "this", "a" and "is" won\'t appear in search results'}</li>
</ul>
</div>
);
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/sidebar_right.jsx b/web/react/components/sidebar_right.jsx
index ab558ad0f..8d16057fc 100644
--- a/web/react/components/sidebar_right.jsx
+++ b/web/react/components/sidebar_right.jsx
@@ -7,10 +7,6 @@ var SearchStore = require('../stores/search_store.jsx');
var PostStore = require('../stores/post_store.jsx');
var Utils = require('../utils/utils.jsx');
-function getStateFromStores() {
- return {search_visible: SearchStore.getSearchResults() != null, post_right_visible: PostStore.getSelectedPost() != null, is_mention_search: SearchStore.getIsMentionSearch()};
-}
-
export default class SidebarRight extends React.Component {
constructor(props) {
super(props);
@@ -19,19 +15,29 @@ export default class SidebarRight extends React.Component {
this.onSelectedChange = this.onSelectedChange.bind(this);
this.onSearchChange = this.onSearchChange.bind(this);
+ this.onShowSearch = this.onShowSearch.bind(this);
this.doStrangeThings = this.doStrangeThings.bind(this);
- this.state = getStateFromStores();
+ this.state = this.getStateFromStores();
+ }
+ getStateFromStores() {
+ return {
+ search_visible: SearchStore.getSearchResults() != null,
+ post_right_visible: PostStore.getSelectedPost() != null,
+ is_mention_search: SearchStore.getIsMentionSearch()
+ };
}
componentDidMount() {
SearchStore.addSearchChangeListener(this.onSearchChange);
PostStore.addSelectedPostChangeListener(this.onSelectedChange);
+ SearchStore.addShowSearchListener(this.onShowSearch);
this.doStrangeThings();
}
componentWillUnmount() {
SearchStore.removeSearchChangeListener(this.onSearchChange);
PostStore.removeSelectedPostChangeListener(this.onSelectedChange);
+ SearchStore.removeShowSearchListener(this.onShowSearch);
}
componentWillUpdate() {
PostStore.jumpPostsViewSidebarOpen();
@@ -64,18 +70,25 @@ export default class SidebarRight extends React.Component {
this.doStrangeThings();
}
onSelectedChange(fromSearch) {
- var newState = getStateFromStores(fromSearch);
+ var newState = this.getStateFromStores(fromSearch);
newState.from_search = fromSearch;
if (!Utils.areObjectsEqual(newState, this.state)) {
this.setState(newState);
}
}
onSearchChange() {
- var newState = getStateFromStores();
+ var newState = this.getStateFromStores();
if (!Utils.areObjectsEqual(newState, this.state)) {
this.setState(newState);
}
}
+ onShowSearch() {
+ if (!this.state.search_visible) {
+ this.setState({
+ search_visible: true
+ });
+ }
+ }
render() {
var content = '';
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/search_store.jsx b/web/react/stores/search_store.jsx
index 95f0ea845..93b89035c 100644
--- a/web/react/stores/search_store.jsx
+++ b/web/react/stores/search_store.jsx
@@ -14,6 +14,7 @@ var SEARCH_CHANGE_EVENT = 'search_change';
var SEARCH_TERM_CHANGE_EVENT = 'search_term_change';
var MENTION_DATA_CHANGE_EVENT = 'mention_data_change';
var ADD_MENTION_EVENT = 'add_mention';
+var SHOW_SEARCH_EVENT = 'show_search';
class SearchStoreClass extends EventEmitter {
constructor() {
@@ -35,6 +36,10 @@ class SearchStoreClass extends EventEmitter {
this.addMentionDataChangeListener = this.addMentionDataChangeListener.bind(this);
this.removeMentionDataChangeListener = this.removeMentionDataChangeListener.bind(this);
+ this.emitShowSearch = this.emitShowSearch.bind(this);
+ this.addShowSearchListener = this.addShowSearchListener.bind(this);
+ this.removeShowSearchListener = this.removeShowSearchListener.bind(this);
+
this.getSearchResults = this.getSearchResults.bind(this);
this.getIsMentionSearch = this.getIsMentionSearch.bind(this);
@@ -80,6 +85,18 @@ class SearchStoreClass extends EventEmitter {
this.removeListener(SEARCH_TERM_CHANGE_EVENT, callback);
}
+ emitShowSearch() {
+ this.emit(SHOW_SEARCH_EVENT);
+ }
+
+ addShowSearchListener(callback) {
+ this.on(SHOW_SEARCH_EVENT, callback);
+ }
+
+ removeShowSearchListener(callback) {
+ this.removeListener(SHOW_SEARCH_EVENT, callback);
+ }
+
getSearchResults() {
return BrowserStore.getItem('search_results');
}
@@ -146,6 +163,9 @@ SearchStore.dispatchToken = AppDispatcher.register((payload) => {
case ActionTypes.RECIEVED_ADD_MENTION:
SearchStore.emitAddMention(action.id, action.username);
break;
+ case ActionTypes.SHOW_SEARCH:
+ SearchStore.emitShowSearch();
+ break;
default:
}
});
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 6be87254f..80c0cf0ee 100644
--- a/web/react/utils/constants.jsx
+++ b/web/react/utils/constants.jsx
@@ -39,6 +39,8 @@ module.exports = {
RECIEVED_LOGS: null,
RECIEVED_ALL_TEAMS: null,
+ SHOW_SEARCH: null,
+
TOGGLE_IMPORT_THEME_MODAL: null,
TOGGLE_INVITE_MEMBER_MODAL: null,
TOGGLE_DELETE_POST_MODAL: null
diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx
index 5af837822..cd347f7d6 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) {
@@ -562,15 +561,13 @@ export function applyTheme(theme) {
if (theme.sidebarTextActiveColor) {
changeCss('.sidebar--left .nav-pills__container li.active a, .sidebar--left .nav-pills__container li.active a:hover, .sidebar--left .nav-pills__container li.active a:focus, .settings-modal .nav-pills>li.active a, .settings-modal .nav-pills>li.active a:hover, .settings-modal .nav-pills>li.active a:active', 'color:' + theme.sidebarTextActiveColor, 2);
changeCss('.sidebar--left .nav li.active a, .sidebar--left .nav li.active a:hover, .sidebar--left .nav li.active a:focus', 'background:' + changeOpacity(theme.sidebarTextActiveColor, 0.1), 1);
- changeCss('.search-help-popover .search-autocomplete__item:hover', 'background:' + changeOpacity(theme.sidebarTextActiveColor, 0.05), 1);
- changeCss('.search-help-popover .search-autocomplete__item.selected', 'background:' + changeOpacity(theme.sidebarTextActiveColor, 0.15), 1);
}
if (theme.sidebarHeaderBg) {
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 +578,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 +611,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,26 +638,26 @@ 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('@media(min-width: 960px){.post.current--user:hover .post__body ', 'background: none;', 1);
changeCss('.sidebar--right', 'color:' + theme.centerChannelColor, 2);
+ changeCss('.search-help-popover .search-autocomplete__item:hover', 'background:' + changeOpacity(theme.centerChannelColor, 0.05), 1);
+ changeCss('.search-help-popover .search-autocomplete__item.selected', 'background:' + changeOpacity(theme.centerChannelColor, 0.15), 1);
}
if (theme.newMessageSeparator) {