From 2b380bb931fb749c9eacf18f2eff5849990ebf89 Mon Sep 17 00:00:00 2001 From: Christian Fehmer Date: Fri, 12 Dec 2025 15:16:11 +0100 Subject: [PATCH 1/2] refactor: use ElementWithUtils in page class (@fehmer) (#7223) --- frontend/__tests__/setup-tests.ts | 1 + frontend/src/ts/controllers/page-controller.ts | 18 +++++++++--------- frontend/src/ts/pages/404.ts | 3 ++- frontend/src/ts/pages/about.ts | 3 ++- frontend/src/ts/pages/account-settings.ts | 3 ++- frontend/src/ts/pages/account.ts | 3 ++- frontend/src/ts/pages/friends.ts | 5 ++--- frontend/src/ts/pages/leaderboards.ts | 3 ++- frontend/src/ts/pages/loading.ts | 3 ++- frontend/src/ts/pages/login.ts | 2 +- frontend/src/ts/pages/page.ts | 5 +++-- frontend/src/ts/pages/profile-search.ts | 2 +- frontend/src/ts/pages/profile.ts | 3 ++- frontend/src/ts/pages/settings.ts | 2 +- frontend/src/ts/pages/test.ts | 3 ++- frontend/src/ts/utils/dom.ts | 16 ++++++++++++++++ 16 files changed, 50 insertions(+), 25 deletions(-) diff --git a/frontend/__tests__/setup-tests.ts b/frontend/__tests__/setup-tests.ts index 6828d2772..0ac3acf1e 100644 --- a/frontend/__tests__/setup-tests.ts +++ b/frontend/__tests__/setup-tests.ts @@ -64,6 +64,7 @@ vi.mock("../src/ts/utils/dom", () => { getOffsetTop: vi.fn().mockReturnValue(0), getOffsetLeft: vi.fn().mockReturnValue(0), animate: vi.fn().mockResolvedValue(null), + promiseAnimate: vi.fn().mockResolvedValue(null), native: document.createElement("div"), }; }; diff --git a/frontend/src/ts/controllers/page-controller.ts b/frontend/src/ts/controllers/page-controller.ts index fbf98be33..e60461598 100644 --- a/frontend/src/ts/controllers/page-controller.ts +++ b/frontend/src/ts/controllers/page-controller.ts @@ -59,14 +59,14 @@ async function showSyncLoading({ loadingOptions: LoadingOptions[]; totalDuration: number; }): Promise { - PageLoading.page.element.removeClass("hidden").css("opacity", 0); + PageLoading.page.element.show().setStyle({ opacity: "0" }); await PageLoading.page.beforeShow({}); const fillDivider = loadingOptions.length; const fillOffset = 100 / fillDivider; //void here to run the loading promise as soon as possible - void Misc.promiseAnimate(PageLoading.page.element[0] as HTMLElement, { + void PageLoading.page.element.promiseAnimate({ opacity: "1", duration: totalDuration / 2, }); @@ -97,13 +97,13 @@ async function showSyncLoading({ } } - await Misc.promiseAnimate(PageLoading.page.element[0] as HTMLElement, { + await PageLoading.page.element.promiseAnimate({ opacity: "0", duration: totalDuration / 2, }); await PageLoading.page.afterHide(); - PageLoading.page.element.addClass("hidden"); + PageLoading.page.element.hide(); } // Global abort controller for keyframe promises @@ -206,12 +206,12 @@ export async function change( //previous page await previousPage?.beforeHide?.(); - previousPage.element.removeClass("hidden").css("opacity", 1); - await Misc.promiseAnimate(previousPage.element[0] as HTMLElement, { + previousPage.element.show().setStyle({ opacity: "1" }); + await previousPage.element.promiseAnimate({ opacity: "0", duration: totalDuration / 2, }); - previousPage.element.addClass("hidden"); + previousPage.element.hide(); await previousPage?.afterHide(); // we need to evaluate and store next page loading mode in case options.loadingOptions.loadingMode is sync @@ -281,8 +281,8 @@ export async function change( }); } - nextPage.element.removeClass("hidden").css("opacity", 0); - await Misc.promiseAnimate(nextPage.element[0] as HTMLElement, { + nextPage.element.show().setStyle({ opacity: "0" }); + await nextPage.element.promiseAnimate({ opacity: "1", duration: totalDuration / 2, }); diff --git a/frontend/src/ts/pages/404.ts b/frontend/src/ts/pages/404.ts index bb67ce5b7..c081af356 100644 --- a/frontend/src/ts/pages/404.ts +++ b/frontend/src/ts/pages/404.ts @@ -1,9 +1,10 @@ import Page from "./page"; import * as Skeleton from "../utils/skeleton"; +import { qsr } from "../utils/dom"; export const page = new Page({ id: "404", - element: $(".page.page404"), + element: qsr(".page.page404"), path: "/404", afterHide: async (): Promise => { Skeleton.remove("page404"); diff --git a/frontend/src/ts/pages/about.ts b/frontend/src/ts/pages/about.ts index 6761be0b7..a82da0c53 100644 --- a/frontend/src/ts/pages/about.ts +++ b/frontend/src/ts/pages/about.ts @@ -10,6 +10,7 @@ import * as Skeleton from "../utils/skeleton"; import { TypingStats, SpeedHistogram } from "@monkeytype/schemas/public"; import { getNumberWithMagnitude, numberWithSpaces } from "../utils/numbers"; import { tryCatch } from "@monkeytype/util/trycatch"; +import { qsr } from "../utils/dom"; function reset(): void { $(".pageAbout .contributors").empty(); @@ -199,7 +200,7 @@ function getHistogramDataBucketed(data: Record): { export const page = new Page({ id: "about", - element: $(".page.pageAbout"), + element: qsr(".page.pageAbout"), path: "/about", afterHide: async (): Promise => { reset(); diff --git a/frontend/src/ts/pages/account-settings.ts b/frontend/src/ts/pages/account-settings.ts index 534ed19bd..4d9ab4502 100644 --- a/frontend/src/ts/pages/account-settings.ts +++ b/frontend/src/ts/pages/account-settings.ts @@ -12,6 +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 { qsr } from "../utils/dom"; const pageElement = $(".page.pageAccountSettings"); @@ -229,7 +230,7 @@ AuthEvent.subscribe((event) => { export const page = new PageWithUrlParams({ id: "accountSettings", display: "Account Settings", - element: pageElement, + element: qsr(".page.pageAccountSettings"), path: "/account-settings", urlParamsSchema: UrlParameterSchema, afterHide: async (): Promise => { diff --git a/frontend/src/ts/pages/account.ts b/frontend/src/ts/pages/account.ts index 5af14a451..537725739 100644 --- a/frontend/src/ts/pages/account.ts +++ b/frontend/src/ts/pages/account.ts @@ -35,6 +35,7 @@ import { SnapshotResult } from "../constants/default-snapshot"; import Ape from "../ape"; import { AccountChart } from "@monkeytype/schemas/configs"; import { SortedTableWithLimit } from "../utils/sorted-table"; +import { qsr } from "../utils/dom"; let filterDebug = false; //toggle filterdebug @@ -1195,7 +1196,7 @@ ConfigEvent.subscribe(({ key }) => { export const page = new Page({ id: "account", - element: $(".page.pageAccount"), + element: qsr(".page.pageAccount"), path: "/account", loadingOptions: { loadingMode: () => { diff --git a/frontend/src/ts/pages/friends.ts b/frontend/src/ts/pages/friends.ts index 31d72ad9f..6b5c9a554 100644 --- a/frontend/src/ts/pages/friends.ts +++ b/frontend/src/ts/pages/friends.ts @@ -30,8 +30,7 @@ import { Friend, UserNameSchema } from "@monkeytype/schemas/users"; import * as Loader from "../elements/loader"; import { LocalStorageWithSchema } from "../utils/local-storage-with-schema"; import { remoteValidation } from "../utils/remote-validation"; - -const pageElement = $(".page.pageFriends"); +import { qsr } from "../utils/dom"; let friendsTable: SortedTable | undefined = undefined; @@ -499,7 +498,7 @@ function update(): void { export const page = new Page({ id: "friends", display: "Friends", - element: pageElement, + element: qsr(".page.pageFriends"), path: "/friends", loadingOptions: { loadingMode: () => { diff --git a/frontend/src/ts/pages/leaderboards.ts b/frontend/src/ts/pages/leaderboards.ts index 33dfcfeba..7336c8197 100644 --- a/frontend/src/ts/pages/leaderboards.ts +++ b/frontend/src/ts/pages/leaderboards.ts @@ -44,6 +44,7 @@ import { isSafeNumber } from "@monkeytype/util/numbers"; import { Mode, Mode2, ModeSchema } from "@monkeytype/schemas/shared"; import * as ServerConfiguration from "../ape/server-configuration"; import { getAvatarElement } from "../utils/discord-avatar"; +import { qsr } from "../utils/dom"; const LeaderboardTypeSchema = z.enum(["allTime", "weekly", "daily"]); type LeaderboardType = z.infer; @@ -1489,7 +1490,7 @@ $(".page.pageLeaderboards .buttonGroup.friendsOnlyButtons").on( export const page = new PageWithUrlParams({ id: "leaderboards", - element: $(".page.pageLeaderboards"), + element: qsr(".page.pageLeaderboards"), path: "/leaderboards", urlParamsSchema: UrlParameterSchema, diff --git a/frontend/src/ts/pages/loading.ts b/frontend/src/ts/pages/loading.ts index 98580073a..4e7437fc5 100644 --- a/frontend/src/ts/pages/loading.ts +++ b/frontend/src/ts/pages/loading.ts @@ -1,6 +1,7 @@ import Page from "./page"; import * as Skeleton from "../utils/skeleton"; import { promiseAnimate } from "../utils/misc"; +import { qsr } from "../utils/dom"; const pageEl = $(".page.pageLoading"); const barEl = pageEl.find(".bar"); @@ -45,7 +46,7 @@ export async function showBar(): Promise { export const page = new Page({ id: "loading", - element: pageEl, + element: qsr(".page.pageLoading"), path: "/", afterHide: async (): Promise => { Skeleton.remove("pageLoading"); diff --git a/frontend/src/ts/pages/login.ts b/frontend/src/ts/pages/login.ts index 73c63da78..b7cd52294 100644 --- a/frontend/src/ts/pages/login.ts +++ b/frontend/src/ts/pages/login.ts @@ -208,7 +208,7 @@ new ValidatedHtmlInputElement(passwordVerifyInputEl, { export const page = new Page({ id: "login", - element: $(".page.pageLogin"), + element: qsr(".page.pageLogin"), path: "/login", afterHide: async (): Promise => { hidePreloader(); diff --git a/frontend/src/ts/pages/page.ts b/frontend/src/ts/pages/page.ts index e177c663c..70375c86f 100644 --- a/frontend/src/ts/pages/page.ts +++ b/frontend/src/ts/pages/page.ts @@ -3,6 +3,7 @@ import { safeParse as parseUrlSearchParams, serialize as serializeUrlSearchParams, } from "zod-urlsearchparams"; +import { ElementWithUtils } from "../utils/dom"; export type PageName = | "loading" @@ -69,7 +70,7 @@ export type LoadingOptions = { type PageProperties = { id: PageName; display?: string; - element: JQuery; + element: ElementWithUtils; path: string; loadingOptions?: LoadingOptions; beforeHide?: () => Promise; @@ -84,7 +85,7 @@ async function empty(): Promise { export default class Page { public id: PageName; public display: string | undefined; - public element: JQuery; + public element: ElementWithUtils; public pathname: string; public loadingOptions: LoadingOptions | undefined; diff --git a/frontend/src/ts/pages/profile-search.ts b/frontend/src/ts/pages/profile-search.ts index 684bf7a8b..1bf6dbd90 100644 --- a/frontend/src/ts/pages/profile-search.ts +++ b/frontend/src/ts/pages/profile-search.ts @@ -20,7 +20,7 @@ function disableButton(): void { export const page = new Page({ id: "profileSearch", - element: $(".page.pageProfileSearch"), + element: qsr(".page.pageProfileSearch"), path: "/profile", afterHide: async (): Promise => { Skeleton.remove("pageProfileSearch"); diff --git a/frontend/src/ts/pages/profile.ts b/frontend/src/ts/pages/profile.ts index 2ee81a646..098a5a5af 100644 --- a/frontend/src/ts/pages/profile.ts +++ b/frontend/src/ts/pages/profile.ts @@ -12,6 +12,7 @@ import * as TestActivity from "../elements/test-activity"; import { TestActivityCalendar } from "../elements/test-activity-calendar"; import { getFirstDayOfTheWeek } from "../utils/date-and-time"; import { addFriend } from "./friends"; +import { qsr } from "../utils/dom"; const firstDayOfTheWeek = getFirstDayOfTheWeek(); @@ -260,7 +261,7 @@ $(".page.pageProfile").on("click", ".profile .addFriendButton", async () => { export const page = new Page({ id: "profile", - element: $(".page.pageProfile"), + element: qsr(".page.pageProfile"), path: "/profile", afterHide: async (): Promise => { Skeleton.remove("pageProfile"); diff --git a/frontend/src/ts/pages/settings.ts b/frontend/src/ts/pages/settings.ts index 239323893..c6707aaf0 100644 --- a/frontend/src/ts/pages/settings.ts +++ b/frontend/src/ts/pages/settings.ts @@ -1011,7 +1011,7 @@ AuthEvent.subscribe((event) => { export const page = new PageWithUrlParams({ id: "settings", - element: $(".page.pageSettings"), + element: qsr(".page.pageSettings"), path: "/settings", urlParamsSchema: StateSchema, afterHide: async (): Promise => { diff --git a/frontend/src/ts/pages/test.ts b/frontend/src/ts/pages/test.ts index 37b37a3ac..0791fdd53 100644 --- a/frontend/src/ts/pages/test.ts +++ b/frontend/src/ts/pages/test.ts @@ -9,10 +9,11 @@ import * as Keymap from "../elements/keymap"; import * as TestConfig from "../test/test-config"; import * as ScrollToTop from "../elements/scroll-to-top"; import { blurInputElement } from "../input/input-element"; +import { qsr } from "../utils/dom"; export const page = new Page({ id: "test", - element: $(".page.pageTest"), + element: qsr(".page.pageTest"), path: "/", beforeHide: async (): Promise => { blurInputElement(); diff --git a/frontend/src/ts/utils/dom.ts b/frontend/src/ts/utils/dom.ts index f47fa6e6b..467a7eef2 100644 --- a/frontend/src/ts/utils/dom.ts +++ b/frontend/src/ts/utils/dom.ts @@ -514,6 +514,22 @@ export class ElementWithUtils { animate(animationParams: AnimationParams): JSAnimation { return animejsAnimate(this.native, animationParams); } + + /** + * Animate the element using Anime.js + * @param animationParams The Anime.js animation parameters + */ + async promiseAnimate(animationParams: AnimationParams): Promise { + return new Promise((resolve) => { + animejsAnimate(this.native, { + ...animationParams, + onComplete: (self, e) => { + animationParams.onComplete?.(self, e); + resolve(); + }, + }); + }); + } } /** From e0b5c465cd26c0d33b17ef9cc0f966ffba1d2baf Mon Sep 17 00:00:00 2001 From: Jack Date: Fri, 12 Dec 2025 16:55:33 +0100 Subject: [PATCH 2/2] refactor: add options object to setConfig (@miodec) (#7228) --- frontend/__tests__/root/config.spec.ts | 14 +- frontend/__tests__/utils/url-handler.spec.ts | 54 ++++++-- frontend/src/ts/config.ts | 24 ++-- .../ts/controllers/challenge-controller.ts | 120 +++++++++++++----- .../src/ts/controllers/theme-controller.ts | 4 +- .../ts/elements/custom-background-filter.ts | 2 +- frontend/src/ts/elements/input-validation.ts | 2 +- .../ts/elements/settings/settings-group.ts | 4 +- frontend/src/ts/modals/mobile-test-config.ts | 2 +- frontend/src/ts/modals/quote-search.ts | 2 +- .../src/ts/test/funbox/funbox-functions.ts | 36 ++++-- frontend/src/ts/test/funbox/funbox-memory.ts | 4 +- frontend/src/ts/test/funbox/funbox.ts | 18 ++- frontend/src/ts/test/practise-words.ts | 4 +- frontend/src/ts/test/test-logic.ts | 18 ++- frontend/src/ts/test/test-timer.ts | 8 +- frontend/src/ts/utils/url-handler.ts | 34 +++-- 17 files changed, 253 insertions(+), 97 deletions(-) diff --git a/frontend/__tests__/root/config.spec.ts b/frontend/__tests__/root/config.spec.ts index 1f20478d3..fc4293f44 100644 --- a/frontend/__tests__/root/config.spec.ts +++ b/frontend/__tests__/root/config.spec.ts @@ -212,7 +212,9 @@ describe("Config", () => { replaceConfig({ numbers: false }); //WHEN - Config.setConfig("numbers", true, true); + Config.setConfig("numbers", true, { + nosave: true, + }); //THEN //wait for debounce @@ -227,7 +229,9 @@ describe("Config", () => { replaceConfig({ numbers: false }); //WHEN - Config.setConfig("numbers", true, true); + Config.setConfig("numbers", true, { + nosave: true, + }); //THEN @@ -241,21 +245,21 @@ describe("Config", () => { it("triggers resize if property is set", () => { ///WHEN - Config.setConfig("maxLineWidth", 50, false); + Config.setConfig("maxLineWidth", 50); expect(miscTriggerResizeMock).toHaveBeenCalled(); }); it("does not triggers resize if property is not set", () => { ///WHEN - Config.setConfig("startGraphsAtZero", true, false); + Config.setConfig("startGraphsAtZero", true); expect(miscTriggerResizeMock).not.toHaveBeenCalled(); }); it("does not triggers resize if property on nosave", () => { ///WHEN - Config.setConfig("maxLineWidth", 50, true); + Config.setConfig("maxLineWidth", 50, { nosave: true }); expect(miscTriggerResizeMock).not.toHaveBeenCalled(); }); diff --git a/frontend/__tests__/utils/url-handler.spec.ts b/frontend/__tests__/utils/url-handler.spec.ts index 81ef43180..a89a7ccdd 100644 --- a/frontend/__tests__/utils/url-handler.spec.ts +++ b/frontend/__tests__/utils/url-handler.spec.ts @@ -56,8 +56,12 @@ describe("url-handler", () => { loadTestSettingsFromUrl(""); //THEN - expect(setConfigMock).toHaveBeenCalledWith("mode", "time", true); - expect(setConfigMock).toHaveBeenCalledWith("time", 60, true); + expect(setConfigMock).toHaveBeenCalledWith("mode", "time", { + nosave: true, + }); + expect(setConfigMock).toHaveBeenCalledWith("time", 60, { + nosave: true, + }); expect(restartTestMock).toHaveBeenCalled(); }); it("sets time", () => { @@ -70,8 +74,12 @@ describe("url-handler", () => { loadTestSettingsFromUrl(""); //THEN - expect(setConfigMock).toHaveBeenCalledWith("mode", "time", true); - expect(setConfigMock).toHaveBeenCalledWith("time", 30, true); + expect(setConfigMock).toHaveBeenCalledWith("mode", "time", { + nosave: true, + }); + expect(setConfigMock).toHaveBeenCalledWith("time", 30, { + nosave: true, + }); expect(restartTestMock).toHaveBeenCalled(); }); it("sets word count", () => { @@ -84,8 +92,12 @@ describe("url-handler", () => { loadTestSettingsFromUrl(""); //THEN - expect(setConfigMock).toHaveBeenCalledWith("mode", "words", true); - expect(setConfigMock).toHaveBeenCalledWith("words", 50, true); + expect(setConfigMock).toHaveBeenCalledWith("mode", "words", { + nosave: true, + }); + expect(setConfigMock).toHaveBeenCalledWith("words", 50, { + nosave: true, + }); expect(restartTestMock).toHaveBeenCalled(); }); it("sets quote length", () => { @@ -98,8 +110,10 @@ describe("url-handler", () => { loadTestSettingsFromUrl(""); //THEN - expect(setConfigMock).toHaveBeenCalledWith("mode", "quote", true); - expect(setConfigMock).toHaveBeenCalledWith("quoteLength", [-2], false); + expect(setConfigMock).toHaveBeenCalledWith("mode", "quote", { + nosave: true, + }); + expect(setConfigMock).toHaveBeenCalledWith("quoteLength", [-2]); expect(setSelectedQuoteIdMock).toHaveBeenCalledWith(512); expect(restartTestMock).toHaveBeenCalled(); }); @@ -111,7 +125,9 @@ describe("url-handler", () => { loadTestSettingsFromUrl(""); //THEN - expect(setConfigMock).toHaveBeenCalledWith("punctuation", true, true); + expect(setConfigMock).toHaveBeenCalledWith("punctuation", true, { + nosave: true, + }); expect(restartTestMock).toHaveBeenCalled(); }); it("sets numbers", () => { @@ -122,7 +138,9 @@ describe("url-handler", () => { loadTestSettingsFromUrl(""); //THEN - expect(setConfigMock).toHaveBeenCalledWith("numbers", false, true); + expect(setConfigMock).toHaveBeenCalledWith("numbers", false, { + nosave: true, + }); expect(restartTestMock).toHaveBeenCalled(); }); it("sets language", () => { @@ -133,7 +151,9 @@ describe("url-handler", () => { loadTestSettingsFromUrl(""); //THEN - expect(setConfigMock).toHaveBeenCalledWith("language", "english", true); + expect(setConfigMock).toHaveBeenCalledWith("language", "english", { + nosave: true, + }); expect(restartTestMock).toHaveBeenCalled(); }); it("sets difficulty", () => { @@ -144,7 +164,9 @@ describe("url-handler", () => { loadTestSettingsFromUrl(""); //THEN - expect(setConfigMock).toHaveBeenCalledWith("difficulty", "master", true); + expect(setConfigMock).toHaveBeenCalledWith("difficulty", "master", { + nosave: true, + }); expect(restartTestMock).toHaveBeenCalled(); }); it("sets funbox", () => { @@ -160,7 +182,9 @@ describe("url-handler", () => { expect(setConfigMock).toHaveBeenCalledWith( "funbox", ["crt", "choo_choo"], - true, + { + nosave: true, + }, ); expect(restartTestMock).toHaveBeenCalled(); }); @@ -177,7 +201,9 @@ describe("url-handler", () => { expect(setConfigMock).toHaveBeenCalledWith( "funbox", ["crt", "choo_choo"], - true, + { + nosave: true, + }, ); expect(restartTestMock).toHaveBeenCalled(); }); diff --git a/frontend/src/ts/config.ts b/frontend/src/ts/config.ts index a14d8a23b..271105df5 100644 --- a/frontend/src/ts/config.ts +++ b/frontend/src/ts/config.ts @@ -91,7 +91,9 @@ function isConfigChangeBlocked(): boolean { export function setConfig( key: T, value: Config[T], - nosave: boolean = false, + options?: { + nosave?: boolean; + }, ): boolean { const metadata = configMetadata[key] as ConfigMetadataObject[T]; if (metadata === undefined) { @@ -168,7 +170,7 @@ export function setConfig( continue; // no need to set if the value is already the same } - const set = setConfig(targetKey, targetValue, nosave); + const set = setConfig(targetKey, targetValue, options); if (!set) { throw new Error( `Failed to set config key "${targetKey}" with value "${targetValue}" for ${metadata.displayString} config override.`, @@ -178,22 +180,24 @@ export function setConfig( } config[key] = value; - if (!nosave) saveToLocalStorage(key, nosave); + if (!options?.nosave) saveToLocalStorage(key, options?.nosave); // @ts-expect-error i can't figure this out ConfigEvent.dispatch({ key: key, newValue: value, - nosave, + nosave: options?.nosave ?? false, previousValue: previousValue as Config[T], }); - if (metadata.triggerResize && !nosave) { + if (metadata.triggerResize && !options?.nosave) { triggerResize(); } - metadata.afterSet?.({ nosave: nosave || false, currentConfig: config }); - + metadata.afterSet?.({ + nosave: options?.nosave ?? false, + currentConfig: config, + }); return true; } @@ -232,7 +236,9 @@ export function toggleFunbox(funbox: FunboxName, nosave?: boolean): boolean { } export function setQuoteLengthAll(nosave?: boolean): boolean { - return setConfig("quoteLength", [0, 1, 2, 3], nosave); + return setConfig("quoteLength", [0, 1, 2, 3], { + nosave, + }); } const lastConfigsToApply: Set = new Set([ @@ -275,7 +281,7 @@ export async function applyConfig( for (const configKey of [...firstKeys, ...lastConfigsToApply]) { const configValue = fullConfig[configKey]; - const set = setConfig(configKey, configValue, true); + const set = setConfig(configKey, configValue, { nosave: true }); if (!set) { configKeysToReset.push(configKey); diff --git a/frontend/src/ts/controllers/challenge-controller.ts b/frontend/src/ts/controllers/challenge-controller.ts index 0e001099c..f56a0f376 100644 --- a/frontend/src/ts/controllers/challenge-controller.ts +++ b/frontend/src/ts/controllers/challenge-controller.ts @@ -241,26 +241,48 @@ export async function setup(challengeName: string): Promise { return false; } if (challenge.type === "customTime") { - setConfig("time", challenge.parameters[0] as number, true); - setConfig("mode", "time", true); - setConfig("difficulty", "normal", true); + setConfig("time", challenge.parameters[0] as number, { + nosave: true, + }); + setConfig("mode", "time", { + nosave: true, + }); + setConfig("difficulty", "normal", { + nosave: true, + }); if (challenge.name === "englishMaster") { - setConfig("language", "english_10k", true); - setConfig("numbers", true, true); - setConfig("punctuation", true, true); + setConfig("language", "english_10k", { + nosave: true, + }); + setConfig("numbers", true, { + nosave: true, + }); + setConfig("punctuation", true, { + nosave: true, + }); } } else if (challenge.type === "customWords") { - setConfig("words", challenge.parameters[0] as number, true); - setConfig("mode", "words", true); - setConfig("difficulty", "normal", true); + setConfig("words", challenge.parameters[0] as number, { + nosave: true, + }); + setConfig("mode", "words", { + nosave: true, + }); + setConfig("difficulty", "normal", { + nosave: true, + }); } else if (challenge.type === "customText") { CustomText.setText((challenge.parameters[0] as string).split(" ")); CustomText.setMode(challenge.parameters[1] as CustomTextMode); CustomText.setLimitValue(challenge.parameters[2] as number); CustomText.setLimitMode(challenge.parameters[3] as CustomTextLimitMode); CustomText.setPipeDelimiter(challenge.parameters[4] as boolean); - setConfig("mode", "custom", true); - setConfig("difficulty", "normal", true); + setConfig("mode", "custom", { + nosave: true, + }); + setConfig("difficulty", "normal", { + nosave: true, + }); } else if (challenge.type === "script") { Loader.show(); const response = await fetch( @@ -278,8 +300,12 @@ export async function setup(challengeName: string): Promise { CustomText.setMode("repeat"); CustomText.setLimitMode("word"); CustomText.setPipeDelimiter(false); - setConfig("mode", "custom", true); - setConfig("difficulty", "normal", true); + setConfig("mode", "custom", { + nosave: true, + }); + setConfig("difficulty", "normal", { + nosave: true, + }); if (challenge.parameters[1] !== null) { setConfig("theme", challenge.parameters[1] as ThemeName); } @@ -287,32 +313,66 @@ export async function setup(challengeName: string): Promise { void Funbox.activate(challenge.parameters[2] as FunboxName[]); } } else if (challenge.type === "accuracy") { - setConfig("time", 0, true); - setConfig("mode", "time", true); - setConfig("difficulty", "master", true); + setConfig("time", 0, { + nosave: true, + }); + setConfig("mode", "time", { + nosave: true, + }); + setConfig("difficulty", "master", { + nosave: true, + }); } else if (challenge.type === "funbox") { - setConfig("funbox", challenge.parameters[0] as FunboxName[], true); - setConfig("difficulty", "normal", true); + setConfig("funbox", challenge.parameters[0] as FunboxName[], { + nosave: true, + }); + setConfig("difficulty", "normal", { + nosave: true, + }); if (challenge.parameters[1] === "words") { - setConfig("words", challenge.parameters[2] as number, true); + setConfig("words", challenge.parameters[2] as number, { + nosave: true, + }); } else if (challenge.parameters[1] === "time") { - setConfig("time", challenge.parameters[2] as number, true); + setConfig("time", challenge.parameters[2] as number, { + nosave: true, + }); } - setConfig("mode", challenge.parameters[1] as Mode, true); + setConfig("mode", challenge.parameters[1] as Mode, { + nosave: true, + }); if (challenge.parameters[3] !== undefined) { - setConfig("difficulty", challenge.parameters[3] as Difficulty, true); + setConfig("difficulty", challenge.parameters[3] as Difficulty, { + nosave: true, + }); } } else if (challenge.type === "special") { if (challenge.name === "semimak") { // so can you make a link that sets up 120s, 10k, punct, stop on word, and semimak as the layout? - setConfig("mode", "time", true); - setConfig("time", 120, true); - setConfig("language", "english_10k", true); - setConfig("punctuation", true, true); - setConfig("stopOnError", "word", true); - setConfig("layout", "semimak", true); - setConfig("keymapLayout", "overrideSync", true); - setConfig("keymapMode", "static", true); + setConfig("mode", "time", { + nosave: true, + }); + setConfig("time", 120, { + nosave: true, + }); + setConfig("language", "english_10k", { + nosave: true, + }); + setConfig("punctuation", true, { + nosave: true, + }); + setConfig("stopOnError", "word", { + nosave: true, + }); + setConfig("layout", "semimak", { + nosave: true, + }); + setConfig("keymapLayout", "overrideSync", { + nosave: true, + }); + setConfig("keymapMode", "static", { + nosave: true, + }); } } ManualRestart.set(); diff --git a/frontend/src/ts/controllers/theme-controller.ts b/frontend/src/ts/controllers/theme-controller.ts index dcce1d1b2..827314b11 100644 --- a/frontend/src/ts/controllers/theme-controller.ts +++ b/frontend/src/ts/controllers/theme-controller.ts @@ -352,7 +352,9 @@ export async function randomizeTheme(): Promise { randomTheme = "custom"; } - setConfig("customTheme", false, true); + setConfig("customTheme", false, { + nosave: true, + }); await apply(randomTheme, colorsOverride); if (randomThemeIndex >= themesList.length) { diff --git a/frontend/src/ts/elements/custom-background-filter.ts b/frontend/src/ts/elements/custom-background-filter.ts index 23e98fa8e..9e056e761 100644 --- a/frontend/src/ts/elements/custom-background-filter.ts +++ b/frontend/src/ts/elements/custom-background-filter.ts @@ -130,7 +130,7 @@ const debouncedSave = debounce(2000, async () => { const arr = Object.keys(filters).map( (filterKey) => filters[filterKey as keyof typeof filters].value, ) as CustomBackgroundFilter; - setConfig("customBackgroundFilter", arr, false); + setConfig("customBackgroundFilter", arr); }); ConfigEvent.subscribe(({ key, newValue }) => { diff --git a/frontend/src/ts/elements/input-validation.ts b/frontend/src/ts/elements/input-validation.ts index 69b987787..21316ae46 100644 --- a/frontend/src/ts/elements/input-validation.ts +++ b/frontend/src/ts/elements/input-validation.ts @@ -294,7 +294,7 @@ export function handleConfigInput({ if (Config[configName] === value) { return; } - const didConfigSave = setConfig(configName, value, false); + const didConfigSave = setConfig(configName, value); if (didConfigSave) { Notifications.add("Saved", 1, { diff --git a/frontend/src/ts/elements/settings/settings-group.ts b/frontend/src/ts/elements/settings/settings-group.ts index 9eecde00c..832c60f07 100644 --- a/frontend/src/ts/elements/settings/settings-group.ts +++ b/frontend/src/ts/elements/settings/settings-group.ts @@ -46,7 +46,9 @@ export default class SettingsGroup { this.configName = configName; this.mode = mode; this.configFunction = (param, nosave) => - setConfig(configName, param as ConfigType[K], nosave); + setConfig(configName, param as ConfigType[K], { + nosave: nosave ?? false, + }); this.setCallback = options?.setCallback; this.updateCallback = options?.updateCallback; this.validation = options?.validation; diff --git a/frontend/src/ts/modals/mobile-test-config.ts b/frontend/src/ts/modals/mobile-test-config.ts index 00dab9fc3..8cd475107 100644 --- a/frontend/src/ts/modals/mobile-test-config.ts +++ b/frontend/src/ts/modals/mobile-test-config.ts @@ -153,7 +153,7 @@ async function setup(modalEl: HTMLElement): Promise { arr = [len]; } - if (setConfig("quoteLength", arr, false)) { + if (setConfig("quoteLength", arr)) { ManualRestart.set(); TestLogic.restart(); } diff --git a/frontend/src/ts/modals/quote-search.ts b/frontend/src/ts/modals/quote-search.ts index ae7b1eed7..14e7c043a 100644 --- a/frontend/src/ts/modals/quote-search.ts +++ b/frontend/src/ts/modals/quote-search.ts @@ -363,7 +363,7 @@ function apply(val: number): void { ); } if (val !== null && !isNaN(val) && val >= 0) { - setConfig("quoteLength", [-2], false); + setConfig("quoteLength", [-2]); TestState.setSelectedQuoteId(val); ManualRestart.set(); } else { diff --git a/frontend/src/ts/test/funbox/funbox-functions.ts b/frontend/src/ts/test/funbox/funbox-functions.ts index b97c55185..4810b36fb 100644 --- a/frontend/src/ts/test/funbox/funbox-functions.ts +++ b/frontend/src/ts/test/funbox/funbox-functions.ts @@ -207,7 +207,9 @@ const list: Partial> = { }, simon_says: { applyConfig(): void { - setConfig("keymapMode", "next", true); + setConfig("keymapMode", "next", { + nosave: true, + }); }, rememberSettings(): void { save("keymapMode", Config.keymapMode); @@ -215,7 +217,9 @@ const list: Partial> = { }, tts: { applyConfig(): void { - setConfig("keymapMode", "off", true); + setConfig("keymapMode", "off", { + nosave: true, + }); }, rememberSettings(): void { save("keymapMode", Config.keymapMode); @@ -368,8 +372,12 @@ const list: Partial> = { if (Config.layout === "default") { layout = "qwerty"; } - setConfig("layout", layout, true); - setConfig("keymapLayout", "overrideSync", true); + setConfig("layout", layout, { + nosave: true, + }); + setConfig("keymapLayout", "overrideSync", { + nosave: true, + }); }, rememberSettings(): void { save("keymapMode", Config.keymapMode); @@ -380,8 +388,12 @@ const list: Partial> = { applyConfig(): void { const layout = Config.customLayoutfluid[0] ?? "qwerty"; - setConfig("layout", layout as Layout, true); - setConfig("keymapLayout", layout as KeymapLayout, true); + setConfig("layout", layout as Layout, { + nosave: true, + }); + setConfig("keymapLayout", layout as KeymapLayout, { + nosave: true, + }); }, rememberSettings(): void { save("keymapMode", Config.keymapMode); @@ -485,9 +497,13 @@ const list: Partial> = { memory: { applyConfig(): void { $("#wordsWrapper").addClass("hidden"); - setConfig("showAllLines", true, true); + setConfig("showAllLines", true, { + nosave: true, + }); if (Config.keymapMode === "next") { - setConfig("keymapMode", "react", true); + setConfig("keymapMode", "react", { + nosave: true, + }); } }, rememberSettings(): void { @@ -684,7 +700,9 @@ const list: Partial> = { if (languages.length === 1) { const lang = languages[0] as LanguageObject; - setConfig("language", lang.name, true); + setConfig("language", lang.name, { + nosave: true, + }); toggleFunbox("polyglot", true); Notifications.add( `Disabled polyglot funbox because only one valid language was found. Check your polyglot languages config (${Config.customPolyglot.join( diff --git a/frontend/src/ts/test/funbox/funbox-memory.ts b/frontend/src/ts/test/funbox/funbox-memory.ts index 886b72b50..5fc953eeb 100644 --- a/frontend/src/ts/test/funbox/funbox-memory.ts +++ b/frontend/src/ts/test/funbox/funbox-memory.ts @@ -20,7 +20,9 @@ export function save( settingsMemory[settingName] ??= { value, setFunction: (param, noSave?) => - setConfig(settingName, param as Config[T], noSave), + setConfig(settingName, param as Config[T], { + nosave: noSave ?? false, + }), }; } diff --git a/frontend/src/ts/test/funbox/funbox.ts b/frontend/src/ts/test/funbox/funbox.ts index 58bd506ff..f3e20af3a 100644 --- a/frontend/src/ts/test/funbox/funbox.ts +++ b/frontend/src/ts/test/funbox/funbox.ts @@ -38,7 +38,7 @@ export function setFunbox(funbox: FunboxName[]): boolean { } } FunboxMemory.load(); - setConfig("funbox", funbox, false); + setConfig("funbox", funbox); return true; } @@ -104,7 +104,9 @@ export async function activate( ), -1, ); - setConfig("funbox", [], true); + setConfig("funbox", [], { + nosave: true, + }); await clear(); return false; } @@ -123,7 +125,9 @@ export async function activate( Misc.createErrorMessage(error, "Failed to activate funbox"), -1, ); - setConfig("funbox", [], true); + setConfig("funbox", [], { + nosave: true, + }); await clear(); return false; } @@ -134,7 +138,9 @@ export async function activate( "Current language does not support this funbox mode", 0, ); - setConfig("funbox", [], true); + setConfig("funbox", [], { + nosave: true, + }); await clear(); return; } @@ -188,7 +194,9 @@ export async function activate( -1, ); } - setConfig("funbox", [], true); + setConfig("funbox", [], { + nosave: true, + }); await clear(); return; } diff --git a/frontend/src/ts/test/practise-words.ts b/frontend/src/ts/test/practise-words.ts index ba1f678a7..f7d0342cd 100644 --- a/frontend/src/ts/test/practise-words.ts +++ b/frontend/src/ts/test/practise-words.ts @@ -148,7 +148,9 @@ export function init( customText = CustomText.getData(); } - setConfig("mode", "custom", true); + setConfig("mode", "custom", { + nosave: true, + }); CustomText.setPipeDelimiter(true); CustomText.setText(newCustomText); CustomText.setLimitMode("section"); diff --git a/frontend/src/ts/test/test-logic.ts b/frontend/src/ts/test/test-logic.ts index 754dae2d8..5a8d449e8 100644 --- a/frontend/src/ts/test/test-logic.ts +++ b/frontend/src/ts/test/test-logic.ts @@ -497,9 +497,11 @@ async function init(): Promise { important: true, }, ); - setConfig("lazyMode", false, false); + setConfig("lazyMode", false); } else if (rememberLazyMode && anySupportsLazyMode) { - setConfig("lazyMode", true, true); + setConfig("lazyMode", true, { + nosave: true, + }); } } else { // normal mode @@ -510,9 +512,11 @@ async function init(): Promise { important: true, }); - setConfig("lazyMode", false, false); + setConfig("lazyMode", false); } else if (rememberLazyMode && !language.noLazyMode) { - setConfig("lazyMode", true, true); + setConfig("lazyMode", true, { + nosave: true, + }); } } @@ -1559,7 +1563,7 @@ $(".pageTest").on("click", "#testConfig .quoteLength .textButton", (e) => { arr = [len]; } - if (setConfig("quoteLength", arr, false)) { + if (setConfig("quoteLength", arr)) { ManualRestart.set(); restart(); } @@ -1595,7 +1599,9 @@ ConfigEvent.subscribe(({ key, newValue, nosave }) => { if (key === "language") { //automatically enable lazy mode for arabic if ((newValue as string)?.startsWith("arabic") && ArabicLazyMode.get()) { - setConfig("lazyMode", true, true); + setConfig("lazyMode", true, { + nosave: true, + }); } restart(); } diff --git a/frontend/src/ts/test/test-timer.ts b/frontend/src/ts/test/test-timer.ts index 55eb5f641..17c2a46db 100644 --- a/frontend/src/ts/test/test-timer.ts +++ b/frontend/src/ts/test/test-timer.ts @@ -127,8 +127,12 @@ function layoutfluid(): void { if (Config.layout !== layout && layout !== undefined) { LayoutfluidFunboxTimer.hide(); - setConfig("layout", layout as Layout, true); - setConfig("keymapLayout", layout as KeymapLayout, true); + setConfig("layout", layout as Layout, { + nosave: true, + }); + setConfig("keymapLayout", layout as KeymapLayout, { + nosave: true, + }); } } if (timerDebug) console.timeEnd("layoutfluid"); diff --git a/frontend/src/ts/utils/url-handler.ts b/frontend/src/ts/utils/url-handler.ts index 0cbab7c86..5a94abd00 100644 --- a/frontend/src/ts/utils/url-handler.ts +++ b/frontend/src/ts/utils/url-handler.ts @@ -181,18 +181,24 @@ export function loadTestSettingsFromUrl(getOverride?: string): void { const applied: Record = {}; if (de[0] !== null) { - setConfig("mode", de[0], true); + setConfig("mode", de[0], { + nosave: true, + }); applied["mode"] = de[0]; } const mode = de[0] ?? Config.mode; if (de[1] !== null) { if (mode === "time") { - setConfig("time", parseInt(de[1], 10), true); + setConfig("time", parseInt(de[1], 10), { + nosave: true, + }); } else if (mode === "words") { - setConfig("words", parseInt(de[1], 10), true); + setConfig("words", parseInt(de[1], 10), { + nosave: true, + }); } else if (mode === "quote") { - setConfig("quoteLength", [-2], false); + setConfig("quoteLength", [-2]); TestState.setSelectedQuoteId(parseInt(de[1], 10)); ManualRestart.set(); } @@ -236,22 +242,30 @@ export function loadTestSettingsFromUrl(getOverride?: string): void { } if (de[3] !== null) { - setConfig("punctuation", de[3], true); + setConfig("punctuation", de[3], { + nosave: true, + }); applied["punctuation"] = de[3] ? "on" : "off"; } if (de[4] !== null) { - setConfig("numbers", de[4], true); + setConfig("numbers", de[4], { + nosave: true, + }); applied["numbers"] = de[4] ? "on" : "off"; } if (de[5] !== null) { - setConfig("language", de[5] as Language, true); + setConfig("language", de[5] as Language, { + nosave: true, + }); applied["language"] = de[5]; } if (de[6] !== null) { - setConfig("difficulty", de[6], true); + setConfig("difficulty", de[6], { + nosave: true, + }); applied["difficulty"] = de[6]; } @@ -263,7 +277,9 @@ export function loadTestSettingsFromUrl(getOverride?: string): void { } else { val = de[7]; } - setConfig("funbox", val, true); + setConfig("funbox", val, { + nosave: true, + }); applied["funbox"] = val.join(", "); }