Переглянути джерело

feat: functions for up-& downloading DataStoreSerializer data as file

Sv443 5 місяців тому
батько
коміт
1417df26d0
5 змінених файлів з 82 додано та 22 видалено
  1. 1 1
      src/features/index.ts
  2. 3 1
      src/index.ts
  3. 65 0
      src/serializer.ts
  4. 0 20
      src/storeSerializer.ts
  5. 13 0
      src/utils/dom.ts

+ 1 - 1
src/features/index.ts

@@ -8,7 +8,7 @@ import langMapping from "../../assets/locales.json" with { type: "json" };
 import { getAutoLikeDialog, getPluginListDialog, showPrompt } from "../dialogs/index.js";
 import { showIconToast } from "../components/index.js";
 import { mode } from "../constants.js";
-import { getStoreSerializer } from "../storeSerializer.js";
+import { getStoreSerializer } from "../serializer.js";
 
 //#region re-exports
 

+ 3 - 1
src/index.ts

@@ -35,7 +35,7 @@ import {
   // menu
   addConfigMenuOptionYT, addConfigMenuOptionYTM,
 } from "./features/index.js";
-import { getStoreSerializer } from "./storeSerializer.js";
+import { downloadData, getStoreSerializer } from "./serializer.js";
 import { MarkdownDialog } from "./components/index.js";
 
 //#region cns. watermark
@@ -531,6 +531,8 @@ function registerDevCommands() {
     await mdDlg.open();
   });
 
+  GM.registerMenuCommand("Download DataStoreSerializer file", () => downloadData());
+
   log("Registered dev menu commands");
 }
 

+ 65 - 0
src/serializer.ts

@@ -0,0 +1,65 @@
+import { DataStoreSerializer } from "@sv443-network/userutils";
+
+import { configStore } from "./config.js";
+import { autoLikeStore } from "./features/input.js";
+import { showPrompt } from "./dialogs/prompt.js";
+import { t } from "./utils/translations.js";
+import { error } from "./utils/logging.js";
+import { downloadFile } from "./utils/dom.js";
+import packageJson from "../package.json" with { type: "json" };
+
+/** Central serializer for all data stores */
+let serializer: DataStoreSerializer | undefined;
+
+/** Returns the serializer for all data stores */
+export function getStoreSerializer() {
+  if(!serializer) {
+    serializer = new DataStoreSerializer([
+      configStore,
+      autoLikeStore,
+    ], {
+      addChecksum: true,
+      ensureIntegrity: true,
+    });
+  }
+  return serializer;
+}
+
+/** Imports data from a file into all data stores */
+export async function importData(blob: File | Blob) {
+  try {
+    const serializer = getStoreSerializer();
+
+    const data = await blob.text();
+    await serializer.deserialize(data);
+
+    const reload = await showPrompt({
+      type: "confirm",
+      message: t("import_success_confirm_reload"),
+    });
+
+    reload && location.reload();
+  }
+  catch(err) {
+    error(err);
+    await showPrompt({
+      type: "alert",
+      message: t("import_error_invalid"),
+    });
+  }
+}
+
+/** Downloads the current data stores as a single file */
+export async function downloadData() {
+  const serializer = getStoreSerializer();
+
+  const pad = (num: number, len = 2) => String(num).padStart(len, "0");
+
+  const d = new Date();
+  const dateStr = `${pad(d.getFullYear(), 4)}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}_${pad(d.getHours())}-${pad(d.getMinutes())}`;
+  const fileName = `BetterYTM ${packageJson.version} data export ${dateStr}.json`;
+
+  const data = JSON.stringify(JSON.parse(await serializer.serialize()), undefined, 2);
+
+  downloadFile(fileName, data, "application/json");
+}

+ 0 - 20
src/storeSerializer.ts

@@ -1,20 +0,0 @@
-import { DataStoreSerializer } from "@sv443-network/userutils";
-
-import { configStore } from "./config.js";
-import { autoLikeStore } from "./features/input.js";
-
-/** Central serializer for all data stores */
-let serializer: DataStoreSerializer | undefined;
-
-export function getStoreSerializer() {
-  if(!serializer) {
-    serializer = new DataStoreSerializer([
-      configStore,
-      autoLikeStore,
-    ], {
-      addChecksum: true,
-      ensureIntegrity: true,
-    });
-  }
-  return serializer;
-}

+ 13 - 0
src/utils/dom.ts

@@ -273,3 +273,16 @@ export function setInnerHtml(element: HTMLElement, html: string) {
   element.innerHTML = ttPolicy?.createHTML(html)
     ?? DOMPurify.sanitize(html, { RETURN_TRUSTED_TYPE: false });
 }
+
+/** Creates an invisible link element and clicks it to download the provided string or Blob data as a file */
+export function downloadFile(fileName: string, data: string | Blob, mimeType = "text/plain") {
+  const blob = data instanceof Blob ? data : new Blob([data], { type: mimeType });
+  const a = document.createElement("a");
+  a.classList.add("bytm-hidden");
+  a.href = URL.createObjectURL(blob);
+  a.download = fileName;
+  document.body.appendChild(a);
+  a.click();
+
+  setTimeout(() => a.remove(), 50);
+}