diff --git a/apps/client/src/widgets/type_widgets/content_widget.tsx b/apps/client/src/widgets/type_widgets/content_widget.tsx index 9ec12e032..5ae400896 100644 --- a/apps/client/src/widgets/type_widgets/content_widget.tsx +++ b/apps/client/src/widgets/type_widgets/content_widget.tsx @@ -40,6 +40,7 @@ import ImageSettings from "./options/images.jsx"; import AdvancedSettings from "./options/advanced.jsx"; import InternationalizationOptions from "./options/i18n.jsx"; import SyncOptions from "./options/sync.jsx"; +import EtapiSettings from "./options/etapi.js"; const TPL = /*html*/`
`; - -// TODO: Deduplicate -interface PostTokensResponse { - authToken: string; -} - -// TODO: Deduplicate -interface Token { - name: string; - utcDateCreated: number; - etapiTokenId: string; -} - -export default class EtapiOptions extends OptionsWidget { - - doRender() { - this.$widget = $(TPL); - - this.$widget.find(".create-etapi-token").on("click", async () => { - const tokenName = await dialogService.prompt({ - title: t("etapi.new_token_title"), - message: t("etapi.new_token_message"), - defaultValue: t("etapi.default_token_name") - }); - - if (!tokenName?.trim()) { - toastService.showError(t("etapi.error_empty_name")); - return; - } - - const { authToken } = await server.post("etapi-tokens", { tokenName }); - - await dialogService.prompt({ - title: t("etapi.token_created_title"), - message: t("etapi.token_created_message"), - defaultValue: authToken - }); - - this.refreshTokens(); - }); - - this.refreshTokens(); - } - - async refreshTokens() { - const $noTokensYet = this.$widget.find(".no-tokens-yet"); - const $tokensTable = this.$widget.find(".tokens-table"); - - const tokens = await server.get("etapi-tokens"); - - $noTokensYet.toggle(tokens.length === 0); - $tokensTable.toggle(tokens.length > 0); - - const $tokensTableBody = $tokensTable.find("tbody"); - $tokensTableBody.empty(); - - for (const token of tokens) { - $tokensTableBody.append( - $("") - .append($("").text(token.name)) - .append($("").text(formatDateTime(token.utcDateCreated))) - .append( - $("").append( - $(``).on("click", () => this.renameToken(token.etapiTokenId, token.name)), - $(``).on("click", () => this.deleteToken(token.etapiTokenId, token.name)) - ) - ) - ); - } - } - - async renameToken(etapiTokenId: string, oldName: string) { - const tokenName = await dialogService.prompt({ - title: t("etapi.rename_token_title"), - message: t("etapi.rename_token_message"), - defaultValue: oldName - }); - - if (!tokenName?.trim()) { - return; - } - - await server.patch(`etapi-tokens/${etapiTokenId}`, { name: tokenName }); - - this.refreshTokens(); - } - - async deleteToken(etapiTokenId: string, name: string) { - if (!(await dialogService.confirm(t("etapi.delete_token_confirmation", { name })))) { - return; - } - - await server.remove(`etapi-tokens/${etapiTokenId}`); - - this.refreshTokens(); - } -} diff --git a/apps/client/src/widgets/type_widgets/options/etapi.tsx b/apps/client/src/widgets/type_widgets/options/etapi.tsx new file mode 100644 index 000000000..ebd497ccc --- /dev/null +++ b/apps/client/src/widgets/type_widgets/options/etapi.tsx @@ -0,0 +1,139 @@ +import { useCallback, useEffect, useState } from "preact/hooks"; +import { t } from "../../../services/i18n"; +import Button from "../../react/Button"; +import FormText from "../../react/FormText"; +import RawHtml from "../../react/RawHtml"; +import OptionsSection from "./components/OptionsSection"; +import { EtapiToken, PostTokensResponse } from "@triliumnext/commons"; +import server from "../../../services/server"; +import toast from "../../../services/toast"; +import dialog from "../../../services/dialog"; +import { formatDateTime } from "../../../utils/formatters"; +import ActionButton from "../../react/ActionButton"; + +type RenameTokenCallback = (tokenId: string, oldName: string) => Promise; +type DeleteTokenCallback = (tokenId: string, name: string ) => Promise; + +export default function EtapiSettings() { + const [ tokens, setTokens ] = useState([]); + + function refreshTokens() { + server.get("etapi-tokens").then(setTokens); + } + + useEffect(refreshTokens, []); + + const createTokenCallback = useCallback(async () => { + const tokenName = await dialog.prompt({ + title: t("etapi.new_token_title"), + message: t("etapi.new_token_message"), + defaultValue: t("etapi.default_token_name") + }); + + if (!tokenName?.trim()) { + toast.showError(t("etapi.error_empty_name")); + return; + } + + const { authToken } = await server.post("etapi-tokens", { tokenName }); + + await dialog.prompt({ + title: t("etapi.token_created_title"), + message: t("etapi.token_created_message"), + defaultValue: authToken + }); + + refreshTokens(); + }, []); + + const renameTokenCallback = useCallback(async (tokenId: string, oldName: string) => { + const tokenName = await dialog.prompt({ + title: t("etapi.rename_token_title"), + message: t("etapi.rename_token_message"), + defaultValue: oldName + }); + + if (!tokenName?.trim()) { + return; + } + + await server.patch(`etapi-tokens/${tokenId}`, { name: tokenName }); + + refreshTokens(); + }, []); + + const deleteTokenCallback = useCallback(async (tokenId: string, name: string) => { + if (!(await dialog.confirm(t("etapi.delete_token_confirmation", { name })))) { + return; + } + + await server.remove(`etapi-tokens/${tokenId}`); + refreshTokens(); + }, []); + + return ( + + + {t("etapi.description")}
+ ${t("etapi.wiki")}`, + // TODO: We use window.open src/public/app/services/link.ts -> prevents regular click behavior on "a" element here because it's a relative path + link_to_openapi_spec: `${t("etapi.openapi_spec")}`, + link_to_swagger_ui: `${t("etapi.swagger_ui")}` + })} /> +
+ +