diff --git a/config.json.example b/config.json.example index 66dc1567..b67a8440 100644 --- a/config.json.example +++ b/config.json.example @@ -31,10 +31,16 @@ "mute": "https://wiki.sponsor.ajay.app/w/Mute_Segment", "chapter": "https://wiki.sponsor.ajay.app/w/Chapter" }, - "extensionCommunicationAllowList": [ - "enamippconapkdmgfgjchkhakpfinmaj", - "deArrow@ajay.app", - "deArrowBETA@ajay.app", - "app.ajay.dearrow.extension" - ] + "extensionImportList": { + "chromium": [ + "enamippconapkdmgfgjchkhakpfinmaj" + ], + "firefox": [ + "deArrow@ajay.app", + "deArrowBETA@ajay.app" + ], + "safari": [ + "app.ajay.dearrow.extension" + ] + } } diff --git a/public/_locales b/public/_locales index d2db6e9a..d0db7a13 160000 --- a/public/_locales +++ b/public/_locales @@ -1 +1 @@ -Subproject commit d2db6e9a0f0336194e8ee1181a9b827ac570775f +Subproject commit d0db7a13346adefb6cb41eaeaed8e1e6f19423c8 diff --git a/public/content.css b/public/content.css index 91ff0bb8..ab8b8a02 100644 --- a/public/content.css +++ b/public/content.css @@ -730,11 +730,12 @@ input::-webkit-inner-spin-button { border-radius: 5px; padding: 10px; max-width: 300px; + width: max-content; white-space: normal; line-height: 1.5em; color: white; font-size: 12px; - z-index: 1000; + z-index: 10000; } .sponsorBlockTooltip a { diff --git a/src/background.ts b/src/background.ts index 3f2ccfb7..c70fcba5 100644 --- a/src/background.ts +++ b/src/background.ts @@ -12,6 +12,7 @@ import { generateUserID } from "@ajayyy/maze-utils/lib/setup"; window.SB = Config; import Utils from "./utils"; +import { getExtensionIdsToImportFrom } from "./utils/crossExtension"; const utils = new Utils({ registerFirefoxContentScript, unregisterFirefoxContentScript @@ -80,7 +81,7 @@ chrome.runtime.onMessage.addListener(function (request, sender, callback) { }); chrome.runtime.onMessageExternal.addListener((request, sender, callback) => { - if (CompileConfig.extensionCommunicationAllowList.includes(sender.id)) { + if (getExtensionIdsToImportFrom().includes(sender.id)) { if (request.message === "requestConfig") { callback({ userID: Config.config.userID, diff --git a/src/config.ts b/src/config.ts index 34165f96..1880110d 100644 --- a/src/config.ts +++ b/src/config.ts @@ -73,6 +73,8 @@ interface SBConfig { useVirtualTime: boolean; showSegmentFailedToFetchWarning: boolean; allowScrollingToEdit: boolean; + deArrowInstalled: boolean; + showDeArrowPromotion: boolean; // Used to cache calculated text color info categoryPillColors: { @@ -307,6 +309,8 @@ const syncDefaults = { useVirtualTime: true, showSegmentFailedToFetchWarning: true, allowScrollingToEdit: true, + deArrowInstalled: false, + showDeArrowPromotion: true, categoryPillColors: {}, diff --git a/src/content.ts b/src/content.ts index 198fa960..ecdba9bf 100644 --- a/src/content.ts +++ b/src/content.ts @@ -36,12 +36,14 @@ import { isFirefoxOrSafari, waitFor } from "@ajayyy/maze-utils"; import { getErrorMessage, getFormattedTime } from "@ajayyy/maze-utils/lib/formating"; import { getChannelIDInfo, getVideo, getIsAdPlaying, getIsLivePremiere, setIsAdPlaying, checkVideoIDChange, getVideoID, getYouTubeVideoID, setupVideoModule, checkIfNewVideoID, isOnInvidious, isOnMobileYouTube } from "@ajayyy/maze-utils/lib/video"; import { Keybind, StorageChangesObject, isSafari, keybindEquals } from "@ajayyy/maze-utils/lib/config"; -import { findValidElement } from "@ajayyy/maze-utils/lib/dom" +import { findValidElement, waitForElement } from "@ajayyy/maze-utils/lib/dom" import { getHash, HashedValue } from "@ajayyy/maze-utils/lib/hash"; import { generateUserID } from "@ajayyy/maze-utils/lib/setup"; import { updateAll } from "@ajayyy/maze-utils/lib/thumbnailManagement"; import { setupThumbnailListener } from "./utils/thumbnails"; import * as documentScript from "../dist/js/document.js"; +import { Tooltip } from "./render/Tooltip"; +import { isDeArrowInstalled } from "./utils/crossExtension"; const utils = new Utils(); @@ -49,6 +51,42 @@ utils.wait(() => Config.isReady(), 5000, 10).then(() => { // Hack to get the CSS loaded on permission-based sites (Invidious) addCSS(); setCategoryColorCSSVariables(); + + // DeArrow promotion + setTimeout(async () => { + if (document.URL === "https://www.youtube.com/" + && Config.config.showDeArrowPromotion + && Config.config.showUpsells + && Config.config.showNewFeaturePopups + && Math.random() < 0.05) { + + if (!await isDeArrowInstalled()) { + const element = await waitForElement("#contents") as HTMLElement; + if (element) { + Config.config.showDeArrowPromotion = false; + + new Tooltip({ + text: chrome.i18n.getMessage("DeArrowPromotionMessage2"), + linkOnClick: () => window.open("https://dearrow.ajay.app"), + referenceNode: element, + prependElement: element.firstElementChild as HTMLElement, + timeout: 15000, + positionRealtive: false, + containerAbsolute: true, + bottomOffset: "inherit", + topOffset: "-82px", + leftOffset: "0", + rightOffset: "0", + displayTriangle: false, + center: true, + opacity: 1 + }); + } + } else { + Config.config.showDeArrowPromotion = false; + } + } + }, 5000) }); const skipBuffer = 0.003; diff --git a/src/options.ts b/src/options.ts index c7a205b7..a4be9f76 100644 --- a/src/options.ts +++ b/src/options.ts @@ -17,6 +17,7 @@ import { localizeHtmlPage } from "@ajayyy/maze-utils/lib/setup"; import { StorageChangesObject } from "@ajayyy/maze-utils/lib/config"; import { getHash } from "@ajayyy/maze-utils/lib/hash"; import { isFirefoxOrSafari } from "@ajayyy/maze-utils"; +import { isDeArrowInstalled } from "./utils/crossExtension"; const utils = new Utils(); let embed = false; @@ -70,8 +71,14 @@ async function init() { // DeArrow promotion if (Config.config.showNewFeaturePopups && Config.config.showUpsells) { - const deArrowPromotion = document.getElementById("deArrowPromotion"); - deArrowPromotion.classList.remove("hidden"); + isDeArrowInstalled().then((installed) => { + if (!installed) { + const deArrowPromotion = document.getElementById("deArrowPromotion"); + deArrowPromotion.classList.remove("hidden"); + + deArrowPromotion.addEventListener("click", () => Config.config.showDeArrowPromotion = false); + } + }); } // Set all of the toggle options to the correct option diff --git a/src/render/Tooltip.tsx b/src/render/Tooltip.tsx index 687ebac3..22e4ab09 100644 --- a/src/render/Tooltip.tsx +++ b/src/render/Tooltip.tsx @@ -1,6 +1,8 @@ import * as React from "react"; import { createRoot, Root } from 'react-dom/client'; import { ButtonListener } from "../types"; +import { isFirefoxOrSafari } from "@ajayyy/maze-utils"; +import { isSafari } from "@ajayyy/maze-utils/lib/config"; export interface TooltipProps { text?: string; @@ -9,6 +11,7 @@ export interface TooltipProps { referenceNode: HTMLElement; prependElement?: HTMLElement; // Element to append before bottomOffset?: string; + topOffset?: string; leftOffset?: string; rightOffset?: string; timeout?: number; @@ -17,7 +20,9 @@ export interface TooltipProps { extraClass?: string; showLogo?: boolean; showGotIt?: boolean; + center?: boolean; positionRealtive?: boolean; + containerAbsolute?: boolean; buttons?: ButtonListener[]; } @@ -30,6 +35,7 @@ export class Tooltip { constructor(props: TooltipProps) { props.bottomOffset ??= "70px"; + props.topOffset ??= "inherit"; props.leftOffset ??= "inherit"; props.rightOffset ??= "inherit"; props.opacity ??= 0.7; @@ -38,11 +44,21 @@ export class Tooltip { props.showLogo ??= true; props.showGotIt ??= true; props.positionRealtive ??= true; + props.containerAbsolute ??= false; + props.center ??= false; this.text = props.text; this.container = document.createElement('div'); this.container.id = "sponsorTooltip" + props.text; if (props.positionRealtive) this.container.style.position = "relative"; + if (props.containerAbsolute) this.container.style.position = "absolute"; + if (props.center) { + if (isFirefoxOrSafari() && !isSafari()) { + this.container.style.width = "-moz-available"; + } else { + this.container.style.width = "-webkit-fill-available"; + } + } if (props.prependElement) { props.referenceNode.insertBefore(this.container, props.prependElement); @@ -58,8 +74,17 @@ export class Tooltip { this.root = createRoot(this.container); this.root.render( -
+
{props.showLogo ? { + if (Config.config.deArrowInstalled) { + return Promise.resolve(true); + } else { + return new Promise((resolve) => { + const extensionIds = getExtensionIdsToImportFrom(); + + let count = 0; + for (const id of extensionIds) { + chrome.runtime.sendMessage(id, { message: "isInstalled" }, (response) => { + if (chrome.runtime.lastError) { + count++; + + if (count === extensionIds.length) { + resolve(false); + } + return; + } + + resolve(response); + if (response) { + Config.config.deArrowInstalled = true; + } + }); + } + }); + } +} + +export function getExtensionIdsToImportFrom(): string[] { + if (isSafari()) { + return CompileConfig.extensionImportList.safari; + } else if (isFirefoxOrSafari()) { + return CompileConfig.extensionImportList.firefox; + } else { + return CompileConfig.extensionImportList.chromium; + } +} \ No newline at end of file