summaryrefslogtreecommitdiffstats
path: root/webapp/components
diff options
context:
space:
mode:
Diffstat (limited to 'webapp/components')
-rw-r--r--webapp/components/about_build_modal.jsx133
-rw-r--r--webapp/components/admin_console/compliance_settings.jsx2
-rw-r--r--webapp/components/admin_console/service_settings.jsx2
-rw-r--r--webapp/components/admin_console/user_item.jsx2
-rw-r--r--webapp/components/analytics/team_analytics.jsx2
-rw-r--r--webapp/components/backstage/add_incoming_webhook.jsx166
-rw-r--r--webapp/components/backstage/add_integration.jsx20
-rw-r--r--webapp/components/backstage/add_integration_option.jsx8
-rw-r--r--webapp/components/backstage/add_outgoing_webhook.jsx240
-rw-r--r--webapp/components/backstage/backstage_category.jsx2
-rw-r--r--webapp/components/backstage/backstage_navbar.jsx4
-rw-r--r--webapp/components/backstage/backstage_sidebar.jsx2
-rw-r--r--webapp/components/backstage/installed_incoming_webhook.jsx16
-rw-r--r--webapp/components/backstage/installed_integrations.jsx42
-rw-r--r--webapp/components/backstage/installed_outgoing_webhook.jsx14
-rw-r--r--webapp/components/error_page.jsx58
-rw-r--r--webapp/components/post.jsx23
-rw-r--r--webapp/components/post_body_additional_content.jsx2
-rw-r--r--webapp/components/posts_view.jsx6
-rw-r--r--webapp/components/rhs_root_post.jsx13
-rw-r--r--webapp/components/search_results_item.jsx35
-rw-r--r--webapp/components/sidebar.jsx24
-rw-r--r--webapp/components/suggestion/at_mention_provider.jsx15
-rw-r--r--webapp/components/tutorial/tutorial_intro_screens.jsx16
-rw-r--r--webapp/components/tutorial/tutorial_view.jsx31
-rw-r--r--webapp/components/user_settings/user_settings_display.jsx2
-rw-r--r--webapp/components/user_settings/user_settings_security.jsx3
27 files changed, 540 insertions, 343 deletions
diff --git a/webapp/components/about_build_modal.jsx b/webapp/components/about_build_modal.jsx
index a47225f7e..4fd946401 100644
--- a/webapp/components/about_build_modal.jsx
+++ b/webapp/components/about_build_modal.jsx
@@ -6,6 +6,7 @@ import {Modal} from 'react-bootstrap';
import {FormattedMessage} from 'react-intl';
import React from 'react';
+import Constants from 'utils/constants.jsx';
export default class AboutBuildModal extends React.Component {
constructor(props) {
@@ -20,6 +21,7 @@ export default class AboutBuildModal extends React.Component {
render() {
const config = global.window.mm_config;
const license = global.window.mm_license;
+ const mattermostLogo = Constants.MATTERMOST_ICON_SVG;
let title = (
<FormattedMessage
@@ -28,6 +30,28 @@ export default class AboutBuildModal extends React.Component {
/>
);
+ let subTitle = (
+ <FormattedMessage
+ id='about.teamEditionSt'
+ defaultMessage='All your team communication in one place, instantly searchable and accessible anywhere.'
+ />
+ );
+
+ let learnMore = (
+ <div>
+ <FormattedMessage
+ id='about.teamEditionLearn'
+ defaultMessage='Join the Mattermost community at '
+ />
+ <a
+ target='_blank'
+ href='http://www.mattermost.org/'
+ >
+ {'mattermost.org'}
+ </a>
+ </div>
+ );
+
let licensee;
if (config.BuildEnterpriseReady === 'true') {
title = (
@@ -36,6 +60,29 @@ export default class AboutBuildModal extends React.Component {
defaultMessage='Enterprise Edition'
/>
);
+
+ subTitle = (
+ <FormattedMessage
+ id='about.enterpriseEditionSt'
+ defaultMessage='Modern enterprise communication from behind your firewall.'
+ />
+ );
+
+ learnMore = (
+ <div>
+ <FormattedMessage
+ id='about.enterpriseEditionLearn'
+ defaultMessage='Learn more about Enterprise Edition at '
+ />
+ <a
+ target='_blank'
+ href='http://about.mattermost.com/'
+ >
+ {'about.mattermost.com'}
+ </a>
+ </div>
+ );
+
if (license.IsLicensed === 'true') {
title = (
<FormattedMessage
@@ -44,14 +91,12 @@ export default class AboutBuildModal extends React.Component {
/>
);
licensee = (
- <div className='row form-group'>
- <div className='col-sm-3 info__label'>
- <FormattedMessage
- id='about.licensed'
- defaultMessage='Licensed by:'
- />
- </div>
- <div className='col-sm-9'>{license.Company}</div>
+ <div className='form-group'>
+ <FormattedMessage
+ id='about.licensed'
+ defaultMessage='Licensed by:'
+ />
+ &nbsp;{license.Company}
</div>
);
}
@@ -59,6 +104,7 @@ export default class AboutBuildModal extends React.Component {
return (
<Modal
+ dialogClassName='about-modal'
show={this.props.show}
onHide={this.doHide}
>
@@ -71,57 +117,54 @@ export default class AboutBuildModal extends React.Component {
</Modal.Title>
</Modal.Header>
<Modal.Body>
- <h4 className='padding-bottom x2'>{'Mattermost'} {title}</h4>
- {licensee}
- <div className='row form-group'>
- <div className='col-sm-3 info__label'>
- <FormattedMessage
- id='about.version'
- defaultMessage='Version:'
+ <div className='about-modal__content'>
+ <div className='about-modal__logo'>
+ <span
+ className='icon'
+ dangerouslySetInnerHTML={{__html: mattermostLogo}}
/>
</div>
- <div className='col-sm-9'>{config.Version}</div>
- </div>
- <div className='row form-group'>
- <div className='col-sm-3 info__label'>
- <FormattedMessage
- id='about.number'
- defaultMessage='Build Number:'
- />
+ <div>
+ <h3 className='about-modal__title'>{'Mattermost'} {title}</h3>
+ <p className='about-modal__subtitle padding-bottom'>{subTitle}</p>
+ <div className='form-group less'>
+ <div>
+ <FormattedMessage
+ id='about.version'
+ defaultMessage='Version:'
+ />
+ &nbsp;{config.Version}&nbsp;({config.BuildNumber})
+ </div>
+ </div>
+ {licensee}
</div>
- <div className='col-sm-9'>{config.BuildNumber}</div>
</div>
- <div className='row form-group'>
- <div className='col-sm-3 info__label'>
+ <div className='about-modal__footer'>
+ {learnMore}
+ <div className='form-group about-modal__copyright'>
<FormattedMessage
- id='about.date'
- defaultMessage='Build Date:'
+ id='about.copyright'
+ defaultMessage='Copyright 2016 Mattermost, Inc. All rights reserved'
/>
</div>
- <div className='col-sm-9'>{config.BuildDate}</div>
</div>
- <div className='row form-group'>
- <div className='col-sm-3 info__label'>
+ <div className='about-modal__hash form-group padding-top x2'>
+ <p>
<FormattedMessage
id='about.hash'
defaultMessage='Build Hash:'
/>
- </div>
- <div className='col-sm-9'>{config.BuildHash}</div>
+ &nbsp;{config.BuildHash}
+ </p>
+ <p>
+ <FormattedMessage
+ id='about.date'
+ defaultMessage='Build Date:'
+ />
+ &nbsp;{config.BuildDate}
+ </p>
</div>
</Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.doHide}
- >
- <FormattedMessage
- id='about.close'
- defaultMessage='Close'
- />
- </button>
- </Modal.Footer>
</Modal>
);
}
diff --git a/webapp/components/admin_console/compliance_settings.jsx b/webapp/components/admin_console/compliance_settings.jsx
index fb2ae26f9..206bb0faa 100644
--- a/webapp/components/admin_console/compliance_settings.jsx
+++ b/webapp/components/admin_console/compliance_settings.jsx
@@ -223,7 +223,7 @@ export default class ComplianceSettings extends React.Component {
</label>
<p className='help-text'>
<FormattedMessage
- id='admin.compliance.enableDesc'
+ id='admin.compliance.enableDailyDesc'
defaultMessage='When true, Mattermost will generate a daily compliance report.'
/>
</p>
diff --git a/webapp/components/admin_console/service_settings.jsx b/webapp/components/admin_console/service_settings.jsx
index c72c97326..2c3f4081c 100644
--- a/webapp/components/admin_console/service_settings.jsx
+++ b/webapp/components/admin_console/service_settings.jsx
@@ -87,7 +87,7 @@ class ServiceSettings extends React.Component {
config.ServiceSettings.EnableCommands = ReactDOM.findDOMNode(this.refs.EnableCommands).checked;
config.ServiceSettings.EnableOnlyAdminIntegrations = ReactDOM.findDOMNode(this.refs.EnableOnlyAdminIntegrations).checked;
- if (this.refs.EnablMultifactorAuthentication) {
+ if (this.refs.EnableMultifactorAuthentication) {
config.ServiceSettings.EnableMultifactorAuthentication = ReactDOM.findDOMNode(this.refs.EnableMultifactorAuthentication).checked;
}
diff --git a/webapp/components/admin_console/user_item.jsx b/webapp/components/admin_console/user_item.jsx
index 91f567d4d..c00050584 100644
--- a/webapp/components/admin_console/user_item.jsx
+++ b/webapp/components/admin_console/user_item.jsx
@@ -333,7 +333,7 @@ export default class UserItem extends React.Component {
<div>
<FormattedMessage
id='admin.user_item.confirmDemoteDescription'
- defaultMessage="If you demote yourself from the System Admin role and there is not another user with System Admin privileges, you\'ll need to re-assign a System Admin by accessing the Mattermost server through a terminal and running the following command."
+ defaultMessage="If you demote yourself from the System Admin role and there is not another user with System Admin privileges, you'll need to re-assign a System Admin by accessing the Mattermost server through a terminal and running the following command."
/>
<br/>
<br/>
diff --git a/webapp/components/analytics/team_analytics.jsx b/webapp/components/analytics/team_analytics.jsx
index efc965f24..9b4eb1f94 100644
--- a/webapp/components/analytics/team_analytics.jsx
+++ b/webapp/components/analytics/team_analytics.jsx
@@ -154,7 +154,7 @@ class TeamAnalytics extends React.Component {
<TableChart
title={
<FormattedMessage
- id='analytics.team.activeUsers'
+ id='analytics.team.recentUsers'
defaultMessage='Recent Active Users'
/>
}
diff --git a/webapp/components/backstage/add_incoming_webhook.jsx b/webapp/components/backstage/add_incoming_webhook.jsx
index fa7531fc6..83027c6b3 100644
--- a/webapp/components/backstage/add_incoming_webhook.jsx
+++ b/webapp/components/backstage/add_incoming_webhook.jsx
@@ -96,10 +96,10 @@ export default class AddIncomingWebhook extends React.Component {
render() {
return (
- <div className='backstage row'>
+ <div className='backstage-content row'>
<div className='add-incoming-webhook'>
- <div className='backstage__header'>
- <h1 className='text'>
+ <div className='backstage-header'>
+ <h1>
<FormattedMessage
id='add_incoming_webhook.header'
defaultMessage='Add Incoming Webhook'
@@ -107,81 +107,91 @@ export default class AddIncomingWebhook extends React.Component {
</h1>
</div>
</div>
- <form className='add-incoming-webhook__body'>
- <div className='add-integration__row'>
- <label
- className='add-integration__label'
- htmlFor='name'
- >
- <FormattedMessage
- id='add_incoming_webhook.name'
- defaultMessage='Name'
- />
- </label>
- <input
- id='name'
- type='text'
- value={this.state.name}
- onChange={this.updateName}
- />
- </div>
- <div className='add-integration__row'>
- <label
- className='add-integration__label'
- htmlFor='description'
- >
- <FormattedMessage
- id='add_incoming_webhook.description'
- defaultMessage='Description'
- />
- </label>
- <input
- id='description'
- type='text'
- value={this.state.description}
- onChange={this.updateDescription}
- />
- </div>
- <div className='add-integration__row'>
- <label
- className='add-integration__label'
- htmlFor='channelId'
- >
- <FormattedMessage
- id='add_incoming_webhook.channel'
- defaultMessage='Channel'
- />
- </label>
- <ChannelSelect
- id='channelId'
- value={this.state.channelId}
- onChange={this.updateChannelId}
- />
- </div>
- <div className='add-integration__submit-row'>
- <Link
- className='btn btn-sm'
- to={'/settings/integrations/add'}
- >
- <FormattedMessage
- id='add_incoming_webhook.cancel'
- defaultMessage='Cancel'
- />
- </Link>
- <SpinnerButton
- className='btn btn-primary'
- type='submit'
- spinning={this.state.saving}
- onClick={this.handleSubmit}
- >
- <FormattedMessage
- id='add_incoming_webhook.save'
- defaultMessage='Save'
- />
- </SpinnerButton>
- </div>
- <FormError errors={[this.state.serverError, this.state.clientError]}/>
- </form>
+ <div className='backstage-form'>
+ <form className='form-horizontal'>
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-3'
+ htmlFor='name'
+ >
+ <FormattedMessage
+ id='add_incoming_webhook.name'
+ defaultMessage='Name'
+ />
+ </label>
+ <div className='col-md-5 col-sm-9'>
+ <input
+ id='name'
+ type='text'
+ className='form-control'
+ value={this.state.name}
+ onChange={this.updateName}
+ />
+ </div>
+ </div>
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-3'
+ htmlFor='description'
+ >
+ <FormattedMessage
+ id='add_incoming_webhook.description'
+ defaultMessage='Description'
+ />
+ </label>
+ <div className='col-md-5 col-sm-9'>
+ <input
+ id='description'
+ type='text'
+ className='form-control'
+ value={this.state.description}
+ onChange={this.updateDescription}
+ />
+ </div>
+ </div>
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-3'
+ htmlFor='channelId'
+ >
+ <FormattedMessage
+ id='add_incoming_webhook.channel'
+ defaultMessage='Channel'
+ />
+ </label>
+ <div className='col-md-5 col-sm-9'>
+ <ChannelSelect
+ id='channelId'
+ value={this.state.channelId}
+ onChange={this.updateChannelId}
+ />
+ </div>
+ </div>
+ <div className='backstage-form__footer'>
+ <FormError errors={[this.state.serverError, this.state.clientError]}/>
+ <Link
+ className='btn btn-sm'
+ to={'/settings/integrations/add'}
+ >
+ <FormattedMessage
+ id='add_incoming_webhook.cancel'
+ defaultMessage='Cancel'
+ />
+ </Link>
+ <SpinnerButton
+ className='btn btn-primary'
+ type='submit'
+ spinning={this.state.saving}
+ onClick={this.handleSubmit}
+ >
+ <FormattedMessage
+ id='add_incoming_webhook.save'
+ defaultMessage='Save'
+ />
+ </SpinnerButton>
+ </div>
+ </form>
+ </div>
</div>
);
}
diff --git a/webapp/components/backstage/add_integration.jsx b/webapp/components/backstage/add_integration.jsx
index cebc1e8b0..5f4a69bfe 100644
--- a/webapp/components/backstage/add_integration.jsx
+++ b/webapp/components/backstage/add_integration.jsx
@@ -57,18 +57,16 @@ export default class AddIntegration extends React.Component {
}
return (
- <div className='backstage row'>
- <div className='add-integration'>
- <div className='backstage__header'>
- <h1 className='text'>
- <FormattedMessage
- id='add_integration.header'
- defaultMessage='Add Integration'
- />
- </h1>
- </div>
+ <div className='backstage-content row'>
+ <div className='backstage-header'>
+ <h1>
+ <FormattedMessage
+ id='add_integration.header'
+ defaultMessage='Add Integration'
+ />
+ </h1>
</div>
- <div className='add-integration__options'>
+ <div>
{options}
</div>
</div>
diff --git a/webapp/components/backstage/add_integration_option.jsx b/webapp/components/backstage/add_integration_option.jsx
index 3c3caf2f4..b17ebb185 100644
--- a/webapp/components/backstage/add_integration_option.jsx
+++ b/webapp/components/backstage/add_integration_option.jsx
@@ -21,16 +21,16 @@ export default class AddIntegrationOption extends React.Component {
return (
<Link
to={link}
- className='add-integration-option'
+ className='add-integration'
>
<img
- className='add-integration-option__image'
+ className='add-integration__image'
src={image}
/>
- <div className='add-integration-option__title'>
+ <div className='add-integration__title'>
{title}
</div>
- <div className='add-integration-option__description'>
+ <div className='add-integration__description'>
{description}
</div>
</Link>
diff --git a/webapp/components/backstage/add_outgoing_webhook.jsx b/webapp/components/backstage/add_outgoing_webhook.jsx
index 3ae2f8606..5d98138df 100644
--- a/webapp/components/backstage/add_outgoing_webhook.jsx
+++ b/webapp/components/backstage/add_outgoing_webhook.jsx
@@ -128,10 +128,10 @@ export default class AddOutgoingWebhook extends React.Component {
render() {
return (
- <div className='backstage row'>
+ <div className='backstage-content row'>
<div className='add-outgoing-webhook'>
- <div className='backstage__header'>
- <h1 className='text'>
+ <div className='backstage-header'>
+ <h1>
<FormattedMessage
id='add_outgoing_webhook.header'
defaultMessage='Add Outgoing Webhook'
@@ -139,115 +139,131 @@ export default class AddOutgoingWebhook extends React.Component {
</h1>
</div>
</div>
- <form className='add-outgoing-webhook__body'>
- <div className='add-integration__row'>
- <label
- className='add-integration__label'
- htmlFor='name'
- >
- <FormattedMessage
- id='add_outgoing_webhook.name'
- defaultMessage='Name'
- />
- </label>
- <input
- id='name'
- type='text'
- value={this.state.name}
- onChange={this.updateName}
- />
- </div>
- <div className='add-integration__row'>
- <label
- className='add-integration__label'
- htmlFor='description'
- >
- <FormattedMessage
- id='add_outgoing_webhook.description'
- defaultMessage='Description'
- />
- </label>
- <input
- id='description'
- type='text'
- value={this.state.description}
- onChange={this.updateDescription}
- />
- </div>
- <div className='add-integration__row'>
- <label
- className='add-integration__label'
- htmlFor='channelId'
- >
- <FormattedMessage
- id='add_outgoing_webhook.channel'
- defaultMessage='Channel'
- />
- </label>
- <ChannelSelect
- id='channelId'
- value={this.state.channelId}
- onChange={this.updateChannelId}
- />
- </div>
- <div className='add-integration__row'>
- <label
- className='add-integration__label'
- htmlFor='triggerWords'
- >
- <FormattedMessage
- id='add_outgoing_webhook.triggerWords'
- defaultMessage='Trigger Words (One Per Line)'
- />
- </label>
- <textarea
- id='triggerWords'
- rows='3'
- value={this.state.triggerWords}
- onChange={this.updateTriggerWords}
- />
- </div>
- <div className='add-integration__row'>
- <label
- className='add-integration__label'
- htmlFor='callbackUrls'
- >
- <FormattedMessage
- id='add_outgoing_webhook.callbackUrls'
- defaultMessage='Callback URLs (One Per Line)'
- />
- </label>
- <textarea
- id='callbackUrls'
- rows='3'
- value={this.state.callbackUrls}
- onChange={this.updateCallbackUrls}
- />
- </div>
- <div className='add-integration__submit-row'>
- <Link
- className='btn btn-sm'
- to={'/settings/integrations/add'}
- >
- <FormattedMessage
- id='add_outgoing_webhook.cancel'
- defaultMessage='Cancel'
- />
- </Link>
- <SpinnerButton
- className='btn btn-primary'
- type='submit'
- spinning={this.state.saving}
- onClick={this.handleSubmit}
- >
- <FormattedMessage
- id='add_outgoing_webhook.save'
- defaultMessage='Save'
- />
- </SpinnerButton>
- </div>
- <FormError errors={[this.state.serverError, this.state.clientError]}/>
- </form>
+ <div className='backstage-form'>
+ <form className='form-horizontal'>
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-3'
+ htmlFor='name'
+ >
+ <FormattedMessage
+ id='add_outgoing_webhook.name'
+ defaultMessage='Name'
+ />
+ </label>
+ <div className='col-md-5 col-sm-9'>
+ <input
+ id='name'
+ type='text'
+ className='form-control'
+ value={this.state.name}
+ onChange={this.updateName}
+ />
+ </div>
+ </div>
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-3'
+ htmlFor='description'
+ >
+ <FormattedMessage
+ id='add_outgoing_webhook.description'
+ defaultMessage='Description'
+ />
+ </label>
+ <div className='col-md-5 col-sm-9'>
+ <input
+ id='description'
+ type='text'
+ className='form-control'
+ value={this.state.description}
+ onChange={this.updateDescription}
+ />
+ </div>
+ </div>
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-3'
+ htmlFor='channelId'
+ >
+ <FormattedMessage
+ id='add_outgoing_webhook.channel'
+ defaultMessage='Channel'
+ />
+ </label>
+ <div className='col-md-5 col-sm-9'>
+ <ChannelSelect
+ id='channelId'
+ value={this.state.channelId}
+ onChange={this.updateChannelId}
+ />
+ </div>
+ </div>
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-3'
+ htmlFor='triggerWords'
+ >
+ <FormattedMessage
+ id='add_outgoing_webhook.triggerWords'
+ defaultMessage='Trigger Words (One Per Line)'
+ />
+ </label>
+ <div className='col-md-5 col-sm-9'>
+ <textarea
+ id='triggerWords'
+ rows='3'
+ className='form-control'
+ value={this.state.triggerWords}
+ onChange={this.updateTriggerWords}
+ />
+ </div>
+ </div>
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-3'
+ htmlFor='callbackUrls'
+ >
+ <FormattedMessage
+ id='add_outgoing_webhook.callbackUrls'
+ defaultMessage='Callback URLs (One Per Line)'
+ />
+ </label>
+ <div className='col-md-5 col-sm-9'>
+ <textarea
+ id='callbackUrls'
+ rows='3'
+ className='form-control'
+ value={this.state.callbackUrls}
+ onChange={this.updateCallbackUrls}
+ />
+ </div>
+ </div>
+ <div className='backstage-form__footer'>
+ <FormError errors={[this.state.serverError, this.state.clientError]}/>
+ <Link
+ className='btn btn-sm'
+ to={'/settings/integrations/add'}
+ >
+ <FormattedMessage
+ id='add_outgoing_webhook.cancel'
+ defaultMessage='Cancel'
+ />
+ </Link>
+ <SpinnerButton
+ className='btn btn-primary'
+ type='submit'
+ spinning={this.state.saving}
+ onClick={this.handleSubmit}
+ >
+ <FormattedMessage
+ id='add_outgoing_webhook.save'
+ defaultMessage='Save'
+ />
+ </SpinnerButton>
+ </div>
+ </form>
+ </div>
</div>
);
}
diff --git a/webapp/components/backstage/backstage_category.jsx b/webapp/components/backstage/backstage_category.jsx
index e8b0b57ae..913c7562c 100644
--- a/webapp/components/backstage/backstage_category.jsx
+++ b/webapp/components/backstage/backstage_category.jsx
@@ -50,7 +50,7 @@ export default class BackstageCategory extends React.Component {
}
return (
- <li className='backstage__sidebar__category'>
+ <li className='backstage-sidebar__category'>
<Link
to={link}
className='category-title'
diff --git a/webapp/components/backstage/backstage_navbar.jsx b/webapp/components/backstage/backstage_navbar.jsx
index 555165791..d1dac6043 100644
--- a/webapp/components/backstage/backstage_navbar.jsx
+++ b/webapp/components/backstage/backstage_navbar.jsx
@@ -39,9 +39,9 @@ export default class BackstageNavbar extends React.Component {
}
return (
- <div className='backstage__navbar row'>
+ <div className='backstage-navbar row'>
<Link
- className='backstage__navbar__back'
+ className='backstage-navbar__back'
to={`/${this.state.team.display_name}/channels/town-square`}
>
<i className='fa fa-angle-left'/>
diff --git a/webapp/components/backstage/backstage_sidebar.jsx b/webapp/components/backstage/backstage_sidebar.jsx
index 63a0df5cb..13c4f8b50 100644
--- a/webapp/components/backstage/backstage_sidebar.jsx
+++ b/webapp/components/backstage/backstage_sidebar.jsx
@@ -10,7 +10,7 @@ import {FormattedMessage} from 'react-intl';
export default class BackstageSidebar extends React.Component {
render() {
return (
- <div className='backstage__sidebar'>
+ <div className='backstage-sidebar'>
<ul>
<BackstageCategory
name='integrations'
diff --git a/webapp/components/backstage/installed_incoming_webhook.jsx b/webapp/components/backstage/installed_incoming_webhook.jsx
index 4ca421a02..f65cf6327 100644
--- a/webapp/components/backstage/installed_incoming_webhook.jsx
+++ b/webapp/components/backstage/installed_incoming_webhook.jsx
@@ -35,26 +35,26 @@ export default class InstalledIncomingWebhook extends React.Component {
const channelName = channel ? channel.display_name : 'cannot find channel';
return (
- <div className='installed-integrations__item installed-integrations__incoming-webhook'>
- <div className='details'>
- <div className='details-row'>
- <span className='name'>
+ <div className='backstage-list__item'>
+ <div className='item-details'>
+ <div className='item-details__row'>
+ <span className='item-details__name'>
{channelName}
</span>
- <span className='type'>
+ <span className='item-details__type'>
<FormattedMessage
id='installed_integrations.incomingWebhookType'
defaultMessage='(Incoming Webhook)'
/>
</span>
</div>
- <div className='details-row'>
- <span className='description'>
+ <div className='item-details__row'>
+ <span className='item-details__description'>
{Utils.getWindowLocationOrigin() + '/hooks/' + incomingWebhook.id}
</span>
</div>
</div>
- <div className='actions'>
+ <div className='item-actions'>
<a
href='#'
onClick={this.handleDeleteClick}
diff --git a/webapp/components/backstage/installed_integrations.jsx b/webapp/components/backstage/installed_integrations.jsx
index ff0b6e4ec..fe84ae81a 100644
--- a/webapp/components/backstage/installed_integrations.jsx
+++ b/webapp/components/backstage/installed_integrations.jsx
@@ -98,9 +98,9 @@ export default class InstalledIntegrations extends React.Component {
const fields = [];
if (incomingWebhooks.length > 0 || outgoingWebhooks.length > 0) {
- let filterClassName = 'type-filter';
+ let filterClassName = 'filter-sort';
if (this.state.typeFilter === '') {
- filterClassName += ' type-filter--selected';
+ filterClassName += ' filter-sort--active';
}
fields.push(
@@ -131,9 +131,9 @@ export default class InstalledIntegrations extends React.Component {
</span>
);
- let filterClassName = 'type-filter';
+ let filterClassName = 'filter-sort';
if (this.state.typeFilter === 'incomingWebhooks') {
- filterClassName += ' type-filter--selected';
+ filterClassName += ' filter-sort--active';
}
fields.push(
@@ -164,9 +164,9 @@ export default class InstalledIntegrations extends React.Component {
</span>
);
- let filterClassName = 'type-filter';
+ let filterClassName = 'filter-sort';
if (this.state.typeFilter === 'outgoingWebhooks') {
- filterClassName += ' type-filter--selected';
+ filterClassName += ' filter-sort--active';
}
fields.push(
@@ -188,7 +188,7 @@ export default class InstalledIntegrations extends React.Component {
}
return (
- <div className='type-filters'>
+ <div className='backstage-filters__sort'>
{fields}
</div>
);
@@ -243,10 +243,10 @@ export default class InstalledIntegrations extends React.Component {
}
return (
- <div className='backstage row'>
+ <div className='backstage-content row'>
<div className='installed-integrations'>
- <div className='backstage__header'>
- <h1 className='text'>
+ <div className='backstage-header'>
+ <h1>
<FormattedMessage
id='installed_integrations.header'
defaultMessage='Installed Integrations'
@@ -269,17 +269,21 @@ export default class InstalledIntegrations extends React.Component {
</button>
</Link>
</div>
- <div className='installed-integrations__filters'>
+ <div className='backstage-filters'>
{this.renderTypeFilters(this.state.incomingWebhooks, this.state.outgoingWebhooks)}
- <input
- type='search'
- placeholder={Utils.localizeMessage('installed_integrations.search', 'Search Integrations')}
- value={this.state.filter}
- onChange={this.updateFilter}
- style={{flexGrow: 0, flexShrink: 0}}
- />
+ <div className='backstage-filter__search'>
+ <i className='fa fa-search'></i>
+ <input
+ type='search'
+ className='form-control'
+ placeholder={Utils.localizeMessage('installed_integrations.search', 'Search Integrations')}
+ value={this.state.filter}
+ onChange={this.updateFilter}
+ style={{flexGrow: 0, flexShrink: 0}}
+ />
+ </div>
</div>
- <div className='installed-integrations__list'>
+ <div className='backstage-list'>
{integrations}
</div>
</div>
diff --git a/webapp/components/backstage/installed_outgoing_webhook.jsx b/webapp/components/backstage/installed_outgoing_webhook.jsx
index 12e1a5c81..fee427260 100644
--- a/webapp/components/backstage/installed_outgoing_webhook.jsx
+++ b/webapp/components/backstage/installed_outgoing_webhook.jsx
@@ -43,21 +43,21 @@ export default class InstalledOutgoingWebhook extends React.Component {
const channelName = channel ? channel.display_name : 'cannot find channel';
return (
- <div className='installed-integrations__item installed-integrations__outgoing-webhook'>
- <div className='details'>
- <div className='details-row'>
- <span className='name'>
+ <div className='backstage-list__item'>
+ <div className='item-details'>
+ <div className='item-details__row'>
+ <span className='item-details__name'>
{channelName}
</span>
- <span className='type'>
+ <span className='item-details__type'>
<FormattedMessage
id='installed_integrations.outgoingWebhookType'
defaultMessage='(Outgoing Webhook)'
/>
</span>
</div>
- <div className='details-row'>
- <span className='description'>
+ <div className='item-details__row'>
+ <span className='item-details__description'>
{Utils.getWindowLocationOrigin() + '/hooks/' + outgoingWebhook.id}
{' - '}
{outgoingWebhook.token}
diff --git a/webapp/components/error_page.jsx b/webapp/components/error_page.jsx
new file mode 100644
index 000000000..53f0fce82
--- /dev/null
+++ b/webapp/components/error_page.jsx
@@ -0,0 +1,58 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import $ from 'jquery';
+
+import React from 'react';
+import {Link} from 'react-router';
+
+import * as Utils from 'utils/utils.jsx';
+
+export default class ErrorPage extends React.Component {
+ componentDidMount() {
+ $('body').attr('class', 'sticky error');
+ }
+ componentWillUnmount() {
+ $('body').attr('class', '');
+ }
+ render() {
+ let title = this.props.location.query.title;
+ if (!title || title === '') {
+ title = Utils.localizeMessage('error.generic.title', 'Error');
+ }
+
+ let message = this.props.location.query.message;
+ if (!message || message === '') {
+ message = Utils.localizeMessage('error.generic.message', 'An error has occoured.');
+ }
+
+ let link = this.props.location.query.link;
+ if (!link || link === '') {
+ link = '/';
+ }
+
+ let linkMessage = this.props.location.query.linkmessage;
+ if (!linkMessage || linkMessage === '') {
+ linkMessage = Utils.localizeMessage('error.generic.link_message', 'Back to Mattermost');
+ }
+
+ return (
+ <div className='container-fluid'>
+ <div className='error__container'>
+ <div className='error__icon'>
+ <i className='fa fa-exclamation-triangle'/>
+ </div>
+ <h2>{title}</h2>
+ <p>{message}</p>
+ <Link to={link}>{linkMessage}</Link>
+ </div>
+ </div>
+ );
+ }
+}
+
+ErrorPage.defaultProps = {
+};
+ErrorPage.propTypes = {
+ location: React.PropTypes.object
+};
diff --git a/webapp/components/post.jsx b/webapp/components/post.jsx
index 30c47ee22..7294cf163 100644
--- a/webapp/components/post.jsx
+++ b/webapp/components/post.jsx
@@ -129,6 +129,7 @@ export default class Post extends React.Component {
const post = this.props.post;
const parentPost = this.props.parentPost;
const posts = this.props.posts;
+ const mattermostLogo = Constants.MATTERMOST_ICON_SVG;
if (!post.props) {
post.props = {};
@@ -184,24 +185,22 @@ export default class Post extends React.Component {
let profilePic = null;
if (!this.props.hideProfilePic) {
- let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp;
- if (post.props && post.props.from_webhook && global.window.mm_config.EnablePostIconOverride === 'true') {
- if (post.props.override_icon_url) {
- src = post.props.override_icon_url;
- } else {
- src = Constants.DEFAULT_WEBHOOK_LOGO;
- }
- } else if (Utils.isSystemMessage(post)) {
- src = Constants.SYSTEM_MESSAGE_PROFILE_IMAGE;
- }
-
profilePic = (
<img
- src={src}
+ src={Utils.getProfilePicSrcForPost(post, timestamp)}
height='36'
width='36'
/>
);
+
+ if (Utils.isSystemMessage(post)) {
+ profilePic = (
+ <span
+ className='icon'
+ dangerouslySetInnerHTML={{__html: mattermostLogo}}
+ />
+ );
+ }
}
return (
diff --git a/webapp/components/post_body_additional_content.jsx b/webapp/components/post_body_additional_content.jsx
index 2cd82f213..452597dde 100644
--- a/webapp/components/post_body_additional_content.jsx
+++ b/webapp/components/post_body_additional_content.jsx
@@ -70,7 +70,7 @@ export default class PostBodyAdditionalContent extends React.Component {
return this.getSlackAttachment();
}
- const link = Utils.extractLinks(this.props.post.message)[0];
+ const link = Utils.extractFirstLink(this.props.post.message);
if (!link) {
return null;
}
diff --git a/webapp/components/posts_view.jsx b/webapp/components/posts_view.jsx
index 917411549..aa7f445ce 100644
--- a/webapp/components/posts_view.jsx
+++ b/webapp/components/posts_view.jsx
@@ -204,10 +204,12 @@ export default class PostsView extends React.Component {
// the previous post was made by the same user as the current post,
// the previous post is not a comment,
// the current post is not a comment,
+ // the previous post is not from a webhook
// the current post is not from a webhook
if (prevPostUserId === postUserId &&
!prevPostIsComment &&
!postIsComment &&
+ !prevPostFromWebhook &&
!postFromWebhook) {
hideProfilePic = true;
}
@@ -308,7 +310,7 @@ export default class PostsView extends React.Component {
if (this.props.scrollType === PostsView.SCROLL_TYPE_BOTTOM) {
this.scrollToBottom();
} else if (this.props.scrollType === PostsView.SCROLL_TYPE_NEW_MESSAGE) {
- window.requestAnimationFrame(() => {
+ window.setTimeout(window.requestAnimationFrame(() => {
// If separator exists scroll to it. Otherwise scroll to bottom.
if (this.refs.newMessageSeparator) {
var objDiv = this.refs.postlist;
@@ -316,7 +318,7 @@ export default class PostsView extends React.Component {
} else if (this.refs.postlist) {
this.refs.postlist.scrollTop = this.refs.postlist.scrollHeight;
}
- });
+ }), 0);
} else if (this.props.scrollType === PostsView.SCROLL_TYPE_POST && this.props.scrollPostId) {
window.requestAnimationFrame(() => {
const postNode = ReactDOM.findDOMNode(this.refs[this.props.scrollPostId]);
diff --git a/webapp/components/rhs_root_post.jsx b/webapp/components/rhs_root_post.jsx
index 7a7c5f692..9a207e429 100644
--- a/webapp/components/rhs_root_post.jsx
+++ b/webapp/components/rhs_root_post.jsx
@@ -213,21 +213,10 @@ export default class RhsRootPost extends React.Component {
);
}
- let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp;
- if (post.props && post.props.from_webhook && global.window.mm_config.EnablePostIconOverride === 'true') {
- if (post.props.override_icon_url) {
- src = post.props.override_icon_url;
- } else {
- src = Constants.DEFAULT_WEBHOOK_LOGO;
- }
- } else if (Utils.isSystemMessage(post)) {
- src = Constants.SYSTEM_MESSAGE_PROFILE_IMAGE;
- }
-
const profilePic = (
<img
className='post-profile-img'
- src={src}
+ src={Utils.getProfilePicSrcForPost(post, timestamp)}
height='36'
width='36'
/>
diff --git a/webapp/components/search_results_item.jsx b/webapp/components/search_results_item.jsx
index 75cbcb2a0..58f09c0e0 100644
--- a/webapp/components/search_results_item.jsx
+++ b/webapp/components/search_results_item.jsx
@@ -1,11 +1,13 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import UserStore from 'stores/user_store.jsx';
import UserProfile from './user_profile.jsx';
+
+import UserStore from 'stores/user_store.jsx';
+
import * as GlobalActions from 'action_creators/global_actions.jsx';
import * as TextFormatting from 'utils/text_formatting.jsx';
-
+import * as Utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx';
import {FormattedMessage, FormattedDate} from 'react-intl';
@@ -29,6 +31,7 @@ export default class SearchResultsItem extends React.Component {
const channel = this.props.channel;
const timestamp = UserStore.getCurrentUser().update_at;
const user = this.props.user || {};
+ const post = this.props.post;
if (channel) {
channelName = channel.display_name;
@@ -47,13 +50,23 @@ export default class SearchResultsItem extends React.Component {
mentionHighlight: this.props.isMentionSearch
};
+ let overrideUsername;
+ let disableProfilePopover = false;
+ if (post.props &&
+ post.props.from_webhook &&
+ post.props.override_username &&
+ global.window.mm_config.EnablePostUsernameOverride === 'true') {
+ overrideUsername = post.props.override_username;
+ disableProfilePopover = true;
+ }
+
return (
<div className='search-item__container'>
<div className='date-separator'>
<hr className='separator__hr'/>
<div className='separator__text'>
<FormattedDate
- value={this.props.post.create_at}
+ value={post.create_at}
day='numeric'
month='long'
year='numeric'
@@ -67,18 +80,24 @@ export default class SearchResultsItem extends React.Component {
<div className='post__content'>
<div className='post__img'>
<img
- src={'/api/v1/users/' + this.props.post.user_id + '/image?time=' + timestamp}
+ src={Utils.getProfilePicSrcForPost(post, timestamp)}
height='36'
width='36'
/>
</div>
<div>
<ul className='post__header'>
- <li className='col__name'><strong><UserProfile user={user}/></strong></li>
+ <li className='col__name'><strong>
+ <UserProfile
+ user={user}
+ overwriteName={overrideUsername}
+ disablePopover={disableProfilePopover}
+ />
+ </strong></li>
<li className='col'>
<time className='search-item-time'>
<FormattedDate
- value={this.props.post.create_at}
+ value={post.create_at}
hour12={true}
hour='2-digit'
minute='2-digit'
@@ -87,7 +106,7 @@ export default class SearchResultsItem extends React.Component {
</li>
<li>
<Link
- to={'/' + window.location.pathname.split('/')[1] + '/pl/' + this.props.post.id}
+ to={'/' + window.location.pathname.split('/')[1] + '/pl/' + post.id}
className='search-item__jump'
>
<FormattedMessage
@@ -112,7 +131,7 @@ export default class SearchResultsItem extends React.Component {
<div className='search-item-snippet'>
<span
onClick={TextFormatting.handleClick}
- dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.props.post.message, formattingOptions)}}
+ dangerouslySetInnerHTML={{__html: TextFormatting.formatText(post.message, formattingOptions)}}
/>
</div>
</div>
diff --git a/webapp/components/sidebar.jsx b/webapp/components/sidebar.jsx
index b22d3ec34..500e73cf2 100644
--- a/webapp/components/sidebar.jsx
+++ b/webapp/components/sidebar.jsx
@@ -138,7 +138,9 @@ export default class Sidebar extends React.Component {
unreadCounts: JSON.parse(JSON.stringify(ChannelStore.getUnreadCounts())),
showTutorialTip: tutorialStep === TutorialSteps.CHANNEL_POPOVER,
currentTeam: TeamStore.getCurrent(),
- currentUser: UserStore.getCurrentUser()
+ currentUser: UserStore.getCurrentUser(),
+ townSquare: ChannelStore.getByName(Constants.DEFAULT_CHANNEL),
+ offTopic: ChannelStore.getByName(Constants.OFFTOPIC_CHANNEL)
};
}
@@ -290,6 +292,16 @@ export default class Sidebar extends React.Component {
createTutorialTip() {
const screens = [];
+ let townSquareDisplayName = Constants.DEFAULT_CHANNEL_UI_NAME;
+ if (this.state.townSquare) {
+ townSquareDisplayName = this.state.townSquare.display_name;
+ }
+
+ let offTopicDisplayName = Constants.OFFTOPIC_CHANNEL_UI_NAME;
+ if (this.state.offTopic) {
+ offTopicDisplayName = this.state.offTopic.display_name;
+ }
+
screens.push(
<div>
<FormattedHTMLMessage
@@ -303,10 +315,14 @@ export default class Sidebar extends React.Component {
<div>
<FormattedHTMLMessage
id='sidebar.tutorialScreen2'
- defaultMessage='<h4>"Town Square" and "Off-Topic" channels</h4>
+ defaultMessage='<h4>"{townsquare}" and "{offtopic}" channels</h4>
<p>Here are two public channels to start:</p>
- <p><strong>Town Square</strong> is a place for team-wide communication. Everyone in your team is a member of this channel.</p>
- <p><strong>Off-Topic</strong> is a place for fun and humor outside of work-related channels. You and your team can decide what other channels to create.</p>'
+ <p><strong>{townsquare}</strong> is a place for team-wide communication. Everyone in your team is a member of this channel.</p>
+ <p><strong>{offtopic}</strong> is a place for fun and humor outside of work-related channels. You and your team can decide what other channels to create.</p>'
+ values={{
+ townsquare: townSquareDisplayName,
+ offtopic: offTopicDisplayName
+ }}
/>
</div>
);
diff --git a/webapp/components/suggestion/at_mention_provider.jsx b/webapp/components/suggestion/at_mention_provider.jsx
index b423528c3..90ec6e660 100644
--- a/webapp/components/suggestion/at_mention_provider.jsx
+++ b/webapp/components/suggestion/at_mention_provider.jsx
@@ -100,13 +100,16 @@ export default class AtMentionProvider {
}
}
- // add dummy users to represent the @all and @channel special mentions
- if ('all'.startsWith(usernamePrefix)) {
- filtered.push({username: 'all'});
- }
+ //Don't imply that @all and @channel can be direct messaged
+ if (!pretext.startsWith('/msg')) {
+ // add dummy users to represent the @all and @channel special mentions
+ if ('all'.startsWith(usernamePrefix)) {
+ filtered.push({username: 'all'});
+ }
- if ('channel'.startsWith(usernamePrefix)) {
- filtered.push({username: 'channel'});
+ if ('channel'.startsWith(usernamePrefix)) {
+ filtered.push({username: 'channel'});
+ }
}
filtered = filtered.sort((a, b) => a.username.localeCompare(b.username));
diff --git a/webapp/components/tutorial/tutorial_intro_screens.jsx b/webapp/components/tutorial/tutorial_intro_screens.jsx
index bad426cfc..0358a6a65 100644
--- a/webapp/components/tutorial/tutorial_intro_screens.jsx
+++ b/webapp/components/tutorial/tutorial_intro_screens.jsx
@@ -19,6 +19,12 @@ const NUM_SCREENS = 3;
import React from 'react';
export default class TutorialIntroScreens extends React.Component {
+ static get propTypes() {
+ return {
+ townSquare: React.PropTypes.object,
+ offTopic: React.PropTypes.object
+ };
+ }
constructor(props) {
super(props);
@@ -153,6 +159,11 @@ export default class TutorialIntroScreens extends React.Component {
);
}
+ let townSquareDisplayName = Constants.DEFAULT_CHANNEL_UI_NAME;
+ if (this.props.townSquare) {
+ townSquareDisplayName = this.props.townSquare.display_name;
+ }
+
return (
<div>
<h3>
@@ -171,7 +182,10 @@ export default class TutorialIntroScreens extends React.Component {
{supportInfo}
<FormattedMessage
id='tutorial_intro.end'
- defaultMessage='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.'
+ defaultMessage='Click “Next” to enter {channel}. This is the first channel teammates see when they sign up. Use it for posting updates everyone needs to know.'
+ values={{
+ channel: townSquareDisplayName
+ }}
/>
{circles}
</div>
diff --git a/webapp/components/tutorial/tutorial_view.jsx b/webapp/components/tutorial/tutorial_view.jsx
index d9e0ef40d..5f2c1a257 100644
--- a/webapp/components/tutorial/tutorial_view.jsx
+++ b/webapp/components/tutorial/tutorial_view.jsx
@@ -1,18 +1,43 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import React from 'react';
-
import TutorialIntroScreens from './tutorial_intro_screens.jsx';
+import ChannelStore from 'stores/channel_store.jsx';
+import Constants from 'utils/constants.jsx';
+
+import React from 'react';
+
export default class TutorialView extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.handleChannelChange = this.handleChannelChange.bind(this);
+
+ this.state = {
+ townSquare: ChannelStore.getByName(Constants.DEFAULT_CHANNEL)
+ };
+ }
+ componentDidMount() {
+ ChannelStore.addChangeListener(this.handleChannelChange);
+ }
+ componentWillUnmount() {
+ ChannelStore.removeChangeListener(this.handleChannelChange);
+ }
+ handleChannelChange() {
+ this.setState({
+ townSquare: ChannelStore.getByName(Constants.DEFAULT_CHANNEL)
+ });
+ }
render() {
return (
<div
id='app-content'
className='app__content'
>
- <TutorialIntroScreens/>
+ <TutorialIntroScreens
+ townSquare={this.state.townSquare}
+ />
</div>
);
}
diff --git a/webapp/components/user_settings/user_settings_display.jsx b/webapp/components/user_settings/user_settings_display.jsx
index d815bd371..d169e01b5 100644
--- a/webapp/components/user_settings/user_settings_display.jsx
+++ b/webapp/components/user_settings/user_settings_display.jsx
@@ -304,7 +304,7 @@ export default class UserSettingsDisplay extends React.Component {
describe = (
<FormattedMessage
id='user.settings.display.showUsername'
- defaultMessage='Show username (team default)'
+ defaultMessage='Show username (default)'
/>
);
} else if (this.state.nameFormat === 'full_name') {
diff --git a/webapp/components/user_settings/user_settings_security.jsx b/webapp/components/user_settings/user_settings_security.jsx
index e4044e6d0..ff5a898a9 100644
--- a/webapp/components/user_settings/user_settings_security.jsx
+++ b/webapp/components/user_settings/user_settings_security.jsx
@@ -1,6 +1,7 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
+import $ from 'jquery';
import SettingItemMin from '../setting_item_min.jsx';
import SettingItemMax from '../setting_item_max.jsx';
import AccessHistoryModal from '../access_history_modal.jsx';
@@ -247,7 +248,7 @@ class SecurityTab extends React.Component {
extraInfo = (
<span>
<FormattedMessage
- id='user.settings.mfa.addHelp'
+ id='user.settings.mfa.addHelpQr'
defaultMessage='Please scan the QR code with the Google Authenticator app on your smartphone and fill in the token with one provided by the app.'
/>
</span>