diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..67fa845b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,83 @@ +# Contributing + +## Table of Contents + +- [Glossary](#📚-glossary) +- [Getting Started](#🎉-getting-started) +- [Commits, Issues, and Pull Requests](#✏-commits-issues-and-pull-requests) +- [Contributor Workflow](#🗃-contributor-workflow) +- [Internationalization](#🌐-internationalization-i18n) +- [Code Quality](#🔧-code-quality) + +## 📚 Glossary + +Here are some terms that we interchangeably refer to in this document: + +- **Pull Request** (PR): Pull requests let you tell others about changes you've pushed to a branch in the repository. Once a pull request is opened, you can discuss and review the potential changes with collaborators and add follow-up commits before your changes are merged into the base branch. +- **Continuous Integration** (CI): A development practice where developers frequently integrate their code changes into a shared repository. Each integration triggers automated tests to ensure that the new code doesn't break existing functionality. CI aims to detect and fix integration errors quickly, promoting collaboration and maintaining a consistent codebase. +- **Continuous Development** (CD): An extension of Continuous Integration (CI) that focuses on automating the deployment of code changes to the production environment. + +## 🎉 Getting Started + +- Because our release CI/CD workflow is automated, we rely on commit messages that follow a format convention for semantic versioning[^1]. + As such, we use the [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) spec in commit messages. + +## ✏ Commits, Issues, and Pull Requests + +There are a few guidelines that we follow in order to maintain the quality of the codebase: + +- Make sure that commit messages are meaningful, and describe the commit itself. +- When creating [Bug Report Issues](https://github.com/YouTube-Enhancer/extension/issues/new?assignees=&labels=&projects=&template=bug_report.md&title=), make sure to follow the template and explain the issue in a clear and straightforward manner. +- Although we do not strictly enforce the [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) spec in pull requests, it is preferred over other ways of creating PR titles. +- In the description of the pull request, briefly describe the goal of the PR and the changes it will bring to the codebase. + +## 🗃 Contributor Workflow + +We use the "contributor workflow" to manage the code. Everyone suggests changes by making pull requests. This helps people contribute, make tests easier, and get feedback from others. + +To contribute to the codebase, the workflow is as follows: + +1. Fork the repository +2. Commit changes to the fork (using the `dev` branch) +3. Create a pull request (on the `dev` branch) + +_It is ill-advised to create pull requests against the `main` branch as `dev` changes are merged to the main branch in batches by the core maintainers._ + +> Read more about forking and making pull requests [here](https://docs.github.com/get-started/exploring-projects-on-github/contributing-to-a-project). + +## 🌐 Internationalization (i18n) + +### Crowdin Translation Project + +Our YouTube Enhancer extension supports multiple languages to provide a more inclusive experience for users around the world. We use Crowdin for managing translations. + +### Contributing Translations + +We welcome contributions to improve translations and make the extension accessible to a wider audience. If you'd like to contribute translations or suggest improvements, follow these steps: + +1. Visit our [Crowdin project](https://crowdin.com/project/youtube-enhancer). +2. Select your language and start translating. +3. If your language is not listed, feel free to request its addition. + +## 🔧 Code Quality + +Before new code gets merged into the repository, we do automated lint tests to verify the format of the code. + +It is recommended to test your code before committing by running the following commands: + +1. Lint check: `npm run lint` +2. Fix lint errors: `npm run lint:fix` + +> You won't need to do this if you use a supported editor[^2], as the process is automated. + +While we don't yet have a strict guideline on what kind of code should be in the repository, here are a few principles we loosely follow to maintain the general consistency of the code: + +- [DRY principle](https://en.wikipedia.org/wiki/Don't_repeat_yourself) +- [Rule of three]() +- [Single source of truth](https://en.wikipedia.org/wiki/Single_source_of_truth) + +--- + +[^1]: [Why Use Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/#why-use-conventional-commits) + +[^2]: [ESLint: A List of Editor Integrations](https://eslint.org/docs/latest/use/integrations#editors) diff --git a/src/defaults.ts b/src/defaults.ts index bf8f7705..9b75d72d 100644 --- a/src/defaults.ts +++ b/src/defaults.ts @@ -28,14 +28,13 @@ function setDefaultValues() { JSON.parse(storedValueString) : storedValueString; // Check if the parsed value is an object and has properties - if (typeof storedValue === "object" && storedValue !== null) { - // Deep merge missing keys with their default values - const updatedValue = deepMerge(defaultValue as Record, storedValue as Record); - // Set the updated value in localStorage - localStorage.setItem(option, JSON.stringify(updatedValue)); - // Set the updated value in chrome storage - void chrome.storage.local.set({ [option]: updatedValue }); - } + if (typeof storedValue !== "object" || storedValue === null) continue; + // Deep merge missing keys with their default values + const updatedValue = deepMerge(defaultValue as Record, storedValue as Record); + // Set the updated value in localStorage + localStorage.setItem(option, JSON.stringify(updatedValue)); + // Set the updated value in chrome storage + void chrome.storage.local.set({ [option]: updatedValue }); } catch (error) { // Handle errors during JSON parsing console.error(`Error parsing stored value for option ${option}:`, error); diff --git a/src/features/automaticTheaterMode/index.ts b/src/features/automaticTheaterMode/index.ts index d110930a..85b2ff7e 100644 --- a/src/features/automaticTheaterMode/index.ts +++ b/src/features/automaticTheaterMode/index.ts @@ -3,6 +3,7 @@ import type { YouTubePlayerDiv } from "@/src/types"; import { isLivePage, isWatchPage, waitForSpecificMessage } from "@/src/utils/utilities"; export async function enableAutomaticTheaterMode() { + if (!isWatchPage()) return; // Wait for the "options" message from the content script const { data: { @@ -16,10 +17,7 @@ export async function enableAutomaticTheaterMode() { // If player element is not available, return if (!playerContainer) return; const { width } = await playerContainer.getSize(); - const { - body: { clientWidth } - } = document; - const isTheaterMode = width === clientWidth; + const isTheaterMode = document.body.clientWidth === width; // Get the size button const sizeButton = document.querySelector("button.ytp-size-button"); // If the size button is not available return diff --git a/src/features/buttonPlacement/utils.ts b/src/features/buttonPlacement/utils.ts index eb54f89d..86a9b0cd 100644 --- a/src/features/buttonPlacement/utils.ts +++ b/src/features/buttonPlacement/utils.ts @@ -31,8 +31,8 @@ export function makeFeatureButton(`#${getFeatureButtonId(buttonName)}`); @@ -166,7 +158,7 @@ export function checkIfFeatureButtonExists(buttonName: AllButtonNames, placement } } } -export function getFeatureButtonId(buttonName: AllButtonNames) { +export function getFeatureButtonId(buttonName: ButtonName) { return `yte-feature-${buttonName}-button` as const; } export function getFeatureButton(buttonName: AllButtonNames) { diff --git a/src/features/customCSS/index.ts b/src/features/customCSS/index.ts index 092d7ed3..a6261bc0 100644 --- a/src/features/customCSS/index.ts +++ b/src/features/customCSS/index.ts @@ -11,24 +11,15 @@ export async function enableCustomCSS() { } = await waitForSpecificMessage("options", "request_data", "content"); // Check if custom CSS is enabled if (!enable_custom_css) return; - if (customCSSExists()) { - updateCustomCSS({ - custom_css_code - }); - return; - } + if (customCSSExists()) return updateCustomCSS({ custom_css_code }); // Create the custom CSS style element - const customCSSStyleElement = createCustomCSSElement({ - custom_css_code - }); + const customCSSStyleElement = createCustomCSSElement({ custom_css_code }); // Insert the custom CSS style element document.head.appendChild(customCSSStyleElement); } export function disableCustomCSS() { // Get the custom CSS style element const customCSSStyleElement = document.querySelector(`#${customCssID}`); - // Check if the custom CSS style element exists - if (!customCSSStyleElement) return; // Remove the custom CSS style element - customCSSStyleElement.remove(); + customCSSStyleElement?.remove(); } diff --git a/src/features/customCSS/utils.ts b/src/features/customCSS/utils.ts index a62bc45e..ec2c6b08 100644 --- a/src/features/customCSS/utils.ts +++ b/src/features/customCSS/utils.ts @@ -5,9 +5,7 @@ import { customCssID } from "@/src/features/customCSS"; export function updateCustomCSS({ custom_css_code }: Pick) { // Get the custom CSS style element const customCSSStyleElement = document.querySelector(`#${customCssID}`); - // Check if the custom CSS style element exists - if (!customCSSStyleElement) return; - customCSSStyleElement.replaceWith(createCustomCSSElement({ custom_css_code })); + customCSSStyleElement?.replaceWith(createCustomCSSElement({ custom_css_code })); } export function createCustomCSSElement({ custom_css_code }: Pick) { // Create the custom CSS style element @@ -20,6 +18,5 @@ export function customCSSExists() { // Get the custom CSS style element const customCSSStyleElement = document.querySelector(`#${customCssID}`); // Check if the custom CSS style element exists - if (!customCSSStyleElement) return false; - return true; + return customCSSStyleElement !== null; } diff --git a/src/features/deepDarkCSS/index.ts b/src/features/deepDarkCSS/index.ts index 0980f72f..dba550ea 100644 --- a/src/features/deepDarkCSS/index.ts +++ b/src/features/deepDarkCSS/index.ts @@ -13,8 +13,9 @@ export async function enableDeepDarkCSS() { // Check if deep dark theme is enabled if (!enable_deep_dark_theme) return; if (deepDarkCSSExists()) { - updateDeepDarkCSS(deep_dark_preset === "Custom" ? getDeepDarkCustomThemeStyle(deep_dark_custom_theme_colors) : deepDarkPresets[deep_dark_preset]); - return; + return updateDeepDarkCSS( + deep_dark_preset === "Custom" ? getDeepDarkCustomThemeStyle(deep_dark_custom_theme_colors) : deepDarkPresets[deep_dark_preset] + ); } // Create the deep dark theme style element const deepDarkThemeStyleElement = createDeepDarkCSSElement( @@ -27,8 +28,6 @@ export async function enableDeepDarkCSS() { export function disableDeepDarkCSS() { // Get the deep dark theme style element const deepDarkThemeStyleElement = document.querySelector(`#${deepDarkCssID}`); - // Check if the deep dark theme style element exists - if (!deepDarkThemeStyleElement) return; // Remove the deep dark theme style element - deepDarkThemeStyleElement.remove(); + deepDarkThemeStyleElement?.remove(); } diff --git a/src/features/deepDarkCSS/utils.ts b/src/features/deepDarkCSS/utils.ts index 75c9c32c..da8bd4cd 100644 --- a/src/features/deepDarkCSS/utils.ts +++ b/src/features/deepDarkCSS/utils.ts @@ -6,9 +6,7 @@ import { deepDarkCssID } from "@/src/features/deepDarkCSS"; export function updateDeepDarkCSS(css_code: string) { // Get the custom CSS style element const customCSSStyleElement = document.querySelector(`#${deepDarkCssID}`); - // Check if the custom CSS style element exists - if (!customCSSStyleElement) return; - customCSSStyleElement.replaceWith(createDeepDarkCSSElement(css_code)); + customCSSStyleElement?.replaceWith(createDeepDarkCSSElement(css_code)); } export function createDeepDarkCSSElement(css_code: string) { // Create the custom CSS style element @@ -21,8 +19,7 @@ export function deepDarkCSSExists() { // Get the custom CSS style element const customCSSStyleElement = document.querySelector(`#${deepDarkCssID}`); // Check if the custom CSS style element exists - if (!customCSSStyleElement) return false; - return true; + return customCSSStyleElement !== null; } export function getDeepDarkCustomThemeStyle({ diff --git a/src/features/featureMenu/index.ts b/src/features/featureMenu/index.ts index 4e3f0038..7a17edd9 100644 --- a/src/features/featureMenu/index.ts +++ b/src/features/featureMenu/index.ts @@ -227,8 +227,5 @@ export function setupFeatureMenuEventListeners(featureMenuOpenType: FeatureMenuO }); } const observer = new MutationObserver(handleMutation); - observer.observe(playerContainer, { - childList: true, - subtree: true - }); + observer.observe(playerContainer, { childList: true, subtree: true }); } diff --git a/src/features/featureMenu/utils.ts b/src/features/featureMenu/utils.ts index fe4a0e26..5b9d6eb8 100644 --- a/src/features/featureMenu/utils.ts +++ b/src/features/featureMenu/utils.ts @@ -81,19 +81,15 @@ export async function addFeatureItemToMenu featureMenuClickListener(menuItem, listener, isToggle), featureName); menuItem.appendChild(menuItemLabel); // If it's a toggle item, create the toggle elements + const menuItemContent = document.createElement("div"); + menuItemContent.classList.add("ytp-menuitem-content"); if (isToggle) { - const menuItemContent = document.createElement("div"); - menuItemContent.classList.add("ytp-menuitem-content"); const menuItemToggle = document.createElement("div"); menuItemToggle.classList.add("ytp-menuitem-toggle-checkbox"); menuItemContent.appendChild(menuItemToggle); - menuItem.appendChild(menuItemContent); menuItem.ariaChecked = initialChecked ? "true" : "false"; - } else { - const menuItemContent = document.createElement("div"); - menuItemContent.classList.add("ytp-menuitem-content"); - menuItem.appendChild(menuItemContent); } + menuItem.appendChild(menuItemContent); // Add the item to the feature menu panel featureMenuPanel.appendChild(menuItem); // Adjust the height and width of the feature menu @@ -164,14 +160,10 @@ export function updateFeatureMenuTitle(title: string) { * @param buttonName the name of the button * @returns { featureMenuItemIconId, featureMenuItemId, featureMenuItemLabelId} */ -export function getFeatureIds(buttonName: AllButtonNames): { - featureMenuItemIconId: FeatureMenuItemIconId; - featureMenuItemId: FeatureMenuItemId; - featureMenuItemLabelId: FeatureMenuItemLabelId; -} { - const featureMenuItemIconId: FeatureMenuItemIconId = `yte-${buttonName}-icon`; - const featureMenuItemId: FeatureMenuItemId = `yte-feature-${buttonName}-menuitem`; - const featureMenuItemLabelId: FeatureMenuItemLabelId = `yte-${buttonName}-label`; +export function getFeatureIds(buttonName: ButtonName) { + const featureMenuItemIconId = `yte-${buttonName}-icon` as const; + const featureMenuItemId = `yte-feature-${buttonName}-menuitem` as const; + const featureMenuItemLabelId = `yte-${buttonName}-label` as const; return { featureMenuItemIconId, featureMenuItemId, diff --git a/src/features/hideLiveStreamChat/index.ts b/src/features/hideLiveStreamChat/index.ts index 33dd89cd..bb3d56c6 100644 --- a/src/features/hideLiveStreamChat/index.ts +++ b/src/features/hideLiveStreamChat/index.ts @@ -14,8 +14,7 @@ export async function enableHideLiveStreamChat() { await waitForAllElements(["div#player", "div#player-wide-container", "div#video-container", "div#player-container"]); const player = document.querySelector("div#movie_player"); if (!player) return; - const playerData = await player.getVideoData(); - if (!playerData.isLive) return; + if (!(await player.getVideoData()).isLive) return; modifyElementsClassList("add", [ { className: "yte-hide-live-stream-chat", @@ -35,8 +34,7 @@ export async function enableHideLiveStreamChat() { export async function disableHideLiveStreamChat() { const player = document.querySelector("div#movie_player"); if (!player) return; - const playerData = await player.getVideoData(); - if (!playerData.isLive) return; + if (!(await player.getVideoData()).isLive) return; modifyElementsClassList("remove", [ { className: "yte-hide-live-stream-chat", diff --git a/src/features/hideShorts/index.ts b/src/features/hideShorts/index.ts index c558c701..2a060dee 100644 --- a/src/features/hideShorts/index.ts +++ b/src/features/hideShorts/index.ts @@ -19,8 +19,6 @@ export async function enableHideShorts() { export function disableHideShorts() { showShorts(); // Disconnect the observer - if (shortsObserver) { - shortsObserver.disconnect(); - shortsObserver = null; - } + shortsObserver?.disconnect(); + shortsObserver = null; } diff --git a/src/features/hideShorts/utils.ts b/src/features/hideShorts/utils.ts index 576cf95d..1f6e6d91 100644 --- a/src/features/hideShorts/utils.ts +++ b/src/features/hideShorts/utils.ts @@ -10,7 +10,6 @@ type ElementVisibilityAction = (element: HTMLElement) => void; function toggleElementVisibility(selector: string, action: ElementVisibilityAction) { const elements = document.querySelectorAll(selector); - if (elements.length === 0) return; elements.forEach((element) => action(element)); } @@ -117,9 +116,7 @@ export function observeShortsElements() { }); // Only call hideShorts if one of the mutations contains one of the selectors - if (containsShortsSelector) { - hideShorts(); - } + if (containsShortsSelector) hideShorts(); }); observer.observe(document.body, observerOptions); diff --git a/src/features/loopButton/index.ts b/src/features/loopButton/index.ts index 9a03efe3..a07bd265 100644 --- a/src/features/loopButton/index.ts +++ b/src/features/loopButton/index.ts @@ -32,52 +32,53 @@ export const addLoopButton: AddButtonFunction = async () => { await addFeatureButton( "loopButton", loopButtonPlacement, - loopButtonPlacement === "feature_menu" ? - window.i18nextInstance.t("pages.content.features.loopButton.button.label") - : window.i18nextInstance.t("pages.content.features.loopButton.button.toggle.off"), + window.i18nextInstance.t( + loopButtonPlacement === "feature_menu" ? + "pages.content.features.loopButton.button.label" + : "pages.content.features.loopButton.button.toggle.off" + ), getFeatureIcon("loopButton", loopButtonPlacement), loopButtonClickListener, true ); const loopChangedHandler = (mutationList: MutationRecord[]) => { const loopSVG = getFeatureIcon("loopButton", loopButtonPlacement); - for (const mutation of mutationList) { - if (mutation.type === "attributes") { - const { attributeName, target } = mutation; - if (attributeName === "loop") { - const { loop } = target as HTMLVideoElement; - const featureName: SingleButtonFeatureNames = "loopButton"; - // Get the feature menu - const featureMenu = document.querySelector("#yte-feature-menu"); - // Check if the feature item already exists in the menu - const featureExistsInMenu = - featureMenu && featureMenu.querySelector(`#${getFeatureIds(featureName).featureMenuItemId}`) !== null; - if (featureExistsInMenu) { - const menuItem = getFeatureButton(featureName); - if (!menuItem) return; - menuItem.ariaChecked = loop ? "true" : "false"; + mutationList.forEach((mutation) => { + if (mutation.type !== "attributes") return; + const { attributeName, target } = mutation; + if (attributeName !== "loop") return; + + const { loop } = target as HTMLVideoElement; + const featureName: SingleButtonFeatureNames = "loopButton"; + // Get the feature menu + const featureMenu = document.querySelector("#yte-feature-menu"); + const featureItem = featureMenu?.querySelector(`#${getFeatureIds(featureName).featureMenuItemId}`) || null; + // Check if the feature item already exists in the menu + const featureExistsInMenu = featureMenu !== null && featureItem !== null; + if (featureExistsInMenu) { + const menuItem = getFeatureButton(featureName); + if (!menuItem) return; + menuItem.ariaChecked = loop ? "true" : "false"; + } + const button = document.querySelector(`#${getFeatureButtonId(featureName)}`); + if (!button) return; + switch (loopButtonPlacement) { + case "feature_menu": { + if (loopSVG instanceof SVGSVGElement) { + button.firstChild?.replaceWith(loopSVG); } - const button = document.querySelector(`#${getFeatureButtonId(featureName)}`); - if (!button) return; - switch (loopButtonPlacement) { - case "feature_menu": { - if (loopSVG instanceof SVGSVGElement) { - button.firstChild?.replaceWith(loopSVG); - } - break; - } - case "below_player": - case "player_controls_left": - case "player_controls_right": { - if (typeof loopSVG === "object" && "off" in loopSVG && "on" in loopSVG) { - button.firstChild?.replaceWith(loop ? loopSVG.on : loopSVG.off); - } - break; - } + break; + } + case "below_player": + case "player_controls_left": + case "player_controls_right": { + if (typeof loopSVG === "object" && "off" in loopSVG && "on" in loopSVG) { + button.firstChild?.replaceWith(loop ? loopSVG.on : loopSVG.off); } + break; } } - } + }); }; const loopChangeMutationObserver = new MutationObserver(loopChangedHandler); loopChangeMutationObserver.observe(videoElement, { attributeFilter: ["loop"], attributes: true }); diff --git a/src/features/loopButton/utils.ts b/src/features/loopButton/utils.ts index 1173d1a9..e5ca4efb 100644 --- a/src/features/loopButton/utils.ts +++ b/src/features/loopButton/utils.ts @@ -6,10 +6,8 @@ export function loopButtonClickListener(checked?: boolean) { } const videoElement = document.querySelector("video.html5-main-video"); if (!videoElement) return; + const loop = videoElement.hasAttribute("loop"); - if (loop) { - videoElement.removeAttribute("loop"); - } else { - videoElement.setAttribute("loop", ""); - } + if (loop) videoElement.removeAttribute("loop"); + else videoElement.setAttribute("loop", ""); } diff --git a/src/features/maximizePlayerButton/index.ts b/src/features/maximizePlayerButton/index.ts index df1356a7..f5f1da17 100644 --- a/src/features/maximizePlayerButton/index.ts +++ b/src/features/maximizePlayerButton/index.ts @@ -1,5 +1,5 @@ import type { AddButtonFunction, RemoveButtonFunction } from "@/src/features"; -import type { Nullable, YouTubePlayerDiv } from "@/src/types"; +import type { YouTubePlayerDiv } from "@/src/types"; import { addFeatureButton, removeFeatureButton } from "@/src/features/buttonPlacement"; import { getFeatureButton, updateFeatureButtonIcon, updateFeatureButtonTitle } from "@/src/features/buttonPlacement/utils"; @@ -25,12 +25,11 @@ export const addMaximizePlayerButton: AddButtonFunction = async () => { function maximizePlayerButtonClickListener(checked?: boolean) { const button = getFeatureButton("maximizePlayerButton"); if (!button) return; - const featureName = "maximizePlayerButton"; const { remove } = createTooltip({ direction: maximizePlayerButtonPlacement === "below_player" ? "down" : "up", element: button, - featureName, - id: `yte-feature-${featureName}-tooltip` + featureName: "maximizePlayerButton", + id: "yte-feature-maximizePlayerButton-tooltip" }); if (checked !== undefined) { if (checked) remove(); @@ -44,9 +43,9 @@ export const addMaximizePlayerButton: AddButtonFunction = async () => { setupVideoPlayerTimeUpdate(); } - const pipElement: Nullable = document.querySelector("button.ytp-pip-button"); - const sizeElement: Nullable = document.querySelector("button.ytp-size-button"); - const miniPlayerElement: Nullable = document.querySelector("button.ytp-miniplayer-button"); + const pipElement = document.querySelector("button.ytp-pip-button"); + const sizeElement = document.querySelector("button.ytp-size-button"); + const miniPlayerElement = document.querySelector("button.ytp-miniplayer-button"); function otherElementClickListener() { // Get the video element const videoElement = document.querySelector("video.video-stream.html5-main-video"); @@ -54,25 +53,26 @@ export const addMaximizePlayerButton: AddButtonFunction = async () => { if (!videoElement) return; const videoContainer = document.querySelector("#movie_player"); if (!videoContainer) return; - if (videoContainer.classList.contains("maximized_video_container") && videoElement.classList.contains("maximized_video")) { - const maximizePlayerButton = getFeatureButton("maximizePlayerButton"); - if (!maximizePlayerButton) return; - maximizePlayer(); - maximizePlayerButton.ariaChecked = "false"; - const button = getFeatureButton("maximizePlayerButton"); + if (!videoContainer.classList.contains("maximized_video_container") || !videoElement.classList.contains("maximized_video")) return; + const maximizePlayerButton = getFeatureButton("maximizePlayerButton"); + if (!maximizePlayerButton) return; + maximizePlayer(); + maximizePlayerButton.ariaChecked = "false"; + const button = getFeatureButton("maximizePlayerButton"); + if (button && button instanceof HTMLButtonElement) { const icon = getFeatureIcon("maximizePlayerButton", "below_player"); - if (button && button instanceof HTMLButtonElement) { - if (typeof icon === "object" && "off" in icon && "on" in icon) updateFeatureButtonIcon(button, icon.off); - updateFeatureButtonTitle("maximizePlayerButton", window.i18nextInstance.t("pages.content.features.maximizePlayerButton.button.toggle.off")); - } + if (typeof icon === "object" && "off" in icon && "on" in icon) updateFeatureButtonIcon(button, icon.off); + updateFeatureButtonTitle("maximizePlayerButton", window.i18nextInstance.t("pages.content.features.maximizePlayerButton.button.toggle.off")); } } await addFeatureButton( "maximizePlayerButton", maximizePlayerButtonPlacement, - maximizePlayerButtonPlacement === "feature_menu" ? - window.i18nextInstance.t("pages.content.features.maximizePlayerButton.button.label") - : window.i18nextInstance.t("pages.content.features.maximizePlayerButton.button.toggle.off"), + window.i18nextInstance.t( + maximizePlayerButtonPlacement === "feature_menu" ? + "pages.content.features.maximizePlayerButton.button.label" + : "pages.content.features.maximizePlayerButton.button.toggle.off" + ), getFeatureIcon("maximizePlayerButton", maximizePlayerButtonPlacement), maximizePlayerButtonClickListener, true @@ -113,16 +113,17 @@ export const addMaximizePlayerButton: AddButtonFunction = async () => { if (!controlsElement) return; if ( - videoContainer.classList.contains("maximized_video_container") && - videoElement.classList.contains("maximized_video") && - controlsElement.classList.contains("maximized_controls") - ) { - const buttonRect = (event.target as HTMLButtonElement).getBoundingClientRect(); - const tooltipRect = ytTooltip.getBoundingClientRect(); - ytTooltip.style.left = `${buttonRect.left - 48}px`; - ytTooltip.style.top = `${buttonRect.top - tooltipRect.height - 14}px`; - ytTooltip.style.zIndex = "2021"; - } + !videoContainer.classList.contains("maximized_video_container") || + !videoElement.classList.contains("maximized_video") || + !controlsElement.classList.contains("maximized_controls") + ) + return; + + const buttonRect = (event.target as HTMLButtonElement).getBoundingClientRect(); + const tooltipRect = ytTooltip.getBoundingClientRect(); + ytTooltip.style.left = `${buttonRect.left - 48}px`; + ytTooltip.style.top = `${buttonRect.top - tooltipRect.height - 14}px`; + ytTooltip.style.zIndex = "2021"; } function seekBarMouseEnterListener(event: MouseEvent) { // TODO: get the seek preview to be in the correct place when the video is maximized from default view @@ -149,15 +150,10 @@ export const addMaximizePlayerButton: AddButtonFunction = async () => { } } - if (pipElement) { - eventManager.addEventListener(pipElement, "click", otherElementClickListener, "maximizePlayerButton"); - } - if (sizeElement) { - eventManager.addEventListener(sizeElement, "click", otherElementClickListener, "maximizePlayerButton"); - } - if (miniPlayerElement) { - eventManager.addEventListener(miniPlayerElement, "click", otherElementClickListener, "maximizePlayerButton"); - } + if (pipElement) eventManager.addEventListener(pipElement, "click", otherElementClickListener, "maximizePlayerButton"); + if (sizeElement) eventManager.addEventListener(sizeElement, "click", otherElementClickListener, "maximizePlayerButton"); + if (miniPlayerElement) eventManager.addEventListener(miniPlayerElement, "click", otherElementClickListener, "maximizePlayerButton"); + const typLeftButtons = [ ...document.querySelectorAll("div.ytp-chrome-controls > div.ytp-left-controls > :not(.yte-maximized-player-button)") ]; @@ -172,16 +168,8 @@ export const addMaximizePlayerButton: AddButtonFunction = async () => { const typRightButtons = document.querySelectorAll( "div.ytp-chrome-controls > div.ytp-right-controls > :not(.yte-maximized-player-button)" ); - typLeftButtons.forEach((button) => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore TODO: figure out the proper type for this - eventManager.addEventListener(button, "mouseenter", ytpLeftButtonMouseEnterListener, "maximizePlayerButton"); - }); - typRightButtons.forEach((button) => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore TODO: figure out the proper type for this - eventManager.addEventListener(button, "mouseenter", ytpRightButtonMouseEnterListener, "maximizePlayerButton"); - }); + typLeftButtons.forEach((button) => eventManager.addEventListener(button, "mouseenter", ytpLeftButtonMouseEnterListener, "maximizePlayerButton")); + typRightButtons.forEach((button) => eventManager.addEventListener(button, "mouseenter", ytpRightButtonMouseEnterListener, "maximizePlayerButton")); }; export const removeMaximizePlayerButton: RemoveButtonFunction = async (placement) => { await removeFeatureButton("maximizePlayerButton", placement); diff --git a/src/features/maximizePlayerButton/utils.ts b/src/features/maximizePlayerButton/utils.ts index a5d66b7d..504145f1 100644 --- a/src/features/maximizePlayerButton/utils.ts +++ b/src/features/maximizePlayerButton/utils.ts @@ -11,9 +11,7 @@ export function updateProgressBarPositions() { const seekBar = document.querySelector("div.ytp-progress-bar"); const scrubber = document.querySelector("div.ytp-scrubber-container"); const hoverProgress = document.querySelector("div.ytp-hover-progress"); - if (!seekBar) return; - if (!scrubber) return; - if (!hoverProgress) return; + if (!seekBar || !scrubber || !hoverProgress) return; const elapsedTime = parseInt(seekBar?.ariaValueNow ?? "0") ?? 0; const duration = parseInt(seekBar?.ariaValueMax ?? "0") ?? 0; const seekBarWidth = seekBar?.clientWidth ?? 0; @@ -27,10 +25,7 @@ export function updateProgressBarPositions() { export function setupVideoPlayerTimeUpdate() { const videoElement = document.querySelector("video.video-stream.html5-main-video"); if (!videoElement) return; - const videoPlayerTimeUpdateListener = () => { - updateProgressBarPositions(); - }; - eventManager.addEventListener(videoElement, "timeupdate", videoPlayerTimeUpdateListener, "maximizePlayerButton"); + eventManager.addEventListener(videoElement, "timeupdate", () => updateProgressBarPositions(), "maximizePlayerButton"); } export function maximizePlayer() { // Get the video element @@ -65,10 +60,8 @@ export function maximizePlayer() { childNodes: [, svgPath] } = svgElement; if (!svgPath || !(svgPath instanceof SVGPathElement)) return; - if (svgPath.getAttribute("d") === theaterModeVariables.pathD) wasInTheatreMode = true; - else wasInTheatreMode = false; - if (wasInTheatreMode) setToTheatreMode = false; - else setToTheatreMode = true; + wasInTheatreMode = svgPath.getAttribute("d") === theaterModeVariables.pathD; + setToTheatreMode = !wasInTheatreMode; // TODO: finish this code to make the maximize player button work properly. (implement ytp-scrubber-container adjustment) if all else fails revert to using the theatre mode button to get the tooltips in the correct place console.log(`setToTheatreMode && wasInTheatreMode: ${setToTheatreMode && wasInTheatreMode}`); console.log(`setToTheatreMode && !wasInTheatreMode: ${setToTheatreMode && !wasInTheatreMode}`); diff --git a/src/features/openTranscriptButton/utils.ts b/src/features/openTranscriptButton/utils.ts index d4f39f1b..a5b19759 100644 --- a/src/features/openTranscriptButton/utils.ts +++ b/src/features/openTranscriptButton/utils.ts @@ -16,8 +16,7 @@ export const addOpenTranscriptButton: AddButtonFunction = async () => { } = await waitForSpecificMessage("options", "request_data", "content"); function transcriptButtonClickerListener() { const transcriptButton = document.querySelector("ytd-video-description-transcript-section-renderer button"); - if (!transcriptButton) return; - transcriptButton.click(); + transcriptButton?.click(); } await addFeatureButton( "openTranscriptButton", diff --git a/src/features/pauseBackgroundPlayers/index.ts b/src/features/pauseBackgroundPlayers/index.ts index d2c9df06..3a0541ec 100644 --- a/src/features/pauseBackgroundPlayers/index.ts +++ b/src/features/pauseBackgroundPlayers/index.ts @@ -1,3 +1,5 @@ +import type { Nullable } from "@/src/types"; + import { browserColorLog, sendContentToBackgroundMessage, waitForSpecificMessage } from "@/src/utils/utilities"; const PauseBackgroundPlayers = () => { @@ -16,40 +18,30 @@ export async function enablePauseBackgroundPlayers() { // ignore home page and channel pages if (window.location.href.match(/^https?:\/\/(?:www\.)?youtube\.com(\/?|\/channel\/.+|\/\@.+)$/gm)) return; browserColorLog("Enabling pauseBackgroundPlayers", "FgMagenta"); - let videoPlayerContainer: HTMLVideoElement | null = null; - if (!videoPlayerContainer) { - videoPlayerContainer = document.querySelector(".html5-main-video"); - } + + let videoPlayerContainer: Nullable = null; + if (!videoPlayerContainer) videoPlayerContainer = document.querySelector(".html5-main-video"); + function detectPlaying() { - if (videoPlayerContainer) { - videoPlayerContainer.addEventListener("playing", PauseBackgroundPlayers); - } + videoPlayerContainer?.addEventListener("playing", PauseBackgroundPlayers); } - let debounceTimeout: null | number = null; + let debounceTimeout: Nullable = null; const observer = new MutationObserver((mutationsList: MutationRecord[]) => { if (debounceTimeout) clearTimeout(debounceTimeout); - // @ts-expect-error - doesn't recognize browser environment properly - debounceTimeout = setTimeout(() => { + debounceTimeout = window.setTimeout(() => { for (const mutation of mutationsList) { - if (mutation.addedNodes.length) { - detectPlaying(); - } + if (mutation.addedNodes.length) detectPlaying(); } }, 100); }); - if (videoPlayerContainer) { - observer.observe(videoPlayerContainer, { childList: true, subtree: true }); - } - if (!videoPlayerContainer?.paused) { - PauseBackgroundPlayers(); - } + + if (videoPlayerContainer) observer.observe(videoPlayerContainer, { childList: true, subtree: true }); + if (!videoPlayerContainer?.paused) PauseBackgroundPlayers(); detectPlaying(); } export function disablePauseBackgroundPlayers() { const videoPlayerContainer: HTMLElement | null = document.querySelector(".html5-main-video"); - if (videoPlayerContainer) { - videoPlayerContainer.removeEventListener("playing", PauseBackgroundPlayers); - } + if (videoPlayerContainer) videoPlayerContainer.removeEventListener("playing", PauseBackgroundPlayers); browserColorLog("Disabling pauseBackgroundPlayers", "FgMagenta"); } diff --git a/src/features/playbackSpeedButtons/index.ts b/src/features/playbackSpeedButtons/index.ts index 617621ec..b1b9991b 100644 --- a/src/features/playbackSpeedButtons/index.ts +++ b/src/features/playbackSpeedButtons/index.ts @@ -41,7 +41,7 @@ export async function updatePlaybackSpeedButtonTooltip { options: { enable_automatically_set_quality, player_quality, player_quality_fallback_strategy } } } = await waitForSpecificMessage("options", "request_data", "content"); + // If automatically set quality option is disabled, return if (!enable_automatically_set_quality) return; // If player quality is not specified, return @@ -31,13 +32,14 @@ export default async function setPlayerQuality(): Promise { // Get the available quality levels const availableQualityLevels = (await playerContainer.getAvailableQualityLevels()) as YoutubePlayerQualityLevel[]; // Check if the specified player quality is available - if (player_quality && player_quality !== "auto") { - const closestQuality = chooseClosestQuality(player_quality, availableQualityLevels, player_quality_fallback_strategy); - if (!closestQuality) return; - // Log the message indicating the player quality being set - browserColorLog(`Setting player quality to ${closestQuality}`, "FgMagenta"); - // Set the playback quality and update the default quality in the dataset - void playerContainer.setPlaybackQualityRange(closestQuality); - playerContainer.dataset.defaultQuality = closestQuality; - } + if (!player_quality || player_quality === "auto") return; + + const closestQuality = chooseClosestQuality(player_quality, availableQualityLevels, player_quality_fallback_strategy); + if (!closestQuality) return; + // Log the message indicating the player quality being set + browserColorLog(`Setting player quality to ${closestQuality}`, "FgMagenta"); + + // Set the playback quality and update the default quality in the dataset + void playerContainer.setPlaybackQualityRange(closestQuality); + playerContainer.dataset.defaultQuality = closestQuality; } diff --git a/src/features/playerSpeed/index.ts b/src/features/playerSpeed/index.ts index 4cfe7748..ec5311bb 100644 --- a/src/features/playerSpeed/index.ts +++ b/src/features/playerSpeed/index.ts @@ -29,9 +29,8 @@ export async function setPlayerSpeed(input?: number): Promise { options: { enable_forced_playback_speed: enablePlayerSpeed, player_speed: playerSpeed } } } = await waitForSpecificMessage("options", "request_data", "content")); - } else if (typeof input === "number") { - playerSpeed = input; - } + } else playerSpeed = input; + // If the player speed is not specified, return if (!playerSpeed) return; // If forced playback speed option is disabled, return @@ -92,9 +91,7 @@ export function setupPlaybackSpeedChangeListener() { let playerSpeed: number = 1; if (speedValueRegex.test(speedValue)) { const speedValueMatch = speedValue.match(speedValueRegex); - if (speedValueMatch) { - playerSpeed = Number(speedValueMatch[1]); - } + if (speedValueMatch) playerSpeed = Number(speedValueMatch[1]); } window.localStorage.setItem("playerSpeed", String(playerSpeed)); }; @@ -124,31 +121,30 @@ export function setupPlaybackSpeedChangeListener() { }); }); const config: MutationObserverInit = { childList: true, subtree: true }; - if (settingsPanelMenu) { - playerSpeedMenuObserver.observe(settingsPanelMenu, config); - customSpeedSliderObserver.observe(settingsPanelMenu, { - attributeFilter: ["aria-valuenow"], - attributes: true, - childList: true, - subtree: true - }); - const menuItems = [ - ...document.querySelectorAll('div.ytp-settings-menu:not(#yte-feature-menu) > .ytp-panel > .ytp-panel-menu [role="menuitem"]') - ]; - const speedMenuItem = menuItems.find( - (el) => - el.children[0].innerHTML === - `` - ); - if (!speedMenuItem) return; - const { - children: [, , speedMenuItemContent] - } = speedMenuItem; - if (!speedMenuItemContent) return; - const { textContent: speedValue } = speedMenuItem; - // If the playback speed is not available, return - if (!speedValue) return; - const playerSpeed = isNaN(Number(speedValue)) ? 1 : Number(speedValue); - window.localStorage.setItem("playerSpeed", String(playerSpeed)); - } + + if (!settingsPanelMenu) return; + playerSpeedMenuObserver.observe(settingsPanelMenu, config); + customSpeedSliderObserver.observe(settingsPanelMenu, { + attributeFilter: ["aria-valuenow"], + attributes: true, + childList: true, + subtree: true + }); + const menuItems = Array.from( + document.querySelectorAll('div.ytp-settings-menu:not(#yte-feature-menu) > .ytp-panel > .ytp-panel-menu [role="menuitem"]') + ); + const speedMenuItem = menuItems.find( + (el) => + el.children[0].innerHTML === + `` + ); + if (!speedMenuItem) return; + const { + children: [, , speedMenuItemContent], + textContent: speedValue + } = speedMenuItem; + // If the playback speed is not available, return + if (!speedMenuItemContent || !speedValue) return; + const playerSpeed = isNaN(Number(speedValue)) ? 1 : Number(speedValue); + window.localStorage.setItem("playerSpeed", String(playerSpeed)); } diff --git a/src/features/remainingTime/index.ts b/src/features/remainingTime/index.ts index b6a9c0a0..339179b2 100644 --- a/src/features/remainingTime/index.ts +++ b/src/features/remainingTime/index.ts @@ -53,9 +53,7 @@ export async function setupRemainingTime() { if (playerVideoData.isLive && !remainingTimeElementExists) return; const remainingTimeElement = document.querySelector("span#ytp-time-remaining") ?? document.createElement("span"); // If the video is live return - if (playerVideoData.isLive && remainingTimeElementExists) { - remainingTimeElement.remove(); - } + if (playerVideoData.isLive && remainingTimeElementExists) remainingTimeElement.remove(); if (!remainingTimeElementExists) { remainingTimeElement.id = "ytp-time-remaining"; remainingTimeElement.textContent = remainingTime; diff --git a/src/features/remainingTime/utils.ts b/src/features/remainingTime/utils.ts index 99a26897..75304722 100644 --- a/src/features/remainingTime/utils.ts +++ b/src/features/remainingTime/utils.ts @@ -8,13 +8,8 @@ export function formatTime(timeInSeconds: number) { Math.floor(timeInSeconds % 60) ]; const formattedUnits: string[] = units.reduce((acc: string[], unit) => { - if (acc.length > 0) { - acc.push(unit.toString().padStart(2, "0")); - } else { - if (unit > 0) { - acc.push(unit.toString()); - } - } + if (acc.length > 0) acc.push(unit.toString().padStart(2, "0")); + else if (unit > 0) acc.push(unit.toString()); return acc; }, []); return `${formattedUnits.length > 0 ? formattedUnits.join(":") : "0"}`; diff --git a/src/features/rememberVolume/utils.ts b/src/features/rememberVolume/utils.ts index 177e8f4d..3aab2efb 100644 --- a/src/features/rememberVolume/utils.ts +++ b/src/features/rememberVolume/utils.ts @@ -29,13 +29,10 @@ export async function setupVolumeChangeListener() { void (async () => { if (!currentTarget) return; const newVolume = await playerContainer.getVolume(); - if (IsWatchPage || IsLivePage) { - // Send a "setVolume" message to the content script - sendContentOnlyMessage("setRememberedVolume", { watchPageVolume: newVolume }); - } else if (IsShortsPage) { - // Send a "setVolume" message to the content script - sendContentOnlyMessage("setRememberedVolume", { shortsPageVolume: newVolume }); - } + // Send a "setVolume" message to the content script + if (IsWatchPage) sendContentOnlyMessage("setRememberedVolume", { watchPageVolume: newVolume }); + // Send a "setVolume" message to the content script + else if (IsShortsPage) sendContentOnlyMessage("setRememberedVolume", { shortsPageVolume: newVolume }); })(); }, "rememberVolume" @@ -55,16 +52,15 @@ export async function setRememberedVolume({ rememberedVolumes: configuration["remembered_volumes"]; }) { // If the remembered volume option is enabled, set the volume and draw the volume display - if (rememberedVolumes && enableRememberVolume) { - const { shortsPageVolume, watchPageVolume } = rememberedVolumes ?? {}; - if (isWatchPage && watchPageVolume) { - // Log the message indicating whether the last volume is being restored or not - browserColorLog(`Restoring watch page volume to ${watchPageVolume}`, "FgMagenta"); - await playerContainer.setVolume(watchPageVolume); - } else if (isShortsPage && shortsPageVolume) { - // Log the message indicating whether the last volume is being restored or not - browserColorLog(`Restoring shorts page volume to ${shortsPageVolume}`, "FgMagenta"); - await playerContainer.setVolume(shortsPageVolume); - } + if (!rememberedVolumes || !enableRememberVolume) return; + const { shortsPageVolume, watchPageVolume } = rememberedVolumes ?? {}; + if (isWatchPage && watchPageVolume) { + // Log the message indicating whether the last volume is being restored or not + browserColorLog(`Restoring watch page volume to ${watchPageVolume}`, "FgMagenta"); + await playerContainer.setVolume(watchPageVolume); + } else if (isShortsPage && shortsPageVolume) { + // Log the message indicating whether the last volume is being restored or not + browserColorLog(`Restoring shorts page volume to ${shortsPageVolume}`, "FgMagenta"); + await playerContainer.setVolume(shortsPageVolume); } } diff --git a/src/features/removeRedirect/index.ts b/src/features/removeRedirect/index.ts index 693d162d..45ee8680 100644 --- a/src/features/removeRedirect/index.ts +++ b/src/features/removeRedirect/index.ts @@ -14,25 +14,21 @@ export default async function enableRemoveRedirect() { ".yt-core-attributed-string__link, .yt-simple-endpoint.style-scope.yt-formatted-string" ); links.forEach((link: HTMLElement) => { - const href: null | string = link.getAttribute("href"); - if (href && href.match(regex)) { - const urlParams: URLSearchParams = new URLSearchParams(href); - link.setAttribute("href", urlParams.get("q") || ""); - } + const href: Nullable = link.getAttribute("href"); + if (!href || !href.match(regex)) return; + const urlParams: URLSearchParams = new URLSearchParams(href); + link.setAttribute("href", urlParams.get("q") || ""); }); const callback: MutationCallback = (mutationsList: MutationRecord[]) => { for (const mutation of mutationsList) { - if (mutation.type === "childList") { - mutation.addedNodes.forEach((node: Nullable) => { - if (node instanceof Element && node.hasAttribute("href")) { - const href: null | string = node.getAttribute("href"); - if (href !== null && href.match(regex)) { - const urlParams: URLSearchParams = new URLSearchParams(href); - node.setAttribute("href", urlParams.get("q") || ""); - } - } - }); - } + if (mutation.type !== "childList") return; + mutation.addedNodes.forEach((node: Nullable) => { + if (!(node instanceof Element) || !("href" in node)) return; + const href: Nullable = node.getAttribute("href"); + if (!href || !href.match(regex)) return; + const urlParams = new URLSearchParams(href); + node.setAttribute("href", urlParams.get("q") || ""); + }); } }; const observer: MutationObserver = new MutationObserver(callback); diff --git a/src/features/screenshotButton/index.ts b/src/features/screenshotButton/index.ts index 44845352..0d6afc6b 100644 --- a/src/features/screenshotButton/index.ts +++ b/src/features/screenshotButton/index.ts @@ -41,9 +41,7 @@ async function takeScreenshot(videoElement: HTMLVideoElement) { listener(); const clipboardImage = new ClipboardItem({ "image/png": blob }); void navigator.clipboard.write([clipboardImage]); - setTimeout(() => { - remove(); - }, 1200); + setTimeout(() => remove(), 1200); break; } case "file": { @@ -77,12 +75,8 @@ export const addScreenshotButton: AddButtonFunction = async () => { const videoElement = document.querySelector("video"); // If video element is not available, return if (!videoElement) return; - try { - // Take a screenshot - await takeScreenshot(videoElement); - } catch (error) { - console.error(error); - } + // Take a screenshot + await takeScreenshot(videoElement).catch(console.error); })(); } await addFeatureButton( diff --git a/src/features/scrollWheelSpeedControl/index.ts b/src/features/scrollWheelSpeedControl/index.ts index dc764c99..6bf9da61 100644 --- a/src/features/scrollWheelSpeedControl/index.ts +++ b/src/features/scrollWheelSpeedControl/index.ts @@ -31,13 +31,9 @@ export default async function adjustSpeedOnScrollWheel() { if (playerVideoData.isLive) return; // Define the event handler for the scroll wheel events const handleWheel = (event: Event) => { - const setOptionsData = async () => { - return (optionsData = await waitForSpecificMessage("options", "request_data", "content")); - }; + const setOptionsData = async () => (optionsData = await waitForSpecificMessage("options", "request_data", "content")); void (async () => { - if (!optionsData) { - return void (await setOptionsData()); - } + if (!optionsData) return void (await setOptionsData()); const { data: { options: { diff --git a/src/features/scrollWheelSpeedControl/utils.ts b/src/features/scrollWheelSpeedControl/utils.ts index 299362ea..25123f84 100644 --- a/src/features/scrollWheelSpeedControl/utils.ts +++ b/src/features/scrollWheelSpeedControl/utils.ts @@ -32,7 +32,7 @@ export function adjustSpeed(scrollDelta: number, speedStep: number): Promise<{ n * @param listener - The event listener function. */ export function setupScrollListeners(selector: Selector, handleWheel: (event: Event) => void) { - const elements: NodeListOf = document.querySelectorAll(selector); + const elements = document.querySelectorAll(selector); if (!elements.length) return browserColorLog(`No elements found with selector ${selector}`, "FgRed"); for (const element of elements) { eventManager.addEventListener(element, "wheel", handleWheel, "scrollWheelSpeedControl", { passive: false }); diff --git a/src/features/scrollWheelVolumeControl/index.ts b/src/features/scrollWheelVolumeControl/index.ts index 2023f3a6..8c47bc01 100644 --- a/src/features/scrollWheelVolumeControl/index.ts +++ b/src/features/scrollWheelVolumeControl/index.ts @@ -31,13 +31,9 @@ export default async function adjustVolumeOnScrollWheel(): Promise { const settingsPanelMenu = document.querySelector("div.ytp-settings-menu:not(#yte-feature-menu)"); // If the settings panel menu is targeted return if (settingsPanelMenu && settingsPanelMenu.contains(event.target as Node)) return; - const setOptionsData = async () => { - return (optionsData = await waitForSpecificMessage("options", "request_data", "content")); - }; + const setOptionsData = async () => (optionsData = await waitForSpecificMessage("options", "request_data", "content")); void (async () => { - if (!optionsData) { - return void (await setOptionsData()); - } + if (!optionsData) return void (await setOptionsData()); const { data: { options: { @@ -96,11 +92,7 @@ export default async function adjustVolumeOnScrollWheel(): Promise { playerContainer: playerContainer }, "yte-osd", - { - max: 100, - type: "volume", - value: newVolume - } + { max: 100, type: "volume", value: newVolume } ); })(); }; diff --git a/src/features/scrollWheelVolumeControl/utils.ts b/src/features/scrollWheelVolumeControl/utils.ts index 947f319e..15cbeb5b 100644 --- a/src/features/scrollWheelVolumeControl/utils.ts +++ b/src/features/scrollWheelVolumeControl/utils.ts @@ -18,17 +18,12 @@ export function adjustVolume( ): Promise<{ newVolume: number; oldVolume: number }> { return new Promise((resolve) => { void (async () => { - if (!playerContainer.getVolume) return; - if (!playerContainer.setVolume) return; - if (!playerContainer.isMuted) return; - if (!playerContainer.unMute) return; + if (!playerContainer.getVolume || !playerContainer.setVolume || !playerContainer.isMuted || !playerContainer.unMute) return; const [volume, isMuted] = await Promise.all([playerContainer.getVolume(), playerContainer.isMuted()]); const newVolume = clamp(toDivisible(volume + scrollDelta * volumeStep, volumeStep), 0, 100); browserColorLog(`Adjusting volume by ${volumeStep} to ${newVolume}. Old volume was ${volume}`, "FgMagenta"); await playerContainer.setVolume(newVolume); - if (isMuted) { - if (typeof playerContainer.unMute === "function") await playerContainer.unMute(); - } + if (isMuted && typeof playerContainer.unMute === "function") await playerContainer.unMute(); resolve({ newVolume, oldVolume: volume }); })(); }); @@ -40,7 +35,7 @@ export function adjustVolume( * @param listener - The event listener function. */ export function setupScrollListeners(selector: Selector, handleWheel: (event: Event) => void) { - const elements: NodeListOf = document.querySelectorAll(selector); + const elements = document.querySelectorAll(selector); if (!elements.length) return browserColorLog(`No elements found with selector ${selector}`, "FgRed"); for (const element of elements) { eventManager.addEventListener(element, "wheel", handleWheel, "scrollWheelVolumeControl", { passive: false }); diff --git a/src/features/shareShortener/index.ts b/src/features/shareShortener/index.ts index 33c71687..384741a8 100644 --- a/src/features/shareShortener/index.ts +++ b/src/features/shareShortener/index.ts @@ -2,7 +2,7 @@ import { browserColorLog, waitForSpecificMessage } from "@/src/utils/utilities"; const regexp: RegExp = new RegExp("(\\?|&)(si|feature|pp)=[^&]*", "g"); let intervalId: NodeJS.Timeout | null = null; let input: HTMLInputElement | null; -function cleanUrl(url: string): string { +function cleanUrl(url: string) { return url.replace(regexp, ""); } @@ -13,13 +13,10 @@ function cleanAndUpdateUrl() { input = null; } intervalId = setInterval(() => { - if (!input) { - input = document.querySelector("#share-url"); - } - if (input) { - if (!input.value.match(regexp)) return; - input.value = cleanUrl(input.value); - } + if (!input) return void (input = document.querySelector("#share-url")); + if (!input.value.match(regexp)) return; + console.log("cleanAndUpdateUrl"); + input.value = cleanUrl(input.value); }, 50); } @@ -27,19 +24,15 @@ function cleanSearchPage(url: string) { if (!url.match(/https?:\/\/(?:www\.)?youtube\.com\/results\?search\_query\=.+/gm)) return; const allElements = Array.from(document.querySelectorAll("*")); allElements.forEach((e) => { - const href: null | string = e.getAttribute("href"); - if (href && href.match(/^\/watch\?v\=.+$/gm)) { - e.setAttribute("href", href.replace(regexp, "")); - } + const href = e.getAttribute("href"); + if (!href || !href.match(/^\/watch\?v\=.+$/gm)) return; + e.setAttribute("href", href.replace(regexp, "")); }); } function handleInput(event: MouseEvent) { const element = event.target as Element; - if (!element.classList.contains("yt-spec-touch-feedback-shape__fill")) { - return; - } - cleanAndUpdateUrl(); + if (element.classList.contains("yt-spec-touch-feedback-shape__fill")) cleanAndUpdateUrl(); } export async function enableShareShortener() { @@ -56,8 +49,7 @@ export async function enableShareShortener() { export function disableShareShortener() { browserColorLog(`Disabling share shortener`, "FgMagenta"); document.removeEventListener("click", handleInput); - if (intervalId) { - clearInterval(intervalId); - intervalId = null; - } + if (!intervalId) return; + clearInterval(intervalId); + intervalId = null; } diff --git a/src/features/shortsAutoScroll/utils.ts b/src/features/shortsAutoScroll/utils.ts index 55b66c92..bda7c742 100644 --- a/src/features/shortsAutoScroll/utils.ts +++ b/src/features/shortsAutoScroll/utils.ts @@ -9,13 +9,11 @@ export const setupAutoScroll = (playerContainer: YouTubePlayerDiv, video: HTMLVi const progressState = playerContainer.getProgressState(); const currentTime = Math.floor(progressState.current); const duration = Math.floor(progressState.duration); - if (currentTime === duration) { - eventManager.removeEventListener(video, "timeupdate", "shortsAutoScroll"); - const nextButton = document.querySelector("#navigation-button-down > ytd-button-renderer > yt-button-shape > button"); - if (!nextButton) return; - // Click the next button - nextButton.click(); - } + if (currentTime !== duration) return; + eventManager.removeEventListener(video, "timeupdate", "shortsAutoScroll"); + const nextButton = document.querySelector("#navigation-button-down > ytd-button-renderer > yt-button-shape > button"); + // Click the next button + nextButton?.click(); }; eventManager.addEventListener(video, "timeupdate", shortTimeUpdate, "shortsAutoScroll"); }; diff --git a/src/features/videoHistory/index.ts b/src/features/videoHistory/index.ts index 371919c3..f7a75576 100644 --- a/src/features/videoHistory/index.ts +++ b/src/features/videoHistory/index.ts @@ -72,22 +72,16 @@ export async function promptUserToResumeVideo(cb: () => void) { const { video_id: videoId } = await playerContainer.getVideoData(); if (!videoId) return; const videoHistoryOneData = await waitForSpecificMessage("videoHistoryOne", "request_data", "content", { id: videoId }); - if (!videoHistoryOneData) { - cb(); - return; - } + if (!videoHistoryOneData) return cb(); + const { data: { video_history_entry } } = videoHistoryOneData; if (video_history_entry && video_history_entry.status === "watching" && video_history_entry.timestamp > 0) { - if (video_history_resume_type === "automatic") { - void playerContainer.seekTo(video_history_entry.timestamp, true); - return cb(); - } - createResumePrompt(video_history_entry, playerContainer, cb); - } else { - cb(); + if (video_history_resume_type !== "automatic") return createResumePrompt(video_history_entry, playerContainer, cb); + void playerContainer.seekTo(video_history_entry.timestamp, true); } + cb(); } // Utility function to check if an element exists const elementExists = (elementId: string) => !!document.getElementById(elementId); @@ -175,11 +169,8 @@ function createResumePrompt(videoHistoryEntry: VideoHistoryEntry, playerContaine const elapsed = round(timestamp - start); const progress = round(Math.min(elapsed / (progressBarDuration * 1000), 1), 2); progressBar.style.width = `${round((1 - progress) * 100, 2)}%`; - if (progress < 1) { - animationFrameId = requestAnimationFrame(updateResumeProgress); - } else { - hidePrompt(); - } + if (progress < 1) animationFrameId = requestAnimationFrame(updateResumeProgress); + else hidePrompt(); } animationFrameId = requestAnimationFrame(updateResumeProgress); } @@ -194,12 +185,10 @@ function createResumePrompt(videoHistoryEntry: VideoHistoryEntry, playerContaine void playerContainer.seekTo(videoHistoryEntry.timestamp, true); cb(); } - if (!elementExists(progressBarId)) { - prompt.appendChild(progressBar); - } - if (!elementExists(closeButtonId)) { - prompt.appendChild(closeButton); - } + + if (!elementExists(progressBarId)) prompt.appendChild(progressBar); + if (!elementExists(closeButtonId)) prompt.appendChild(closeButton); + const { listener: resumePromptCloseButtonMouseOverListener } = createTooltip({ element: closeButton, featureName: "videoHistory", @@ -209,16 +198,14 @@ function createResumePrompt(videoHistoryEntry: VideoHistoryEntry, playerContaine eventManager.removeEventListener(closeButton, "mouseover", "videoHistory"); eventManager.addEventListener(closeButton, "mouseover", resumePromptCloseButtonMouseOverListener, "videoHistory"); startCountdown(); - const closeListener = () => { - hidePrompt(); - }; + + const closeListener = () => hidePrompt(); eventManager.removeEventListener(resumeButton, "click", "videoHistory"); eventManager.addEventListener(resumeButton, "click", resumeButtonClickListener, "videoHistory"); eventManager.removeEventListener(closeButton, "click", "videoHistory"); eventManager.addEventListener(closeButton, "click", closeListener, "videoHistory"); // Display the prompt - if (!elementExists(promptId)) { - prompt.appendChild(resumeButton); - playerContainer.appendChild(prompt); - } + if (elementExists(promptId)) return; + prompt.appendChild(resumeButton); + playerContainer.appendChild(prompt); } diff --git a/src/features/volumeBoost/index.ts b/src/features/volumeBoost/index.ts index 31b72efc..7a337283 100644 --- a/src/features/volumeBoost/index.ts +++ b/src/features/volumeBoost/index.ts @@ -14,11 +14,15 @@ export default async function volumeBoost() { } = await waitForSpecificMessage("options", "request_data", "content"); if (!enable_volume_boost) return; setupVolumeBoost(); - if (volume_boost_mode === "per_video") { - await addVolumeBoostButton(); - } else if (volume_boost_mode === "global") { - applyVolumeBoost(volume_boost_amount); - } + if (volume_boost_mode === "per_video") await addVolumeBoostButton(); + else if (volume_boost_mode === "global") applyVolumeBoost(volume_boost_amount); +} +export async function enableVolumeBoost() { + setupVolumeBoost(); +======= + + if (volume_boost_mode === "per_video") await addVolumeBoostButton(); + else if (volume_boost_mode === "global") applyVolumeBoost(volume_boost_amount); } export async function enableVolumeBoost() { setupVolumeBoost(); @@ -30,27 +34,25 @@ export async function enableVolumeBoost() { applyVolumeBoost(volume_boost_amount); } function setupVolumeBoost() { - if (!window.audioCtx || !window.gainNode) { - browserColorLog(`Enabling volume boost`, "FgMagenta"); - try { - const player = document.querySelector("video"); - if (!player) return; - window.audioCtx = new (window.AudioContext || window.webkitAudioContext)(); - const source = window.audioCtx.createMediaElementSource(player); - const gainNode = window.audioCtx.createGain(); - source.connect(gainNode); - gainNode.connect(window.audioCtx.destination); - window.gainNode = gainNode; - } catch (error) { - browserColorLog(`Failed to enable volume boost: ${formatError(error)}`, "FgRed"); - } + if (window.audioCtx && window.gainNode) return; + browserColorLog(`Enabling volume boost`, "FgMagenta"); + try { + const player = document.querySelector("video"); + if (!player) return; + window.audioCtx = new (window.AudioContext || window.webkitAudioContext)(); + const source = window.audioCtx.createMediaElementSource(player); + const gainNode = window.audioCtx.createGain(); + source.connect(gainNode); + gainNode.connect(window.audioCtx.destination); + window.gainNode = gainNode; + } catch (error) { + browserColorLog(`Failed to enable volume boost: ${formatError(error)}`, "FgRed"); } } export function disableVolumeBoost() { - if (window.gainNode) { - browserColorLog(`Disabling volume boost`, "FgMagenta"); - window.gainNode.gain.value = 1; // Set gain back to default - } + if (!window.gainNode) return; + browserColorLog(`Disabling volume boost`, "FgMagenta"); + window.gainNode.gain.value = 1; // Set gain back to default } export function applyVolumeBoost(volume_boost_amount: number) { browserColorLog(`Setting volume boost to ${Math.pow(10, volume_boost_amount / 20)}`, "FgMagenta"); @@ -74,17 +76,13 @@ export const addVolumeBoostButton: AddButtonFunction = async () => { getFeatureIcon("volumeBoostButton", volumeBoostButtonPlacement), (checked) => { void (async () => { - if (checked !== undefined) { - updateFeatureButtonTitle( - "volumeBoostButton", - window.i18nextInstance.t(`pages.content.features.volumeBoostButton.button.toggle.${checked ? "on" : "off"}`) - ); - if (checked) { - await enableVolumeBoost(); - } else { - disableVolumeBoost(); - } - } + if (checked === undefined) return; + updateFeatureButtonTitle( + "volumeBoostButton", + window.i18nextInstance.t(`pages.content.features.volumeBoostButton.button.toggle.${checked ? "on" : "off"}`) + ); + if (checked) await enableVolumeBoost(); + else disableVolumeBoost(); })(); }, true diff --git a/src/hooks/useNotifications/provider.tsx b/src/hooks/useNotifications/provider.tsx index d67fa48c..3390d0b5 100644 --- a/src/hooks/useNotifications/provider.tsx +++ b/src/hooks/useNotifications/provider.tsx @@ -19,15 +19,11 @@ export const NotificationsProvider = ({ children }: NotificationProviderProps) = }; const createNotification: CreateNotification = (type, message, action) => { const removeNotificationAfterMs = action && action === "reset_settings" ? 15_000 : 5_000; - const notification = { action, message, removeAfterMs: removeNotificationAfterMs, timestamp: +new Date(), type } satisfies Notification; - return notification; + return { action, message, removeAfterMs: removeNotificationAfterMs, timestamp: +new Date(), type } satisfies Notification; }; const scheduleNotificationRemoval: ScheduleNotificationRemoval = (notification, removeAfterMs) => { - if (removeAfterMs) { - setTimeout(() => { - removeNotification(notification); - }, removeAfterMs); - } + if (!removeAfterMs) return; + setTimeout(() => removeNotification(notification), removeAfterMs); }; const addNotification: AddNotification = (type, message, action) => { const notification = createNotification(type, message, action); @@ -58,7 +54,7 @@ export const NotificationsProvider = ({ children }: NotificationProviderProps) = }; updateNotifications(); return () => { - if (animationFrameId !== null) cancelAnimationFrame(animationFrameId); + if (animationFrameId) cancelAnimationFrame(animationFrameId); }; }, []); const contextValue = { addNotification, notifications, removeNotification } satisfies NotificationsContextProps; diff --git a/src/pages/background/index.ts b/src/pages/background/index.ts index 25d484f3..f452f919 100644 --- a/src/pages/background/index.ts +++ b/src/pages/background/index.ts @@ -49,37 +49,26 @@ chrome.runtime.onMessage.addListener((message: ContentToBackgroundSendOnlyMessag chrome.tabs.query({ url: "https://www.youtube.com/*" }, (tabs) => { for (const tab of tabs) { // Skip the active tab - if (tab.id === activeTabId) continue; - if (tab.id !== undefined) { - chrome.scripting.executeScript( - { - func: () => { - const videos = document.querySelectorAll("video"); - videos.forEach((video) => { - if (!video.paused) { - video.pause(); - } - }); - const audios = document.querySelectorAll("audio"); - audios.forEach((audio) => { - if (!audio.paused) { - audio.pause(); - } - }); - }, - target: { tabId: tab.id } + if (!tab.id || tab.id === activeTabId) continue; + chrome.scripting.executeScript( + { + func: () => { + const videos = document.querySelectorAll("video"); + videos.forEach((video) => { + if (!video.paused) video.pause(); + }); + const audios = document.querySelectorAll("audio"); + audios.forEach((audio) => { + if (!audio.paused) audio.pause(); + }); }, - (results) => { - if (chrome.runtime.lastError) { - console.error(chrome.runtime.lastError.message); - } else { - if (results[0].result) { - console.log(results); - } - } - } - ); - } + target: { tabId: tab.id } + }, + (results) => { + if (chrome.runtime.lastError) console.error(chrome.runtime.lastError.message); + else if (results[0].result) console.log(results); + } + ); } }); }); diff --git a/src/pages/content/index.ts b/src/pages/content/index.ts index ebb4b414..7d80fc7d 100644 --- a/src/pages/content/index.ts +++ b/src/pages/content/index.ts @@ -146,15 +146,13 @@ document.addEventListener("yte-message-from-youtube", () => { } case "pageLoaded": { chrome.storage.onChanged.addListener(storageListeners); - window.onunload = () => { - chrome.storage.onChanged.removeListener(storageListeners); - }; + window.onunload = () => chrome.storage.onChanged.removeListener(storageListeners); break; } case "videoHistoryOne": { - const { data } = message; - if (!data) return; - const { video_history_entry } = data; + const { + data: { video_history_entry } + } = message; if (!video_history_entry) return; const { id, status, timestamp } = video_history_entry; setVideoHistory(id, timestamp, status); @@ -235,12 +233,7 @@ const storageChangeHandler = async (changes: StorageChanges, areaName: string) = (acc, feature) => { const { [feature]: oldPlacement } = oldValue; const { [feature]: newPlacement } = newValue; - return Object.assign(acc, { - [feature]: { - new: newPlacement, - old: oldPlacement - } - }); + return Object.assign(acc, { [feature]: { new: newPlacement, old: oldPlacement } }); }, {} as Record ) @@ -482,8 +475,7 @@ const storageChangeHandler = async (changes: StorageChanges, areaName: string) = }; Object.entries(castedChanges).forEach(([key, change]) => { if (isValidChange(change)) { - if (!change.newValue) return; - if (!change.oldValue) return; + if (!change.newValue || !change.oldValue) return; const oldValue = parseStoredValue(change.oldValue) as configuration[typeof key]; const newValue = parseStoredValue(change.newValue) as configuration[typeof key]; const { [key]: handler } = keyActions; diff --git a/src/pages/embedded/index.ts b/src/pages/embedded/index.ts index e03fdf42..6da6a8fe 100644 --- a/src/pages/embedded/index.ts +++ b/src/pages/embedded/index.ts @@ -265,11 +265,10 @@ function handleSoftNavigate() { } window.addEventListener("DOMContentLoaded", function () { void (async () => { - const response = await waitForSpecificMessage("language", "request_data", "content"); - if (!response) return; const { data: { language } - } = response; + } = await waitForSpecificMessage("language", "request_data", "content"); + if (!language) return; const i18nextInstance = await i18nService(language); window.i18nextInstance = i18nextInstance; if (isFirstLoad) { @@ -335,16 +334,14 @@ window.addEventListener("DOMContentLoaded", function () { if (volumeBoostMode === "global") { await removeVolumeBoostButton(); await enableVolumeBoost(); - } else { - disableVolumeBoost(); - await addVolumeBoostButton(); + break; } - } else { disableVolumeBoost(); - if (volumeBoostMode === "per_video") { - await removeVolumeBoostButton(); - } + await addVolumeBoostButton(); + break; } + disableVolumeBoost(); + if (volumeBoostMode === "per_video") await removeVolumeBoostButton(); break; } case "volumeBoostAmountChange": { @@ -353,8 +350,7 @@ window.addEventListener("DOMContentLoaded", function () { } = message; switch (volumeBoostMode) { case "global": { - if (!volumeBoostEnabled) return; - applyVolumeBoost(volumeBoostAmount); + if (volumeBoostEnabled) applyVolumeBoost(volumeBoostAmount); break; } case "per_video": { @@ -367,9 +363,6 @@ window.addEventListener("DOMContentLoaded", function () { break; } case "playerSpeedChange": { - const { - data: { enableForcedPlaybackSpeed, playerSpeed } - } = message; const { data: { options: { playback_buttons_speed: playbackSpeedPerClick } @@ -405,11 +398,8 @@ window.addEventListener("DOMContentLoaded", function () { const { data: { screenshotButtonEnabled } } = message; - if (screenshotButtonEnabled) { - await addScreenshotButton(); - } else { - await removeScreenshotButton(); - } + if (screenshotButtonEnabled) await addScreenshotButton(); + else await removeScreenshotButton(); break; } case "hideEndScreenCardsChange": { @@ -448,21 +438,19 @@ window.addEventListener("DOMContentLoaded", function () { const { data: { maximizePlayerButtonEnabled } } = message; - if (maximizePlayerButtonEnabled) { - await addMaximizePlayerButton(); - } else { - await removeMaximizePlayerButton(); - const maximizePlayerButton = document.querySelector("video.html5-main-video"); - if (!maximizePlayerButton) return; - // Get the video element - const videoElement = document.querySelector("video.html5-main-video"); - // If video element is not available, return - if (!videoElement) return; - const videoContainer = document.querySelector("video.html5-main-video"); - if (!videoContainer) return; - if (videoContainer.classList.contains("maximized_video_container") && videoElement.classList.contains("maximized_video")) { - maximizePlayer(); - } + if (maximizePlayerButtonEnabled) return await addMaximizePlayerButton(); + + await removeMaximizePlayerButton(); + const maximizePlayerButton = document.querySelector("video.html5-main-video"); + if (!maximizePlayerButton) return; + // Get the video element + const videoElement = document.querySelector("video.html5-main-video"); + // If video element is not available, return + if (!videoElement) return; + const videoContainer = document.querySelector("video.html5-main-video"); + if (!videoContainer) return; + if (videoContainer.classList.contains("maximized_video_container") && videoElement.classList.contains("maximized_video")) { + maximizePlayer(); } break; } @@ -470,33 +458,24 @@ window.addEventListener("DOMContentLoaded", function () { const { data: { videoHistoryEnabled } } = message; - if (videoHistoryEnabled) { - await setupVideoHistory(); - } else { - eventManager.removeEventListeners("videoHistory"); - } + if (videoHistoryEnabled) await setupVideoHistory(); + else eventManager.removeEventListeners("videoHistory"); break; } case "remainingTimeChange": { const { data: { remainingTimeEnabled } } = message; - if (remainingTimeEnabled) { - await enableRemainingTime(); - } else { - removeRemainingTimeDisplay(); - } + if (remainingTimeEnabled) await enableRemainingTime(); + else removeRemainingTimeDisplay(); break; } case "loopButtonChange": { const { data: { loopButtonEnabled } } = message; - if (loopButtonEnabled) { - await addLoopButton(); - } else { - await removeLoopButton(); - } + if (loopButtonEnabled) await addLoopButton(); + else await removeLoopButton(); break; } case "copyTimestampUrlButtonChange": { @@ -580,33 +559,24 @@ window.addEventListener("DOMContentLoaded", function () { const { data: { scrollWheelVolumeControlEnabled } } = message; - if (scrollWheelVolumeControlEnabled) { - await adjustVolumeOnScrollWheel(); - } else { - eventManager.removeEventListeners("scrollWheelVolumeControl"); - } + if (scrollWheelVolumeControlEnabled) await adjustVolumeOnScrollWheel(); + else eventManager.removeEventListeners("scrollWheelVolumeControl"); break; } case "scrollWheelSpeedControlChange": { const { data: { scrollWheelSpeedControlEnabled } } = message; - if (scrollWheelSpeedControlEnabled) { - await adjustSpeedOnScrollWheel(); - } else { - eventManager.removeEventListeners("scrollWheelSpeedControl"); - } + if (scrollWheelSpeedControlEnabled) await adjustSpeedOnScrollWheel(); + else eventManager.removeEventListeners("scrollWheelSpeedControl"); break; } case "rememberVolumeChange": { const { data: { rememberVolumeEnabled } } = message; - if (rememberVolumeEnabled) { - await enableRememberVolume(); - } else { - eventManager.removeEventListeners("rememberVolume"); - } + if (rememberVolumeEnabled) await enableRememberVolume(); + else eventManager.removeEventListeners("rememberVolume"); break; } case "hideTranslateCommentChange": { @@ -622,26 +592,16 @@ window.addEventListener("DOMContentLoaded", function () { const { data: { hideScrollBarEnabled } } = message; - if (hideScrollBarEnabled) { - if (!scrollBarHidden) { - hideScrollBar(); - } - } else { - if (scrollBarHidden) { - showScrollBar(); - } - } + if (hideScrollBarEnabled && !scrollBarHidden) hideScrollBar(); + if (!hideScrollBarEnabled && scrollBarHidden) showScrollBar(); break; } case "hideShortsChange": { const { data: { hideShortsEnabled } } = message; - if (hideShortsEnabled) { - await enableHideShorts(); - } else { - disableHideShorts(); - } + if (hideShortsEnabled) await enableHideShorts(); + else disableHideShorts(); break; } case "hideEndScreenCardsButtonChange": { @@ -667,11 +627,8 @@ window.addEventListener("DOMContentLoaded", function () { const { data: { hideLiveStreamChatEnabled } } = message; - if (hideLiveStreamChatEnabled) { - await enableHideLiveStreamChat(); - } else { - await disableHideLiveStreamChat(); - } + if (hideLiveStreamChatEnabled) await enableHideLiveStreamChat(); + else await disableHideLiveStreamChat(); break; } case "languageChange": { @@ -694,7 +651,9 @@ window.addEventListener("DOMContentLoaded", function () { updateFeatureMenuItemLabel( feature, window.i18nextInstance.t( - `pages.content.features.${multiFeatureName}.buttons.${multiButtonName}.label` as `pages.content.features.${typeof multiFeatureName}.buttons.${KeysOfUnion}.label`, + `pages.content.features.${multiFeatureName}.buttons.${multiButtonName}.label` as `pages.content.features.${typeof multiFeatureName}.buttons.${KeysOfUnion< + FeatureToMultiButtonMap[typeof multiFeatureName] + >}.label`, { SPEED: options.playback_buttons_speed } @@ -723,31 +682,38 @@ window.addEventListener("DOMContentLoaded", function () { toggleFeature, window.i18nextInstance.t(`pages.content.features.${toggleFeature}.button.toggle.${buttonChecked ? "on" : "off"}`) ); - } else { - if (featureToMultiButtonsMap.has(featureName)) { - const multiFeatureName = featureName as MultiButtonFeatureNames; - const multiButtonName = feature as MultiButtonNames; - switch (multiFeatureName) { - case "playbackSpeedButtons": { - updateFeatureMenuItemLabel( - feature, - window.i18nextInstance.t( - `pages.content.features.${multiFeatureName}.buttons.${multiButtonName}.label` as `pages.content.features.${typeof multiFeatureName}.buttons.${KeysOfUnion}.label`, - { - SPEED: options.playback_buttons_speed - } - ) - ); - break; - } + return; + } + + if (featureToMultiButtonsMap.has(featureName)) { + const multiFeatureName = featureName as MultiButtonFeatureNames; + const multiButtonName = feature as MultiButtonNames; + switch (multiFeatureName) { + case "playbackSpeedButtons": { + updateFeatureMenuItemLabel( + feature, + window.i18nextInstance.t( + `pages.content.features.${multiFeatureName}.buttons.${multiButtonName}.label` as `pages.content.features.${typeof multiFeatureName}.buttons.${KeysOfUnion< + FeatureToMultiButtonMap[typeof multiFeatureName] + >}.label`, + { + SPEED: options.playback_buttons_speed + } + ) + ); + break; } - } else { - updateFeatureButtonTitle( - feature, - window.i18nextInstance.t(`pages.content.features.${featureName as SingleButtonNames}.button.label`) - ); } + return; } + updateFeatureMenuItemLabel( + feature, + window.i18nextInstance.t( + featureToMultiButtonsMap.has(featureName) ? + `pages.content.features.${featureName as MultiButtonFeatureNames}.buttons.${feature as MultiButtonNames}.label` + : `pages.content.features.${featureName as SingleButtonNames}.button.label` + ) + ); } } break; @@ -781,62 +747,46 @@ window.addEventListener("DOMContentLoaded", function () { const { data: { openTranscriptButtonEnabled } } = message; - if (openTranscriptButtonEnabled) { - await openTranscriptButton(); - } else { - await removeOpenTranscriptButton(); - } + if (openTranscriptButtonEnabled) await openTranscriptButton(); + else await removeOpenTranscriptButton(); break; } case "openYTSettingsOnHoverChange": { const { data: { openYouTubeSettingsOnHoverEnabled } } = message; - if (openYouTubeSettingsOnHoverEnabled) { - await enableOpenYouTubeSettingsOnHover(); - } else { - disableOpenYouTubeSettingsOnHover(); - } + if (openYouTubeSettingsOnHoverEnabled) await enableOpenYouTubeSettingsOnHover(); + else disableOpenYouTubeSettingsOnHover(); break; } case "removeRedirectChange": { const { data: { removeRedirectEnabled } } = message; - if (removeRedirectEnabled) { - await enableRemoveRedirect(); - } + if (removeRedirectEnabled) await enableRemoveRedirect(); break; } case "pauseBackgroundPlayersChange": { const { data: { pauseBackgroundPlayersEnabled } } = message; - if (pauseBackgroundPlayersEnabled) { - await enablePauseBackgroundPlayers(); - } else { - disablePauseBackgroundPlayers(); - } + if (pauseBackgroundPlayersEnabled) await enablePauseBackgroundPlayers(); + else disablePauseBackgroundPlayers(); break; } case "shareShortenerChange": { const { data: { shareShortenerEnabled } } = message; - if (shareShortenerEnabled) { - await enableShareShortener(); - } else { - disableShareShortener(); - } + if (shareShortenerEnabled) await enableShareShortener(); + else disableShareShortener(); break; } case "skipContinueWatchingChange": { const { data: { skipContinueWatchingEnabled } } = message; - if (skipContinueWatchingEnabled) { - await enableSkipContinueWatching(); - } + if (skipContinueWatchingEnabled) await enableSkipContinueWatching(); break; } case "hidePaidPromotionBannerChange": { @@ -854,32 +804,18 @@ window.addEventListener("DOMContentLoaded", function () { const { data: { deepDarkCustomThemeColors, deepDarkPreset, deepDarkThemeEnabled } } = message; - if (deepDarkThemeEnabled) { - if (deepDarkCSSExists()) { - updateDeepDarkCSS( - deepDarkPreset === "Custom" ? getDeepDarkCustomThemeStyle(deepDarkCustomThemeColors) : deepDarkPresets[deepDarkPreset] - ); - } else { - await enableDeepDarkCSS(); - } - } else { - disableDeepDarkCSS(); - } + if (!deepDarkThemeEnabled) return disableDeepDarkCSS(); + if (!deepDarkCSSExists()) return await enableDeepDarkCSS(); + updateDeepDarkCSS(deepDarkPreset === "Custom" ? getDeepDarkCustomThemeStyle(deepDarkCustomThemeColors) : deepDarkPresets[deepDarkPreset]); break; } case "customCSSChange": { const { data: { customCSSCode, customCSSEnabled } } = message; - if (customCSSEnabled) { - if (customCSSExists()) { - updateCustomCSS({ custom_css_code: customCSSCode }); - } else { - await enableCustomCSS(); - } - } else { - disableCustomCSS(); - } + if (!customCSSEnabled) return disableCustomCSS(); + if (!customCSSExists()) return enableCustomCSS(); + updateCustomCSS({ custom_css_code: customCSSCode }); break; } case "buttonPlacementChange": { @@ -956,11 +892,8 @@ window.addEventListener("DOMContentLoaded", function () { const { data: { shortsAutoScrollEnabled } } = message; - if (shortsAutoScrollEnabled) { - await enableShortsAutoScroll(); - } else { - disableShortsAutoScroll(); - } + if (shortsAutoScrollEnabled) await enableShortsAutoScroll(); + else disableShortsAutoScroll(); break; } default: { diff --git a/src/utils/EventManager.ts b/src/utils/EventManager.ts index 7554b1a1..79f80f98 100644 --- a/src/utils/EventManager.ts +++ b/src/utils/EventManager.ts @@ -41,7 +41,7 @@ export type EventManager = { addEventListener: ( target: HTMLElementTagNameMap[keyof HTMLElementTagNameMap], eventName: K, - callback: EventCallback, + callback: EventCallback, featureName: FeatureName, options?: AddEventListenerOptions | boolean ) => void; @@ -75,16 +75,24 @@ export const eventManager: EventManager = { const existingListener = existingListeners.find((listener) => listener.callback === callback); // If the listener hasn't been added, add it if (!existingListener) { - const listenerInfo: EventListenerInfo = { + const listenerInfo: EventListenerInfo = { callback, eventName, target }; + // @ts-expect-error see: https://github.com/microsoft/TypeScript/issues/27808 + // TODO: Add a better way to handle the subtype constraints existingListeners.push(listenerInfo); eventListeners.set(eventName, existingListeners); targetListeners.set(target, eventListeners); this.listeners.set(featureName, targetListeners); - target.addEventListener(eventName, callback, options); + target.addEventListener( + eventName, + // @ts-expect-error The types are fucked in this one + // TODO: Let the future deal with it + callback, + options + ); } }, // event listener info objects