diff --git a/app/scenes/Settings/Slack.js b/app/scenes/Settings/Slack.js index 99a2ce11..944678ce 100644 --- a/app/scenes/Settings/Slack.js +++ b/app/scenes/Settings/Slack.js @@ -11,6 +11,8 @@ import HelpText from 'components/HelpText'; import SlackButton from './components/SlackButton'; import CollectionsStore from 'stores/CollectionsStore'; import IntegrationsStore from 'stores/IntegrationsStore'; +import Notice from 'shared/components/Notice'; +import getQueryVariable from 'shared/utils/getQueryVariable'; type Props = { collections: CollectionsStore, @@ -19,7 +21,10 @@ type Props = { @observer class Slack extends React.Component { + error: ?string; + componentDidMount() { + this.error = getQueryVariable('error'); this.props.integrations.fetchPage(); } @@ -36,6 +41,12 @@ class Slack extends React.Component {

Slack

+ {this.error === 'access_denied' && ( + + Whoops, you need to accept the permissions in Slack to connect + Outline to your team. Try again? + + )} Preview Outline links your team mates share and use the{' '} /outline slash command in Slack to search for documents diff --git a/server/auth/slack.js b/server/auth/slack.js index e6f3279c..58728e09 100644 --- a/server/auth/slack.js +++ b/server/auth/slack.js @@ -78,8 +78,13 @@ router.get('slack.callback', async ctx => { }); router.get('slack.commands', async ctx => { - const { code } = ctx.request.query; - ctx.assertPresent(code, 'code is required'); + const { code, error } = ctx.request.query; + ctx.assertPresent(code || error, 'code is required'); + + if (error) { + ctx.redirect(`/settings/integrations/slack?error=${error}`); + return; + } const endpoint = `${process.env.URL || ''}/auth/slack.commands`; const data = await Slack.oauthAccess(code, endpoint); @@ -108,8 +113,13 @@ router.get('slack.commands', async ctx => { }); router.get('slack.post', async ctx => { - const { code, state } = ctx.request.query; - ctx.assertPresent(code, 'code is required'); + const { code, error, state } = ctx.request.query; + ctx.assertPresent(code || error, 'code is required'); + + if (error) { + ctx.redirect(`/settings/integrations/slack?error=${error}`); + return; + } const collectionId = state; ctx.assertUuid(collectionId, 'collectionId must be an uuid'); diff --git a/server/pages/Home.js b/server/pages/Home.js index 43511c56..da4eb5e7 100644 --- a/server/pages/Home.js +++ b/server/pages/Home.js @@ -4,6 +4,7 @@ import { Helmet } from 'react-helmet'; import styled from 'styled-components'; import Grid from 'styled-components-grid'; import breakpoint from 'styled-components-breakpoint'; +import Notice from '../../shared/components/Notice'; import Hero from './components/Hero'; import SigninButtons from './components/SigninButtons'; import { developers, githubUrl } from '../../shared/utils/routeHelpers'; @@ -120,13 +121,6 @@ function Home(props: Props) { ); } -const Notice = styled.p` - background: #ffd95c; - color: hsla(46, 100%, 20%, 1); - padding: 10px; - border-radius: 4px; -`; - const Screenshot = styled.img` width: 100%; box-shadow: 0 0 80px 0 rgba(124, 124, 124, 0.5), diff --git a/shared/components/Notice.js b/shared/components/Notice.js new file mode 100644 index 00000000..02179f9c --- /dev/null +++ b/shared/components/Notice.js @@ -0,0 +1,11 @@ +// @flow +import styled from 'styled-components'; + +const Notice = styled.p` + background: #ffd95c; + color: hsla(46, 100%, 20%, 1); + padding: 10px; + border-radius: 4px; +`; + +export default Notice; diff --git a/shared/utils/getQueryVariable.js b/shared/utils/getQueryVariable.js new file mode 100644 index 00000000..415ffe75 --- /dev/null +++ b/shared/utils/getQueryVariable.js @@ -0,0 +1,12 @@ +// @flow + +export default function getQueryVariable(variable: string) { + var query = window.location.search.substring(1); + var vars = query.split('&'); + for (var i = 0; i < vars.length; i++) { + var pair = vars[i].split('='); + if (pair[0] == variable) { + return pair[1]; + } + } +}