From 10fcd31c0145bec997c1c686288dcbb9a7032683 Mon Sep 17 00:00:00 2001 From: Vishal27alpha <120443254+Vishal27alpha@users.noreply.github.com> Date: Thu, 12 Feb 2026 21:55:32 +0530 Subject: [PATCH 1/6] feat(settings): add result saving toggle to account settings and practice alias --- backend/src/api/controllers/user.ts | 2 +- frontend/src/html/pages/account-settings.html | 19 ++++++ frontend/src/styles/account-settings.scss | 66 +++++++++++++++++++ .../src/ts/ape/adapters/ts-rest-adapter.ts | 11 +++- .../src/ts/commandline/lists/result-saving.ts | 2 +- frontend/src/ts/firebase.ts | 1 + frontend/src/ts/modals/simple-modals-base.ts | 2 + frontend/src/ts/modals/simple-modals.ts | 22 +++++++ frontend/src/ts/pages/account-settings.ts | 34 ++++++++++ 9 files changed, 154 insertions(+), 5 deletions(-) diff --git a/backend/src/api/controllers/user.ts b/backend/src/api/controllers/user.ts index 964602852f10..816242cab515 100644 --- a/backend/src/api/controllers/user.ts +++ b/backend/src/api/controllers/user.ts @@ -270,7 +270,7 @@ export async function deleteUser(req: MonkeyRequest): Promise { if (error) { if (error instanceof MonkeyError && error.status === 404) { - //userinfo was already deleted. We ignore this and still try to remove the other data + //userinfo was already deleted. We ignore and still try to remove the other data } else { throw error; } diff --git a/frontend/src/html/pages/account-settings.html b/frontend/src/html/pages/account-settings.html index 6ef41fa45ad2..847d1a2ebd96 100644 --- a/frontend/src/html/pages/account-settings.html +++ b/frontend/src/html/pages/account-settings.html @@ -133,6 +133,25 @@ +
+
+ + result saving +
+
+ Toggle result saving. When disabled, results will not be saved ( + practice mode + ). +
+
+ +
+
+
- + + + + on
diff --git a/frontend/src/styles/account-settings.scss b/frontend/src/styles/account-settings.scss index 32e5fdc7e04c..445ee309dca5 100644 --- a/frontend/src/styles/account-settings.scss +++ b/frontend/src/styles/account-settings.scss @@ -117,68 +117,27 @@ } &.resultSaving { .buttons { - justify-items: center; - } - .toggleSwitch { - display: inline-flex; - align-items: center; - gap: 0.75rem; - cursor: pointer; - user-select: none; + grid-template-columns: repeat(auto-fit, minmax(4.5rem, 1fr)); + gap: 0.5rem; } - .toggleSwitch input { + #toggleResultSaving { position: absolute; opacity: 0; width: 0; height: 0; } - .toggleSwitch .track { - width: 3rem; - height: 1.6rem; - border-radius: 999px; - background: var(--sub-alt-color); - border: 1px solid var(--sub-color); - position: relative; - transition: - background 0.2s ease, - border-color 0.2s ease; - } - - .toggleSwitch .track::after { - content: ""; + .toggleLabel { position: absolute; - top: 2px; - left: 2px; - width: 1.2rem; - height: 1.2rem; - border-radius: 999px; - background: var(--text-color); - transition: - transform 0.2s ease, - background 0.2s ease; - } - - .toggleSwitch input:checked + .track { - background: var(--main-color); - border-color: var(--main-color); - } - - .toggleSwitch input:checked + .track::after { - transform: translateX(1.4rem); - background: var(--bg-color); - } - - .toggleSwitch .toggleLabel { - color: var(--sub-color); - text-transform: uppercase; - letter-spacing: 0.08em; - font-size: 0.75rem; - } - - .toggleSwitch input:checked ~ .toggleLabel { - color: var(--main-color); + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; } } } diff --git a/frontend/src/ts/ape/adapters/ts-rest-adapter.ts b/frontend/src/ts/ape/adapters/ts-rest-adapter.ts index 78cb4ae7717f..54d6d5ed3bc9 100644 --- a/frontend/src/ts/ape/adapters/ts-rest-adapter.ts +++ b/frontend/src/ts/ape/adapters/ts-rest-adapter.ts @@ -29,14 +29,9 @@ function buildApi(timeout: number): (args: ApiFetcherArgs) => Promise<{ }> { return async (request: ApiFetcherArgs) => { try { - // ===== DEV AUTH (LOCAL ONLY) ===== - if (import.meta.env.DEV) { - request.headers["Authorization"] = "Uid localuser|vishal@test.com"; - } else { - const token = await getIdToken(); - if (token !== null) { - request.headers["Authorization"] = `Bearer ${token}`; - } + const token = await getIdToken(); + if (token !== null) { + request.headers["Authorization"] = `Bearer ${token}`; } const usePolyfill = AbortSignal?.timeout === undefined; diff --git a/frontend/src/ts/modals/simple-modals.ts b/frontend/src/ts/modals/simple-modals.ts index 90ec3c78c7b3..9e4c4dbc73be 100644 --- a/frontend/src/ts/modals/simple-modals.ts +++ b/frontend/src/ts/modals/simple-modals.ts @@ -7,8 +7,6 @@ import * as Settings from "../pages/settings"; import * as ThemePicker from "../elements/settings/theme-picker"; import * as CustomText from "../test/custom-text"; import { FirebaseError } from "firebase/app"; -import * as TestState from "../test/test-state"; -import * as ModesNotice from "../elements/modes-notice"; import { isAuthenticated, @@ -819,25 +817,6 @@ list.optOutOfLeaderboards = new SimpleModal({ }, }); -list.toggleResultSaving = new SimpleModal({ - id: "toggleResultSaving", - title: "Result saving", - text: "Toggle result saving. When disabled, results will not be saved (practice mode).", - buttonText: "toggle", - execFn: async (): Promise => { - const current = TestState.savingEnabled; - TestState.setSaving(!current); - await ModesNotice.update(); - - return { - status: 1, - message: !current - ? "Result saving enabled" - : "Result saving disabled (practice mode)", - }; - }, -}); - list.applyCustomFont = new SimpleModal({ id: "applyCustomFont", title: "Custom font", diff --git a/frontend/src/ts/pages/account-settings.ts b/frontend/src/ts/pages/account-settings.ts index aed1a77faac9..176100c81f03 100644 --- a/frontend/src/ts/pages/account-settings.ts +++ b/frontend/src/ts/pages/account-settings.ts @@ -36,6 +36,26 @@ const state: State = { tab: "account", }; +function setResultSaving(checked: boolean): void { + TestState.setSaving(checked); + void ModesNotice.update(); + + pageElement.qs("#toggleResultSaving")?.setChecked(checked); + pageElement + .qs(".section.resultSaving .toggleLabel") + ?.setText(checked ? "on" : "off"); + pageElement + .qsa(".section.resultSaving .resultSavingToggle") + ?.removeClass("active"); + pageElement + .qs( + `.section.resultSaving .resultSavingToggle[data-value="${ + checked ? "on" : "off" + }"]`, + ) + ?.addClass("active"); +} + function updateAuthenticationSections(): void { pageElement.qsa(".section.passwordAuthSettings button")?.addClass("hidden"); pageElement.qsa(".section.googleAuthSettings button")?.addClass("hidden"); @@ -124,8 +144,8 @@ function updateTabs(): void { pageElement.qs(`.tab[data-tab="${state.tab}"]`)?.addClass("active"); }, ); - pageElement.qsa("button")?.removeClass("active"); - pageElement.qs(`button[data-tab="${state.tab}"]`)?.addClass("active"); + pageElement.qsa(".tabs button")?.removeClass("active"); + pageElement.qs(`.tabs button[data-tab="${state.tab}"]`)?.addClass("active"); } function updateAccountSections(): void { @@ -149,24 +169,14 @@ function updateAccountSections(): void { } } -function updateResultSavingToggle(): void { - const toggle = pageElement.qs( - "#toggleResultSaving", - ) as HTMLInputElement | null; - if (!toggle) return; - - toggle.checked = TestState.savingEnabled; - pageElement - .qs(".section.resultSaving .toggleLabel") - ?.setText(TestState.savingEnabled ? "on" : "off"); -} - export function updateUI(): void { if (getActivePage() !== "accountSettings") return; updateAuthenticationSections(); updateIntegrationSections(); updateAccountSections(); - updateResultSavingToggle(); + + setResultSaving(TestState.savingEnabled); + void ApeKeyTable.update(updateUI); void BlockedUserTable.update(); updateTabs(); @@ -256,23 +266,26 @@ qs(".pageAccountSettings")?.onChild( showPopup("resetPersonalBests"); }, ); + qs(".pageAccountSettings")?.onChild( "change", "#toggleResultSaving", (event) => { - const target = event.target as HTMLInputElement | null; - if (!target) return; - - TestState.setSaving(target.checked); - void ModesNotice.update(); - updateResultSavingToggle(); - - Notifications.add( - target.checked - ? "Result saving enabled" - : "Result saving disabled (practice mode)", - 1, - ); + const checked = (event.target as HTMLInputElement).checked; + setResultSaving(checked); + }, +); + +qs(".pageAccountSettings")?.onChild( + "click", + ".section.resultSaving .resultSavingToggle", + (event) => { + const value = (event.childTarget as HTMLElement).getAttribute("data-value"); + if (value === "on") { + setResultSaving(true); + } else if (value === "off") { + setResultSaving(false); + } }, ); diff --git a/frontend/src/ts/test/test-state.ts b/frontend/src/ts/test/test-state.ts index 1b5079d9d377..6312a941e0f7 100644 --- a/frontend/src/ts/test/test-state.ts +++ b/frontend/src/ts/test/test-state.ts @@ -5,7 +5,22 @@ export let isRepeated = false; export let isPaceRepeat = false; export let isActive = false; export let activeChallenge: null | Challenge = null; -export let savingEnabled = true; +const savingEnabledStorageKey = "resultSavingEnabled"; + +function getInitialSavingEnabled(): boolean { + try { + if (typeof window === "undefined" || !("localStorage" in window)) { + return true; + } + const stored = window.localStorage.getItem(savingEnabledStorageKey); + if (stored === null) return true; + return stored === "true"; + } catch { + return true; + } +} + +export let savingEnabled = getInitialSavingEnabled(); export let bailedOut = false; export let selectedQuoteId = 1; export let activeWordIndex = 0; @@ -33,6 +48,14 @@ export function setActiveChallenge(val: null | Challenge): void { export function setSaving(val: boolean): void { savingEnabled = val; + try { + if (typeof window === "undefined" || !("localStorage" in window)) { + return; + } + window.localStorage.setItem(savingEnabledStorageKey, String(val)); + } catch { + // ignore storage failures (e.g., private mode) + } } export function setBailedOut(tf: boolean): void { From 5405393583dab68ba4ef9f6ec11cf05d2cab098a Mon Sep 17 00:00:00 2001 From: Vishal27alpha <120443254+Vishal27alpha@users.noreply.github.com> Date: Fri, 13 Feb 2026 17:19:59 +0530 Subject: [PATCH 3/6] fix(settings): address review feedback (move setting location and add command active state) --- frontend/src/html/pages/account-settings.html | 21 --------- frontend/src/html/pages/settings.html | 27 +++++++++++ frontend/src/styles/account-settings.scss | 25 ---------- frontend/src/styles/settings.scss | 25 ++++++++++ .../src/ts/commandline/lists/result-saving.ts | 2 + frontend/src/ts/pages/account-settings.ts | 46 ------------------- frontend/src/ts/pages/settings.ts | 38 +++++++++++++++ 7 files changed, 92 insertions(+), 92 deletions(-) diff --git a/frontend/src/html/pages/account-settings.html b/frontend/src/html/pages/account-settings.html index 833b0c607d74..c731bd519630 100644 --- a/frontend/src/html/pages/account-settings.html +++ b/frontend/src/html/pages/account-settings.html @@ -133,27 +133,6 @@ -
-
- - result saving -
-
- Toggle result saving. When disabled, results will not be saved ( - practice mode - ). -
-
- - - - on -
-
diff --git a/frontend/src/html/pages/settings.html b/frontend/src/html/pages/settings.html index d33167a8e55e..2ead73e8192c 100644 --- a/frontend/src/html/pages/settings.html +++ b/frontend/src/html/pages/settings.html @@ -106,6 +106,33 @@ +
+
+ + result saving + +
+
+ Toggle result saving. When disabled, results will not be saved ( + practice mode + ). +
+
+ + + + on +
+
diff --git a/frontend/src/styles/account-settings.scss b/frontend/src/styles/account-settings.scss index 445ee309dca5..68eb21dee253 100644 --- a/frontend/src/styles/account-settings.scss +++ b/frontend/src/styles/account-settings.scss @@ -115,31 +115,6 @@ color: var(--error-color); } } - &.resultSaving { - .buttons { - grid-template-columns: repeat(auto-fit, minmax(4.5rem, 1fr)); - gap: 0.5rem; - } - - #toggleResultSaving { - position: absolute; - opacity: 0; - width: 0; - height: 0; - } - - .toggleLabel { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border: 0; - } - } } &[data-tab="apeKeys"], &[data-tab="blockedUsers"] { diff --git a/frontend/src/styles/settings.scss b/frontend/src/styles/settings.scss index 6aa5b86db954..7581b6e6cd2a 100644 --- a/frontend/src/styles/settings.scss +++ b/frontend/src/styles/settings.scss @@ -197,6 +197,31 @@ } } } + &.resultSaving { + .buttons { + grid-template-columns: repeat(auto-fit, minmax(4.5rem, 1fr)); + gap: 0.5rem; + } + + #toggleResultSaving { + position: absolute; + opacity: 0; + width: 0; + height: 0; + } + + .toggleLabel { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; + } + } &[data-config-name="fontFamily"] { grid-template-areas: diff --git a/frontend/src/ts/commandline/lists/result-saving.ts b/frontend/src/ts/commandline/lists/result-saving.ts index 4619684b5a08..70e8748fd523 100644 --- a/frontend/src/ts/commandline/lists/result-saving.ts +++ b/frontend/src/ts/commandline/lists/result-saving.ts @@ -13,6 +13,7 @@ const subgroup: CommandsSubgroup = { TestState.setSaving(false); void ModesNotice.update(); }, + active: () => !TestState.savingEnabled, }, { id: "setResultSavingOn", @@ -22,6 +23,7 @@ const subgroup: CommandsSubgroup = { TestState.setSaving(true); void ModesNotice.update(); }, + active: () => TestState.savingEnabled, }, ], }; diff --git a/frontend/src/ts/pages/account-settings.ts b/frontend/src/ts/pages/account-settings.ts index 176100c81f03..4a9ba3dcb5b0 100644 --- a/frontend/src/ts/pages/account-settings.ts +++ b/frontend/src/ts/pages/account-settings.ts @@ -5,13 +5,11 @@ import { getActivePage } from "../signals/core"; import { swapElements } from "../utils/misc"; import { getSnapshot } from "../db"; import Ape from "../ape"; -import * as TestState from "../test/test-state"; import * as StreakHourOffsetModal from "../modals/streak-hour-offset"; import { showLoaderBar } from "../signals/loader-bar"; import * as ApeKeyTable from "../elements/account-settings/ape-key-table"; import * as BlockedUserTable from "../elements/account-settings/blocked-user-table"; import * as Notifications from "../elements/notifications"; -import * as ModesNotice from "../elements/modes-notice"; import { z } from "zod"; import * as AuthEvent from "../observables/auth-event"; import { qs, qsa, qsr, onDOMReady } from "../utils/dom"; @@ -36,26 +34,6 @@ const state: State = { tab: "account", }; -function setResultSaving(checked: boolean): void { - TestState.setSaving(checked); - void ModesNotice.update(); - - pageElement.qs("#toggleResultSaving")?.setChecked(checked); - pageElement - .qs(".section.resultSaving .toggleLabel") - ?.setText(checked ? "on" : "off"); - pageElement - .qsa(".section.resultSaving .resultSavingToggle") - ?.removeClass("active"); - pageElement - .qs( - `.section.resultSaving .resultSavingToggle[data-value="${ - checked ? "on" : "off" - }"]`, - ) - ?.addClass("active"); -} - function updateAuthenticationSections(): void { pageElement.qsa(".section.passwordAuthSettings button")?.addClass("hidden"); pageElement.qsa(".section.googleAuthSettings button")?.addClass("hidden"); @@ -175,8 +153,6 @@ export function updateUI(): void { updateIntegrationSections(); updateAccountSections(); - setResultSaving(TestState.savingEnabled); - void ApeKeyTable.update(updateUI); void BlockedUserTable.update(); updateTabs(); @@ -267,28 +243,6 @@ qs(".pageAccountSettings")?.onChild( }, ); -qs(".pageAccountSettings")?.onChild( - "change", - "#toggleResultSaving", - (event) => { - const checked = (event.target as HTMLInputElement).checked; - setResultSaving(checked); - }, -); - -qs(".pageAccountSettings")?.onChild( - "click", - ".section.resultSaving .resultSavingToggle", - (event) => { - const value = (event.childTarget as HTMLElement).getAttribute("data-value"); - if (value === "on") { - setResultSaving(true); - } else if (value === "off") { - setResultSaving(false); - } - }, -); - qs(".pageAccountSettings")?.onChild("click", "#updateAccountName", () => { showPopup("updateName"); }); diff --git a/frontend/src/ts/pages/settings.ts b/frontend/src/ts/pages/settings.ts index 72fff7a2da59..026b8ad676c7 100644 --- a/frontend/src/ts/pages/settings.ts +++ b/frontend/src/ts/pages/settings.ts @@ -11,6 +11,7 @@ import * as ThemePicker from "../elements/settings/theme-picker"; import * as Notifications from "../elements/notifications"; import * as ImportExportSettingsModal from "../modals/import-export-settings"; import * as ConfigEvent from "../observables/config-event"; +import * as ModesNotice from "../elements/modes-notice"; import { getActivePage } from "../signals/core"; import { PageWithUrlParams } from "./page"; import { isAuthenticated } from "../firebase"; @@ -18,6 +19,7 @@ import { get as getTypingSpeedUnit } from "../utils/typing-speed-units"; import SlimSelect from "slim-select"; import * as Skeleton from "../utils/skeleton"; import * as CustomBackgroundFilter from "../elements/custom-background-filter"; +import * as TestState from "../test/test-state"; import { ThemeName, CustomLayoutFluid, @@ -481,6 +483,23 @@ function showAccountSection(): void { qsa(`.pageSettings .section.needsAccount`)?.show(); refreshTagsSettingsSection(); refreshPresetsSettingsSection(); + updateResultSavingUI(TestState.savingEnabled); +} + +function updateResultSavingUI(checked: boolean): void { + const section = qs(".pageSettings .section.resultSaving"); + section?.qs("#toggleResultSaving")?.setChecked(checked); + section?.qs(".toggleLabel")?.setText(checked ? "on" : "off"); + section?.qsa(".resultSavingToggle")?.removeClass("active"); + section + ?.qs(`.resultSavingToggle[data-value="${checked ? "on" : "off"}"]`) + ?.addClass("active"); +} + +function setResultSaving(checked: boolean): void { + TestState.setSaving(checked); + void ModesNotice.update(); + updateResultSavingUI(checked); } function setActiveFunboxButton(): void { @@ -806,6 +825,24 @@ qs(".pageSettings .section.presets")?.onChild( }, ); +qs(".pageSettings")?.onChild("change", "#toggleResultSaving", (event) => { + const checked = (event.target as HTMLInputElement).checked; + setResultSaving(checked); +}); + +qs(".pageSettings")?.onChild( + "click", + ".section.resultSaving .resultSavingToggle", + (event) => { + const value = (event.childTarget as HTMLElement).getAttribute("data-value"); + if (value === "on") { + setResultSaving(true); + } else if (value === "off") { + setResultSaving(false); + } + }, +); + qs("#importSettingsButton")?.on("click", () => { ImportExportSettingsModal.show("import"); }); @@ -1069,6 +1106,7 @@ export const page = new PageWithUrlParams({ await update(); // theme UI updates manually to avoid duplication await ThemePicker.updateThemeUI(); + updateResultSavingUI(TestState.savingEnabled); handleHighlightSection(options.urlParams?.highlight); }, From 8cc349f3fcc33f98f06680e69444799d4fd300c1 Mon Sep 17 00:00:00 2001 From: Vishal27alpha <120443254+Vishal27alpha@users.noreply.github.com> Date: Sat, 14 Feb 2026 01:22:08 +0530 Subject: [PATCH 4/6] refactor(settings): simplify result saving section and remove custom styles --- frontend/src/html/pages/settings.html | 10 ++-------- frontend/src/styles/settings.scss | 25 ------------------------- frontend/src/ts/pages/settings.ts | 7 ------- 3 files changed, 2 insertions(+), 40 deletions(-) diff --git a/frontend/src/html/pages/settings.html b/frontend/src/html/pages/settings.html index 2ead73e8192c..d912186020e9 100644 --- a/frontend/src/html/pages/settings.html +++ b/frontend/src/html/pages/settings.html @@ -123,14 +123,8 @@ ).
- - - - on + +
diff --git a/frontend/src/styles/settings.scss b/frontend/src/styles/settings.scss index 7581b6e6cd2a..6aa5b86db954 100644 --- a/frontend/src/styles/settings.scss +++ b/frontend/src/styles/settings.scss @@ -197,31 +197,6 @@ } } } - &.resultSaving { - .buttons { - grid-template-columns: repeat(auto-fit, minmax(4.5rem, 1fr)); - gap: 0.5rem; - } - - #toggleResultSaving { - position: absolute; - opacity: 0; - width: 0; - height: 0; - } - - .toggleLabel { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border: 0; - } - } &[data-config-name="fontFamily"] { grid-template-areas: diff --git a/frontend/src/ts/pages/settings.ts b/frontend/src/ts/pages/settings.ts index 026b8ad676c7..34d9c273074e 100644 --- a/frontend/src/ts/pages/settings.ts +++ b/frontend/src/ts/pages/settings.ts @@ -488,8 +488,6 @@ function showAccountSection(): void { function updateResultSavingUI(checked: boolean): void { const section = qs(".pageSettings .section.resultSaving"); - section?.qs("#toggleResultSaving")?.setChecked(checked); - section?.qs(".toggleLabel")?.setText(checked ? "on" : "off"); section?.qsa(".resultSavingToggle")?.removeClass("active"); section ?.qs(`.resultSavingToggle[data-value="${checked ? "on" : "off"}"]`) @@ -825,11 +823,6 @@ qs(".pageSettings .section.presets")?.onChild( }, ); -qs(".pageSettings")?.onChild("change", "#toggleResultSaving", (event) => { - const checked = (event.target as HTMLInputElement).checked; - setResultSaving(checked); -}); - qs(".pageSettings")?.onChild( "click", ".section.resultSaving .resultSavingToggle", From 29f6e0bb5ea39d71ffd3a24328837dd2bb6f4879 Mon Sep 17 00:00:00 2001 From: Vishal27alpha <120443254+Vishal27alpha@users.noreply.github.com> Date: Sun, 15 Feb 2026 00:38:25 +0530 Subject: [PATCH 5/6] fix(settings): persist result saving preference in user config instead of localStorage --- frontend/src/html/pages/settings.html | 6 ++-- .../ts/commandline/commandline-metadata.ts | 5 +++ .../src/ts/commandline/lists/result-saving.ts | 13 +++---- frontend/src/ts/config-metadata.ts | 6 ++++ frontend/src/ts/constants/default-config.ts | 1 + frontend/src/ts/elements/modes-notice.ts | 3 +- frontend/src/ts/pages/settings.ts | 35 +++---------------- frontend/src/ts/test/test-logic.ts | 6 ++-- frontend/src/ts/test/test-state.ts | 28 --------------- packages/schemas/src/configs.ts | 1 + 10 files changed, 30 insertions(+), 74 deletions(-) diff --git a/frontend/src/html/pages/settings.html b/frontend/src/html/pages/settings.html index d912186020e9..ffe03d7a0dbf 100644 --- a/frontend/src/html/pages/settings.html +++ b/frontend/src/html/pages/settings.html @@ -108,7 +108,7 @@
@@ -123,8 +123,8 @@ ).
- - + +
diff --git a/frontend/src/ts/commandline/commandline-metadata.ts b/frontend/src/ts/commandline/commandline-metadata.ts index c6255d843fbc..ae920ec09125 100644 --- a/frontend/src/ts/commandline/commandline-metadata.ts +++ b/frontend/src/ts/commandline/commandline-metadata.ts @@ -205,6 +205,11 @@ export const commandlineConfigMetadata: CommandlineConfigMetadataObject = { options: "fromSchema", }, }, + resultSavingEnabled: { + subgroup: { + options: "fromSchema", + }, + }, blindMode: { subgroup: { options: "fromSchema", diff --git a/frontend/src/ts/commandline/lists/result-saving.ts b/frontend/src/ts/commandline/lists/result-saving.ts index 70e8748fd523..c4c261d81f12 100644 --- a/frontend/src/ts/commandline/lists/result-saving.ts +++ b/frontend/src/ts/commandline/lists/result-saving.ts @@ -1,5 +1,4 @@ -import * as TestState from "../../test/test-state"; -import * as ModesNotice from "../../elements/modes-notice"; +import Config, { setConfig } from "../../config"; import { Command, CommandsSubgroup } from "../types"; const subgroup: CommandsSubgroup = { @@ -10,20 +9,18 @@ const subgroup: CommandsSubgroup = { display: "off", alias: "disabled incognito", exec: (): void => { - TestState.setSaving(false); - void ModesNotice.update(); + setConfig("resultSavingEnabled", false); }, - active: () => !TestState.savingEnabled, + active: () => !Config.resultSavingEnabled, }, { id: "setResultSavingOn", display: "on", alias: "enabled incognito", exec: (): void => { - TestState.setSaving(true); - void ModesNotice.update(); + setConfig("resultSavingEnabled", true); }, - active: () => TestState.savingEnabled, + active: () => Config.resultSavingEnabled, }, ], }; diff --git a/frontend/src/ts/config-metadata.ts b/frontend/src/ts/config-metadata.ts index 12787734b205..73a6867fd99d 100644 --- a/frontend/src/ts/config-metadata.ts +++ b/frontend/src/ts/config-metadata.ts @@ -209,6 +209,12 @@ export const configMetadata: ConfigMetadataObject = { changeRequiresRestart: false, group: "behavior", }, + resultSavingEnabled: { + icon: "fa-save", + displayString: "result saving", + changeRequiresRestart: false, + group: "behavior", + }, blindMode: { icon: "fa-eye-slash", displayString: "blind mode", diff --git a/frontend/src/ts/constants/default-config.ts b/frontend/src/ts/constants/default-config.ts index 7f2df921e2af..7da4ea735898 100644 --- a/frontend/src/ts/constants/default-config.ts +++ b/frontend/src/ts/constants/default-config.ts @@ -86,6 +86,7 @@ const obj: Config = { minAccCustom: 90, monkey: false, repeatQuotes: "off", + resultSavingEnabled: true, oppositeShiftMode: "off", customBackground: "", customBackgroundSize: "cover", diff --git a/frontend/src/ts/elements/modes-notice.ts b/frontend/src/ts/elements/modes-notice.ts index cac27c4e3696..66165f4693c1 100644 --- a/frontend/src/ts/elements/modes-notice.ts +++ b/frontend/src/ts/elements/modes-notice.ts @@ -32,6 +32,7 @@ ConfigEvent.subscribe(({ key }) => { "quickRestart", "customPolyglot", "alwaysShowDecimalPlaces", + "resultSavingEnabled", ]; if (configKeys.includes(key)) { void update(); @@ -49,7 +50,7 @@ export async function update(): Promise { ); } - if (!TestState.savingEnabled) { + if (!Config.resultSavingEnabled) { testModesNotice.appendHtml( `
saving disabled
`, ); diff --git a/frontend/src/ts/pages/settings.ts b/frontend/src/ts/pages/settings.ts index 34d9c273074e..5708766857e5 100644 --- a/frontend/src/ts/pages/settings.ts +++ b/frontend/src/ts/pages/settings.ts @@ -11,7 +11,6 @@ import * as ThemePicker from "../elements/settings/theme-picker"; import * as Notifications from "../elements/notifications"; import * as ImportExportSettingsModal from "../modals/import-export-settings"; import * as ConfigEvent from "../observables/config-event"; -import * as ModesNotice from "../elements/modes-notice"; import { getActivePage } from "../signals/core"; import { PageWithUrlParams } from "./page"; import { isAuthenticated } from "../firebase"; @@ -19,7 +18,6 @@ import { get as getTypingSpeedUnit } from "../utils/typing-speed-units"; import SlimSelect from "slim-select"; import * as Skeleton from "../utils/skeleton"; import * as CustomBackgroundFilter from "../elements/custom-background-filter"; -import * as TestState from "../test/test-state"; import { ThemeName, CustomLayoutFluid, @@ -82,6 +80,10 @@ async function initGroups(): Promise { ); groups["difficulty"] = new SettingsGroup("difficulty", "button"); groups["quickRestart"] = new SettingsGroup("quickRestart", "button"); + groups["resultSavingEnabled"] = new SettingsGroup( + "resultSavingEnabled", + "button", + ); groups["showAverage"] = new SettingsGroup("showAverage", "button"); groups["keymapMode"] = new SettingsGroup("keymapMode", "button", { updateCallback: () => { @@ -483,21 +485,6 @@ function showAccountSection(): void { qsa(`.pageSettings .section.needsAccount`)?.show(); refreshTagsSettingsSection(); refreshPresetsSettingsSection(); - updateResultSavingUI(TestState.savingEnabled); -} - -function updateResultSavingUI(checked: boolean): void { - const section = qs(".pageSettings .section.resultSaving"); - section?.qsa(".resultSavingToggle")?.removeClass("active"); - section - ?.qs(`.resultSavingToggle[data-value="${checked ? "on" : "off"}"]`) - ?.addClass("active"); -} - -function setResultSaving(checked: boolean): void { - TestState.setSaving(checked); - void ModesNotice.update(); - updateResultSavingUI(checked); } function setActiveFunboxButton(): void { @@ -823,19 +810,6 @@ qs(".pageSettings .section.presets")?.onChild( }, ); -qs(".pageSettings")?.onChild( - "click", - ".section.resultSaving .resultSavingToggle", - (event) => { - const value = (event.childTarget as HTMLElement).getAttribute("data-value"); - if (value === "on") { - setResultSaving(true); - } else if (value === "off") { - setResultSaving(false); - } - }, -); - qs("#importSettingsButton")?.on("click", () => { ImportExportSettingsModal.show("import"); }); @@ -1099,7 +1073,6 @@ export const page = new PageWithUrlParams({ await update(); // theme UI updates manually to avoid duplication await ThemePicker.updateThemeUI(); - updateResultSavingUI(TestState.savingEnabled); handleHighlightSection(options.urlParams?.highlight); }, diff --git a/frontend/src/ts/test/test-logic.ts b/frontend/src/ts/test/test-logic.ts index 83b8bb5b109e..402cb43f1ba8 100644 --- a/frontend/src/ts/test/test-logic.ts +++ b/frontend/src/ts/test/test-logic.ts @@ -195,7 +195,7 @@ export function restart(options = {} as RestartOptions): void { options.withSameWordset = true; } - if (TestState.savingEnabled) { + if (Config.resultSavingEnabled) { TestInput.pushKeypressesToHistory(); TestInput.pushErrorToHistory(); TestInput.pushAfkToHistory(); @@ -1192,7 +1192,7 @@ async function saveResult( ): Promise>> { AccountButton.loading(true); - if (!TestState.savingEnabled) { + if (!Config.resultSavingEnabled) { Notifications.add("Result not saved: disabled by user", -1, { duration: 3, customTitle: "Notice", @@ -1355,7 +1355,7 @@ export function fail(reason: string): void { TestInput.pushErrorToHistory(); TestInput.pushAfkToHistory(); void finish(true); - if (!TestState.savingEnabled) return; + if (!Config.resultSavingEnabled) return; const testSeconds = TestStats.calculateTestSeconds(performance.now()); const afkseconds = TestStats.calculateAfkSeconds(testSeconds); let tt = Numbers.roundTo2(testSeconds - afkseconds); diff --git a/frontend/src/ts/test/test-state.ts b/frontend/src/ts/test/test-state.ts index 6312a941e0f7..c7c91884399f 100644 --- a/frontend/src/ts/test/test-state.ts +++ b/frontend/src/ts/test/test-state.ts @@ -5,22 +5,6 @@ export let isRepeated = false; export let isPaceRepeat = false; export let isActive = false; export let activeChallenge: null | Challenge = null; -const savingEnabledStorageKey = "resultSavingEnabled"; - -function getInitialSavingEnabled(): boolean { - try { - if (typeof window === "undefined" || !("localStorage" in window)) { - return true; - } - const stored = window.localStorage.getItem(savingEnabledStorageKey); - if (stored === null) return true; - return stored === "true"; - } catch { - return true; - } -} - -export let savingEnabled = getInitialSavingEnabled(); export let bailedOut = false; export let selectedQuoteId = 1; export let activeWordIndex = 0; @@ -46,18 +30,6 @@ export function setActiveChallenge(val: null | Challenge): void { activeChallenge = val; } -export function setSaving(val: boolean): void { - savingEnabled = val; - try { - if (typeof window === "undefined" || !("localStorage" in window)) { - return; - } - window.localStorage.setItem(savingEnabledStorageKey, String(val)); - } catch { - // ignore storage failures (e.g., private mode) - } -} - export function setBailedOut(tf: boolean): void { bailedOut = tf; } diff --git a/packages/schemas/src/configs.ts b/packages/schemas/src/configs.ts index 998ce464fbf2..576fe8285518 100644 --- a/packages/schemas/src/configs.ts +++ b/packages/schemas/src/configs.ts @@ -394,6 +394,7 @@ export const ConfigSchema = z difficulty: DifficultySchema, quickRestart: QuickRestartSchema, repeatQuotes: RepeatQuotesSchema, + resultSavingEnabled: z.boolean(), blindMode: z.boolean(), alwaysShowWordsHistory: z.boolean(), singleListCommandLine: SingleListCommandLineSchema, From e3855c37fab5fe0a15f25e66fcd9d40b1a859c24 Mon Sep 17 00:00:00 2001 From: Vishal27alpha <120443254+Vishal27alpha@users.noreply.github.com> Date: Sun, 15 Feb 2026 11:35:59 +0530 Subject: [PATCH 6/6] fix: revert files per review feedback fix(settings): move result saving to config and align command metadata - removed localStorage persistence - deleted result-saving command list - added alias to command metadata - reverted unnecessary modal changes# --- frontend/src/html/pages/account-settings.html | 1 - .../ts/commandline/commandline-metadata.ts | 2 + frontend/src/ts/commandline/lists.ts | 4 +- .../src/ts/commandline/lists/result-saving.ts | 38 ------------------- frontend/src/ts/elements/modes-notice.ts | 2 +- frontend/src/ts/firebase.ts | 1 - frontend/src/ts/modals/simple-modals-base.ts | 2 - frontend/src/ts/modals/simple-modals.ts | 1 - frontend/src/ts/pages/account-settings.ts | 4 +- 9 files changed, 6 insertions(+), 49 deletions(-) delete mode 100644 frontend/src/ts/commandline/lists/result-saving.ts diff --git a/frontend/src/html/pages/account-settings.html b/frontend/src/html/pages/account-settings.html index c731bd519630..6ef41fa45ad2 100644 --- a/frontend/src/html/pages/account-settings.html +++ b/frontend/src/html/pages/account-settings.html @@ -133,7 +133,6 @@
-