summaryrefslogtreecommitdiffstats
path: root/web
diff options
context:
space:
mode:
Diffstat (limited to 'web')
-rw-r--r--web/react/components/login.jsx6
-rw-r--r--web/react/components/settings_sidebar.jsx4
-rw-r--r--web/react/utils/text_formatting.jsx104
-rw-r--r--web/sass-files/sass/partials/_content.scss5
-rw-r--r--web/sass-files/sass/partials/_headers.scss1
-rw-r--r--web/sass-files/sass/partials/_post.scss18
-rw-r--r--web/sass-files/sass/partials/_signup.scss15
7 files changed, 115 insertions, 38 deletions
diff --git a/web/react/components/login.jsx b/web/react/components/login.jsx
index f7f5bd23d..423ba9067 100644
--- a/web/react/components/login.jsx
+++ b/web/react/components/login.jsx
@@ -125,7 +125,7 @@ export default class Login extends React.Component {
let emailSignup;
if (global.window.mm_config.EnableSignUpWithEmail === 'true') {
emailSignup = (
- <div>
+ <div className='signup__email-container'>
<div className={'form-group' + errorClass}>
<input
autoFocus={focusEmail}
@@ -206,7 +206,7 @@ export default class Login extends React.Component {
href='/'
className='signup-team-login'
>
- {'Sign up now'}
+ {'Create one now'}
</a>
</span>
</div>
@@ -215,11 +215,11 @@ export default class Login extends React.Component {
return (
<div className='signup-team__container'>
- {verifiedBox}
<h5 className='margin--less'>{'Sign in to:'}</h5>
<h2 className='signup-team__name'>{teamDisplayName}</h2>
<h2 className='signup-team__subdomain'>{'on '}{global.window.mm_config.SiteName}</h2>
<form onSubmit={this.handleSubmit}>
+ {verifiedBox}
<div className={'form-group' + errorClass}>
{serverError}
</div>
diff --git a/web/react/components/settings_sidebar.jsx b/web/react/components/settings_sidebar.jsx
index 68d9cea48..4af46c35a 100644
--- a/web/react/components/settings_sidebar.jsx
+++ b/web/react/components/settings_sidebar.jsx
@@ -1,14 +1,10 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-var utils = require('../utils/utils.jsx');
export default class SettingsSidebar extends React.Component {
componentDidUpdate() {
$('.settings-modal').find('.modal-body').scrollTop(0);
$('.settings-modal').find('.modal-body').perfectScrollbar('update');
- if (utils.isSafari()) {
- $('.settings-modal .settings-links .nav').addClass('absolute');
- }
}
constructor(props) {
super(props);
diff --git a/web/react/utils/text_formatting.jsx b/web/react/utils/text_formatting.jsx
index 705d85cf6..7f1d7175d 100644
--- a/web/react/utils/text_formatting.jsx
+++ b/web/react/utils/text_formatting.jsx
@@ -259,30 +259,73 @@ function autolinkHashtags(text, tokens) {
return output.replace(/(^|\W)(#[a-zA-Z][a-zA-Z0-9.\-_]*)\b/g, replaceHashtagWithToken);
}
-function highlightSearchTerm(text, tokens, searchTerm) {
- let output = text;
+const puncStart = /^[.,()&$!\[\]{}':;\\]+/;
+const puncEnd = /[.,()&$#!\[\]{}':;\\]+$/;
- var newTokens = new Map();
- for (const [alias, token] of tokens) {
- if (token.originalText.indexOf(searchTerm.replace(/\*$/, '')) > -1) {
- const index = tokens.size + newTokens.size;
- const newAlias = `MM_SEARCHTERM${index}`;
+function parseSearchTerms(searchTerm) {
+ let terms = [];
- newTokens.set(newAlias, {
- value: `<span class='search-highlight'>${alias}</span>`,
- originalText: token.originalText
- });
+ let termString = searchTerm;
- output = output.replace(alias, newAlias);
+ while (termString) {
+ let captured;
+
+ // check for a quoted string
+ captured = (/^"(.*?)"/).exec(termString);
+ if (captured) {
+ termString = termString.substring(captured[0].length);
+ terms.push(captured[1]);
+ continue;
+ }
+
+ // check for a search flag (and don't add it to terms)
+ captured = (/^(?:in|from|channel): ?\S+/).exec(termString);
+ if (captured) {
+ termString = termString.substring(captured[0].length);
+ continue;
+ }
+
+ // capture any plain text up until the next quote or search flag
+ captured = (/^.+?(?=\bin|\bfrom|\bchannel|"|$)/).exec(termString);
+ if (captured) {
+ termString = termString.substring(captured[0].length);
+
+ // break the text up into words based on how the server splits them in SqlPostStore.SearchPosts and then discard empty terms
+ terms.push(...captured[0].split(/[ <>+\-\(\)\~\@]/).filter((term) => !!term));
+ continue;
}
+
+ // we should never reach this point since at least one of the regexes should match something in the remaining text
+ throw new Error('Infinite loop in search term parsing: ' + termString);
}
- // the new tokens are stashed in a separate map since we can't add objects to a map during iteration
- for (const newToken of newTokens) {
- tokens.set(newToken[0], newToken[1]);
+ // remove punctuation from each term
+ terms = terms.map((term) => term.replace(puncStart, '').replace(puncEnd, ''));
+
+ return terms;
+}
+
+function convertSearchTermToRegex(term) {
+ let pattern;
+ if (term.endsWith('*')) {
+ pattern = '\\b' + escapeRegex(term.substring(0, term.length - 1));
+ } else {
+ pattern = '\\b' + escapeRegex(term) + '\\b';
}
- function replaceSearchTermWithToken(fullMatch, prefix, word) {
+ return new RegExp(pattern, 'gi');
+}
+
+function highlightSearchTerm(text, tokens, searchTerm) {
+ const terms = parseSearchTerms(searchTerm);
+
+ if (terms.length === 0) {
+ return text;
+ }
+
+ let output = text;
+
+ function replaceSearchTermWithToken(word) {
const index = tokens.size;
const alias = `MM_SEARCHTERM${index}`;
@@ -291,10 +334,35 @@ function highlightSearchTerm(text, tokens, searchTerm) {
originalText: word
});
- return prefix + alias;
+ return alias;
}
- return output.replace(new RegExp(`()(${escapeRegex(searchTerm)})`, 'gi'), replaceSearchTermWithToken);
+ for (const term of terms) {
+ // highlight existing tokens matching search terms
+ var newTokens = new Map();
+ for (const [alias, token] of tokens) {
+ if (token.originalText === term.replace(/\*$/, '')) {
+ const index = tokens.size + newTokens.size;
+ const newAlias = `MM_SEARCHTERM${index}`;
+
+ newTokens.set(newAlias, {
+ value: `<span class='search-highlight'>${alias}</span>`,
+ originalText: token.originalText
+ });
+
+ output = output.replace(alias, newAlias);
+ }
+ }
+
+ // the new tokens are stashed in a separate map since we can't add objects to a map during iteration
+ for (const newToken of newTokens) {
+ tokens.set(newToken[0], newToken[1]);
+ }
+
+ output = output.replace(convertSearchTermToRegex(term), replaceSearchTermWithToken);
+ }
+
+ return output;
}
function replaceTokens(text, tokens) {
diff --git a/web/sass-files/sass/partials/_content.scss b/web/sass-files/sass/partials/_content.scss
index d86e225f3..6228cc45e 100644
--- a/web/sass-files/sass/partials/_content.scss
+++ b/web/sass-files/sass/partials/_content.scss
@@ -18,14 +18,15 @@
margin-left: 220px;
position: relative;
background: #fff;
- display: flex;
+ @include display-flex;
+ @include flex-direction(column);
flex-direction: column;
.channel__wrap & {
padding-top: 0;
}
}
#post-create {
- flex: 0 0 auto;
+ @include flex(0 0 auto);
background: #fff;
width: 100%;
z-index: 3;
diff --git a/web/sass-files/sass/partials/_headers.scss b/web/sass-files/sass/partials/_headers.scss
index 74a7cecff..67c938b8c 100644
--- a/web/sass-files/sass/partials/_headers.scss
+++ b/web/sass-files/sass/partials/_headers.scss
@@ -2,6 +2,7 @@
padding: 3px 0;
height: 58px;
flex: 0 0 58px;
+ @include flex(0 0 50px);
}
.row {
&.header {
diff --git a/web/sass-files/sass/partials/_post.scss b/web/sass-files/sass/partials/_post.scss
index 33748052d..ef30cb213 100644
--- a/web/sass-files/sass/partials/_post.scss
+++ b/web/sass-files/sass/partials/_post.scss
@@ -46,7 +46,6 @@ body.ios {
.textarea-wrapper {
position:relative;
- min-height: 36px;
.textbox-preview-area {
position: absolute;
z-index: 2;
@@ -62,6 +61,7 @@ body.ios {
font-size: 13px;
cursor: pointer;
}
+ min-height:36px;
}
.date-separator, .new-separator {
@@ -197,7 +197,7 @@ body.ios {
}
#post-list {
- flex: 1 1 auto;
+ @include flex(1 1 auto);
position: relative;
overflow-y: hidden;
.post-list-holder-by-time {
@@ -544,9 +544,13 @@ body.ios {
}
&.post-info {
.post-profile-time {
- width: 150px;
- display: inline-block;
- margin-left: 50px;
+ color: #a8adb7;
+ vertical-align: top;
+ max-width: 220px;
+ overflow: hidden;
+ display: block;
+ white-space: nowrap;
+ text-overflow: ellipsis;
}
}
.post-header-col {
@@ -554,6 +558,8 @@ body.ios {
display: inline-block;
margin-right: 10px;
&.post-header__reply {
+ position: relative;
+ top: -1px;
min-width: 70px;
.dropdown-menu {
right: 0;
@@ -706,4 +712,4 @@ body.ios {
margin: 0;
}
}
-}
+} \ No newline at end of file
diff --git a/web/sass-files/sass/partials/_signup.scss b/web/sass-files/sass/partials/_signup.scss
index b9486e254..6216dd9ae 100644
--- a/web/sass-files/sass/partials/_signup.scss
+++ b/web/sass-files/sass/partials/_signup.scss
@@ -80,7 +80,7 @@
}
.inner__content {
- padding: 0 15px;
+ padding: 0 1rem;
margin: 30px 0 20px;
}
@@ -133,17 +133,18 @@
.or__container {
height: 1px;
background: #dddddd;
- text-align: center;
+ text-align: left;
margin: 2em 0;
span {
- width: 33px;
+ width: 40px;
top: -10px;
position: relative;
- font-size: em(16px);
+ font-size: 1.14286em;
line-height: 20px;
font-weight: 600;
background: #fff;
display: inline-block;
+ text-align: center;
}
}
@@ -152,6 +153,10 @@
padding-left: 18px;
}
+ .signup__email-container {
+ margin-left: 1rem;
+ }
+
.btn {
font-size: 1em;
padding: em(7px) em(15px);
@@ -173,7 +178,7 @@
min-width: 200px;
width: 200px;
padding: 0 1em;
- margin: 1em auto;
+ margin: 1em 1rem;
height: 40px;
line-height: 34px;
color: #fff;