PLT-2698 Integrations improvements (#2883)

* Fixed Integrations link to show up for non-admins (when enabled) and when only slash commands are enabled

* Updated BackstageSidebar to only show enabled integrations

* Added placeholder to integrations list when none exist

* Added loading spinner to integrations pages when they're loading
Этот коммит содержится в:
Harrison Healey
2016-05-05 09:32:12 -04:00
коммит произвёл Christopher Speller
родитель fcb5b70eb3
Коммит 2c92540471
8 изменённых файлов: 133 добавлений и 79 удалений

Просмотреть файл

@@ -40,6 +40,10 @@ export default class BackstageCategory extends React.Component {
<ul className='sections'> <ul className='sections'>
{ {
React.Children.map(children, (child) => { React.Children.map(children, (child) => {
if (!child) {
return child;
}
return React.cloneElement(child, { return React.cloneElement(child, {
parentLink: link parentLink: link
}); });

Просмотреть файл

@@ -10,6 +10,51 @@ import {FormattedMessage} from 'react-intl';
export default class BackstageSidebar extends React.Component { export default class BackstageSidebar extends React.Component {
render() { render() {
let incomingWebhooks = null;
if (window.mm_config.EnableIncomingWebhooks === 'true') {
incomingWebhooks = (
<BackstageSection
name='incoming_webhooks'
title={(
<FormattedMessage
id='backstage_sidebar.integrations.incoming_webhooks'
defaultMessage='Incoming Webhooks'
/>
)}
/>
);
}
let outgoingWebhooks = null;
if (window.mm_config.EnableOutgoingWebhooks === 'true') {
outgoingWebhooks = (
<BackstageSection
name='outgoing_webhooks'
title={(
<FormattedMessage
id='backstage_sidebar.integrations.outgoing_webhooks'
defaultMessage='Outgoing Webhooks'
/>
)}
/>
);
}
let commands = null;
if (window.mm_config.EnableCommands === 'true') {
commands = (
<BackstageSection
name='commands'
title={(
<FormattedMessage
id='backstage_sidebar.integrations.commands'
defaultMessage='Slash Commands'
/>
)}
/>
);
}
return ( return (
<div className='backstage-sidebar'> <div className='backstage-sidebar'>
<ul> <ul>
@@ -24,33 +69,9 @@ export default class BackstageSidebar extends React.Component {
/> />
} }
> >
<BackstageSection {incomingWebhooks}
name='incoming_webhooks' {outgoingWebhooks}
title={( {commands}
<FormattedMessage
id='backstage_sidebar.integrations.incoming_webhooks'
defaultMessage='Incoming Webhooks'
/>
)}
/>
<BackstageSection
name='outgoing_webhooks'
title={(
<FormattedMessage
id='backstage_sidebar.integrations.outgoing_webhooks'
defaultMessage='Outgoing Webhooks'
/>
)}
/>
<BackstageSection
name='commands'
title={(
<FormattedMessage
id='backstage_sidebar.integrations.commands'
defaultMessage='Slash Commands'
/>
)}
/>
</BackstageCategory> </BackstageCategory>
</ul> </ul>
</div> </div>

Просмотреть файл

@@ -21,21 +21,16 @@ export default class InstalledCommands extends React.Component {
this.deleteCommand = this.deleteCommand.bind(this); this.deleteCommand = this.deleteCommand.bind(this);
this.state = { this.state = {
commands: [] commands: IntegrationStore.getCommands(),
loading: !IntegrationStore.hasReceivedCommands()
}; };
} }
componentWillMount() { componentDidMount() {
IntegrationStore.addChangeListener(this.handleIntegrationChange); IntegrationStore.addChangeListener(this.handleIntegrationChange);
if (window.mm_config.EnableCommands === 'true') { if (window.mm_config.EnableCommands === 'true' && this.state.loading) {
if (IntegrationStore.hasReceivedCommands()) { AsyncClient.listTeamCommands();
this.setState({
commands: IntegrationStore.getCommands()
});
} else {
AsyncClient.listTeamCommands();
}
} }
} }
@@ -44,10 +39,9 @@ export default class InstalledCommands extends React.Component {
} }
handleIntegrationChange() { handleIntegrationChange() {
const commands = IntegrationStore.getCommands();
this.setState({ this.setState({
commands commands: IntegrationStore.getCommands(),
loading: !IntegrationStore.hasReceivedCommands()
}); });
} }
@@ -86,6 +80,13 @@ export default class InstalledCommands extends React.Component {
/> />
} }
addLink={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/commands/add'} addLink={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/commands/add'}
emptyText={
<FormattedMessage
id='installed_commands.empty'
defaultMessage='No slash commands found'
/>
}
loading={this.state.loading}
> >
{commands} {commands}
</InstalledIntegrations> </InstalledIntegrations>

Просмотреть файл

@@ -20,21 +20,16 @@ export default class InstalledIncomingWebhooks extends React.Component {
this.deleteIncomingWebhook = this.deleteIncomingWebhook.bind(this); this.deleteIncomingWebhook = this.deleteIncomingWebhook.bind(this);
this.state = { this.state = {
incomingWebhooks: [] incomingWebhooks: IntegrationStore.getIncomingWebhooks(),
loading: !IntegrationStore.hasReceivedIncomingWebhooks()
}; };
} }
componentWillMount() { componentDidMount() {
IntegrationStore.addChangeListener(this.handleIntegrationChange); IntegrationStore.addChangeListener(this.handleIntegrationChange);
if (window.mm_config.EnableIncomingWebhooks === 'true') { if (window.mm_config.EnableIncomingWebhooks === 'true' && this.state.loading) {
if (IntegrationStore.hasReceivedIncomingWebhooks()) { AsyncClient.listIncomingHooks();
this.setState({
incomingWebhooks: IntegrationStore.getIncomingWebhooks()
});
} else {
AsyncClient.listIncomingHooks();
}
} }
} }
@@ -44,7 +39,8 @@ export default class InstalledIncomingWebhooks extends React.Component {
handleIntegrationChange() { handleIntegrationChange() {
this.setState({ this.setState({
incomingWebhooks: IntegrationStore.getIncomingWebhooks() incomingWebhooks: IntegrationStore.getIncomingWebhooks(),
loading: !IntegrationStore.hasReceivedIncomingWebhooks()
}); });
} }
@@ -78,6 +74,13 @@ export default class InstalledIncomingWebhooks extends React.Component {
/> />
} }
addLink={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/incoming_webhooks/add'} addLink={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/incoming_webhooks/add'}
emptyText={
<FormattedMessage
id='installed_incoming_webhooks.empty'
defaultMessage='No incoming webhooks found'
/>
}
loading={this.state.loading}
> >
{incomingWebhooks} {incomingWebhooks}
</InstalledIntegrations> </InstalledIntegrations>

Просмотреть файл

@@ -6,6 +6,7 @@ import React from 'react';
import * as Utils from 'utils/utils.jsx'; import * as Utils from 'utils/utils.jsx';
import {Link} from 'react-router'; import {Link} from 'react-router';
import LoadingScreen from 'components/loading_screen.jsx';
export default class InstalledIntegrations extends React.Component { export default class InstalledIntegrations extends React.Component {
static get propTypes() { static get propTypes() {
@@ -13,7 +14,9 @@ export default class InstalledIntegrations extends React.Component {
children: React.PropTypes.node, children: React.PropTypes.node,
header: React.PropTypes.node.isRequired, header: React.PropTypes.node.isRequired,
addLink: React.PropTypes.string.isRequired, addLink: React.PropTypes.string.isRequired,
addText: React.PropTypes.node.isRequired addText: React.PropTypes.node.isRequired,
emptyText: React.PropTypes.node.isRequired,
loading: React.PropTypes.bool.isRequired
}; };
} }
@@ -36,9 +39,23 @@ export default class InstalledIntegrations extends React.Component {
render() { render() {
const filter = this.state.filter.toLowerCase(); const filter = this.state.filter.toLowerCase();
const children = React.Children.map(this.props.children, (child) => { let children;
return React.cloneElement(child, {filter});
}); if (this.props.loading) {
children = <LoadingScreen/>;
} else {
children = React.Children.map(this.props.children, (child) => {
return React.cloneElement(child, {filter});
});
if (children.length === 0) {
children = (
<span className='backstage-list__item backstage-list_empty'>
{this.props.emptyText}
</span>
);
}
}
return ( return (
<div className='backstage-content'> <div className='backstage-content'>

Просмотреть файл

@@ -21,21 +21,16 @@ export default class InstalledOutgoingWebhooks extends React.Component {
this.deleteOutgoingWebhook = this.deleteOutgoingWebhook.bind(this); this.deleteOutgoingWebhook = this.deleteOutgoingWebhook.bind(this);
this.state = { this.state = {
outgoingWebhooks: [] outgoingWebhooks: IntegrationStore.getOutgoingWebhooks(),
loading: !IntegrationStore.hasReceivedOutgoingWebhooks()
}; };
} }
componentWillMount() { componentDidMount() {
IntegrationStore.addChangeListener(this.handleIntegrationChange); IntegrationStore.addChangeListener(this.handleIntegrationChange);
if (window.mm_config.EnableOutgoingWebhooks === 'true') { if (window.mm_config.EnableOutgoingWebhooks === 'true' && this.state.loading) {
if (IntegrationStore.hasReceivedOutgoingWebhooks()) { AsyncClient.listOutgoingHooks();
this.setState({
outgoingWebhooks: IntegrationStore.getOutgoingWebhooks()
});
} else {
AsyncClient.listOutgoingHooks();
}
} }
} }
@@ -45,7 +40,8 @@ export default class InstalledOutgoingWebhooks extends React.Component {
handleIntegrationChange() { handleIntegrationChange() {
this.setState({ this.setState({
outgoingWebhooks: IntegrationStore.getOutgoingWebhooks() outgoingWebhooks: IntegrationStore.getOutgoingWebhooks(),
loading: !IntegrationStore.hasReceivedOutgoingWebhooks()
}); });
} }
@@ -84,6 +80,13 @@ export default class InstalledOutgoingWebhooks extends React.Component {
/> />
} }
addLink={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/outgoing_webhooks/add'} addLink={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/outgoing_webhooks/add'}
emptyText={
<FormattedMessage
id='installed_outgoing_webhooks.empty'
defaultMessage='No outgoing webhooks found'
/>
}
loading={this.state.loading}
> >
{outgoingWebhooks} {outgoingWebhooks}
</InstalledIntegrations> </InstalledIntegrations>

Просмотреть файл

@@ -143,19 +143,21 @@ export default class NavbarDropdown extends React.Component {
); );
} }
if (window.mm_config.EnableIncomingWebhooks === 'true' || window.mm_config.EnableOutgoingWebhooks === 'true') { const integrationsEnabled =
if (isAdmin || window.EnableAdminOnlyIntegrations !== 'true') { window.mm_config.EnableIncomingWebhooks === 'true' ||
integrationsLink = ( window.mm_config.EnableOutgoingWebhooks === 'true' ||
<li> window.mm_config.EnableCommands === 'true';
<Link to={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations'}> if (integrationsEnabled && (isAdmin || window.EnableOnlyAdminIntegrations !== 'true')) {
<FormattedMessage integrationsLink = (
id='navbar_dropdown.integrations' <li>
defaultMessage='Integrations' <Link to={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations'}>
/> <FormattedMessage
</Link> id='navbar_dropdown.integrations'
</li> defaultMessage='Integrations'
); />
} </Link>
</li>
);
} }
if (isSystemAdmin) { if (isSystemAdmin) {

Просмотреть файл

@@ -902,8 +902,10 @@
"get_team_invite_link_modal.helpDisabled": "User creation has been disabled for your team. Please ask your team administrator for details.", "get_team_invite_link_modal.helpDisabled": "User creation has been disabled for your team. Please ask your team administrator for details.",
"get_team_invite_link_modal.title": "Team Invite Link", "get_team_invite_link_modal.title": "Team Invite Link",
"installed_commands.add": "Add Slash Command", "installed_commands.add": "Add Slash Command",
"installed_commands.empty": "No commands found",
"installed_commands.header": "Slash Commands", "installed_commands.header": "Slash Commands",
"installed_incoming_webhooks.add": "Add Incoming Webhook", "installed_incoming_webhooks.add": "Add Incoming Webhook",
"installed_incoming_webhooks.empty": "No incoming webhooks found",
"installed_incoming_webhooks.header": "Incoming Webhooks", "installed_incoming_webhooks.header": "Incoming Webhooks",
"installed_integrations.creation": "Created by {creator} on {createAt, date, full}", "installed_integrations.creation": "Created by {creator} on {createAt, date, full}",
"installed_integrations.delete": "Delete", "installed_integrations.delete": "Delete",
@@ -912,6 +914,7 @@
"installed_integrations.token": "Token: {token}", "installed_integrations.token": "Token: {token}",
"installed_integrations.url": "URL: {url}", "installed_integrations.url": "URL: {url}",
"installed_outgoing_webhooks.add": "Add Outgoing Webhook", "installed_outgoing_webhooks.add": "Add Outgoing Webhook",
"installed_outgoing_webhooks.empty": "No outgoing webhooks found",
"installed_outgoing_webhooks.header": "Outgoing Webhooks", "installed_outgoing_webhooks.header": "Outgoing Webhooks",
"integrations.command.description": "Slash commands send events to external integrations", "integrations.command.description": "Slash commands send events to external integrations",
"integrations.command.title": "Slash Command", "integrations.command.title": "Slash Command",