Ver código fonte

feat: replace img icons with svg elements

Sv443 4 meses atrás
pai
commit
2fbc5569d5

+ 1 - 1
src/components/longButton.ts

@@ -92,7 +92,7 @@ export async function createLongBtn({
   if("src" in rest)
     (imgElem as HTMLImageElement).src = rest.src;
   else
-    setInnerHtml(imgElem, await resourceAsString(rest.resourceName as "_") ?? "");
+    setInnerHtml(imgElem, await resourceAsString(rest.resourceName as "_"));
 
   const txtElem = document.createElement("span");
   txtElem.classList.add("bytm-generic-long-btn-txt", "bytm-no-select");

+ 1 - 0
src/components/toggleInput.ts

@@ -47,6 +47,7 @@ export async function createToggleInput({
 
   const toggleKnobEl = document.createElement("div");
   toggleKnobEl.classList.add("bytm-toggle-input-knob");
+  // TODO: this doesn't make the knob show up on Chromium
   setInnerHtml(toggleKnobEl, " ");
 
   const toggleElClicked = (e: Event) => {

+ 1 - 3
src/dialogs/featHelp.ts

@@ -37,9 +37,7 @@ export async function getFeatHelpDialog({
 
 async function renderHeader() {
   const headerEl = document.createElement("div");
-  const helpIconSvg = await resourceAsString("icon-help");
-  if(helpIconSvg)
-    setInnerHtml(headerEl, helpIconSvg);
+  setInnerHtml(headerEl, await resourceAsString("icon-help"));
 
   return headerEl;
 }

+ 1 - 3
src/dialogs/prompt.ts

@@ -72,9 +72,7 @@ class PromptDialog extends BytmDialog {
   protected async renderHeader({ type }: PromptDialogRenderProps) {
     const headerEl = document.createElement("div");
     headerEl.id = "bytm-prompt-dialog-header";
-    const iconSvg = await resourceAsString(type === "alert" ? "icon-alert" : "icon-prompt");
-    if(iconSvg)
-      setInnerHtml(headerEl, iconSvg);
+    setInnerHtml(headerEl, await resourceAsString(type === "alert" ? "icon-alert" : "icon-prompt"));
 
     return headerEl;
   }

+ 2 - 6
src/features/input.ts

@@ -397,9 +397,7 @@ async function addAutoLikeToggleBtn(siblingEl: HTMLElement, channelId: string, c
         const chanId = sanitizeChannelId(buttonEl.dataset.channelId ?? channelId);
 
         const imgEl = buttonEl.querySelector<HTMLElement>(".bytm-generic-btn-img");
-        const imgHtml = await resourceAsString(`icon-auto_like${toggled ? "_enabled" : ""}`);
-        if(imgEl && imgHtml)
-          setInnerHtml(imgEl, imgHtml);
+        imgEl && setInnerHtml(imgEl, await resourceAsString(`icon-auto_like${toggled ? "_enabled" : ""}`));
 
         if(autoLikeStore.getData().channels.find((ch) => ch.id === chanId) === undefined) {
           await autoLikeStore.setData({
@@ -447,8 +445,6 @@ async function addAutoLikeToggleBtn(siblingEl: HTMLElement, channelId: string, c
       buttonEl.classList.remove("toggled");
 
     const imgEl = buttonEl.querySelector<HTMLElement>(".bytm-generic-btn-img");
-    const imgHtml = await resourceAsString(`icon-auto_like${enabled ? "_enabled" : ""}`);
-    if(imgEl && imgHtml)
-      setInnerHtml(imgEl, imgHtml);
+    imgEl && setInnerHtml(imgEl, await resourceAsString(`icon-auto_like${enabled ? "_enabled" : ""}`));
   });
 }

+ 10 - 9
src/features/layout.ts

@@ -2,7 +2,7 @@ import { addParent, autoPlural, debounce, fetchAdvanced, pauseFor } from "@sv443
 import { getFeature, getFeatures } from "../config.js";
 import { siteEvents } from "../siteEvents.js";
 import { addSelectorListener } from "../observers.js";
-import { error, getResourceUrl, log, warn, t, onInteraction, openInTab, getBestThumbnailUrl, getDomain, getCurrentMediaType, domLoaded, waitVideoElementReady, addStyleFromResource, fetchVideoVotes, getWatchId, tp, getVideoTime, setInnerHtml, formatNumber } from "../utils/index.js";
+import { error, getResourceUrl, log, warn, t, onInteraction, openInTab, getBestThumbnailUrl, getDomain, getCurrentMediaType, domLoaded, waitVideoElementReady, addStyleFromResource, fetchVideoVotes, getWatchId, tp, getVideoTime, setInnerHtml, formatNumber, resourceAsString } from "../utils/index.js";
 import { mode, scriptInfo } from "../constants.js";
 import { openCfgMenu } from "../menu/menu_old.js";
 import { createCircularBtn, createRipple } from "../components/index.js";
@@ -509,10 +509,12 @@ export async function initThumbnailOverlay() {
       if(getFeature("thumbnailOverlayToggleBtnShown")) {
         addSelectorListener("playerBarMiddleButtons", "#bytm-thumbnail-overlay-toggle", {
           async listener(toggleBtnElem) {
-            const toggleBtnImgElem = toggleBtnElem.querySelector<HTMLImageElement>("img");
+            const toggleBtnIconElem = toggleBtnElem.querySelector<HTMLImageElement>("svg");
 
-            if(toggleBtnImgElem)
-              toggleBtnImgElem.src = await getResourceUrl(`icon-image${showOverlay ? "_filled" : ""}` as "icon-image" | "icon-image_filled");
+            if(toggleBtnIconElem) {
+              setInnerHtml(toggleBtnElem, await resourceAsString(`icon-image${showOverlay ? "_filled" : ""}` as "icon-image" | "icon-image_filled"));
+              toggleBtnElem.querySelector("svg")?.classList.add("bytm-generic-btn-img");
+            }
             if(toggleBtnElem)
               toggleBtnElem.ariaLabel = toggleBtnElem.title = t(`thumbnail_overlay_toggle_btn_tooltip${showOverlay ? "_hide" : "_show"}`);
           },
@@ -613,13 +615,12 @@ export async function initThumbnailOverlay() {
             updateOverlayVisibility();
           });
 
-          const imgElem = document.createElement("img");
-          imgElem.classList.add("bytm-generic-btn-img");
-
-          toggleBtnElem.appendChild(imgElem);
+          setInnerHtml(toggleBtnElem, await resourceAsString("icon-image"));
+          toggleBtnElem.querySelector("svg")?.classList.add("bytm-generic-btn-img");
 
           addSelectorListener("playerBarMiddleButtons", "ytmusic-like-button-renderer#like-button-renderer", {
-            listener: (likeContainer) => likeContainer.insertAdjacentElement("afterend", toggleBtnElem),
+            listener: (likeContainer) =>
+              likeContainer.insertAdjacentElement("afterend", toggleBtnElem),
           });
         }
 

+ 9 - 16
src/features/lyrics.ts

@@ -1,5 +1,5 @@
 import { fetchAdvanced } from "@sv443-network/userutils";
-import { error, getResourceUrl, info, log, warn, t, tp, getCurrentMediaType, constructUrl, onInteraction, openInTab, LyricsError } from "../utils/index.js";
+import { error, info, log, warn, t, tp, getCurrentMediaType, constructUrl, onInteraction, openInTab, LyricsError, resourceAsString, setInnerHtml } from "../utils/index.js";
 import { emitInterface } from "../interface.js";
 import { mode, scriptInfo } from "../constants.js";
 import { getFeature } from "../config.js";
@@ -29,10 +29,6 @@ async function addActualLyricsBtn(likeContainer: HTMLElement) {
 
   currentSongTitle = songTitleElem.title;
 
-  const spinnerIconUrl = await getResourceUrl("icon-spinner");
-  const lyricsIconUrl = await getResourceUrl("icon-lyrics");
-  const errorIconUrl = await getResourceUrl("icon-error");
-
   const onMutation = async (mutations: MutationRecord[]) => {
     for await(const mut of mutations) {
       const newTitle = (mut.target as HTMLElement).title;
@@ -46,16 +42,15 @@ async function addActualLyricsBtn(likeContainer: HTMLElement) {
         lyricsBtn.style.cursor = "wait";
         lyricsBtn.style.pointerEvents = "none";
 
-        const imgElem = lyricsBtn.querySelector<HTMLImageElement>("img")!;
-        imgElem.src = spinnerIconUrl;
-        imgElem.classList.add("bytm-spinner");
+        setInnerHtml(lyricsBtn, await resourceAsString("icon-spinner"));
+        lyricsBtn.querySelector("svg")?.classList.add("bytm-generic-btn-img", "bytm-spinner");
 
         currentSongTitle = newTitle;
 
         const url = await getCurrentLyricsUrl(); // can take a second or two
 
-        imgElem.src = lyricsIconUrl;
-        imgElem.classList.remove("bytm-spinner");
+        setInnerHtml(lyricsBtn, await resourceAsString("icon-lyrics"));
+        lyricsBtn.querySelector("svg")?.classList.add("bytm-generic-btn-img");
 
         if(!url) {
           let artist, song;
@@ -65,7 +60,8 @@ async function addActualLyricsBtn(likeContainer: HTMLElement) {
           }
           const query = artist && song ? "?q=" + encodeURIComponent(sanitizeArtists(artist) + " - " + sanitizeSong(song)) : "";
 
-          imgElem.src = errorIconUrl;
+          setInnerHtml(lyricsBtn, await resourceAsString("icon-error"));
+          lyricsBtn.querySelector("svg")?.classList.add("bytm-generic-btn-img");
 
           lyricsBtn.ariaLabel = lyricsBtn.title = t("lyrics_not_found_click_open_search");
           lyricsBtn.style.cursor = "pointer";
@@ -318,10 +314,6 @@ export async function createLyricsBtn(geniusUrl?: string, hideIfLoading = true)
   linkElem.style.visibility = hideIfLoading && geniusUrl ? "initial" : "hidden";
   linkElem.style.display = hideIfLoading && geniusUrl ? "inline-flex" : "none";
 
-  const imgElem = document.createElement("img");
-  imgElem.classList.add("bytm-generic-btn-img");
-  imgElem.src = await getResourceUrl("icon-lyrics");
-
   onInteraction(linkElem, (e) => {
     const url = linkElem.href ?? geniusUrl;
     if(!url || e instanceof MouseEvent)
@@ -333,7 +325,8 @@ export async function createLyricsBtn(geniusUrl?: string, hideIfLoading = true)
     stopPropagation: false,
   });
 
-  linkElem.appendChild(imgElem);
+  setInnerHtml(linkElem, await resourceAsString("icon-lyrics"));
+  linkElem.querySelector("svg")?.classList.add("bytm-generic-btn-img");
 
   onInteraction(linkElem, async (e) => {
     if(e.ctrlKey || e.altKey) {

+ 8 - 2
src/utils/dom.ts

@@ -264,8 +264,14 @@ DOMPurify.addHook("afterSanitizeAttributes", (node) => {
   }
 });
 
-/** Sets innerHTML directly on Firefox and Safari, while on Chromium a [Trusted Types policy](https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API) is used to set the HTML */
-export function setInnerHtml(element: HTMLElement, html: string) {
+/**
+ * Sets innerHTML directly on Firefox and Safari, while on Chromium a [Trusted Types policy](https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API) is used to set the HTML.  
+ * If no HTML string is given, the element's innerHTML will be set to an empty string.
+ */
+export function setInnerHtml(element: HTMLElement, html?: string | null) {
+  if(!html)
+    html = "";
+
   if(!ttPolicy && window?.trustedTypes?.createPolicy) {
     ttPolicy = window.trustedTypes.createPolicy("bytm-sanitize-html", {
       createHTML: (dirty: string) => DOMPurify.sanitize(dirty, {

+ 3 - 3
src/utils/translations.ts

@@ -2,10 +2,10 @@ import { tr, Stringifiable, fetchAdvanced } from "@sv443-network/userutils";
 import { error, getResourceUrl, info } from "./index.js";
 import { emitInterface, setGlobalProp } from "../interface.js";
 import { getFeature } from "../config.js";
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
 import langMapping from "../../assets/locales.json" with { type: "json" };
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
-import tr_enUS from "../../assets/translations/en-US.json";
+import tr_enUS from "../../assets/translations/en-US.json" with { type: "json" };
+
+void [langMapping, tr_enUS];
 
 export type TrLocale = keyof typeof langMapping;
 export type TrKey = keyof (typeof tr_enUS["translations"]);