Explorar o código

fix: ExIm dialog text props

Sven hai 10 meses
pai
achega
932d7ea491
Modificáronse 3 ficheiros con 116 adicións e 18 borrados
  1. 2 2
      changelog.md
  2. 95 0
      contributing.md
  3. 19 16
      src/components/ExImDialog.ts

+ 2 - 2
changelog.md

@@ -46,11 +46,11 @@
     - `showToast()` to show a custom toast notification with a message string or element and duration
     - `showIconToast()` to show a custom toast notification with a message string or element, icon and duration
     - `createRipple()` to create a click ripple animation effect on a given element (experimental)
-    - TODO: `ExImDialog` class for creating a BytmDialog instance that is designed for exporting and importing generic data as a string
+    - `ExImDialog` class for creating a BytmDialog instance that is designed for exporting and importing generic data as a string
   - Added functions:
     - `getAutoLikeData()` to return the current auto-like data (authenticated function)
     - `saveAutoLikeData()` to overwrite the auto-like data (authenticated function)
-    - `fetchVideoVotes()` to fetch the approximate like and dislike count of a video from [ReturnYoutubeDislikes](https://returnyoutubedislike.com/)
+    - `fetchVideoVotes()` to fetch the approximate like and dislike count of a video from [Return Youtube Dislike](https://returnyoutubedislike.com/)
   - Added new SelectorObserver instance `browseResponse` for pages like `/channel/{id}`
   - Added library `compare-versions` to the plugin interface at `unsafeWindow.BYTM.compareVersions` for easier plugin version comparison
   - Added events

+ 95 - 0
contributing.md

@@ -309,6 +309,7 @@ Functions marked with 🔒 need to be passed a per-session and per-plugin authen
   - [getSessionId()](#getsessionid) - Returns the unique session ID that is generated on every started session
 - DOM:
   - [BytmDialog](#bytmdialog) - A class for creating and managing dialogs
+  - [ExImDialog](#eximdialog) - Subclass of BytmDialog for allowing users to export and import serializable data
   - [addSelectorListener()](#addselectorlistener) - Adds a listener that checks for changes in DOM elements matching a CSS selector
   - [onInteraction()](#oninteraction) - Adds accessible event listeners to the specified element for button or link-like keyboard and mouse interactions
   - [getVideoTime()](#getvideotime) - Returns the current video time (on both YT and YTM)
@@ -1330,6 +1331,100 @@ Functions marked with 🔒 need to be passed a per-session and per-plugin authen
 
 <br>
 
+> #### ExImDialog
+> Usage:
+> ```ts
+> new unsafeWindow.BYTM.ExImDialog(options: ExImDialogOptions): ExImDialog
+> ```
+>   
+> A subclass of [BytmDialog](#bytmdialog) that can be used to create and manage a generic export/import dialog.  
+>   
+> Features:
+> - Has all the features of the [BytmDialog](#bytmdialog) class
+> - Can be used to export and import any kind of data that can be serialized to a string
+> - Built-in textareas for the user to paste or copy data to/from
+> - Copy to clipboard button for the export textarea
+> - Ability to copy a second variety of the data when shift-clicking the copy button
+> - Exported data is hidden by default in case it contains sensitive information
+> - Based on BYTM's translation system for all text content
+>   
+> Options properties:  
+> All properties from the [BytmDialog](#bytmdialog) class are available here as well, except for `renderHeader`, `renderBody` and `renderFooter`  
+> | Property | Description |
+> | :-- | :-- |
+> | `title: string \| (() => (string \| Promise<string>))` | Title of the dialog |
+> | `descImport: string \| (() => (string \| Promise<string>))` | Description of the dialog when importing |
+> | `descExport: string \| (() => (string \| Promise<string>))` | Description of the dialog when exporting |
+> | `onImport: (data: string) => void` | Callback function that gets called when the user imports data |
+> | `exportData: string \| (() => (string \| Promise<string>))` | The data to export (or a function that returns the data as string, either sync or async) |
+> | `exportDataSpecial?: string \| (() => (string \| Promise<string>))` | Optional variant of the data, used for special cases like when shift-clicking the copy button |
+>   
+> <details><summary><b>Example <i>(click to expand)</i></b></summary>
+> 
+> ```ts
+> const exImDialog = new unsafeWindow.BYTM.ExImDialog({
+>   id: "my-exim-dialog",
+>   width: 500,
+>   height: 400,
+>   exportData,
+>   exportDataSpecial,
+>   onImport,
+>   title: "My ExIm Dialog",
+>   descImport: "Past the data you want to import below and click import!",
+>   descExport: "Copy the data below to save it or share it with others. Warning: may contain sensitive information!",
+> });
+> 
+> type MyDataType = { foo: string };
+> 
+> async function exportData() {
+>   // compress the data to save space
+>   const exportData = JSON.stringify({ foo: "bar" });
+>   return await unsafeWindow.BYTM.UserUtils.compress(exportData, "deflate");
+> }
+> 
+> function exportDataSpecial() {
+>   // return the data uncompressed when shift-clicking the copy button
+>   return JSON.stringify({ foo: "bar" });
+> }
+> 
+> async function onImport(data: string) {
+>   let decompData: string;
+>   try {
+>     // since the data could either be compressed or not, try to decompress it and see if it errors
+>     decompData = await unsafeWindow.BYTM.UserUtils.decompress(data, "deflate");
+>   }
+>   catch {
+>     // the data is not compressed, so just use it as is
+>     decompData = data;
+>   }
+> 
+>   let parsedData: MyDataType;
+>   try {
+>     parsedData = JSON.parse(decompData);
+>   }
+>   catch(err) {
+>     console.error("The user imported invalid data");
+>     return;
+>   }
+> 
+>   console.log("The user successfully imported data:", parsedData);
+> }
+> 
+> async function run() {
+>   exImDialog.on("close", () => {
+>     console.log("The dialog was closed");
+>   });
+> 
+>   await exImDialog.open();
+>   console.log("The dialog is now open");
+> }
+> 
+> run();
+> ```
+> </details>
+
+<br>
+
 > #### createHotkeyInput()
 > Usage:  
 > ```ts

+ 19 - 16
src/components/ExImDialog.ts

@@ -1,28 +1,25 @@
 import { BytmDialog, type BytmDialogOptions } from "./BytmDialog.js";
-import { t, type TrKey } from "../utils/translations.js";
-import { scriptInfo } from "../constants.js";
+import { t } from "../utils/translations.js";
 import { onInteraction } from "../utils/input.js";
 import { copyToClipboard } from "../utils/dom.js";
 import { createLongBtn, createRipple, showToast } from "./index.js";
 import "./ExImDialog.css";
 
 type ExImDialogOpts =
-  Omit<BytmDialogOptions, "renderHeader" | "renderBody" | "renderFooter">
+  & Omit<BytmDialogOptions, "renderHeader" | "renderBody" | "renderFooter">
   & {
+    /** Title of the dialog */
+    title: string | (() => (string | Promise<string>));
+    /** Description when importing */
+    descImport: string | (() => (string | Promise<string>));
+    /** Description when exporting */
+    descExport: string | (() => (string | Promise<string>));
+    /** Function that gets called when the user imports data */
+    onImport: (data: string) => void;
     /** The data to export (or a function that returns the data as string, sync or async) */
     exportData: string | (() => (string | Promise<string>));
     /** Optional variant of the data, used for special cases like when shift-clicking the copy button */
     exportDataSpecial?: string | (() => (string | Promise<string>));
-    /** Function that gets called when the user imports data */
-    onImport: (data: string) => void;
-    /** Translation key for the dialog title */
-    trKeyTitle: TrKey | (string & {});
-    /** Translation key for the dialog description when importing */
-    trKeyDescImport: TrKey | (string & {});
-    /** Translation key for the dialog description when exporting */
-    trKeyDescExport: TrKey | (string & {});
-    /** Whether the data should be hidden by default when exporting */
-    dataHidden?: boolean;
   };
 
 //#region class
@@ -51,7 +48,9 @@ export class ExImDialog extends BytmDialog {
     headerEl.role = "heading";
     headerEl.ariaLevel = "1";
     headerEl.tabIndex = 0;
-    headerEl.textContent = headerEl.ariaLabel = t(opts.trKeyTitle as "_", scriptInfo.name);
+    headerEl.textContent = headerEl.ariaLabel = typeof opts.title === "function"
+      ? await opts.title()
+      : opts.title;
 
     return headerEl;
   }
@@ -72,7 +71,9 @@ export class ExImDialog extends BytmDialog {
       descEl.classList.add("bytm-exim-dialog-desc");
       descEl.role = "note";
       descEl.tabIndex = 0;
-      descEl.textContent = descEl.ariaLabel = t(opts.trKeyDescExport);
+      descEl.textContent = descEl.ariaLabel = typeof opts.descExport === "function"
+        ? await opts.descExport()
+        : opts.descExport;
 
       const dataEl = document.createElement("textarea");
       dataEl.classList.add("bytm-exim-dialog-data");
@@ -111,7 +112,9 @@ export class ExImDialog extends BytmDialog {
       descEl.classList.add("bytm-exim-dialog-desc");
       descEl.role = "note";
       descEl.tabIndex = 0;
-      descEl.textContent = descEl.ariaLabel = t(opts.trKeyDescImport);
+      descEl.textContent = descEl.ariaLabel = typeof opts.descImport === "function"
+        ? await opts.descImport()
+        : opts.descImport;
 
       const dataEl = document.createElement("textarea");
       dataEl.classList.add("bytm-exim-dialog-data");