diff --git a/frontend/src/html/head.html b/frontend/src/html/head.html index 88ff20419272..1588dfd003b9 100644 --- a/frontend/src/html/head.html +++ b/frontend/src/html/head.html @@ -1,4 +1,8 @@ + + + + > = { subColor: "#ffaca3", textColor: "#feffdb", }, + spiderman: { + bgColor: "#0d1219", + mainColor: "#e23636", + subColor: "#0476f2", + textColor: "#f0f0f0", + }, }; export const ThemesList: Theme[] = Object.keys(themes) diff --git a/frontend/src/ts/controllers/ad-controller.ts b/frontend/src/ts/controllers/ad-controller.ts index a612a81f0fe5..ca66690dd2f2 100644 --- a/frontend/src/ts/controllers/ad-controller.ts +++ b/frontend/src/ts/controllers/ad-controller.ts @@ -7,7 +7,7 @@ import Config from "../config"; import * as TestState from "../test/test-state"; import * as EG from "./eg-ad-controller"; import * as PW from "./pw-ad-controller"; -import { onDocumentReady, qs } from "../utils/dom"; +import { onDOMReady, qs } from "../utils/dom"; const breakpoint = 900; let widerThanBreakpoint = true; @@ -317,7 +317,7 @@ BannerEvent.subscribe(() => { updateVerticalMargin(); }); -onDocumentReady(() => { +onDOMReady(() => { updateBreakpoint(true); updateBreakpoint2(); }); diff --git a/frontend/src/ts/pages/account-settings.ts b/frontend/src/ts/pages/account-settings.ts index 4e558d1cfacc..2ff8344c9357 100644 --- a/frontend/src/ts/pages/account-settings.ts +++ b/frontend/src/ts/pages/account-settings.ts @@ -12,7 +12,7 @@ import * as BlockedUserTable from "../elements/account-settings/blocked-user-tab import * as Notifications from "../elements/notifications"; import { z } from "zod"; import * as AuthEvent from "../observables/auth-event"; -import { qs, qsr, onDocumentReady } from "../utils/dom"; +import { qs, qsr, onWindowLoad } from "../utils/dom"; const pageElement = qsr(".page.pageAccountSettings"); @@ -52,82 +52,58 @@ function updateAuthenticationSections(): void { ); if (passwordProvider) { - pageElement - .qs(".section.passwordAuthSettings #emailPasswordAuth") - ?.removeClass("hidden"); - pageElement - .qs(".section.passwordAuthSettings #passPasswordAuth") - ?.removeClass("hidden"); + pageElement.qs(".section.passwordAuthSettings #emailPasswordAuth")?.show(); + pageElement.qs(".section.passwordAuthSettings #passPasswordAuth")?.show(); if (googleProvider || githubProvider) { pageElement .qs(".section.passwordAuthSettings #removePasswordAuth") - ?.removeClass("hidden"); + ?.show(); } } else { - pageElement - .qs(".section.passwordAuthSettings #addPasswordAuth") - ?.removeClass("hidden"); + pageElement.qs(".section.passwordAuthSettings #addPasswordAuth")?.show(); } if (googleProvider) { - pageElement - .qs(".section.googleAuthSettings #removeGoogleAuth") - ?.removeClass("hidden"); + pageElement.qs(".section.googleAuthSettings #removeGoogleAuth")?.show(); if (passwordProvider || githubProvider) { - pageElement - .qs(".section.googleAuthSettings #removeGoogleAuth") - ?.removeClass("disabled"); + pageElement.qs(".section.googleAuthSettings #removeGoogleAuth")?.enable(); } else { pageElement .qs(".section.googleAuthSettings #removeGoogleAuth") - ?.addClass("disabled"); + ?.disable(); } } else { - pageElement - .qs(".section.googleAuthSettings #addGoogleAuth") - ?.removeClass("hidden"); + pageElement.qs(".section.googleAuthSettings #addGoogleAuth")?.show(); } if (githubProvider) { - pageElement - .qs(".section.githubAuthSettings #removeGithubAuth") - ?.removeClass("hidden"); + pageElement.qs(".section.githubAuthSettings #removeGithubAuth")?.show(); if (passwordProvider || googleProvider) { - pageElement - .qs(".section.githubAuthSettings #removeGithubAuth") - ?.removeClass("disabled"); + pageElement.qs(".section.githubAuthSettings #removeGithubAuth")?.enable(); } else { pageElement .qs(".section.githubAuthSettings #removeGithubAuth") - ?.addClass("disabled"); + ?.disable(); } } else { - pageElement - .qs(".section.githubAuthSettings #addGithubAuth") - ?.removeClass("hidden"); + pageElement.qs(".section.githubAuthSettings #addGithubAuth")?.show(); } } function updateIntegrationSections(): void { //no code and no discord if (!isAuthenticated()) { - pageElement.qs(".section.discordIntegration")?.addClass("hidden"); + pageElement.qs(".section.discordIntegration")?.hide(); } else { if (!getSnapshot()) return; - pageElement.qs(".section.discordIntegration")?.removeClass("hidden"); + pageElement.qs(".section.discordIntegration")?.show(); if (getSnapshot()?.discordId === undefined) { //show button - pageElement - .qs(".section.discordIntegration .buttons") - ?.removeClass("hidden"); - pageElement.qs(".section.discordIntegration .info")?.addClass("hidden"); + pageElement.qs(".section.discordIntegration .buttons")?.show(); + pageElement.qs(".section.discordIntegration .info")?.hide(); } else { - pageElement - .qs(".section.discordIntegration .buttons") - ?.addClass("hidden"); - pageElement - .qs(".section.discordIntegration .info") - ?.removeClass("hidden"); + pageElement.qs(".section.discordIntegration .buttons")?.hide(); + pageElement.qs(".section.discordIntegration .info")?.show(); } } } @@ -141,40 +117,32 @@ function updateTabs(): void { // }, async () => { - pageElement.qs(".tab")?.removeClass("active"); + pageElement.qsa(".tab")?.removeClass("active"); pageElement.qs(`.tab[data-tab="${state.tab}"]`)?.addClass("active"); }, ); - pageElement.qs("button")?.removeClass("active"); + pageElement.qsa("button")?.removeClass("active"); pageElement.qs(`button[data-tab="${state.tab}"]`)?.addClass("active"); } function updateAccountSections(): void { - pageElement.qs(".section.optOutOfLeaderboards .optedOut")?.addClass("hidden"); - pageElement - .qs(".section.optOutOfLeaderboards .buttons") - ?.removeClass("hidden"); - pageElement.qs(".section.setStreakHourOffset .info")?.addClass("hidden"); - pageElement - .qs(".section.setStreakHourOffset .buttons") - ?.removeClass("hidden"); + pageElement.qs(".section.optOutOfLeaderboards .optedOut")?.hide(); + pageElement.qs(".section.optOutOfLeaderboards .buttons")?.show(); + pageElement.qs(".section.setStreakHourOffset .info")?.hide(); + pageElement.qs(".section.setStreakHourOffset .buttons")?.show(); const snapshot = getSnapshot(); if (snapshot?.lbOptOut === true) { - pageElement - .qs(".section.optOutOfLeaderboards .optedOut") - ?.removeClass("hidden"); - pageElement - .qs(".section.optOutOfLeaderboards .buttons") - ?.addClass("hidden"); + pageElement.qs(".section.optOutOfLeaderboards .optedOut")?.show(); + pageElement.qs(".section.optOutOfLeaderboards .buttons")?.hide(); } if (snapshot?.streakHourOffset !== undefined) { - pageElement.qs(".section.setStreakHourOffset .info")?.removeClass("hidden"); + pageElement.qs(".section.setStreakHourOffset .info")?.show(); const sign = snapshot?.streakHourOffset > 0 ? "+" : ""; pageElement .qs(".section.setStreakHourOffset .info span") ?.setText(sign + snapshot?.streakHourOffset); - pageElement.qs(".section.setStreakHourOffset .buttons")?.addClass("hidden"); + pageElement.qs(".section.setStreakHourOffset .buttons")?.hide(); } } @@ -242,6 +210,6 @@ export const page = new PageWithUrlParams({ }, }); -onDocumentReady(() => { +onWindowLoad(() => { Skeleton.save("pageAccountSettings"); }); diff --git a/frontend/src/ts/ready.ts b/frontend/src/ts/ready.ts index d0ca342bc099..20f523cdf82a 100644 --- a/frontend/src/ts/ready.ts +++ b/frontend/src/ts/ready.ts @@ -10,9 +10,9 @@ import { getActiveFunboxesWithFunction } from "./test/funbox/list"; import { configLoadPromise } from "./config"; import { authPromise } from "./firebase"; import { animate } from "animejs"; -import { onDocumentReady, qs } from "./utils/dom"; +import { onDOMReady, qs } from "./utils/dom"; -onDocumentReady(async () => { +onDOMReady(async () => { await configLoadPromise; await authPromise; diff --git a/frontend/src/ts/test/test-logic.ts b/frontend/src/ts/test/test-logic.ts index 0532c52123d1..8af562e23f94 100644 --- a/frontend/src/ts/test/test-logic.ts +++ b/frontend/src/ts/test/test-logic.ts @@ -273,14 +273,21 @@ export function restart(options = {} as RestartOptions): void { ConnectionState.showOfflineBanner(); } + // TestUI.beforeTestRestart(); + + let source: "testPage" | "resultPage"; let el: HTMLElement; if (TestState.resultVisible) { //results are being displayed el = document.querySelector("#result") as HTMLElement; + source = "resultPage"; } else { //words are being displayed el = document.querySelector("#typingTest") as HTMLElement; + source = "testPage"; } + + TestState.setResultVisible(false); TestState.setTestRestarting(true); animate(el, { @@ -316,8 +323,7 @@ export function restart(options = {} as RestartOptions): void { fb.functions.restart(); } - TestUI.onTestRestart(); - TestState.setResultVisible(false); + TestUI.onTestRestart(source); const typingTestEl = document.querySelector("#typingTest") as HTMLElement; animate(typingTestEl, { diff --git a/frontend/src/ts/test/test-ui.ts b/frontend/src/ts/test/test-ui.ts index 6ebe02254b00..b05e4213a5ce 100644 --- a/frontend/src/ts/test/test-ui.ts +++ b/frontend/src/ts/test/test-ui.ts @@ -1844,7 +1844,7 @@ export function onTestStart(): void { TimerProgress.update(); } -export function onTestRestart(): void { +export function onTestRestart(source: "testPage" | "resultPage"): void { $("#result").addClass("hidden"); $("#typingTest").css("opacity", 0).removeClass("hidden"); getInputElement().style.left = "0"; @@ -1875,7 +1875,7 @@ export function onTestRestart(): void { void ModesNotice.update(); } - if (TestState.resultVisible) { + if (source === "resultPage") { if (Config.randomTheme !== "off") { void ThemeController.randomizeTheme(); } diff --git a/frontend/src/ts/utils/dom.ts b/frontend/src/ts/utils/dom.ts index 467a7eef2f26..7955d0378be0 100644 --- a/frontend/src/ts/utils/dom.ts +++ b/frontend/src/ts/utils/dom.ts @@ -52,10 +52,11 @@ export function qsr( } /** - * Execute a callback function when the document is fully loaded. + * Execute a callback function when the DOM is fully loaded. If you need to wait + * for all resources (images, stylesheets, scripts, etc.) to load, use `onWindowLoad` instead. * If the document is already loaded, the callback is executed immediately. */ -export function onDocumentReady(callback: () => void): void { +export function onDOMReady(callback: () => void): void { if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", callback); } else { @@ -63,6 +64,20 @@ export function onDocumentReady(callback: () => void): void { } } +/** + * Execute a callback function when the window 'load' event fires, which occurs + * after the entire page (including all dependent resources such as images, + * stylesheets, and scripts) has fully loaded. + * If the window is already loaded, the callback is executed immediately. + */ +export function onWindowLoad(callback: () => void): void { + if (document.readyState === "complete") { + callback(); + } else { + window.addEventListener("load", callback); + } +} + /** * Creates an ElementWithUtils wrapping a newly created element. * @param tagName The tag name of the element to create. diff --git a/frontend/static/themes/spiderman.css b/frontend/static/themes/spiderman.css new file mode 100644 index 000000000000..33f5c5dffd66 --- /dev/null +++ b/frontend/static/themes/spiderman.css @@ -0,0 +1,12 @@ +:root { + --bg-color: #0d1219; + --main-color: #e23636; + --caret-color: #e23636; + --sub-color: #0476f2; + --sub-alt-color: #0b1c2e; + --text-color: #f0f0f0; + --error-color: #0476f2; + --error-extra-color: #0353a8; + --colorful-error-color: #0476f2; + --colorful-error-extra-color: #0353a8; +} diff --git a/packages/schemas/src/themes.ts b/packages/schemas/src/themes.ts index 0633201c7a25..ace96ba0a33c 100644 --- a/packages/schemas/src/themes.ts +++ b/packages/schemas/src/themes.ts @@ -189,6 +189,7 @@ export const ThemeNameSchema = z.enum( "wavez", "witch_girl", "pale_nimbus", + "spiderman", ], { errorMap: customEnumErrorHandler("Must be a known theme"),