diff --git a/apps/client/src/widgets/dialogs/export.css b/apps/client/src/widgets/dialogs/export.css new file mode 100644 index 000000000..a3916d1be --- /dev/null +++ b/apps/client/src/widgets/dialogs/export.css @@ -0,0 +1,16 @@ +.export-dialog form .form-check { + padding-top: 10px; + padding-bottom: 10px; +} + +.export-dialog form .format-choice { + padding-left: 40px; +} + +.export-dialog form .opml-versions { + padding-left: 60px; +} + +.export-dialog form .form-check-label { + padding: 2px; +} \ No newline at end of file diff --git a/apps/client/src/widgets/dialogs/export.ts b/apps/client/src/widgets/dialogs/export.ts deleted file mode 100644 index d9b13f4ed..000000000 --- a/apps/client/src/widgets/dialogs/export.ts +++ /dev/null @@ -1,264 +0,0 @@ -import treeService from "../../services/tree.js"; -import utils from "../../services/utils.js"; -import ws from "../../services/ws.js"; -import toastService, { type ToastOptions } from "../../services/toast.js"; -import froca from "../../services/froca.js"; -import openService from "../../services/open.js"; -import BasicWidget from "../basic_widget.js"; -import { t } from "../../services/i18n.js"; -import type { EventData } from "../../components/app_context.js"; -import { Modal } from "bootstrap"; -import { openDialog } from "../../services/dialog.js"; - -const TPL = /*html*/` -`; - -export default class ExportDialog extends BasicWidget { - - private taskId: string; - private branchId: string | null; - private modal?: Modal; - private $form!: JQuery; - private $noteTitle!: JQuery; - private $subtreeFormats!: JQuery; - private $singleFormats!: JQuery; - private $subtreeType!: JQuery; - private $singleType!: JQuery; - private $exportButton!: JQuery; - private $opmlVersions!: JQuery; - - constructor() { - super(); - - this.taskId = ""; - this.branchId = null; - } - - doRender() { - this.$widget = $(TPL); - this.modal = Modal.getOrCreateInstance(this.$widget[0]); - this.$form = this.$widget.find(".export-form"); - this.$noteTitle = this.$widget.find(".export-note-title"); - this.$subtreeFormats = this.$widget.find(".export-subtree-formats"); - this.$singleFormats = this.$widget.find(".export-single-formats"); - this.$subtreeType = this.$widget.find(".export-type-subtree"); - this.$singleType = this.$widget.find(".export-type-single"); - this.$exportButton = this.$widget.find(".export-button"); - this.$opmlVersions = this.$widget.find(".opml-versions"); - - this.$form.on("submit", () => { - this.modal?.hide(); - - const exportType = this.$widget.find("input[name='export-type']:checked").val(); - - if (!exportType) { - toastService.showError(t("export.choose_export_type")); - return; - } - - const exportFormat = exportType === "subtree" ? this.$widget.find("input[name=export-subtree-format]:checked").val() : this.$widget.find("input[name=export-single-format]:checked").val(); - - const exportVersion = exportFormat === "opml" ? this.$widget.find("input[name='opml-version']:checked").val() : "1.0"; - - if (this.branchId) { - this.exportBranch(this.branchId, String(exportType), String(exportFormat), String(exportVersion)); - } - - return false; - }); - - this.$widget.find("input[name=export-type]").on("change", (e) => { - if ((e.currentTarget as HTMLInputElement).value === "subtree") { - if (this.$widget.find("input[name=export-subtree-format]:checked").length === 0) { - this.$widget.find("input[name=export-subtree-format]:first").prop("checked", true); - } - - this.$subtreeFormats.slideDown(); - this.$singleFormats.slideUp(); - } else { - if (this.$widget.find("input[name=export-single-format]:checked").length === 0) { - this.$widget.find("input[name=export-single-format]:first").prop("checked", true); - } - - this.$subtreeFormats.slideUp(); - this.$singleFormats.slideDown(); - } - }); - - this.$widget.find("input[name=export-subtree-format]").on("change", (e) => { - if ((e.currentTarget as HTMLInputElement).value === "opml") { - this.$opmlVersions.slideDown(); - } else { - this.$opmlVersions.slideUp(); - } - }); - } - - async showExportDialogEvent({ notePath, defaultType }: EventData<"showExportDialog">) { - this.taskId = ""; - this.$exportButton.removeAttr("disabled"); - - if (defaultType === "subtree") { - this.$subtreeType.prop("checked", true).trigger("change"); - - this.$widget.find("input[name=export-subtree-format]:checked").trigger("change"); - } else if (defaultType === "single") { - this.$singleType.prop("checked", true).trigger("change"); - } else { - throw new Error(`Unrecognized type '${defaultType}'`); - } - - this.$widget.find(".opml-v2").prop("checked", true); // setting default - - openDialog(this.$widget); - - const { noteId, parentNoteId } = treeService.getNoteIdAndParentIdFromUrl(notePath); - - if (parentNoteId) { - this.branchId = await froca.getBranchId(parentNoteId, noteId); - } - if (noteId) { - this.$noteTitle.text(await treeService.getNoteTitle(noteId)); - } - } - - exportBranch(branchId: string, type: string, format: string, version: string) { - this.taskId = utils.randomString(10); - - const url = openService.getUrlForDownload(`api/branches/${branchId}/export/${type}/${format}/${version}/${this.taskId}`); - - openService.download(url); - } -} - -ws.subscribeToMessages(async (message) => { - function makeToast(id: string, message: string): ToastOptions { - return { - id: id, - title: t("export.export_status"), - message: message, - icon: "arrow-square-up-right" - }; - } - - if (message.taskType !== "export") { - return; - } - - if (message.type === "taskError") { - toastService.closePersistent(message.taskId); - toastService.showError(message.message); - } else if (message.type === "taskProgressCount") { - toastService.showPersistent(makeToast(message.taskId, t("export.export_in_progress", { progressCount: message.progressCount }))); - } else if (message.type === "taskSucceeded") { - const toast = makeToast(message.taskId, t("export.export_finished_successfully")); - toast.closeAfter = 5000; - - toastService.showPersistent(toast); - } -}); diff --git a/apps/client/src/widgets/dialogs/export.tsx b/apps/client/src/widgets/dialogs/export.tsx new file mode 100644 index 000000000..4a6b2c39d --- /dev/null +++ b/apps/client/src/widgets/dialogs/export.tsx @@ -0,0 +1,163 @@ +import { useState } from "preact/hooks"; +import { EventData } from "../../components/app_context"; +import { closeActiveDialog, openDialog } from "../../services/dialog"; +import { t } from "../../services/i18n"; +import tree from "../../services/tree"; +import Button from "../react/Button"; +import FormRadioGroup from "../react/FormRadioGroup"; +import Modal from "../react/Modal"; +import ReactBasicWidget from "../react/ReactBasicWidget"; +import "./export.css"; +import ws from "../../services/ws"; +import toastService, { ToastOptions } from "../../services/toast"; +import utils from "../../services/utils"; +import open from "../../services/open"; +import froca from "../../services/froca"; + +interface ExportDialogProps { + branchId?: string | null; + noteTitle?: string; + defaultType?: "subtree" | "single"; +} + +function ExportDialogComponent({ branchId, noteTitle, defaultType }: ExportDialogProps) { + const [ exportType, setExportType ] = useState(defaultType ?? "subtree"); + const [ subtreeFormat, setSubtreeFormat ] = useState("html"); + const [ singleFormat, setSingleFormat ] = useState("html"); + const [ opmlVersion, setOpmlVersion ] = useState("2.0"); + + return (branchId && + { + const format = (exportType === "subtree" ? subtreeFormat : singleFormat); + const version = (format === "opml" ? opmlVersion : "1.0"); + exportBranch(branchId, exportType, format, version); + closeActiveDialog(); + }} + footer={