From e049f34a5e8f6bd314663e0ecf1328893c0189af Mon Sep 17 00:00:00 2001 From: Aden Linday Date: Sat, 28 Mar 2026 09:06:54 +1030 Subject: [PATCH] feat: WIP Engage progress --- src/SEQTA.ts | 7 +- src/background.ts | 5 +- src/plugins/monofile.ts | 130 ++++++++++++------ src/seqta/ui/AddBetterSEQTAElements.ts | 6 + .../utils/Adders/AddExtensionSettings.ts | 35 +++-- src/seqta/utils/isSeqtaEngage.ts | 4 + src/seqta/utils/setupSettingsButton.ts | 19 +-- 7 files changed, 140 insertions(+), 66 deletions(-) create mode 100644 src/seqta/utils/isSeqtaEngage.ts diff --git a/src/SEQTA.ts b/src/SEQTA.ts index a646d64c..92bae369 100644 --- a/src/SEQTA.ts +++ b/src/SEQTA.ts @@ -50,7 +50,12 @@ if (document.childNodes[1]) { } async function init() { - if (hasSEQTAText && document.title.includes("SEQTA Learn") && !IsSEQTAPage) { + if ( + hasSEQTAText && + (document.title.includes("SEQTA Learn") || + document.title.includes("SEQTA Engage")) && + !IsSEQTAPage + ) { IsSEQTAPage = true; console.info("[BetterSEQTA+] Verified SEQTA Page"); diff --git a/src/background.ts b/src/background.ts index 0eae7596..e5959f81 100644 --- a/src/background.ts +++ b/src/background.ts @@ -6,7 +6,10 @@ function reloadSeqtaPages() { const result = browser.tabs.query({}); function open(tabs: any) { for (let tab of tabs) { - if (tab.title.includes("SEQTA Learn")) { + if ( + tab.title?.includes("SEQTA Learn") || + tab.title?.includes("SEQTA Engage") + ) { browser.tabs.reload(tab.id); } } diff --git a/src/plugins/monofile.ts b/src/plugins/monofile.ts index 13288393..3fec7963 100644 --- a/src/plugins/monofile.ts +++ b/src/plugins/monofile.ts @@ -17,6 +17,7 @@ import { StorageChangeHandler } from "@/seqta/utils/listeners/StorageChanges"; import { eventManager } from "@/seqta/utils/listeners/EventManager"; // UI and theme management +import { isSeqtaEngageExperience } from "@/seqta/utils/isSeqtaEngage"; import RegisterClickListeners from "@/seqta/utils/listeners/ClickListeners"; import { AddBetterSEQTAElements } from "@/seqta/ui/AddBetterSEQTAElements"; import { updateAllColors } from "@/seqta/ui/colors/Manager"; @@ -82,7 +83,12 @@ export function hideSideBar() { } } +let betterSeqtaFinishLoadDone = false; + export async function finishLoad() { + if (betterSeqtaFinishLoadDone) return; + betterSeqtaFinishLoadDone = true; + try { document.querySelector(".legacy-root")?.classList.remove("hidden"); @@ -115,19 +121,19 @@ export function GetCSSElement(file: string) { } function removeThemeTagsFromNotices() { - // Grabs an array of the notice iFrames const userHTMLArray = document.getElementsByClassName("userHTML"); - // Iterates through the array, applying the iFrame css for (const item of userHTMLArray) { - // Grabs the HTML of the body tag - const item1 = item as HTMLIFrameElement; - const body = item1.contentWindow!.document.querySelectorAll("body")[0]; - if (body) { - // Replaces the theme tag with nothing + const iframe = item as HTMLIFrameElement; + try { + const doc = iframe.contentDocument; + if (!doc?.body) continue; + const body = doc.body; const bodyText = body.innerHTML; body.innerHTML = bodyText .replace(/\[\[[\w]+[:][\w]+[\]\]]+/g, "") .replace(/ +/, " "); + } catch { + // Cross-origin or otherwise inaccessible iframe (common during Engage load / filter frames) } } } @@ -296,6 +302,11 @@ async function handleNotices(node: Element): Promise { } async function handleSublink(sublink: string | undefined): Promise { + if (isSeqtaEngageExperience()) { + finishLoad(); + return; + } + switch (sublink) { case "news": await handleNewsPage(); @@ -382,15 +393,22 @@ async function handleDashboard(node: Element): Promise { document.head.append(style); await waitForElm(".dashlet", true, 10); - animate( - ".dashboard > *", - { opacity: [0, 1], y: [10, 0] }, - { - delay: stagger(0.1), - duration: 0.5, - ease: [0.22, 0.03, 0.26, 1], - }, - ); + try { + const children = document.querySelectorAll(".dashboard > *"); + if (children.length) { + animate( + children, + { opacity: [0, 1], y: [10, 0] }, + { + delay: stagger(0.1), + duration: 0.5, + ease: [0.22, 0.03, 0.26, 1], + }, + ); + } + } catch { + // Avoid uncaught errors if motion hits an unexpected DOM state during load. + } document.head.querySelector("style.dashboardHider")?.remove(); } @@ -400,15 +418,22 @@ async function handleDocuments(node: Element): Promise { if (!settingsState.animations) return; await waitForElm(".document", true, 10); - animate( - ".documents tbody tr.document", - { opacity: [0, 1], y: [10, 0] }, - { - delay: stagger(0.05), - duration: 0.5, - ease: [0.22, 0.03, 0.26, 1], - }, - ); + try { + const rows = document.querySelectorAll(".documents tbody tr.document"); + if (rows.length) { + animate( + rows, + { opacity: [0, 1], y: [10, 0] }, + { + delay: stagger(0.05), + duration: 0.5, + ease: [0.22, 0.03, 0.26, 1], + }, + ); + } + } catch { + // ignore + } } async function handleReports(node: Element): Promise { @@ -416,15 +441,22 @@ async function handleReports(node: Element): Promise { if (!settingsState.animations) return; await waitForElm(".report", true, 10); - animate( - ".reports .item", - { opacity: [0, 1], y: [10, 0] }, - { - delay: stagger(0.05, { startDelay: 0.2 }), - duration: 0.5, - ease: [0.22, 0.03, 0.26, 1], - }, - ); + try { + const items = document.querySelectorAll(".reports .item"); + if (items.length) { + animate( + items, + { opacity: [0, 1], y: [10, 0] }, + { + delay: stagger(0.05, { startDelay: 0.2 }), + duration: 0.5, + ease: [0.22, 0.03, 0.26, 1], + }, + ); + } + } catch { + // ignore + } } function CheckNoticeTextColour(notice: any) { @@ -449,6 +481,26 @@ function CheckNoticeTextColour(notice: any) { } export function tryLoad() { + if (isSeqtaEngageExperience()) { + updateIframesWithDarkMode(); + window.addEventListener( + "load", + () => removeThemeTagsFromNotices(), + { once: true }, + ); + window.addEventListener( + "load", + () => void finishLoad(), + { once: true }, + ); + waitForElm(".login").then(() => void finishLoad()); + waitForElm(".day-container").then(() => void finishLoad()); + waitForElm(".code", true, 50).then((elm: any) => { + if (!elm.innerText.includes("BetterSEQTA")) void LoadPageElements(); + }); + return; + } + waitForElm(".login").then(() => { finishLoad(); }); @@ -466,13 +518,10 @@ export function tryLoad() { }); updateIframesWithDarkMode(); - // Waits for page to call on load, run scripts - document.addEventListener( + window.addEventListener( "load", - function () { - removeThemeTagsFromNotices(); - }, - true, + () => removeThemeTagsFromNotices(), + { once: true }, ); } @@ -489,6 +538,7 @@ function ReplaceMenuSVG(element: HTMLElement, svg: string) { const processedSymbol = Symbol("processed"); export async function ObserveMenuItemPosition() { + if (isSeqtaEngageExperience()) return; await waitForElm("#menu > ul > li"); eventManager.register( diff --git a/src/seqta/ui/AddBetterSEQTAElements.ts b/src/seqta/ui/AddBetterSEQTAElements.ts index 37bf8904..a2d91d79 100644 --- a/src/seqta/ui/AddBetterSEQTAElements.ts +++ b/src/seqta/ui/AddBetterSEQTAElements.ts @@ -1,4 +1,5 @@ import { addExtensionSettings } from "@/seqta/utils/Adders/AddExtensionSettings"; +import { isSeqtaEngageExperience } from "@/seqta/utils/isSeqtaEngage"; import { loadHomePage } from "@/seqta/utils/Loaders/LoadHomePage"; import { SendNewsPage } from "@/seqta/utils/SendNewsPage"; import { setupSettingsButton } from "@/seqta/utils/setupSettingsButton"; @@ -42,6 +43,11 @@ export async function getUserInfo() { } export async function AddBetterSEQTAElements() { + if (isSeqtaEngageExperience()) { + addExtensionSettings(); + return; + } + if (settingsState.onoff) { if (settingsState.DarkMode) { document.documentElement.classList.add("dark"); diff --git a/src/seqta/utils/Adders/AddExtensionSettings.ts b/src/seqta/utils/Adders/AddExtensionSettings.ts index e8162e10..d12f20c5 100644 --- a/src/seqta/utils/Adders/AddExtensionSettings.ts +++ b/src/seqta/utils/Adders/AddExtensionSettings.ts @@ -9,21 +9,8 @@ import Settings from "@/interface/pages/settings.svelte"; let isSettingsRendered = false; -export function addExtensionSettings() { - const extensionPopup = document.createElement("div"); - extensionPopup.classList.add("outside-container", "hide"); - extensionPopup.id = "ExtensionPopup"; - - const extensionContainer = document.querySelector( - "#container", - ) as HTMLDivElement; - if (extensionContainer) extensionContainer.appendChild(extensionPopup); - - const container = document.getElementById("container"); - - new SettingsResizer(); - - container!.onclick = (event) => { +function extensionOutsideClickHandler(extensionPopup: HTMLElement) { + return (event: MouseEvent) => { if (!SettingsClicked) return; if (!(event.target as HTMLElement).closest("#AddedSettings")) { @@ -33,6 +20,24 @@ export function addExtensionSettings() { }; } +export function addExtensionSettings() { + if (document.getElementById("ExtensionPopup")) return; + + const extensionPopup = document.createElement("div"); + extensionPopup.classList.add("outside-container", "hide"); + extensionPopup.id = "ExtensionPopup"; + + const extensionContainer = + document.querySelector("#container") ?? document.getElementById("container"); + const mountParent = extensionContainer ?? document.body; + mountParent.appendChild(extensionPopup); + + new SettingsResizer(); + + const handler = extensionOutsideClickHandler(extensionPopup); + (extensionContainer ?? document.body).addEventListener("click", handler, false); +} + export function renderSettingsIfNeeded() { if (isSettingsRendered) return; diff --git a/src/seqta/utils/isSeqtaEngage.ts b/src/seqta/utils/isSeqtaEngage.ts new file mode 100644 index 00000000..727ec982 --- /dev/null +++ b/src/seqta/utils/isSeqtaEngage.ts @@ -0,0 +1,4 @@ +/** SEQTA Engage (React) uses a different shell from classic SEQTA Learn. */ +export function isSeqtaEngageExperience(): boolean { + return document.title.includes("SEQTA Engage"); +} diff --git a/src/seqta/utils/setupSettingsButton.ts b/src/seqta/utils/setupSettingsButton.ts index 07f06317..e4e88c67 100644 --- a/src/seqta/utils/setupSettingsButton.ts +++ b/src/seqta/utils/setupSettingsButton.ts @@ -9,10 +9,11 @@ import { renderSettingsIfNeeded } from "./Adders/AddExtensionSettings"; import { delay } from "./delay"; export function setupSettingsButton() { - var AddedSettings = document.getElementById("AddedSettings"); - var extensionPopup = document.getElementById("ExtensionPopup"); + const AddedSettings = document.getElementById("AddedSettings"); + const extensionPopup = document.getElementById("ExtensionPopup"); + if (!AddedSettings || !extensionPopup) return; - AddedSettings!.addEventListener("click", async () => { + AddedSettings.addEventListener("click", async () => { if (SettingsClicked) { closeExtensionPopup(extensionPopup as HTMLElement); } else { @@ -23,20 +24,20 @@ export function setupSettingsButton() { if (settingsState.animations) { animate(0, 1, { onUpdate: (progress) => { - extensionPopup!.style.opacity = progress.toString(); - extensionPopup!.style.transform = `scale(${progress})`; + extensionPopup.style.opacity = progress.toString(); + extensionPopup.style.transform = `scale(${progress})`; }, type: "spring", stiffness: 280, damping: 20, }); } else { - extensionPopup!.style.opacity = "1"; - extensionPopup!.style.transform = "scale(1)"; - extensionPopup!.style.transition = + extensionPopup.style.opacity = "1"; + extensionPopup.style.transform = "scale(1)"; + extensionPopup.style.transition = "opacity 0s linear, transform 0s linear"; } - extensionPopup!.classList.remove("hide"); + extensionPopup.classList.remove("hide"); changeSettingsClicked(true); } });