From 2a11a23d5bff1fd51bee6435968ee7795c6529c4 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Mon, 25 Apr 2022 21:34:18 -0700 Subject: [PATCH] fix: Updated design of api tokens to clarify, closes #3422 --- app/components/CopyToClipboard.ts | 7 +-- app/scenes/Settings/Tokens.tsx | 2 +- .../Settings/components/TokenListItem.tsx | 59 +++++++++++++++---- .../Settings/components/TokenRevokeDialog.tsx | 31 ++++++++++ shared/i18n/locales/en_US/translation.json | 7 +++ 5 files changed, 91 insertions(+), 15 deletions(-) create mode 100644 app/scenes/Settings/components/TokenRevokeDialog.tsx diff --git a/app/components/CopyToClipboard.ts b/app/components/CopyToClipboard.ts index 65279b29f..e9982a141 100644 --- a/app/components/CopyToClipboard.ts +++ b/app/components/CopyToClipboard.ts @@ -5,7 +5,7 @@ type Props = { text: string; children?: React.ReactElement; onClick?: React.MouseEventHandler; - onCopy: () => void; + onCopy?: () => void; }; class CopyToClipboard extends React.PureComponent { @@ -17,9 +17,8 @@ class CopyToClipboard extends React.PureComponent { 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); diff --git a/app/scenes/Settings/Tokens.tsx b/app/scenes/Settings/Tokens.tsx index a7a0d7c7a..5379b1fc1 100644 --- a/app/scenes/Settings/Tokens.tsx +++ b/app/scenes/Settings/Tokens.tsx @@ -61,7 +61,7 @@ function Tokens() { items={apiKeys.orderedData} heading={{t("Tokens")}} renderItem={(token: ApiKey) => ( - + )} /> Promise; }; -const TokenListItem = ({ token, onDelete }: Props) => { +const TokenListItem = ({ token }: Props) => { + const { t } = useTranslation(); + const { showToast } = useToasts(); + const { dialogs } = useStores(); + const [linkCopied, setLinkCopied] = React.useState(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: ( + + ), + }); + }, [t, dialogs, token]); + return ( - {token.name} – {token.secret} - - } + title={token.name} + subtitle={{token.secret}} actions={ - + <> + + + + + } /> ); diff --git a/app/scenes/Settings/components/TokenRevokeDialog.tsx b/app/scenes/Settings/components/TokenRevokeDialog.tsx new file mode 100644 index 000000000..2645ff57e --- /dev/null +++ b/app/scenes/Settings/components/TokenRevokeDialog.tsx @@ -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 ( + + {t("Are you sure you want to revoke the {{ tokenName }} token?", { + tokenName: token.name, + })} + + ); +} diff --git a/shared/i18n/locales/en_US/translation.json b/shared/i18n/locales/en_US/translation.json index 559ec2877..de561e871 100644 --- a/shared/i18n/locales/en_US/translation.json +++ b/shared/i18n/locales/en_US/translation.json @@ -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",