fix: Updated design of api tokens to clarify, closes #3422
This commit is contained in:
@@ -5,7 +5,7 @@ type Props = {
|
||||
text: string;
|
||||
children?: React.ReactElement;
|
||||
onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
onCopy: () => void;
|
||||
onCopy?: () => void;
|
||||
};
|
||||
|
||||
class CopyToClipboard extends React.PureComponent<Props> {
|
||||
@@ -17,9 +17,8 @@ class CopyToClipboard extends React.PureComponent<Props> {
|
||||
debug: process.env.NODE_ENV !== "production",
|
||||
format: "text/plain",
|
||||
});
|
||||
if (onCopy) {
|
||||
onCopy();
|
||||
}
|
||||
|
||||
onCopy?.();
|
||||
|
||||
if (elem && elem.props && typeof elem.props.onClick === "function") {
|
||||
elem.props.onClick(ev);
|
||||
|
||||
@@ -61,7 +61,7 @@ function Tokens() {
|
||||
items={apiKeys.orderedData}
|
||||
heading={<Subheading sticky>{t("Tokens")}</Subheading>}
|
||||
renderItem={(token: ApiKey) => (
|
||||
<TokenListItem key={token.id} token={token} onDelete={token.delete} />
|
||||
<TokenListItem key={token.id} token={token} />
|
||||
)}
|
||||
/>
|
||||
<Modal
|
||||
|
||||
@@ -1,26 +1,65 @@
|
||||
import { CopyIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ApiKey from "~/models/ApiKey";
|
||||
import Button from "~/components/Button";
|
||||
import CopyToClipboard from "~/components/CopyToClipboard";
|
||||
import ListItem from "~/components/List/Item";
|
||||
import useStores from "~/hooks/useStores";
|
||||
import useToasts from "~/hooks/useToasts";
|
||||
import TokenRevokeDialog from "./TokenRevokeDialog";
|
||||
|
||||
type Props = {
|
||||
token: ApiKey;
|
||||
onDelete: (tokenId: string) => Promise<void>;
|
||||
};
|
||||
|
||||
const TokenListItem = ({ token, onDelete }: Props) => {
|
||||
const TokenListItem = ({ token }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { showToast } = useToasts();
|
||||
const { dialogs } = useStores();
|
||||
const [linkCopied, setLinkCopied] = React.useState<boolean>(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (linkCopied) {
|
||||
setTimeout(() => {
|
||||
setLinkCopied(false);
|
||||
}, 3000);
|
||||
}
|
||||
}, [linkCopied]);
|
||||
|
||||
const handleCopy = React.useCallback(() => {
|
||||
setLinkCopied(true);
|
||||
showToast(t("API token copied to clipboard"), {
|
||||
type: "success",
|
||||
});
|
||||
}, [showToast, t]);
|
||||
|
||||
const showRevokeConfirmation = React.useCallback(() => {
|
||||
dialogs.openModal({
|
||||
title: t("Revoke token"),
|
||||
isCentered: true,
|
||||
content: (
|
||||
<TokenRevokeDialog onSubmit={dialogs.closeAllModals} token={token} />
|
||||
),
|
||||
});
|
||||
}, [t, dialogs, token]);
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
key={token.id}
|
||||
title={
|
||||
<>
|
||||
{token.name} – <code>{token.secret}</code>
|
||||
</>
|
||||
}
|
||||
title={token.name}
|
||||
subtitle={<code>{token.secret}</code>}
|
||||
actions={
|
||||
<Button onClick={() => onDelete(token.id)} neutral>
|
||||
<>
|
||||
<CopyToClipboard text={token.secret} onCopy={handleCopy}>
|
||||
<Button type="button" icon={<CopyIcon />} neutral borderOnHover>
|
||||
{linkCopied ? t("Copied") : t("Copy")}
|
||||
</Button>
|
||||
</CopyToClipboard>
|
||||
<Button onClick={showRevokeConfirmation} neutral>
|
||||
Revoke
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
31
app/scenes/Settings/components/TokenRevokeDialog.tsx
Normal file
31
app/scenes/Settings/components/TokenRevokeDialog.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ApiKey from "~/models/ApiKey";
|
||||
import ConfirmationDialog from "~/components/ConfirmationDialog";
|
||||
|
||||
type Props = {
|
||||
token: ApiKey;
|
||||
onSubmit: () => void;
|
||||
};
|
||||
|
||||
export default function TokenRevokeDialog({ token, onSubmit }: Props) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleSubmit = async () => {
|
||||
await token.delete();
|
||||
onSubmit();
|
||||
};
|
||||
|
||||
return (
|
||||
<ConfirmationDialog
|
||||
onSubmit={handleSubmit}
|
||||
submitText={t("Revoke")}
|
||||
savingText={`${t("Revoking")}…`}
|
||||
danger
|
||||
>
|
||||
{t("Are you sure you want to revoke the {{ tokenName }} token?", {
|
||||
tokenName: token.name,
|
||||
})}
|
||||
</ConfirmationDialog>
|
||||
);
|
||||
}
|
||||
@@ -569,6 +569,13 @@
|
||||
"Document published": "Document published",
|
||||
"Document updated": "Document updated",
|
||||
"Disconnect": "Disconnect",
|
||||
"API token copied to clipboard": "API token copied to clipboard",
|
||||
"Revoke token": "Revoke token",
|
||||
"Copied": "Copied",
|
||||
"Copy": "Copy",
|
||||
"Revoke": "Revoke",
|
||||
"Revoking": "Revoking",
|
||||
"Are you sure you want to revoke the {{ tokenName }} token?": "Are you sure you want to revoke the {{ tokenName }} token?",
|
||||
"Active": "Active",
|
||||
"Everyone": "Everyone",
|
||||
"Admins": "Admins",
|
||||
|
||||
Reference in New Issue
Block a user