diff --git a/Extensions/combined/manifest-chrome.json b/Extensions/combined/manifest-chrome.json index 3513a43d..00cf4925 100644 --- a/Extensions/combined/manifest-chrome.json +++ b/Extensions/combined/manifest-chrome.json @@ -11,8 +11,12 @@ "48": "icons/icon48.png", "128": "icons/icon128.png" }, - "host_permissions": ["*://*.youtube.com/*"], - "permissions": ["storage"], + "host_permissions": [ + "*://*.youtube.com/*" + ], + "permissions": [ + "storage" + ], "action": { "default_popup": "popup.html" }, @@ -23,18 +27,31 @@ "*://www.youtube.com/*", "*://m.youtube.com/*" ], - "exclude_matches": ["*://*.music.youtube.com/*"], - "js": ["ryd.content-script.js"], - "css": ["content-style.css"] + "exclude_matches": [ + "*://*.music.youtube.com/*" + ], + "js": [ + "ryd.content-script.js" + ], + "css": [ + "content-style.css" + ] } ], "externally_connectable": { - "matches": ["*://*.youtube.com/*"] + "matches": [ + "*://*.youtube.com/*" + ] }, "web_accessible_resources": [ { - "resources": ["ryd.script.js"], - "matches": ["*://*.youtube.com/*"] + "resources": [ + "ryd.script.js", + "menu-fixer.js" + ], + "matches": [ + "*://*.youtube.com/*" + ] } ], "options_ui": { diff --git a/Extensions/combined/manifest-firefox.json b/Extensions/combined/manifest-firefox.json index a1fb0e33..33de9f73 100644 --- a/Extensions/combined/manifest-firefox.json +++ b/Extensions/combined/manifest-firefox.json @@ -5,7 +5,9 @@ "version": "__RYD_VERSION__", "manifest_version": 2, "background": { - "scripts": ["ryd.background.js"] + "scripts": [ + "ryd.background.js" + ] }, "icons": { "48": "icons/icon48.png", @@ -22,22 +24,34 @@ }, "content_scripts": [ { - "matches": ["*://*.youtube.com/*"], - "exclude_matches": ["*://*.music.youtube.com/*"], + "matches": [ + "*://*.youtube.com/*" + ], + "exclude_matches": [ + "*://*.music.youtube.com/*" + ], "run_at": "document_idle", - "css": ["content-style.css"], - "js": ["ryd.content-script.js"] + "css": [ + "content-style.css" + ], + "js": [ + "ryd.content-script.js" + ] } ], "options_ui": { "page": "popup.html", "open_in_tab": false - } - // uncomment this section for local storage to work in firefox locally - // ,"browser_specific_settings": { - // "gecko": { - // "id": "addon@example.com", - // "strict_min_version": "42.0" - // } + }, + "web_accessible_resources": [ + "menu-fixer.js" + ] + + // uncomment this section for local storage to work in firefox locally, + // ,"browser_specific_settings": { + // "gecko": { + // "id": "addon@example.com", + // "strict_min_version": "42.0" // } + // } } diff --git a/Extensions/combined/menu-fixer.js b/Extensions/combined/menu-fixer.js new file mode 100644 index 00000000..73649a5a --- /dev/null +++ b/Extensions/combined/menu-fixer.js @@ -0,0 +1,61 @@ +function debounceAsync(func, wait, renderer) { + let timeout; + let lastCallTime = 0; + + return async function (...args) { + const context = this; + const now = Date.now(); + + if (!lastCallTime || now - lastCallTime > wait) { + // If the last call was long enough ago or this is the first call, execute immediately + lastCallTime = now; + return await func.apply(context, args); + } else { + // Hide all optional menu items - prevents endless loop + if (renderer?.polymerController?.flexAsTopLevelButtons) { + renderer.polymerController.flexAsTopLevelButtons = []; + } + + // Otherwise, delay the call + return new Promise((resolve) => { + clearTimeout(timeout); + timeout = setTimeout(async () => { + lastCallTime = Date.now(); + resolve(await func.apply(context, args)); + }, wait); + }); + } + }; +} + +const fixYtdMenuRenderer = (ytdMenuRenderer) => { + if (!ytdMenuRenderer?.polymerController?.maybeUpdateFlexibleMenuImpl) { + return; + } + const originalMaybeUpdateFlexibleMenuImpl = ytdMenuRenderer.polymerController.maybeUpdateFlexibleMenuImpl.bind( + ytdMenuRenderer.polymerController, + ); + + ytdMenuRenderer.polymerController.maybeUpdateFlexibleMenuImpl = debounceAsync( + originalMaybeUpdateFlexibleMenuImpl, + 100, + ytdMenuRenderer, + ); +}; + +const fixedYtdMenuRenderers = []; +const observer = new MutationObserver(() => { + const ytdMenuRenderers = [...document.querySelectorAll("ytd-menu-renderer")].filter( + (el) => !fixedYtdMenuRenderers.includes(el), + ); + if (!ytdMenuRenderers.length) return; + + for (const el of ytdMenuRenderers) { + fixYtdMenuRenderer(el); + fixedYtdMenuRenderers.push(el); + } +}); +observer.observe(document.documentElement, { + subtree: true, + childList: true, +}); diff --git a/Extensions/combined/ryd.content-script.js b/Extensions/combined/ryd.content-script.js index 58303833..16da8211 100644 --- a/Extensions/combined/ryd.content-script.js +++ b/Extensions/combined/ryd.content-script.js @@ -6,11 +6,7 @@ import { isShorts, setInitialState, initExtConfig } from "./src/state"; //--- Import Video & Browser Functions ---// import { getBrowser, isVideoLoaded, cLog } from "./src/utils"; -import { - addLikeDislikeEventListener, - createSmartimationObserver, - storageChangeHandler, -} from "./src/events"; +import { addLikeDislikeEventListener, createSmartimationObserver, storageChangeHandler } from "./src/events"; await initExtConfig(); @@ -47,3 +43,11 @@ document.addEventListener("yt-navigate-finish", async function (event) { if (jsInitChecktimer !== null) clearInterval(jsInitChecktimer); await setEventListeners(); }); + +const s = document.createElement("script"); +s.src = chrome.runtime.getURL("menu-fixer.js"); +s.onload = function () { + this.remove(); +}; +// see also "Dynamic values in the injected code" section in this answer +(document.head || document.documentElement).appendChild(s); diff --git a/package.json b/package.json index a003ee1e..a36f92c7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "return-youtube-dislike", - "version": "3.0.0-16", + "version": "3.0.0-17", "description": "Chrome extension to return youtube dislikes", "main": "ryd.content-script.js", "scripts": {