Ver código fonte

chore: build

Sv443 1 ano atrás
pai
commit
60fa178103
1 arquivos alterados com 290 adições e 14 exclusões
  1. 290 14
      dist/BetterYTM.user.js

+ 290 - 14
dist/BetterYTM.user.js

@@ -172,6 +172,44 @@ I welcome every contribution on GitHub!
       }
     });
 
+    /** Abstract class that can be extended to create an event emitter with helper methods and a strongly typed event map */
+    class NanoEmitter {
+        constructor() {
+            Object.defineProperty(this, "events", {
+                enumerable: true,
+                configurable: true,
+                writable: true,
+                value: createNanoEvents()
+            });
+            Object.defineProperty(this, "unsubscribers", {
+                enumerable: true,
+                configurable: true,
+                writable: true,
+                value: []
+            });
+        }
+        /** Subscribes to an event - returns a function that unsubscribes the event listener */
+        on(event, cb) {
+            // eslint-disable-next-line prefer-const
+            let unsub;
+            const unsubProxy = () => {
+                if (!unsub)
+                    return;
+                unsub();
+                this.unsubscribers = this.unsubscribers.filter(u => u !== unsub);
+            };
+            unsub = this.events.on(event, cb);
+            this.unsubscribers.push(unsub);
+            return unsubProxy;
+        }
+        /** Unsubscribes all event listeners */
+        unsubscribeAll() {
+            for (const unsub of this.unsubscribers)
+                unsub();
+            this.unsubscribers = [];
+        }
+    }
+
     // I know TS enums are impure but it doesn't really matter here, plus they look cooler
     var LogLevel;
     (function (LogLevel) {
@@ -190,11 +228,6 @@ I welcome every contribution on GitHub!
     const repo = "Sv443/BetterYTM";
     /** Which host the userscript was installed from */
     const host = (hostRaw.match(/^#{{.+}}$/) ? "github" : hostRaw);
-    const platformNames = {
-        github: "GitHub",
-        greasyfork: "GreasyFork",
-        openuserjs: "OpenUserJS",
-    };
     /**
      * How much info should be logged to the devtools console
      * 0 = Debug (show everything) or 1 = Info (show only important stuff)
@@ -205,7 +238,7 @@ I welcome every contribution on GitHub!
         name: GM.info.script.name,
         version: GM.info.script.version,
         namespace: GM.info.script.namespace,
-        buildNumber: "e5722da", // asserted as generic string instead of literal
+        buildNumber: "f5f1df1", // asserted as generic string instead of literal
     };
 
     /** Options that are applied to every SelectorObserver instance */
@@ -1818,8 +1851,8 @@ I welcome every contribution on GitHub!
             //#SECTION body
             const getChangelogHtml = (() => __awaiter(this, void 0, void 0, function* () {
                 try {
-                    const changelogHtmlFull = yield (yield UserUtils.fetchAdvanced(yield getResourceUrl("doc-changelog"))).text();
-                    return yield parseMarkdown(changelogHtmlFull);
+                    const changelogMd = yield getChangelogMd();
+                    return yield parseMarkdown(changelogMd);
                 }
                 catch (err) {
                     return `Error: ${err}`;
@@ -2890,6 +2923,43 @@ I welcome every contribution on GitHub!
         });
     }
 
+    let verNotifDialog = null;
+    /** Creates and/or returns the dialog to be shown when a new version is available */
+    function getVersionNotifDialog({ latestTag, }) {
+        return __awaiter(this, void 0, void 0, function* () {
+            if (!verNotifDialog) {
+                const changelogMdFull = yield getChangelogMd();
+                const changelogMd = changelogMdFull.split("<div class=\"split\">")[1];
+                const changelogHtml = yield parseMarkdown(changelogMd);
+                verNotifDialog = new BytmDialog({
+                    id: "version-notif",
+                    closeOnBgClick: false,
+                    closeOnEscPress: false,
+                    destroyOnClose: true,
+                    renderBody: () => renderBody({ latestTag, changelogHtml }),
+                });
+            }
+            return verNotifDialog;
+        });
+    }
+    function renderBody({ latestTag, changelogHtml, }) {
+        const platformNames = {
+            github: "GitHub",
+            greasyfork: "GreasyFork",
+            openuserjs: "OpenUserJS",
+        };
+        const wrapperEl = document.createElement("div");
+        const pEl = document.createElement("p");
+        pEl.textContent = t("new_version_available", scriptInfo.name, scriptInfo.version, latestTag, platformNames[host]);
+        wrapperEl.appendChild(pEl);
+        const btnEl = document.createElement("button");
+        btnEl.className = "bytm-btn";
+        btnEl.textContent = t("update_now");
+        btnEl.addEventListener("click", () => window.open(pkg.updates[host]));
+        wrapperEl.appendChild(btnEl);
+        return wrapperEl;
+    }
+
     const releaseURL = "https://github.com/Sv443/BetterYTM/releases/latest";
     function checkVersion() {
         var _a;
@@ -2908,15 +2978,11 @@ I welcome every contribution on GitHub!
                 const latestTag = (_a = res.finalUrl.split("/").pop()) === null || _a === void 0 ? void 0 : _a.replace(/[a-zA-Z]/g, "");
                 if (!latestTag)
                     return;
-                // const latestTag = "1.2.0";
                 const versionComp = compareVersions(scriptInfo.version, latestTag);
                 info("Version check - current version:", scriptInfo.version, "- latest version:", latestTag);
                 if (versionComp < 0) {
-                    // const dialog = getVersionNotifDialog({ latestTag });
-                    // await dialog.open();
-                    // TODO: replace with custom dialog
-                    if (confirm(t("new_version_available", scriptInfo.name, scriptInfo.version, latestTag, platformNames[host])))
-                        window.open(pkg.updates[host]);
+                    const dialog = yield getVersionNotifDialog({ latestTag });
+                    yield dialog.open();
                 }
             }
             catch (err) {
@@ -3394,6 +3460,210 @@ I welcome every contribution on GitHub!
         return trans;
     }
 
+    /** ID of the last opened (top-most) dialog */
+    let lastDialogId = null;
+    /** Creates and manages a modal dialog element */
+    class BytmDialog extends NanoEmitter {
+        constructor(options) {
+            super();
+            Object.defineProperty(this, "options", {
+                enumerable: true,
+                configurable: true,
+                writable: true,
+                value: void 0
+            });
+            Object.defineProperty(this, "id", {
+                enumerable: true,
+                configurable: true,
+                writable: true,
+                value: void 0
+            });
+            Object.defineProperty(this, "dialogOpen", {
+                enumerable: true,
+                configurable: true,
+                writable: true,
+                value: false
+            });
+            Object.defineProperty(this, "dialogRendered", {
+                enumerable: true,
+                configurable: true,
+                writable: true,
+                value: false
+            });
+            Object.defineProperty(this, "listenersAttached", {
+                enumerable: true,
+                configurable: true,
+                writable: true,
+                value: false
+            });
+            this.options = Object.assign({ closeOnBgClick: true, closeOnEscPress: true, closeBtnEnabled: true, destroyOnClose: false }, options);
+            this.id = options.id;
+        }
+        /** Call after DOMContentLoaded to pre-render the dialog (or call just before calling open()) */
+        render() {
+            return __awaiter(this, void 0, void 0, function* () {
+                if (this.dialogRendered)
+                    return;
+                this.dialogRendered = true;
+                const bgElem = document.createElement("div");
+                bgElem.id = `bytm-${this.id}-dialog-bg`;
+                bgElem.classList.add("bytm-dialog-bg");
+                if (this.options.closeOnBgClick)
+                    bgElem.ariaLabel = bgElem.title = t("close_menu_tooltip");
+                bgElem.style.visibility = "hidden";
+                bgElem.style.display = "none";
+                bgElem.inert = true;
+                bgElem.appendChild(yield this.getDialogContent());
+                document.body.appendChild(bgElem);
+                this.attachListeners(bgElem);
+                this.events.emit("render");
+            });
+        }
+        /** Clears all dialog contents (unmounts them from the DOM) in preparation for a new rendering call */
+        unmount() {
+            var _a;
+            this.dialogRendered = false;
+            const elem = document.querySelector(`#bytm-${this.id}-dialog-bg`);
+            elem && clearInner(elem);
+            (_a = document.querySelector(`#bytm-${this.id}-dialog-bg`)) === null || _a === void 0 ? void 0 : _a.remove();
+            this.events.emit("clear");
+        }
+        /** Clears and then re-renders the dialog */
+        rerender() {
+            return __awaiter(this, void 0, void 0, function* () {
+                this.unmount();
+                yield this.render();
+            });
+        }
+        /**
+         * Opens the dialog - renders it if it hasn't been rendered yet
+         * Prevents default action and immediate propagation of the passed event
+         */
+        open(e) {
+            var _a;
+            return __awaiter(this, void 0, void 0, function* () {
+                e === null || e === void 0 ? void 0 : e.preventDefault();
+                e === null || e === void 0 ? void 0 : e.stopImmediatePropagation();
+                if (this.isOpen())
+                    return;
+                this.dialogOpen = true;
+                if (!this.isRendered())
+                    yield this.render();
+                document.body.classList.add("bytm-disable-scroll");
+                (_a = document.querySelector("ytmusic-app")) === null || _a === void 0 ? void 0 : _a.setAttribute("inert", "true");
+                const dialogBg = document.querySelector(`#bytm-${this.id}-dialog-bg`);
+                if (!dialogBg)
+                    return warn(`Couldn't find background element for dialog with ID '${this.id}'`);
+                dialogBg.style.visibility = "visible";
+                dialogBg.style.display = "block";
+                dialogBg.inert = false;
+                lastDialogId = this.id;
+                this.events.emit("open");
+            });
+        }
+        /** Closes the dialog - prevents default action and immediate propagation of the passed event */
+        close(e) {
+            var _a;
+            e === null || e === void 0 ? void 0 : e.preventDefault();
+            e === null || e === void 0 ? void 0 : e.stopImmediatePropagation();
+            if (!this.isOpen())
+                return;
+            this.dialogOpen = false;
+            document.body.classList.remove("bytm-disable-scroll");
+            (_a = document.querySelector("ytmusic-app")) === null || _a === void 0 ? void 0 : _a.removeAttribute("inert");
+            const dialogBg = document.querySelector(`#bytm-${this.id}-dialog-bg`);
+            if (!dialogBg)
+                return warn(`Couldn't find background element for dialog with ID '${this.id}'`);
+            dialogBg.style.visibility = "hidden";
+            dialogBg.style.display = "none";
+            dialogBg.inert = true;
+            if (BytmDialog.getLastDialogId() === this.id)
+                lastDialogId = null;
+            this.events.emit("close");
+            if (this.options.destroyOnClose)
+                this.destroy();
+        }
+        /** Returns true if the dialog is open */
+        isOpen() {
+            return this.dialogOpen;
+        }
+        /** Returns true if the dialog has been rendered */
+        isRendered() {
+            return this.dialogRendered;
+        }
+        /** Clears the dialog and removes all event listeners */
+        destroy() {
+            this.events.emit("destroy");
+            this.unmount();
+            this.unsubscribeAll();
+        }
+        /** Returns the ID of the top-most dialog (the dialog that has been opened last) */
+        static getLastDialogId() {
+            return lastDialogId;
+        }
+        /** Called once to attach all generic event listeners */
+        attachListeners(bgElem) {
+            if (this.listenersAttached)
+                return;
+            this.listenersAttached = true;
+            if (this.options.closeOnBgClick) {
+                bgElem.addEventListener("click", (e) => {
+                    var _a;
+                    if (this.isOpen() && ((_a = e.target) === null || _a === void 0 ? void 0 : _a.id) === `bytm-${this.id}-dialog-bg`)
+                        this.close(e);
+                });
+            }
+            if (this.options.closeOnEscPress) {
+                document.body.addEventListener("keydown", (e) => {
+                    if (e.key === "Escape" && this.isOpen() && BytmDialog.getLastDialogId() === this.id)
+                        this.close(e);
+                });
+            }
+        }
+        getDialogContent() {
+            var _a, _b, _c, _d;
+            return __awaiter(this, void 0, void 0, function* () {
+                const header = (_b = (_a = this.options).renderHeader) === null || _b === void 0 ? void 0 : _b.call(_a);
+                const footer = (_d = (_c = this.options).renderFooter) === null || _d === void 0 ? void 0 : _d.call(_c);
+                const dialogWrapperEl = document.createElement("div");
+                dialogWrapperEl.id = `bytm-${this.id}-dialog`;
+                dialogWrapperEl.classList.add("bytm-dialog");
+                dialogWrapperEl.ariaLabel = dialogWrapperEl.title = "";
+                //#SECTION header
+                const headerWrapperEl = document.createElement("div");
+                headerWrapperEl.classList.add("bytm-dialog-header");
+                if (header) {
+                    const headerTitleWrapperEl = document.createElement("div");
+                    headerTitleWrapperEl.classList.add("bytm-dialog-title-wrapper");
+                    headerTitleWrapperEl.role = "heading";
+                    headerTitleWrapperEl.ariaLevel = "1";
+                    headerTitleWrapperEl.appendChild(header);
+                    headerWrapperEl.appendChild(headerTitleWrapperEl);
+                }
+                if (this.options.closeBtnEnabled) {
+                    const closeBtnEl = document.createElement("img");
+                    closeBtnEl.classList.add("bytm-dialog-close");
+                    closeBtnEl.src = yield getResourceUrl("img-close");
+                    closeBtnEl.role = "button";
+                    closeBtnEl.tabIndex = 0;
+                    closeBtnEl.addEventListener("click", () => this.close());
+                    headerWrapperEl.appendChild(closeBtnEl);
+                }
+                dialogWrapperEl.appendChild(headerWrapperEl);
+                // TODO:
+                //#SECTION body
+                const bodyWrapperEl = document.createElement("div");
+                bodyWrapperEl.appendChild(this.options.renderBody());
+                dialogWrapperEl.appendChild(bodyWrapperEl);
+                //#SECTION footer
+                if (footer) {
+                    dialogWrapperEl.appendChild(footer);
+                }
+                return dialogWrapperEl;
+            });
+        }
+    }
+
     //#SECTION video time
     const videoSelector = getDomain() === "ytm" ? "ytmusic-player video" : "#content ytd-player video";
     /**
@@ -3617,6 +3887,12 @@ I welcome every contribution on GitHub!
             gfm: true,
         });
     }
+    /** Returns the content of the changelog markdown file */
+    function getChangelogMd() {
+        return __awaiter(this, void 0, void 0, function* () {
+            return yield (yield UserUtils.fetchAdvanced(yield getResourceUrl("doc-changelog"))).text();
+        });
+    }
 
     const selectorMap = new Map();
     /**