diff options
Diffstat (limited to 'webapp/components')
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:' + /> + {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:' + /> + {config.Version} ({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> + {config.BuildHash} + </p> + <p> + <FormattedMessage + id='about.date' + defaultMessage='Build Date:' + /> + {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> |