summaryrefslogtreecommitdiffstats
path: root/web/react
diff options
context:
space:
mode:
Diffstat (limited to 'web/react')
-rw-r--r--web/react/components/admin_console/admin_controller.jsx4
-rw-r--r--web/react/components/admin_console/admin_sidebar.jsx6
-rw-r--r--web/react/components/login.jsx2
-rw-r--r--web/react/components/post_body.jsx9
-rw-r--r--web/react/components/search_results.jsx11
-rw-r--r--web/react/components/settings_sidebar.jsx4
-rw-r--r--web/react/components/sidebar_header.jsx3
-rw-r--r--web/react/components/signup_team.jsx100
-rw-r--r--web/react/components/team_general_tab.jsx61
-rw-r--r--web/react/components/tutorial/tutorial_intro_screens.jsx37
-rw-r--r--web/react/utils/markdown.jsx15
-rw-r--r--web/react/utils/utils.jsx26
12 files changed, 185 insertions, 93 deletions
diff --git a/web/react/components/admin_console/admin_controller.jsx b/web/react/components/admin_console/admin_controller.jsx
index d309ced2e..8e0ab0555 100644
--- a/web/react/components/admin_console/admin_controller.jsx
+++ b/web/react/components/admin_console/admin_controller.jsx
@@ -6,6 +6,7 @@ var AdminStore = require('../../stores/admin_store.jsx');
var TeamStore = require('../../stores/team_store.jsx');
var AsyncClient = require('../../utils/async_client.jsx');
var LoadingScreen = require('../loading_screen.jsx');
+var Utils = require('../../utils/utils.jsx');
var EmailSettingsTab = require('./email_settings.jsx');
var LogSettingsTab = require('./log_settings.jsx');
@@ -46,7 +47,8 @@ export default class AdminController extends React.Component {
};
if (!props.tab) {
- history.replaceState(null, null, `/admin_console/${this.state.selected}`);
+ var tokenIndex = Utils.getUrlParameter('session_token_index');
+ history.replaceState(null, null, `/admin_console/${this.state.selected}?session_token_index=${tokenIndex}`);
}
}
diff --git a/web/react/components/admin_console/admin_sidebar.jsx b/web/react/components/admin_console/admin_sidebar.jsx
index f2fb1c96d..0d52ae347 100644
--- a/web/react/components/admin_console/admin_sidebar.jsx
+++ b/web/react/components/admin_console/admin_sidebar.jsx
@@ -3,6 +3,7 @@
var AdminSidebarHeader = require('./admin_sidebar_header.jsx');
var SelectTeamModal = require('./select_team_modal.jsx');
+var Utils = require('../../utils/utils.jsx');
export default class AdminSidebar extends React.Component {
constructor(props) {
@@ -24,12 +25,13 @@ export default class AdminSidebar extends React.Component {
handleClick(name, teamId, e) {
e.preventDefault();
this.props.selectTab(name, teamId);
- history.pushState({name, teamId}, null, `/admin_console/${name}/${teamId || ''}`);
+ var tokenIndex = Utils.getUrlParameter('session_token_index');
+ history.pushState({name, teamId}, null, `/admin_console/${name}/${teamId || ''}?session_token_index=${tokenIndex}`);
}
isSelected(name, teamId) {
if (this.props.selected === name) {
- if (name === 'team_users') {
+ if (name === 'team_users' || name === 'team_analytics') {
if (this.props.selectedTeam != null && this.props.selectedTeam === teamId) {
return 'active';
}
diff --git a/web/react/components/login.jsx b/web/react/components/login.jsx
index c519959af..2b9ce67ca 100644
--- a/web/react/components/login.jsx
+++ b/web/react/components/login.jsx
@@ -185,7 +185,7 @@ export default class Login extends React.Component {
if (this.props.inviteId) {
userSignUp = (
<div>
- <span>{'Do not have an account? '}
+ <span>{`Don't have an account? `}
<a
href={'/signup_user_complete/?id=' + this.props.inviteId}
className='signup-team-login'
diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx
index e1f495d54..e4094daf3 100644
--- a/web/react/components/post_body.jsx
+++ b/web/react/components/post_body.jsx
@@ -77,12 +77,12 @@ export default class PostBody extends React.Component {
this.isGifLoading = true;
const gif = new Image();
- gif.src = src;
gif.onload = (
() => {
this.setState({gifLoaded: true});
}
);
+ gif.src = src;
}
createGifEmbed(link) {
@@ -92,7 +92,12 @@ export default class PostBody extends React.Component {
if (!this.state.gifLoaded) {
this.loadGif(link);
- return null;
+ return (
+ <img
+ className='gif-div placeholder'
+ height='500px'
+ />
+ );
}
return (
diff --git a/web/react/components/search_results.jsx b/web/react/components/search_results.jsx
index ce19c48f0..b56a7b006 100644
--- a/web/react/components/search_results.jsx
+++ b/web/react/components/search_results.jsx
@@ -83,7 +83,16 @@ export default class SearchResults extends React.Component {
var ctls = null;
if (noResults) {
- ctls = <div className='sidebar--right__subheader'>No results</div>;
+ 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>
+ </ul>
+ </div>
+ );
} else {
ctls = results.order.map(function mymap(id) {
var post = results.posts[id];
diff --git a/web/react/components/settings_sidebar.jsx b/web/react/components/settings_sidebar.jsx
index 4af46c35a..68d9cea48 100644
--- a/web/react/components/settings_sidebar.jsx
+++ b/web/react/components/settings_sidebar.jsx
@@ -1,10 +1,14 @@
// 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/components/sidebar_header.jsx b/web/react/components/sidebar_header.jsx
index 3f777d93c..46730e1e6 100644
--- a/web/react/components/sidebar_header.jsx
+++ b/web/react/components/sidebar_header.jsx
@@ -62,6 +62,9 @@ export default class SidebarHeader extends React.Component {
<p>
{'Team administrators can also access their '}<strong>{'Team Settings'}</strong>{' from this menu.'}
</p>
+ <p>
+ {'System administrators will find a '}<strong>{'System Console'}</strong>{' option to administrate the entire system.'}
+ </p>
</div>
);
diff --git a/web/react/components/signup_team.jsx b/web/react/components/signup_team.jsx
index f926f5cbb..37760a2a2 100644
--- a/web/react/components/signup_team.jsx
+++ b/web/react/components/signup_team.jsx
@@ -12,11 +12,6 @@ export default class TeamSignUp extends React.Component {
this.updatePage = this.updatePage.bind(this);
- if (global.window.mm_config.EnableTeamListing === 'true') {
- this.state = {page: 'team_listing'};
- return;
- }
-
var count = 0;
if (global.window.mm_config.EnableSignUpWithEmail === 'true') {
@@ -41,50 +36,83 @@ export default class TeamSignUp extends React.Component {
}
render() {
- if (this.state.page === 'team_listing') {
- return (
- <div>
- <h3>{'Choose a Team'}</h3>
- <div className='signup-team-all'>
- {
- this.props.teams.map((team) => {
- return (
- <div
- key={'team_' + team.name}
- className='signup-team-dir'
- >
- <a
- href={'/' + team.name}
+ var teamListing = null;
+
+ if (global.window.mm_config.EnableTeamListing === 'true') {
+ if (this.props.teams.length === 0) {
+ if (global.window.mm_config.EnableTeamCreation !== 'true') {
+ teamListing = (<div>{'There are no teams include in the Team Directory and team creation has been disabled.'}</div>);
+ }
+ } else {
+ teamListing = (
+ <div>
+ <h3>{'Choose a Team'}</h3>
+ <div className='signup-team-all'>
+ {
+ this.props.teams.map((team) => {
+ return (
+ <div
+ key={'team_' + team.name}
+ className='signup-team-dir'
>
- <div className='signup-team-dir__group'>
- <span className='signup-team-dir__name'>{team.display_name}</span>
- <span
- className='glyphicon glyphicon-menu-right right signup-team-dir__arrow'
- aria-hidden='true'
- />
- </div>
- </a>
- </div>
- );
- })
- }
+ <a
+ href={'/' + team.name}
+ >
+ <div className='signup-team-dir__group'>
+ <span className='signup-team-dir__name'>{team.display_name}</span>
+ <span
+ className='glyphicon glyphicon-menu-right right signup-team-dir__arrow'
+ aria-hidden='true'
+ />
+ </div>
+ </a>
+ </div>
+ );
+ })
+ }
+ </div>
</div>
+ );
+ }
+ }
+
+ if (global.window.mm_config.EnableTeamCreation !== 'true') {
+ if (teamListing == null) {
+ return (<div>{'Team creation has been disabled. Please contact an administrator for access.'}</div>);
+ }
+
+ return (
+ <div>
+ {teamListing}
</div>
);
}
if (this.state.page === 'choose') {
return (
- <ChoosePage
- updatePage={this.updatePage}
- />
+ <div>
+ {teamListing}
+ <ChoosePage
+ updatePage={this.updatePage}
+ />
+ </div>
);
}
if (this.state.page === 'email') {
- return <EmailSignUpPage />;
+ return (
+ <div>
+ {teamListing}
+ <EmailSignUpPage />
+ </div>
+ );
} else if (this.state.page === 'gitlab') {
- return <SSOSignupPage service={Constants.GITLAB_SERVICE} />;
+ return (
+ <div>
+ {teamListing}
+ <SSOSignupPage service={Constants.GITLAB_SERVICE} />
+ </div>
+ );
}
}
}
diff --git a/web/react/components/team_general_tab.jsx b/web/react/components/team_general_tab.jsx
index 69ba44664..587ef5ec2 100644
--- a/web/react/components/team_general_tab.jsx
+++ b/web/react/components/team_general_tab.jsx
@@ -54,7 +54,6 @@ export default class GeneralTab extends React.Component {
handleTeamListingRadio(listing) {
if (global.window.mm_config.EnableTeamListing !== 'true' && listing) {
- ReactDOM.findDOMNode(this.refs.teamListingRadioNo).checked = true;
this.setState({clientError: 'Team directory has been disabled. Please ask a system admin to enable it.'});
} else {
this.setState({allow_team_listing: listing});
@@ -278,13 +277,13 @@ export default class GeneralTab extends React.Component {
</label>
<br/>
</div>
- <div><br/>{'When allowed the team will appear on the main page as part of team directory.'}</div>
+ <div><br/>{'Including this team will display the team name from the Team Directory section of the Home Page, and provide a link to the sign-in page.'}</div>
</div>
];
teamListingSection = (
<SettingItemMax
- title='Allow in Team Directory'
+ title='Include this team in the Team Directory'
inputs={inputs}
submit={this.handleTeamListingSubmit}
server_error={serverError}
@@ -302,7 +301,7 @@ export default class GeneralTab extends React.Component {
teamListingSection = (
<SettingItemMin
- title='Allow in Team Directory'
+ title='Include this team in the Team Directory'
describe={describe}
updateSection={this.onUpdateTeamListingSection}
/>
@@ -337,13 +336,13 @@ export default class GeneralTab extends React.Component {
</label>
<br/>
</div>
- <div><br/>{'When allowed the team signup link will be included on the login page and anyone can signup to this team.'}</div>
+ <div><br/>{'When allowed, a link to account creation will be included on the sign-in page of this team and allow any visitor to sign-up.'}</div>
</div>
];
openInviteSection = (
<SettingItemMax
- title='Allow Open Invitations'
+ title='Allow anyone to sign-up from login page'
inputs={inputs}
submit={this.handleOpenInviteSubmit}
server_error={serverError}
@@ -360,7 +359,7 @@ export default class GeneralTab extends React.Component {
openInviteSection = (
<SettingItemMin
- title='Allow Open Invitations'
+ title='Allow anyone to sign-up from login page'
describe={describe}
updateSection={this.onUpdateOpenInviteSection}
/>
@@ -373,29 +372,28 @@ export default class GeneralTab extends React.Component {
const inputs = [];
inputs.push(
- <div
- key='teamInviteSetting'
- className='form-group'
- >
- <label className='col-sm-5 control-label'>{'Invite Code'}</label>
- <div className='col-sm-7'>
- <input
- className='form-control'
- type='text'
- onChange={this.updateInviteId}
- value={this.state.invite_id}
- maxLength='32'
- />
- </div>
- <div><br/>{'When allowing open invites this code is used as part of the signup process. Changing this code will invalidate the previous open signup link.'}</div>
- <div className='help-text'>
- <button
- className='btn btn-default'
- onClick={this.handleGenerateInviteId}
- >
- {'Re-Generate'}
- </button>
+ <div key='teamInviteSetting'>
+ <div className='row'>
+ <label className='col-sm-5 control-label'>{'Invite Code'}</label>
+ <div className='col-sm-7'>
+ <input
+ className='form-control'
+ type='text'
+ onChange={this.updateInviteId}
+ value={this.state.invite_id}
+ maxLength='32'
+ />
+ <div className='padding-top x2'>
+ <a
+ href='#'
+ onClick={this.handleGenerateInviteId}
+ >
+ {'Re-Generate'}
+ </a>
+ </div>
+ </div>
</div>
+ <div className='setting-list__hint'>{'When allowing open invites this code is used as part of the signup process. Changing this code will invalidate the previous open signup link.'}</div>
</div>
);
@@ -413,7 +411,7 @@ export default class GeneralTab extends React.Component {
inviteSection = (
<SettingItemMin
title={`Invite Code`}
- describe={`Click 'Edit' to re-generate invite Code.`}
+ describe={`Click 'Edit' to regenerate Invite Code.`}
updateSection={this.onUpdateInviteIdSection}
/>
);
@@ -494,8 +492,11 @@ export default class GeneralTab extends React.Component {
<h3 className='tab-header'>{'General Settings'}</h3>
<div className='divider-dark first'/>
{nameSection}
+ <div className='divider-light'/>
{openInviteSection}
+ <div className='divider-light'/>
{teamListingSection}
+ <div className='divider-light'/>
{inviteSection}
<div className='divider-dark'/>
</div>
diff --git a/web/react/components/tutorial/tutorial_intro_screens.jsx b/web/react/components/tutorial/tutorial_intro_screens.jsx
index c7abccae3..a99e9fe28 100644
--- a/web/react/components/tutorial/tutorial_intro_screens.jsx
+++ b/web/react/components/tutorial/tutorial_intro_screens.jsx
@@ -35,6 +35,9 @@ export default class TutorialIntroScreens extends React.Component {
preference = PreferenceStore.setPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), newValue);
AsyncClient.savePreferences([preference]);
}
+ componentDidMount() {
+ $('.tutorials__scroll').perfectScrollbar();
+ }
createScreen() {
switch (this.state.currentScreen) {
case 0:
@@ -50,7 +53,7 @@ export default class TutorialIntroScreens extends React.Component {
<div>
<h3>{'Welcome to:'}</h3>
<h1>{'Mattermost'}</h1>
- <p>{'Your team communications all in one place, instantly searchable and available anywhere.'}</p>
+ <p>{'Your team communication all in one place, instantly searchable and available anywhere.'}</p>
<p>{'Keep your team connected to help them achieve what matters most.'}</p>
<div className='tutorial__circles'>
<div className='circle active'/>
@@ -65,7 +68,7 @@ export default class TutorialIntroScreens extends React.Component {
<div>
<h3>{'How Mattermost works:'}</h3>
<p>{'Communication happens in public discussion channels, private groups and direct messages.'}</p>
- <p>{'Everything is archived and searchable from any web-enabled laptop, tablet or phone.'}</p>
+ <p>{'Everything is archived and searchable from any web-enabled desktop, laptop or phone.'}</p>
<div className='tutorial__circles'>
<div className='circle'/>
<div className='circle active'/>
@@ -120,7 +123,7 @@ export default class TutorialIntroScreens extends React.Component {
</a>
{'.'}
</p>
- {'Click “Next” to enter Town Square. This is the first channel teammates see when they sign up - use it for posting updates everyone needs to know.'}
+ {'Click “Next” to enter Town Square. This is the first channel teammates see when they sign up. Use it for posting updates everyone needs to know.'}
<div className='tutorial__circles'>
<div className='circle'/>
<div className='circle'/>
@@ -130,20 +133,26 @@ export default class TutorialIntroScreens extends React.Component {
);
}
render() {
+ const height = Utils.windowHeight() - 100;
const screen = this.createScreen();
return (
- <div className='tutorial-steps__container'>
- <div className='tutorial__content'>
- <div className='tutorial__steps'>
- {screen}
- <button
- className='btn btn-primary'
- tabIndex='1'
- onClick={this.handleNext}
- >
- {'Next'}
- </button>
+ <div
+ className='tutorials__scroll'
+ style={{height}}
+ >
+ <div className='tutorial-steps__container'>
+ <div className='tutorial__content'>
+ <div className='tutorial__steps'>
+ {screen}
+ <button
+ className='btn btn-primary'
+ tabIndex='1'
+ onClick={this.handleNext}
+ >
+ {'Next'}
+ </button>
+ </div>
</div>
</div>
</div>
diff --git a/web/react/utils/markdown.jsx b/web/react/utils/markdown.jsx
index 179416ea0..f9416b2de 100644
--- a/web/react/utils/markdown.jsx
+++ b/web/react/utils/markdown.jsx
@@ -34,6 +34,11 @@ const highlightJsIni = require('highlight.js/lib/languages/ini.js');
const Constants = require('../utils/constants.jsx');
const HighlightedLanguages = Constants.HighlightedLanguages;
+function markdownImageLoaded(image) {
+ image.style.height = 'auto';
+}
+window.markdownImageLoaded = markdownImageLoaded;
+
class MattermostInlineLexer extends marked.InlineLexer {
constructor(links, options) {
super(links, options);
@@ -132,6 +137,16 @@ class MattermostMarkdownRenderer extends marked.Renderer {
return super.br();
}
+ image(href, title, text) {
+ let out = '<img src="' + href + '" alt="' + text + '"';
+ if (title) {
+ out += ' title="' + title + '"';
+ }
+ out += ' onload="window.markdownImageLoaded(this)" class="markdown-inline-img"';
+ out += this.options.xhtml ? '/>' : '>';
+ return out;
+ }
+
heading(text, level, raw) {
const id = `${this.options.headerPrefix}${raw.toLowerCase().replace(/[^\w]+/g, '-')}`;
return `<h${level} id="${id}" class="markdown__heading">${text}</h${level}>`;
diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx
index 35c6e29b6..e8d34dccd 100644
--- a/web/react/utils/utils.jsx
+++ b/web/react/utils/utils.jsx
@@ -59,6 +59,20 @@ export function isTestDomain() {
return false;
}
+export function isChrome() {
+ if (navigator.userAgent.indexOf('Chrome') > -1) {
+ return true;
+ }
+ return false;
+}
+
+export function isSafari() {
+ if (navigator.userAgent.indexOf('Safari') !== -1 && navigator.userAgent.indexOf('Chrome') === -1) {
+ return true;
+ }
+ return false;
+}
+
export function isInRole(roles, inRole) {
var parts = roles.split(' ');
for (var i = 0; i < parts.length; i++) {
@@ -500,16 +514,16 @@ export function applyTheme(theme) {
changeCss('#post-create', 'background:' + theme.centerChannelBg, 1);
changeCss('.date-separator .separator__text, .new-separator .separator__text', 'background:' + theme.centerChannelBg, 1);
changeCss('.post-image__column .post-image__details', 'background:' + theme.centerChannelBg, 1);
- changeCss('.sidebar--right, .dropdown-menu, .popover', 'background:' + theme.centerChannelBg, 1);
+ changeCss('.sidebar--right, .dropdown-menu, .popover, .tip-overlay', 'background:' + theme.centerChannelBg, 1);
changeCss('.popover.bottom>.arrow:after', 'border-bottom-color:' + theme.centerChannelBg, 1);
- changeCss('.popover.right>.arrow:after', 'border-right-color:' + theme.centerChannelBg, 1);
+ changeCss('.popover.right>.arrow:after, .tip-overlay.tip-overlay--sidebar .arrow, .tip-overlay.tip-overlay--header .arrow', 'border-right-color:' + theme.centerChannelBg, 1);
changeCss('.popover.left>.arrow:after', 'border-left-color:' + theme.centerChannelBg, 1);
- changeCss('.popover.top>.arrow:after', 'border-top-color:' + theme.centerChannelBg, 1);
+ changeCss('.popover.top>.arrow:after, .tip-overlay.tip-overlay--chat .arrow', 'border-top-color:' + theme.centerChannelBg, 1);
changeCss('.search-bar__container .search__form .search-bar, .form-control', 'background:' + theme.centerChannelBg, 1);
}
if (theme.centerChannelColor) {
- 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', 'color:' + theme.centerChannelColor, 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.7), 1);
changeCss('.channel-header__links a:hover, .channel-header__links a:active', 'fill:' + theme.centerChannelColor, 2);
@@ -519,7 +533,7 @@ export function applyTheme(theme) {
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', 'background:' + theme.centerChannelColor, 1);
+ changeCss('.post-body hr, .loading-screen .loading__content .round, .tutorial__circles .circle, .tip-overlay .tutorial__circles .circle.active', '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);
@@ -568,7 +582,7 @@ export function applyTheme(theme) {
}
if (theme.buttonBg) {
- changeCss('.btn.btn-primary', 'background:' + theme.buttonBg, 1);
+ changeCss('.btn.btn-primary, .tutorial__circles .circle.active', 'background:' + theme.buttonBg, 1);
changeCss('.btn.btn-primary:hover, .btn.btn-primary:active, .btn.btn-primary:focus', 'background:' + changeColor(theme.buttonBg, -0.25), 1);
changeCss('.file-playback-controls', 'color:' + changeColor(theme.buttonBg, -0.25), 1);
}