chore: Slack integration screen improvements (#2049)
* feat: Add collection iconography and colors to Slack settings page fix: Use standardized list components fix: Slack icon size chore: Convert to translation strings * fix: Missing translation, convert to Scene
This commit is contained in:
@ -1,9 +1,9 @@
|
||||
// @flow
|
||||
import * as React from "react";
|
||||
import styled from "styled-components";
|
||||
import SlackLogo from "../SlackLogo";
|
||||
import GoogleLogo from "./GoogleLogo";
|
||||
import MicrosoftLogo from "./MicrosoftLogo";
|
||||
import SlackLogo from "./SlackLogo";
|
||||
|
||||
type Props = {|
|
||||
providerName: string,
|
||||
|
@ -19,14 +19,14 @@ import styled from "styled-components";
|
||||
import Flex from "components/Flex";
|
||||
import Scrollable from "components/Scrollable";
|
||||
|
||||
import SlackIcon from "components/SlackIcon";
|
||||
import ZapierIcon from "components/ZapierIcon";
|
||||
import Sidebar from "./Sidebar";
|
||||
import Header from "./components/Header";
|
||||
import Section from "./components/Section";
|
||||
import SidebarLink from "./components/SidebarLink";
|
||||
import TeamButton from "./components/TeamButton";
|
||||
import Version from "./components/Version";
|
||||
import SlackIcon from "./icons/Slack";
|
||||
import ZapierIcon from "./icons/Zapier";
|
||||
import env from "env";
|
||||
import useCurrentTeam from "hooks/useCurrentTeam";
|
||||
import useStores from "hooks/useStores";
|
||||
|
@ -1,138 +1,136 @@
|
||||
// @flow
|
||||
import { find } from "lodash";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
|
||||
import getQueryVariable from "shared/utils/getQueryVariable";
|
||||
import AuthStore from "stores/AuthStore";
|
||||
import CollectionsStore from "stores/CollectionsStore";
|
||||
import IntegrationsStore from "stores/IntegrationsStore";
|
||||
import Button from "components/Button";
|
||||
import CenteredContent from "components/CenteredContent";
|
||||
import CollectionIcon from "components/CollectionIcon";
|
||||
import Heading from "components/Heading";
|
||||
import HelpText from "components/HelpText";
|
||||
import List from "components/List";
|
||||
import ListItem from "components/List/Item";
|
||||
import Notice from "components/Notice";
|
||||
import PageTitle from "components/PageTitle";
|
||||
import Scene from "components/Scene";
|
||||
import SlackIcon from "components/SlackIcon";
|
||||
import SlackButton from "./components/SlackButton";
|
||||
import env from "env";
|
||||
import useCurrentTeam from "hooks/useCurrentTeam";
|
||||
import useStores from "hooks/useStores";
|
||||
|
||||
type Props = {
|
||||
collections: CollectionsStore,
|
||||
integrations: IntegrationsStore,
|
||||
auth: AuthStore,
|
||||
};
|
||||
function Slack() {
|
||||
const team = useCurrentTeam();
|
||||
const { collections, integrations } = useStores();
|
||||
const { t } = useTranslation();
|
||||
const error = getQueryVariable("error");
|
||||
|
||||
@observer
|
||||
class Slack extends React.Component<Props> {
|
||||
error: ?string;
|
||||
React.useEffect(() => {
|
||||
collections.fetchPage({ limit: 100 });
|
||||
integrations.fetchPage({ limit: 100 });
|
||||
}, [collections, integrations]);
|
||||
|
||||
componentDidMount() {
|
||||
this.error = getQueryVariable("error");
|
||||
this.props.collections.fetchPage({ limit: 100 });
|
||||
this.props.integrations.fetchPage();
|
||||
}
|
||||
const commandIntegration = find(integrations.slackIntegrations, {
|
||||
type: "command",
|
||||
});
|
||||
|
||||
get commandIntegration() {
|
||||
return find(this.props.integrations.slackIntegrations, {
|
||||
type: "command",
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { collections, integrations, auth } = this.props;
|
||||
const teamId = auth.team ? auth.team.id : "";
|
||||
|
||||
return (
|
||||
<CenteredContent>
|
||||
<PageTitle title="Slack" />
|
||||
<h1>Slack</h1>
|
||||
{this.error === "access_denied" && (
|
||||
<Notice>
|
||||
return (
|
||||
<Scene title="Slack" icon={<SlackIcon color="currentColor" />}>
|
||||
<Heading>Slack</Heading>
|
||||
{error === "access_denied" && (
|
||||
<Notice>
|
||||
<Trans>
|
||||
Whoops, you need to accept the permissions in Slack to connect
|
||||
Outline to your team. Try again?
|
||||
</Notice>
|
||||
)}
|
||||
{this.error === "unauthenticated" && (
|
||||
<Notice>
|
||||
</Trans>
|
||||
</Notice>
|
||||
)}
|
||||
{error === "unauthenticated" && (
|
||||
<Notice>
|
||||
<Trans>
|
||||
Something went wrong while authenticating your request. Please try
|
||||
logging in again?
|
||||
</Notice>
|
||||
</Trans>
|
||||
</Notice>
|
||||
)}
|
||||
<HelpText>
|
||||
<Trans
|
||||
defaults="Get rich previews of Outline links shared in Slack and use the <em>{{ command }}</em> slash command to search for documents without leaving your chat."
|
||||
values={{ command: "/outline" }}
|
||||
components={{ em: <Code /> }}
|
||||
/>
|
||||
</HelpText>
|
||||
<p>
|
||||
{commandIntegration ? (
|
||||
<Button onClick={commandIntegration.delete}>{t("Disconnect")}</Button>
|
||||
) : (
|
||||
<SlackButton
|
||||
scopes={["commands", "links:read", "links:write"]}
|
||||
redirectUri={`${env.URL}/auth/slack.commands`}
|
||||
state={team.id}
|
||||
/>
|
||||
)}
|
||||
<HelpText>
|
||||
Preview Outline links your team mates share and use the{" "}
|
||||
<Code>/outline</Code> slash command in Slack to search for documents
|
||||
in your team’s wiki.
|
||||
</HelpText>
|
||||
<p>
|
||||
{this.commandIntegration ? (
|
||||
<Button onClick={this.commandIntegration.delete}>Disconnect</Button>
|
||||
) : (
|
||||
<SlackButton
|
||||
scopes={["commands", "links:read", "links:write"]}
|
||||
redirectUri={`${env.URL}/auth/slack.commands`}
|
||||
state={teamId}
|
||||
/>
|
||||
)}
|
||||
</p>
|
||||
<p> </p>
|
||||
</p>
|
||||
<p> </p>
|
||||
|
||||
<h2>Collections</h2>
|
||||
<HelpText>
|
||||
<h2>{t("Collections")}</h2>
|
||||
<HelpText>
|
||||
<Trans>
|
||||
Connect Outline collections to Slack channels and messages will be
|
||||
posted in Slack when documents are published or updated.
|
||||
</HelpText>
|
||||
automatically posted to Slack when documents are published or updated.
|
||||
</Trans>
|
||||
</HelpText>
|
||||
|
||||
<List>
|
||||
{collections.orderedData.map((collection) => {
|
||||
const integration = find(integrations.slackIntegrations, {
|
||||
collectionId: collection.id,
|
||||
});
|
||||
|
||||
if (integration) {
|
||||
return (
|
||||
<ListItem key={integration.id}>
|
||||
<span>
|
||||
<strong>{collection.name}</strong> posting activity to the{" "}
|
||||
<strong>{integration.settings.channel}</strong> Slack
|
||||
channel
|
||||
</span>
|
||||
<Button onClick={integration.delete}>Disconnect</Button>
|
||||
</ListItem>
|
||||
);
|
||||
}
|
||||
<List>
|
||||
{collections.orderedData.map((collection) => {
|
||||
const integration = find(integrations.slackIntegrations, {
|
||||
collectionId: collection.id,
|
||||
});
|
||||
|
||||
if (integration) {
|
||||
return (
|
||||
<ListItem key={collection.id}>
|
||||
<strong>{collection.name}</strong>
|
||||
<ListItem
|
||||
key={integration.id}
|
||||
title={collection.name}
|
||||
image={<CollectionIcon collection={collection} />}
|
||||
subtitle={
|
||||
<Trans
|
||||
defaults={`Connected to the <em>{{ channelName }}</em> channel`}
|
||||
values={{ channelName: integration.settings.channel }}
|
||||
components={{ em: <strong /> }}
|
||||
/>
|
||||
}
|
||||
actions={
|
||||
<Button onClick={integration.delete}>
|
||||
{t("Disconnect")}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
key={collection.id}
|
||||
title={collection.name}
|
||||
image={<CollectionIcon collection={collection} />}
|
||||
actions={
|
||||
<SlackButton
|
||||
scopes={["incoming-webhook"]}
|
||||
redirectUri={`${env.URL}/auth/slack.post`}
|
||||
state={collection.id}
|
||||
label="Connect"
|
||||
label={t("Connect")}
|
||||
/>
|
||||
</ListItem>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
</CenteredContent>
|
||||
);
|
||||
}
|
||||
}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
</Scene>
|
||||
);
|
||||
}
|
||||
|
||||
const List = styled.ol`
|
||||
list-style: none;
|
||||
margin: 8px 0;
|
||||
padding: 0;
|
||||
`;
|
||||
|
||||
const ListItem = styled.li`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #eaebea;
|
||||
`;
|
||||
|
||||
const Code = styled.code`
|
||||
padding: 4px 6px;
|
||||
margin: 0 2px;
|
||||
@ -140,4 +138,4 @@ const Code = styled.code`
|
||||
border-radius: 4px;
|
||||
`;
|
||||
|
||||
export default inject("collections", "integrations", "auth")(Slack);
|
||||
export default observer(Slack);
|
||||
|
@ -1,19 +1,20 @@
|
||||
// @flow
|
||||
import * as React from "react";
|
||||
import styled from "styled-components";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { slackAuth } from "shared/utils/routeHelpers";
|
||||
import Button from "components/Button";
|
||||
import SlackLogo from "components/SlackLogo";
|
||||
import SlackIcon from "components/SlackIcon";
|
||||
import env from "env";
|
||||
|
||||
type Props = {
|
||||
type Props = {|
|
||||
scopes?: string[],
|
||||
redirectUri: string,
|
||||
state?: string,
|
||||
label?: string,
|
||||
};
|
||||
|};
|
||||
|
||||
function SlackButton({ state = "", scopes, redirectUri, label }: Props) {
|
||||
const { t } = useTranslation();
|
||||
const handleClick = () =>
|
||||
(window.location.href = slackAuth(
|
||||
state,
|
||||
@ -25,22 +26,12 @@ function SlackButton({ state = "", scopes, redirectUri, label }: Props) {
|
||||
return (
|
||||
<Button
|
||||
onClick={handleClick}
|
||||
icon={<SpacedSlackLogo size={24} fill="#000" />}
|
||||
icon={<SlackIcon fill="currentColor" />}
|
||||
neutral
|
||||
>
|
||||
{label ? (
|
||||
label
|
||||
) : (
|
||||
<span>
|
||||
Add to <strong>Slack</strong>
|
||||
</span>
|
||||
)}
|
||||
{label || t("Add to Slack")}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
const SpacedSlackLogo = styled(SlackLogo)`
|
||||
padding-right: 4px;
|
||||
`;
|
||||
|
||||
export default SlackButton;
|
||||
|
@ -10,7 +10,7 @@
|
||||
"build:webpack": "webpack --config webpack.config.prod.js",
|
||||
"build": "yarn clean && yarn build:webpack && yarn build:i18n && yarn build:server",
|
||||
"start": "node ./build/server/index.js",
|
||||
"dev": "nodemon --exec \"yarn build:server && yarn build:i18n && node build/server/index.js\" -e js --ignore build/ --ignore app/",
|
||||
"dev": "nodemon --exec \"yarn build:server && yarn build:i18n && node --inspect=0.0.0.0 build/server/index.js\" -e js --ignore build/ --ignore app/",
|
||||
"lint": "eslint app server shared",
|
||||
"deploy": "git push heroku master",
|
||||
"heroku-postbuild": "yarn build && yarn db:migrate",
|
||||
|
@ -149,7 +149,7 @@ if (SLACK_CLIENT_ID) {
|
||||
}
|
||||
|
||||
// this code block accounts for the root domain being unable to
|
||||
// access authentcation for subdomains. We must forward to the
|
||||
// access authentication for subdomains. We must forward to the
|
||||
// appropriate subdomain to complete the oauth flow
|
||||
if (!user) {
|
||||
try {
|
||||
|
@ -10,8 +10,14 @@ import { sendEmail } from "../mailer";
|
||||
import { DataTypes, sequelize, encryptedFields, Op } from "../sequelize";
|
||||
import { DEFAULT_AVATAR_HOST } from "../utils/avatars";
|
||||
import { publicS3Endpoint, uploadToS3FromUrl } from "../utils/s3";
|
||||
import UserAuthentication from "./UserAuthentication";
|
||||
import { Star, Team, Collection, NotificationSetting, ApiKey } from ".";
|
||||
import {
|
||||
UserAuthentication,
|
||||
Star,
|
||||
Team,
|
||||
Collection,
|
||||
NotificationSetting,
|
||||
ApiKey,
|
||||
} from ".";
|
||||
|
||||
const User = sequelize.define(
|
||||
"user",
|
||||
|
@ -348,6 +348,7 @@
|
||||
"Shared": "Shared",
|
||||
"by {{ name }}": "by {{ name }}",
|
||||
"Last accessed": "Last accessed",
|
||||
"Add to Slack": "Add to Slack",
|
||||
"Import started": "Import started",
|
||||
"Export in progress…": "Export in progress…",
|
||||
"It is possible to import a zip file of folders and Markdown files previously exported from an Outline instance. Support will soon be added for importing from other services.": "It is possible to import a zip file of folders and Markdown files previously exported from an Outline instance. Support will soon be added for importing from other services.",
|
||||
@ -384,6 +385,13 @@
|
||||
"You can globally enable and disable public document sharing in the <em>security settings</em>.": "You can globally enable and disable public document sharing in the <em>security settings</em>.",
|
||||
"Shared documents": "Shared documents",
|
||||
"No share links, yet.": "No share links, yet.",
|
||||
"Whoops, you need to accept the permissions in Slack to connect Outline to your team. Try again?": "Whoops, you need to accept the permissions in Slack to connect Outline to your team. Try again?",
|
||||
"Something went wrong while authenticating your request. Please try logging in again?": "Something went wrong while authenticating your request. Please try logging in again?",
|
||||
"Get rich previews of Outline links shared in Slack and use the <em>{{ command }}</em> slash command to search for documents without leaving your chat.": "Get rich previews of Outline links shared in Slack and use the <em>{{ command }}</em> slash command to search for documents without leaving your chat.",
|
||||
"Connect Outline collections to Slack channels and messages will be automatically posted to Slack when documents are published or updated.": "Connect Outline collections to Slack channels and messages will be automatically posted to Slack when documents are published or updated.",
|
||||
"Connected to the <em>{{ channelName }}</em> channel": "Connected to the <em>{{ channelName }}</em> channel",
|
||||
"Disconnect": "Disconnect",
|
||||
"Connect": "Connect",
|
||||
"You’ve not starred any documents yet.": "You’ve not starred any documents yet.",
|
||||
"There are no templates just yet.": "There are no templates just yet.",
|
||||
"You can create templates to help your team create consistent and accurate documentation.": "You can create templates to help your team create consistent and accurate documentation.",
|
||||
|
Reference in New Issue
Block a user