From c7927bcd8628e846c6db930898774f2c68b03d96 Mon Sep 17 00:00:00 2001 From: Christian Fehmer Date: Thu, 11 Dec 2025 15:40:20 +0100 Subject: [PATCH 1/4] refactor(config): remove helper functions (@fehmer) (#7216) --- .../controllers/preset-controller.spec.ts | 4 +- frontend/__tests__/root/config.spec.ts | 519 +-------------- frontend/__tests__/utils/url-handler.spec.ts | 59 +- frontend/src/ts/auth.ts | 6 +- frontend/src/ts/commandline/lists.ts | 6 +- .../lists/add-or-remove-theme-to-favorites.ts | 6 +- .../ts/commandline/lists/background-filter.ts | 10 +- .../commandline/lists/custom-themes-list.ts | 7 +- .../src/ts/commandline/lists/font-family.ts | 4 +- .../src/ts/commandline/lists/min-burst.ts | 12 +- frontend/src/ts/commandline/lists/themes.ts | 4 +- frontend/src/ts/config.ts | 603 +----------------- .../ts/controllers/challenge-controller.ts | 66 +- .../src/ts/controllers/preset-controller.ts | 8 +- .../src/ts/controllers/theme-controller.ts | 8 +- .../ts/elements/custom-background-filter.ts | 4 +- frontend/src/ts/elements/keymap.ts | 2 +- .../ts/elements/settings/settings-group.ts | 6 +- .../src/ts/elements/settings/theme-picker.ts | 21 +- frontend/src/ts/event-handlers/footer.ts | 8 +- .../src/ts/modals/custom-test-duration.ts | 4 +- frontend/src/ts/modals/custom-text.ts | 6 +- frontend/src/ts/modals/custom-word-amount.ts | 4 +- .../src/ts/modals/import-export-settings.ts | 4 +- frontend/src/ts/modals/mobile-test-config.ts | 16 +- frontend/src/ts/modals/quote-search.ts | 4 +- frontend/src/ts/modals/simple-modals.ts | 8 +- frontend/src/ts/pages/account.ts | 10 +- frontend/src/ts/pages/settings.ts | 514 +++++---------- frontend/src/ts/ready.ts | 4 +- .../src/ts/test/funbox/funbox-functions.ts | 92 ++- frontend/src/ts/test/funbox/funbox-memory.ts | 14 +- frontend/src/ts/test/funbox/funbox.ts | 31 +- frontend/src/ts/test/practise-words.ts | 4 +- frontend/src/ts/test/test-logic.ts | 36 +- frontend/src/ts/test/test-timer.ts | 6 +- frontend/src/ts/test/test-ui.ts | 4 +- frontend/src/ts/test/words-generator.ts | 12 +- frontend/src/ts/utils/url-handler.ts | 34 +- 39 files changed, 418 insertions(+), 1752 deletions(-) diff --git a/frontend/__tests__/controllers/preset-controller.spec.ts b/frontend/__tests__/controllers/preset-controller.spec.ts index bdbca77e1060..dec7aa85e425 100644 --- a/frontend/__tests__/controllers/preset-controller.spec.ts +++ b/frontend/__tests__/controllers/preset-controller.spec.ts @@ -16,7 +16,7 @@ describe("PresetController", () => { // })); const dbGetSnapshotMock = vi.spyOn(DB, "getSnapshot"); - const configApplyMock = vi.spyOn(UpdateConfig, "apply"); + const configApplyMock = vi.spyOn(UpdateConfig, "applyConfig"); const configSaveFullConfigMock = vi.spyOn( UpdateConfig, "saveFullConfigToLocalStorage", @@ -108,7 +108,7 @@ describe("PresetController", () => { settingGroups: ["test"], }); - UpdateConfig.setNumbers(true); + UpdateConfig.setConfig("numbers", true); const oldConfig = structuredClone(UpdateConfig.default); //WHEN diff --git a/frontend/__tests__/root/config.spec.ts b/frontend/__tests__/root/config.spec.ts index be53263b3be4..d209eadbebed 100644 --- a/frontend/__tests__/root/config.spec.ts +++ b/frontend/__tests__/root/config.spec.ts @@ -2,12 +2,10 @@ import { describe, it, expect, beforeEach, afterAll, vi } from "vitest"; import * as Config from "../../src/ts/config"; import * as Misc from "../../src/ts/utils/misc"; import { - CustomThemeColors, ConfigKey, Config as ConfigType, CaretStyleSchema, } from "@monkeytype/schemas/configs"; -import { randomBytes } from "crypto"; import * as FunboxValidation from "../../src/ts/test/funbox/funbox-validation"; import * as ConfigValidation from "../../src/ts/config-validation"; import * as ConfigEvent from "../../src/ts/observables/config-event"; @@ -300,495 +298,13 @@ describe("Config", () => { }); }); - //TODO move the rest to schema/tests or remove after removing the setX functions from Config - it("setMode", () => { - expect(Config.setMode("zen")).toBe(true); - expect(Config.setMode("invalid" as any)).toBe(false); - }); - it("setPlaySoundOnError", () => { - expect(Config.setPlaySoundOnError("off")).toBe(true); - expect(Config.setPlaySoundOnError("1")).toBe(true); - expect(Config.setPlaySoundOnError("invalid" as any)).toBe(false); - }); - it("setPlaySoundOnClick", () => { - expect(Config.setPlaySoundOnClick("off")).toBe(true); - expect(Config.setPlaySoundOnClick("15")).toBe(true); - expect(Config.setPlaySoundOnClick("invalid" as any)).toBe(false); - }); - it("setSoundVolume", () => { - expect(Config.setSoundVolume(0.1)).toBe(true); - expect(Config.setSoundVolume(1.0)).toBe(true); - expect(Config.setSoundVolume("invalid" as any)).toBe(false); - }); - it("setDifficulty", () => { - expect(Config.setDifficulty("expert")).toBe(true); - expect(Config.setDifficulty("invalid" as any)).toBe(false); - }); - it("setAccountChart", () => { - expect(Config.setAccountChart(["on", "off", "off", "on"])).toBe(true); - //arrays not having 4 values will get [on, on, on, on] as default - expect(Config.setAccountChart(["on", "off"] as any)).toBe(false); - expect(Config.setAccountChart(["on", "off", "on", "true"] as any)).toBe( - false, - ); - }); - it("setStopOnError", () => { - expect(Config.setStopOnError("off")).toBe(true); - expect(Config.setStopOnError("word")).toBe(true); - expect(Config.setStopOnError("invalid" as any)).toBe(false); - }); - it("setTypingSpeedUnit", () => { - expect(Config.setTypingSpeedUnit("wpm")).toBe(true); - expect(Config.setTypingSpeedUnit("cpm")).toBe(true); - expect(Config.setTypingSpeedUnit("invalid" as any)).toBe(false); - }); - it("setPaceCaret", () => { - expect(Config.setPaceCaret("average")).toBe(true); - expect(Config.setPaceCaret("last")).toBe(true); - expect(Config.setPaceCaret("invalid" as any)).toBe(false); - }); - it("setMinWpm", () => { - expect(Config.setMinWpm("custom")).toBe(true); - expect(Config.setMinWpm("off")).toBe(true); - expect(Config.setMinWpm("invalid" as any)).toBe(false); - }); - it("setMinAcc", () => { - expect(Config.setMinAcc("custom")).toBe(true); - expect(Config.setMinAcc("off")).toBe(true); - expect(Config.setMinAcc("invalid" as any)).toBe(false); - }); - it("setMinBurst", () => { - expect(Config.setMinBurst("fixed")).toBe(true); - expect(Config.setMinBurst("off")).toBe(true); - expect(Config.setMinBurst("invalid" as any)).toBe(false); - }); - it("setSingleListCommandLine", () => { - expect(Config.setSingleListCommandLine("on")).toBe(true); - expect(Config.setSingleListCommandLine("manual")).toBe(true); - expect(Config.setSingleListCommandLine("invalid" as any)).toBe(false); - }); - it("setAds", () => { - expect(Config.setAds("on")).toBe(true); - expect(Config.setAds("sellout")).toBe(true); - }); - it("setRepeatQuotes", () => { - expect(Config.setRepeatQuotes("off")).toBe(true); - expect(Config.setRepeatQuotes("typing")).toBe(true); - expect(Config.setRepeatQuotes("invalid" as any)).toBe(false); - }); - it("setOppositeShiftMode", () => { - expect(Config.setOppositeShiftMode("on")).toBe(true); - expect(Config.setOppositeShiftMode("keymap")).toBe(true); - expect(Config.setOppositeShiftMode("invalid" as any)).toBe(false); - }); - it("setCaretStyle", () => { - expect(Config.setCaretStyle("banana")).toBe(true); - expect(Config.setCaretStyle("block")).toBe(true); - expect(Config.setCaretStyle("invalid" as any)).toBe(false); - }); - it("setPaceCaretStyle", () => { - expect(Config.setPaceCaretStyle("carrot")).toBe(true); - expect(Config.setPaceCaretStyle("outline")).toBe(true); - expect(Config.setPaceCaretStyle("invalid" as any)).toBe(false); - }); - it("setShowAverage", () => { - expect(Config.setShowAverage("acc")).toBe(true); - expect(Config.setShowAverage("both")).toBe(true); - expect(Config.setShowAverage("invalid" as any)).toBe(false); - }); - it("setHighlightMode", () => { - expect(Config.setHighlightMode("letter")).toBe(true); - expect(Config.setHighlightMode("next_three_words")).toBe(true); - expect(Config.setHighlightMode("invalid" as any)).toBe(false); - }); - it("setTapeMode", () => { - expect(Config.setTapeMode("letter")).toBe(true); - expect(Config.setTapeMode("off")).toBe(true); - expect(Config.setTapeMode("invalid" as any)).toBe(false); - }); - it("setTimerStyle", () => { - expect(Config.setTimerStyle("bar")).toBe(true); - expect(Config.setTimerStyle("mini")).toBe(true); - expect(Config.setTimerStyle("invalid" as any)).toBe(false); - }); - it("setLiveSpeedStyle", () => { - expect(Config.setLiveSpeedStyle("text")).toBe(true); - expect(Config.setLiveSpeedStyle("mini")).toBe(true); - expect(Config.setLiveSpeedStyle("invalid" as any)).toBe(false); - }); - it("setLiveAccStyle", () => { - expect(Config.setLiveAccStyle("text")).toBe(true); - expect(Config.setLiveAccStyle("mini")).toBe(true); - expect(Config.setLiveAccStyle("invalid" as any)).toBe(false); - }); - it("setLiveBurstStyle", () => { - expect(Config.setLiveBurstStyle("text")).toBe(true); - expect(Config.setLiveBurstStyle("mini")).toBe(true); - expect(Config.setLiveBurstStyle("invalid" as any)).toBe(false); - }); - it("setTimerColor", () => { - expect(Config.setTimerColor("text")).toBe(true); - expect(Config.setTimerColor("sub")).toBe(true); - expect(Config.setTimerColor("invalid" as any)).toBe(false); - }); - it("setTimerOpacity", () => { - expect(Config.setTimerOpacity("1")).toBe(true); - expect(Config.setTimerOpacity("0.5")).toBe(true); - expect(Config.setTimerOpacity("invalid" as any)).toBe(false); - }); - it("setSmoothCaret", () => { - expect(Config.setSmoothCaret("fast")).toBe(true); - expect(Config.setSmoothCaret("medium")).toBe(true); - expect(Config.setSmoothCaret("invalid" as any)).toBe(false); - }); - it("setCodeUnindentOnBackspace", () => { - testBoolean(Config.setCodeUnindentOnBackspace); - }); - it("setQuickRestartMode", () => { - expect(Config.setQuickRestartMode("off")).toBe(true); - expect(Config.setQuickRestartMode("tab")).toBe(true); - expect(Config.setQuickRestartMode("invalid" as any)).toBe(false); - }); - it("setConfidenceMode", () => { - expect(Config.setConfidenceMode("max")).toBe(true); - expect(Config.setConfidenceMode("on")).toBe(true); - expect(Config.setConfidenceMode("invalid" as any)).toBe(false); - }); - it("setIndicateTypos", () => { - expect(Config.setIndicateTypos("below")).toBe(true); - expect(Config.setIndicateTypos("off")).toBe(true); - expect(Config.setIndicateTypos("invalid" as any)).toBe(false); - }); - it("setRandomTheme", () => { - expect(Config.setRandomTheme("fav")).toBe(true); - expect(Config.setRandomTheme("off")).toBe(true); - expect(Config.setRandomTheme("invalid" as any)).toBe(false); - }); - it("setKeymapMode", () => { - expect(Config.setKeymapMode("next")).toBe(true); - expect(Config.setKeymapMode("react")).toBe(true); - expect(Config.setKeymapMode("invalid" as any)).toBe(false); - }); - it("setKeymapLegendStyle", () => { - expect(Config.setKeymapLegendStyle("blank")).toBe(true); - expect(Config.setKeymapLegendStyle("lowercase")).toBe(true); - expect(Config.setKeymapLegendStyle("invalid" as any)).toBe(false); - }); - it("setKeymapStyle", () => { - expect(Config.setKeymapStyle("matrix")).toBe(true); - expect(Config.setKeymapStyle("split")).toBe(true); - expect(Config.setKeymapStyle("invalid" as any)).toBe(false); - }); - it("setKeymapShowTopRow", () => { - expect(Config.setKeymapShowTopRow("always")).toBe(true); - expect(Config.setKeymapShowTopRow("never")).toBe(true); - expect(Config.setKeymapShowTopRow("invalid" as any)).toBe(false); - }); - it("setKeymapSize", () => { - expect(Config.setKeymapSize(0.5)).toBe(true); - expect(Config.setKeymapSize(2)).toBe(true); - expect(Config.setKeymapSize(3.5)).toBe(true); - expect(Config.setKeymapSize("invalid" as any)).toBe(false); - - //invalid values being "auto-fixed" - expect(Config.setKeymapSize(0)).toBe(true); - expect(getConfig().keymapSize).toBe(0.5); - expect(Config.setKeymapSize(4)).toBe(true); - expect(getConfig().keymapSize).toBe(3.5); - expect(Config.setKeymapSize(1.25)).toBe(true); - expect(getConfig().keymapSize).toBe(1.3); - expect(Config.setKeymapSize(1.24)).toBe(true); - expect(getConfig().keymapSize).toBe(1.2); - }); - it("setCustomBackgroundSize", () => { - expect(Config.setCustomBackgroundSize("contain")).toBe(true); - expect(Config.setCustomBackgroundSize("cover")).toBe(true); - expect(Config.setCustomBackgroundSize("invalid" as any)).toBe(false); - }); - it("setCustomBackgroundFilter", () => { - expect(Config.setCustomBackgroundFilter([0, 1, 2, 3])).toBe(true); - - expect(Config.setCustomBackgroundFilter([0, 1, 2, 3, 4] as any)).toBe( - false, - ); - expect(Config.setCustomBackgroundFilter([] as any)).toBe(false); - expect(Config.setCustomBackgroundFilter(["invalid"] as any)).toBe(false); - expect(Config.setCustomBackgroundFilter([1, 2, 3, 4, 5, 6] as any)).toBe( - false, - ); - }); - it("setMonkeyPowerLevel", () => { - expect(Config.setMonkeyPowerLevel("2")).toBe(true); - expect(Config.setMonkeyPowerLevel("off")).toBe(true); - - expect(Config.setMonkeyPowerLevel("invalid" as any)).toBe(false); - }); - it("setCustomThemeColors", () => { - expect(Config.setCustomThemeColors(customThemeColors(10))).toBe(true); - - expect(Config.setCustomThemeColors(customThemeColors(9))).toBe(false); - expect(Config.setCustomThemeColors(customThemeColors(5))).toBe(false); - expect(Config.setCustomThemeColors(customThemeColors(11))).toBe(false); - - const tenColors = customThemeColors(10); - tenColors[0] = "black"; - expect(Config.setCustomThemeColors(tenColors)).toBe(false); - tenColors[0] = "#123456"; - expect(Config.setCustomThemeColors(tenColors)).toBe(true); - tenColors[0] = "#1234"; - expect(Config.setCustomThemeColors(tenColors)).toBe(false); - }); - it("setNumbers", () => { - testBoolean(Config.setNumbers); - }); - it("setPunctuation", () => { - testBoolean(Config.setPunctuation); - }); - it("setBlindMode", () => { - testBoolean(Config.setBlindMode); - }); - it("setAccountChart", () => { - expect(Config.setAccountChart(["on", "off", "off", "on"])).toBe(true); - expect(Config.setAccountChart(["on", "off"] as any)).toBe(false); - expect(Config.setAccountChart(["on", "off", "on", "true"] as any)).toBe( - false, - ); - }); - it("setAlwaysShowDecimalPlaces", () => { - testBoolean(Config.setAlwaysShowDecimalPlaces); - }); - it("setShowOutOfFocusWarning", () => { - testBoolean(Config.setShowOutOfFocusWarning); - }); - it("setAlwaysShowWordsHistory", () => { - testBoolean(Config.setAlwaysShowWordsHistory); - }); - it("setCapsLockWarning", () => { - testBoolean(Config.setCapsLockWarning); - }); - it("setShowAllLines", () => { - testBoolean(Config.setShowAllLines); - }); - it("setQuickEnd", () => { - testBoolean(Config.setQuickEnd); - }); - it("setFlipTestColors", () => { - testBoolean(Config.setFlipTestColors); - }); - it("setColorfulMode", () => { - testBoolean(Config.setColorfulMode); - }); - it("setStrictSpace", () => { - testBoolean(Config.setStrictSpace); - }); - it("setHideExtraLetters", () => { - testBoolean(Config.setHideExtraLetters); - }); - it("setKeyTips", () => { - testBoolean(Config.setKeyTips); - }); - it("setStartGraphsAtZero", () => { - testBoolean(Config.setStartGraphsAtZero); - }); - it("setSmoothLineScroll", () => { - testBoolean(Config.setSmoothLineScroll); - }); - it("setFreedomMode", () => { - testBoolean(Config.setFreedomMode); - }); - it("setAutoSwitchTheme", () => { - testBoolean(Config.setAutoSwitchTheme); - }); - it("setCustomTheme", () => { - testBoolean(Config.setCustomTheme); - }); - it("setBritishEnglish", () => { - testBoolean(Config.setBritishEnglish); - }); - it("setLazyMode", () => { - testBoolean(Config.setLazyMode); - }); - it("setMonkey", () => { - testBoolean(Config.setMonkey); - }); - it("setBurstHeatmap", () => { - testBoolean(Config.setBurstHeatmap); - }); - it("setRepeatedPace", () => { - testBoolean(Config.setRepeatedPace); - }); - it("setFavThemes", () => { - expect(Config.setFavThemes([])).toBe(true); - expect(Config.setFavThemes(["8008", "80s_after_dark"])).toBe(true); - expect(Config.setFavThemes(["test"] as any)).toBe(false); - expect(Config.setFavThemes("invalid" as any)).toBe(false); - }); - it("setFunbox", () => { - expect(Config.setFunbox(["mirror"])).toBe(true); - expect(Config.setFunbox(["mirror", "58008"])).toBe(true); - }); - it("setPaceCaretCustomSpeed", () => { - expect(Config.setPaceCaretCustomSpeed(0)).toBe(true); - expect(Config.setPaceCaretCustomSpeed(1)).toBe(true); - expect(Config.setPaceCaretCustomSpeed(11.11)).toBe(true); - - expect(Config.setPaceCaretCustomSpeed("invalid" as any)).toBe(false); - expect(Config.setPaceCaretCustomSpeed(-1)).toBe(false); - }); - it("setMinWpmCustomSpeed", () => { - expect(Config.setMinWpmCustomSpeed(0)).toBe(true); - expect(Config.setMinWpmCustomSpeed(1)).toBe(true); - expect(Config.setMinWpmCustomSpeed(11.11)).toBe(true); - - expect(Config.setMinWpmCustomSpeed("invalid" as any)).toBe(false); - expect(Config.setMinWpmCustomSpeed(-1)).toBe(false); - }); - it("setMinAccCustom", () => { - expect(Config.setMinAccCustom(0)).toBe(true); - expect(Config.setMinAccCustom(1)).toBe(true); - expect(Config.setMinAccCustom(11.11)).toBe(true); - - expect(Config.setMinAccCustom("invalid" as any)).toBe(false); - expect(Config.setMinAccCustom(-1)).toBe(false); - }); - it("setMinBurstCustomSpeed", () => { - expect(Config.setMinBurstCustomSpeed(0)).toBe(true); - expect(Config.setMinBurstCustomSpeed(1)).toBe(true); - expect(Config.setMinBurstCustomSpeed(11.11)).toBe(true); - - expect(Config.setMinBurstCustomSpeed("invalid" as any)).toBe(false); - expect(Config.setMinBurstCustomSpeed(-1)).toBe(false); - }); - it("setTimeConfig", () => { - expect(Config.setTimeConfig(0)).toBe(true); - expect(Config.setTimeConfig(1)).toBe(true); - - expect(Config.setTimeConfig(11.11)).toBe(false); - }); - it("setWordCount", () => { - expect(Config.setWordCount(0)).toBe(true); - expect(Config.setWordCount(1)).toBe(true); - - expect(Config.setWordCount("invalid" as any)).toBe(false); - expect(Config.setWordCount(11.11)).toBe(false); - }); - it("setFontFamily", () => { - expect(Config.setFontFamily("Arial")).toBe(true); - expect(Config.setFontFamily("roboto_mono")).toBe(true); - expect(Config.setFontFamily("test_font")).toBe(true); - expect(Config.setFontFamily(stringOfLength(50))).toBe(true); - - expect(Config.setFontFamily(stringOfLength(51))).toBe(false); - expect(Config.setFontFamily("test font")).toBe(false); - expect(Config.setFontFamily("test!font")).toBe(false); - }); - it("setTheme", () => { - expect(Config.setTheme("serika")).toBe(true); - expect(Config.setTheme("serika_dark")).toBe(true); - - expect(Config.setTheme("invalid" as any)).toBe(false); - }); - it("setThemeLight", () => { - expect(Config.setThemeLight("serika")).toBe(true); - expect(Config.setThemeLight("serika_dark")).toBe(true); - - expect(Config.setThemeLight("invalid" as any)).toBe(false); - }); - it("setThemeDark", () => { - expect(Config.setThemeDark("serika")).toBe(true); - expect(Config.setThemeDark("serika_dark")).toBe(true); - - expect(Config.setThemeDark("invalid" as any)).toBe(false); - }); - it("setLanguage", () => { - expect(Config.setLanguage("english")).toBe(true); - expect(Config.setLanguage("english_1k")).toBe(true); - - expect(Config.setLanguage("invalid" as any)).toBe(false); - }); - it("setKeymapLayout", () => { - expect(Config.setKeymapLayout("overrideSync")).toBe(true); - expect(Config.setKeymapLayout("override_sync" as any)).toBe(false); - expect(Config.setKeymapLayout("override sync" as any)).toBe(false); - expect(Config.setKeymapLayout("override-sync!" as any)).toBe(false); - }); - it("setLayout", () => { - expect(Config.setLayout("semimak")).toBe(true); - expect(Config.setLayout("default")).toBe(true); - expect(Config.setLayout("semi_mak" as any)).toBe(false); - expect(Config.setLayout("overrideSync" as any)).toBe(false); - }); - it("setFontSize", () => { - expect(Config.setFontSize(1)).toBe(true); - - expect(Config.setFontSize(0)).toBe(false); - expect(Config.setFontSize("5" as any)).toBe(false); - expect(Config.setFontSize("invalid" as any)).toBe(false); - }); - it("setMaxLineWidth", () => { - expect(Config.setMaxLineWidth(0)).toBe(true); - expect(Config.setMaxLineWidth(50)).toBe(true); - expect(Config.setMaxLineWidth(50.5)).toBe(true); - }); - it("setCustomBackground", () => { - expect(Config.setCustomBackground("http://example.com/test.png")).toBe( - true, - ); - expect(Config.setCustomBackground("https://www.example.com/test.gif")).toBe( - true, - ); - expect(Config.setCustomBackground("https://example.com/test.jpg")).toBe( - true, - ); - expect(Config.setCustomBackground("http://www.example.com/test.jpeg")).toBe( - true, - ); - - //gets converted - expect( - Config.setCustomBackground(" http://example.com/test.png "), - ).toBe(true); - - expect(Config.setCustomBackground("http://www.example.com/test.tiff")).toBe( - false, - ); - expect( - Config.setCustomBackground( - "http://www.example.com/test?test=foo&bar=baz", - ), - ).toBe(false); - expect(Config.setCustomBackground("invalid")).toBe(false); - }); - it("setQuoteLength", () => { - expect(Config.setQuoteLength([0])).toBe(true); - expect(Config.setQuoteLength([-3])).toBe(true); - expect(Config.setQuoteLength([3])).toBe(true); - - expect(Config.setQuoteLength(-4 as any)).toBe(false); - expect(Config.setQuoteLength(4 as any)).toBe(false); - expect(Config.setQuoteLength(3 as any)).toBe(false); - expect(Config.setQuoteLength(2 as any)).toBe(false); - - expect(Config.setQuoteLength([0, -3, 2])).toBe(true); - - expect(Config.setQuoteLength([-4 as any, 5 as any])).toBe(false); - }); - it("setCustomLayoutfluid", () => { - expect(Config.setCustomLayoutfluid(["qwerty", "qwertz"])).toBe(true); - - expect(Config.setCustomLayoutfluid(["qwerty"])).toBe(false); - expect(Config.setCustomLayoutfluid([])).toBe(false); - expect(Config.setCustomLayoutfluid("qwerty#qwertz" as any)).toBe(false); - expect(Config.setCustomLayoutfluid("invalid" as any)).toBe(false); - }); - describe("apply", () => { it("should fill missing values with defaults", async () => { //GIVEN replaceConfig({ mode: "words", }); - await Config.apply({ + await Config.applyConfig({ numbers: true, punctuation: true, }); @@ -833,7 +349,7 @@ describe("Config", () => { ]; it.each(testCases)("$display", async ({ value, expected }) => { - await Config.apply(value); + await Config.applyConfig(value); const config = getConfig(); const applied = Object.fromEntries( @@ -870,7 +386,7 @@ describe("Config", () => { ]; it.each(testCases)("$display", async ({ value, expected }) => { - await Config.apply(value); + await Config.applyConfig(value); const config = getConfig(); const applied = Object.fromEntries( Object.entries(config).filter(([key]) => @@ -885,7 +401,7 @@ describe("Config", () => { replaceConfig({ numbers: true, }); - await Config.apply({ + await Config.applyConfig({ ...Config.getConfigChanges(), punctuation: true, }); @@ -897,7 +413,7 @@ describe("Config", () => { replaceConfig({ minWpm: "off", }); - await Config.apply({ + await Config.applyConfig({ minWpmCustomSpeed: 100, }); const config = getConfig(); @@ -909,7 +425,7 @@ describe("Config", () => { replaceConfig({ minWpm: "off", }); - await Config.apply({ + await Config.applyConfig({ minWpm: "custom", minWpmCustomSpeed: 100, }); @@ -920,7 +436,7 @@ describe("Config", () => { it("should keep the keymap off when applying keymapLayout", async () => { replaceConfig({}); - await Config.apply({ + await Config.applyConfig({ keymapLayout: "qwerty", }); const config = getConfig(); @@ -929,24 +445,3 @@ describe("Config", () => { }); }); }); - -function customThemeColors(n: number): CustomThemeColors { - const arr = new Array(n).fill("#000") as CustomThemeColors; - arr[0] = "#123456"; // we have a protection against all colors being the same - return arr; -} - -function testBoolean(fn: (val: boolean) => boolean): void { - expect(fn(true)).toBe(true); - expect(fn(false)).toBe(true); - - expect(fn("true" as any)).toBe(false); - expect(fn("0" as any)).toBe(false); - expect(fn("invalid" as any)).toBe(false); -} - -function stringOfLength(length: number): string { - return randomBytes(Math.ceil(length / 2)) - .toString("hex") - .slice(0, length); -} diff --git a/frontend/__tests__/utils/url-handler.spec.ts b/frontend/__tests__/utils/url-handler.spec.ts index ee7e0ebe5781..81ef431804dd 100644 --- a/frontend/__tests__/utils/url-handler.spec.ts +++ b/frontend/__tests__/utils/url-handler.spec.ts @@ -19,33 +19,16 @@ describe("url-handler", () => { describe("loadTestSettingsFromUrl", () => { const findGetParameterMock = vi.spyOn(Misc, "findGetParameter"); - const setModeMock = vi.spyOn(UpdateConfig, "setMode"); - const setTimeConfigMock = vi.spyOn(UpdateConfig, "setTimeConfig"); - const setWordCountMock = vi.spyOn(UpdateConfig, "setWordCount"); - const setQuoteLengthMock = vi.spyOn(UpdateConfig, "setQuoteLength"); + const setConfigMock = vi.spyOn(UpdateConfig, "setConfig"); const setSelectedQuoteIdMock = vi.spyOn(TestState, "setSelectedQuoteId"); - const setPunctuationMock = vi.spyOn(UpdateConfig, "setPunctuation"); - const setNumbersMock = vi.spyOn(UpdateConfig, "setNumbers"); - const setLanguageMock = vi.spyOn(UpdateConfig, "setLanguage"); - const setDifficultyMock = vi.spyOn(UpdateConfig, "setDifficulty"); - const setFunboxMock = vi.spyOn(UpdateConfig, "setFunbox"); - const restartTestMock = vi.spyOn(TestLogic, "restart"); const addNotificationMock = vi.spyOn(Notifications, "add"); beforeEach(() => { [ + setConfigMock, findGetParameterMock, - setModeMock, - setTimeConfigMock, - setWordCountMock, - setQuoteLengthMock, setSelectedQuoteIdMock, - setPunctuationMock, - setNumbersMock, - setLanguageMock, - setDifficultyMock, - setFunboxMock, restartTestMock, addNotificationMock, ].forEach((it) => it.mockClear()); @@ -61,7 +44,7 @@ describe("url-handler", () => { loadTestSettingsFromUrl(""); //THEN - expect(setModeMock).not.toHaveBeenCalled(); + expect(setConfigMock).not.toHaveBeenCalled(); }); it("handles mode2 as number", () => { //GIVEN @@ -73,8 +56,8 @@ describe("url-handler", () => { loadTestSettingsFromUrl(""); //THEN - expect(setModeMock).toHaveBeenCalledWith("time", true); - expect(setTimeConfigMock).toHaveBeenCalledWith(60, true); + expect(setConfigMock).toHaveBeenCalledWith("mode", "time", true); + expect(setConfigMock).toHaveBeenCalledWith("time", 60, true); expect(restartTestMock).toHaveBeenCalled(); }); it("sets time", () => { @@ -87,8 +70,8 @@ describe("url-handler", () => { loadTestSettingsFromUrl(""); //THEN - expect(setModeMock).toHaveBeenCalledWith("time", true); - expect(setTimeConfigMock).toHaveBeenCalledWith(30, true); + expect(setConfigMock).toHaveBeenCalledWith("mode", "time", true); + expect(setConfigMock).toHaveBeenCalledWith("time", 30, true); expect(restartTestMock).toHaveBeenCalled(); }); it("sets word count", () => { @@ -101,8 +84,8 @@ describe("url-handler", () => { loadTestSettingsFromUrl(""); //THEN - expect(setModeMock).toHaveBeenCalledWith("words", true); - expect(setWordCountMock).toHaveBeenCalledWith(50, true); + expect(setConfigMock).toHaveBeenCalledWith("mode", "words", true); + expect(setConfigMock).toHaveBeenCalledWith("words", 50, true); expect(restartTestMock).toHaveBeenCalled(); }); it("sets quote length", () => { @@ -115,8 +98,8 @@ describe("url-handler", () => { loadTestSettingsFromUrl(""); //THEN - expect(setModeMock).toHaveBeenCalledWith("quote", true); - expect(setQuoteLengthMock).toHaveBeenCalledWith([-2], false); + expect(setConfigMock).toHaveBeenCalledWith("mode", "quote", true); + expect(setConfigMock).toHaveBeenCalledWith("quoteLength", [-2], false); expect(setSelectedQuoteIdMock).toHaveBeenCalledWith(512); expect(restartTestMock).toHaveBeenCalled(); }); @@ -128,7 +111,7 @@ describe("url-handler", () => { loadTestSettingsFromUrl(""); //THEN - expect(setPunctuationMock).toHaveBeenCalledWith(true, true); + expect(setConfigMock).toHaveBeenCalledWith("punctuation", true, true); expect(restartTestMock).toHaveBeenCalled(); }); it("sets numbers", () => { @@ -139,7 +122,7 @@ describe("url-handler", () => { loadTestSettingsFromUrl(""); //THEN - expect(setNumbersMock).toHaveBeenCalledWith(false, true); + expect(setConfigMock).toHaveBeenCalledWith("numbers", false, true); expect(restartTestMock).toHaveBeenCalled(); }); it("sets language", () => { @@ -150,7 +133,7 @@ describe("url-handler", () => { loadTestSettingsFromUrl(""); //THEN - expect(setLanguageMock).toHaveBeenCalledWith("english", true); + expect(setConfigMock).toHaveBeenCalledWith("language", "english", true); expect(restartTestMock).toHaveBeenCalled(); }); it("sets difficulty", () => { @@ -161,7 +144,7 @@ describe("url-handler", () => { loadTestSettingsFromUrl(""); //THEN - expect(setDifficultyMock).toHaveBeenCalledWith("master", true); + expect(setConfigMock).toHaveBeenCalledWith("difficulty", "master", true); expect(restartTestMock).toHaveBeenCalled(); }); it("sets funbox", () => { @@ -174,7 +157,11 @@ describe("url-handler", () => { loadTestSettingsFromUrl(""); //THEN - expect(setFunboxMock).toHaveBeenCalledWith(["crt", "choo_choo"], true); + expect(setConfigMock).toHaveBeenCalledWith( + "funbox", + ["crt", "choo_choo"], + true, + ); expect(restartTestMock).toHaveBeenCalled(); }); it("sets funbox legacy", () => { @@ -187,7 +174,11 @@ describe("url-handler", () => { loadTestSettingsFromUrl(""); //THEN - expect(setFunboxMock).toHaveBeenCalledWith(["crt", "choo_choo"], true); + expect(setConfigMock).toHaveBeenCalledWith( + "funbox", + ["crt", "choo_choo"], + true, + ); expect(restartTestMock).toHaveBeenCalled(); }); it("adds notification", () => { diff --git a/frontend/src/ts/auth.ts b/frontend/src/ts/auth.ts index 6286478316ca..8e33f5822057 100644 --- a/frontend/src/ts/auth.ts +++ b/frontend/src/ts/auth.ts @@ -1,6 +1,6 @@ import Ape from "./ape"; import * as Notifications from "./elements/notifications"; -import Config, * as UpdateConfig from "./config"; +import Config, { applyConfig, saveFullConfigToLocalStorage } from "./config"; import * as Misc from "./utils/misc"; import * as DB from "./db"; import * as Loader from "./elements/loader"; @@ -89,8 +89,8 @@ async function getDataAndInit(): Promise { console.log( "no local config or local and db configs are different - applying db", ); - await UpdateConfig.apply(snapshot.config); - UpdateConfig.saveFullConfigToLocalStorage(true); + await applyConfig(snapshot.config); + saveFullConfigToLocalStorage(true); //funboxes might be different and they wont activate on the account page for (const fb of getActiveFunboxesWithFunction("applyGlobalCSS")) { diff --git a/frontend/src/ts/commandline/lists.ts b/frontend/src/ts/commandline/lists.ts index dae0636b67cc..b7405613e294 100644 --- a/frontend/src/ts/commandline/lists.ts +++ b/frontend/src/ts/commandline/lists.ts @@ -17,7 +17,7 @@ import LoadChallengeCommands, { update as updateLoadChallengeCommands, } from "./lists/load-challenge"; -import Config, * as UpdateConfig from "../config"; +import Config, { applyConfigFromJson, setConfig } from "../config"; import * as Misc from "../utils/misc"; import * as JSONData from "../utils/json-data"; import { randomizeTheme } from "../controllers/theme-controller"; @@ -87,7 +87,7 @@ export const commands: CommandsSubgroup = { display: "Search for quotes", icon: "fa-search", exec: (): void => { - UpdateConfig.setMode("quote"); + setConfig("mode", "quote"); void QuoteSearchModal.show(); }, shouldFocusTestUI: false, @@ -246,7 +246,7 @@ export const commands: CommandsSubgroup = { input: true, exec: async ({ input }): Promise => { if (input === undefined || input === "") return; - await UpdateConfig.applyFromJson(input); + await applyConfigFromJson(input); }, }, { diff --git a/frontend/src/ts/commandline/lists/add-or-remove-theme-to-favorites.ts b/frontend/src/ts/commandline/lists/add-or-remove-theme-to-favorites.ts index 0ddd9462d212..f311f8fc05bd 100644 --- a/frontend/src/ts/commandline/lists/add-or-remove-theme-to-favorites.ts +++ b/frontend/src/ts/commandline/lists/add-or-remove-theme-to-favorites.ts @@ -1,5 +1,5 @@ import { ThemeName } from "@monkeytype/schemas/configs"; -import Config, * as UpdateConfig from "../../config"; +import Config, { setConfig } from "../../config"; import { randomTheme } from "../../controllers/theme-controller"; import { Command } from "../types"; @@ -18,7 +18,7 @@ const commands: Command[] = [ const { theme, favThemes, customTheme } = Config; const themeToUpdate = randomTheme ?? theme; if (!customTheme && !favThemes.includes(themeToUpdate as ThemeName)) { - UpdateConfig.setFavThemes([...favThemes, themeToUpdate as ThemeName]); + setConfig("favThemes", [...favThemes, themeToUpdate as ThemeName]); } }, }, @@ -37,7 +37,7 @@ const commands: Command[] = [ const { theme, favThemes, customTheme } = Config; const themeToUpdate = (randomTheme as ThemeName) ?? theme; if (!customTheme && favThemes.includes(themeToUpdate)) { - UpdateConfig.setFavThemes([ + setConfig("favThemes", [ ...favThemes.filter((t) => t !== themeToUpdate), ]); } diff --git a/frontend/src/ts/commandline/lists/background-filter.ts b/frontend/src/ts/commandline/lists/background-filter.ts index 31b9ccab843c..2958b4447e61 100644 --- a/frontend/src/ts/commandline/lists/background-filter.ts +++ b/frontend/src/ts/commandline/lists/background-filter.ts @@ -1,4 +1,4 @@ -import Config, * as UpdateConfig from "../../config"; +import Config, { setConfig } from "../../config"; import { Command, CommandsSubgroup } from "../types"; const subgroup: CommandsSubgroup = { @@ -17,7 +17,7 @@ const subgroup: CommandsSubgroup = { if (input === undefined || input === "") return; const newFilters = Config.customBackgroundFilter; newFilters[0] = parseFloat(input); - UpdateConfig.setCustomBackgroundFilter(newFilters); + setConfig("customBackgroundFilter", newFilters); }, }, { @@ -32,7 +32,7 @@ const subgroup: CommandsSubgroup = { if (input === undefined || input === "") return; const newFilters = Config.customBackgroundFilter; newFilters[1] = parseFloat(input); - UpdateConfig.setCustomBackgroundFilter(newFilters); + setConfig("customBackgroundFilter", newFilters); }, }, { @@ -47,7 +47,7 @@ const subgroup: CommandsSubgroup = { if (input === undefined || input === "") return; const newFilters = Config.customBackgroundFilter; newFilters[2] = parseFloat(input); - UpdateConfig.setCustomBackgroundFilter(newFilters); + setConfig("customBackgroundFilter", newFilters); }, }, { @@ -62,7 +62,7 @@ const subgroup: CommandsSubgroup = { if (input === undefined || input === "") return; const newFilters = Config.customBackgroundFilter; newFilters[3] = parseFloat(input); - UpdateConfig.setCustomBackgroundFilter(newFilters); + setConfig("customBackgroundFilter", newFilters); }, }, ], diff --git a/frontend/src/ts/commandline/lists/custom-themes-list.ts b/frontend/src/ts/commandline/lists/custom-themes-list.ts index ca6aae004754..fe112c258ac9 100644 --- a/frontend/src/ts/commandline/lists/custom-themes-list.ts +++ b/frontend/src/ts/commandline/lists/custom-themes-list.ts @@ -1,4 +1,4 @@ -import * as UpdateConfig from "../../config"; +import { setConfig } from "../../config"; import { isAuthenticated } from "../../firebase"; import * as DB from "../../db"; import * as ThemeController from "../../controllers/theme-controller"; @@ -50,9 +50,8 @@ export function update(): void { ThemeController.preview("custom", theme.colors); }, exec: (): void => { - // UpdateConfig.setCustomThemeId(theme._id); - UpdateConfig.setCustomTheme(true); - UpdateConfig.setCustomThemeColors(theme.colors); + setConfig("customTheme", true); + setConfig("customThemeColors", theme.colors); }, }); } diff --git a/frontend/src/ts/commandline/lists/font-family.ts b/frontend/src/ts/commandline/lists/font-family.ts index c60293147428..9b8141b1b17d 100644 --- a/frontend/src/ts/commandline/lists/font-family.ts +++ b/frontend/src/ts/commandline/lists/font-family.ts @@ -4,7 +4,7 @@ import FileStorage from "../../utils/file-storage"; import { applyFontFamily } from "../../controllers/theme-controller"; import { updateUI } from "../../elements/settings/custom-font-picker"; import * as Notifications from "../../elements/notifications"; -import Config, * as UpdateConfig from "../../config"; +import Config, { setConfig } from "../../config"; const fromMeta = buildCommandForConfigKey("fontFamily"); @@ -29,7 +29,7 @@ if (fromMeta.subgroup) { exec: ({ input }): void => { if (input === undefined || input === "") return; const fontName = input.replaceAll(/ /g, "_"); - UpdateConfig.setFontFamily(fontName); + setConfig("fontFamily", fontName); }, }, { diff --git a/frontend/src/ts/commandline/lists/min-burst.ts b/frontend/src/ts/commandline/lists/min-burst.ts index c2468aa0093e..bf8480f3987b 100644 --- a/frontend/src/ts/commandline/lists/min-burst.ts +++ b/frontend/src/ts/commandline/lists/min-burst.ts @@ -1,4 +1,4 @@ -import Config, * as UpdateConfig from "../../config"; +import Config, { setConfig } from "../../config"; import { get as getTypingSpeedUnit } from "../../utils/typing-speed-units"; import { Command, CommandsSubgroup } from "../types"; @@ -11,7 +11,7 @@ const subgroup: CommandsSubgroup = { display: "off", configValue: "off", exec: (): void => { - UpdateConfig.setMinBurst("off"); + setConfig("minBurst", "off"); }, }, { @@ -21,11 +21,11 @@ const subgroup: CommandsSubgroup = { input: true, exec: ({ input }): void => { if (input === undefined || input === "") return; - UpdateConfig.setMinBurst("fixed"); + setConfig("minBurst", "fixed"); const newVal = getTypingSpeedUnit(Config.typingSpeedUnit).toWpm( parseInt(input), ); - UpdateConfig.setMinBurstCustomSpeed(newVal); + setConfig("minBurstCustomSpeed", newVal); }, }, { @@ -35,11 +35,11 @@ const subgroup: CommandsSubgroup = { input: true, exec: ({ input }): void => { if (input === undefined || input === "") return; - UpdateConfig.setMinBurst("flex"); + setConfig("minBurst", "flex"); const newVal = getTypingSpeedUnit(Config.typingSpeedUnit).toWpm( parseInt(input), ); - UpdateConfig.setMinBurstCustomSpeed(newVal); + setConfig("minBurstCustomSpeed", newVal); }, }, ], diff --git a/frontend/src/ts/commandline/lists/themes.ts b/frontend/src/ts/commandline/lists/themes.ts index 701a230c9936..dc2e765f6a83 100644 --- a/frontend/src/ts/commandline/lists/themes.ts +++ b/frontend/src/ts/commandline/lists/themes.ts @@ -1,4 +1,4 @@ -import Config, * as UpdateConfig from "../../config"; +import Config, { setConfig } from "../../config"; import { capitalizeFirstLetterOfEachWord } from "../../utils/strings"; import * as ThemeController from "../../controllers/theme-controller"; import { Command, CommandsSubgroup } from "../types"; @@ -33,7 +33,7 @@ const createThemeCommand = (theme: Theme): Command => { ThemeController.preview(theme.name); }, exec: (): void => { - UpdateConfig.setTheme(theme.name); + setConfig("theme", theme.name); }, }; }; diff --git a/frontend/src/ts/config.ts b/frontend/src/ts/config.ts index 681c83401f7c..86ab103c5a6c 100644 --- a/frontend/src/ts/config.ts +++ b/frontend/src/ts/config.ts @@ -17,8 +17,6 @@ import { } from "./utils/misc"; import * as ConfigSchemas from "@monkeytype/schemas/configs"; import { Config, FunboxName } from "@monkeytype/schemas/configs"; -import { Mode } from "@monkeytype/schemas/shared"; -import { Language } from "@monkeytype/schemas/languages"; import { LocalStorageWithSchema } from "./utils/local-storage-with-schema"; import { migrateConfig } from "./utils/config"; import { getDefaultConfig } from "./constants/default-config"; @@ -26,7 +24,6 @@ import { parseWithSchema as parseJsonWithSchema } from "@monkeytype/util/json"; import { ZodSchema } from "zod"; import * as TestState from "./test/test-state"; import { ConfigMetadataObject, configMetadata } from "./config-metadata"; -import { FontName } from "@monkeytype/schemas/fonts"; const configLS = new LocalStorageWithSchema({ key: "config", @@ -197,71 +194,6 @@ export function setConfig( return true; } -//numbers -export function setNumbers(numb: boolean, nosave?: boolean): boolean { - return setConfig("numbers", numb, nosave); -} - -//punctuation -export function setPunctuation(punc: boolean, nosave?: boolean): boolean { - return setConfig("punctuation", punc, nosave); -} - -export function setMode(mode: Mode, nosave?: boolean): boolean { - return setConfig("mode", mode, nosave); -} - -export function setPlaySoundOnError( - val: ConfigSchemas.PlaySoundOnError, - nosave?: boolean, -): boolean { - return setConfig("playSoundOnError", val, nosave); -} - -export function setPlaySoundOnClick( - val: ConfigSchemas.PlaySoundOnClick, - nosave?: boolean, -): boolean { - return setConfig("playSoundOnClick", val, nosave); -} - -export function setSoundVolume( - val: ConfigSchemas.SoundVolume, - nosave?: boolean, -): boolean { - return setConfig("soundVolume", val, nosave); -} - -export function setPlayTimeWarning( - value: ConfigSchemas.PlayTimeWarning, - nosave?: boolean, -): boolean { - return setConfig("playTimeWarning", value, nosave); -} - -//difficulty -export function setDifficulty( - diff: ConfigSchemas.Difficulty, - nosave?: boolean, -): boolean { - return setConfig("difficulty", diff, nosave); -} - -//set fav themes -export function setFavThemes( - themes: ConfigSchemas.FavThemes, - nosave?: boolean, -): boolean { - return setConfig("favThemes", themes, nosave); -} - -export function setFunbox( - funbox: ConfigSchemas.Funbox, - nosave?: boolean, -): boolean { - return setConfig("funbox", funbox, nosave); -} - export function toggleFunbox(funbox: FunboxName, nosave?: boolean): boolean { if (isConfigChangeBlocked()) return false; @@ -289,524 +221,10 @@ export function toggleFunbox(funbox: FunboxName, nosave?: boolean): boolean { return true; } -export function setBlindMode(blind: boolean, nosave?: boolean): boolean { - return setConfig("blindMode", blind, nosave); -} - -export function setAccountChart( - array: ConfigSchemas.AccountChart, - nosave?: boolean, -): boolean { - return setConfig("accountChart", array, nosave); -} - -export function setStopOnError( - soe: ConfigSchemas.StopOnError, - nosave?: boolean, -): boolean { - return setConfig("stopOnError", soe, nosave); -} - -export function setAlwaysShowDecimalPlaces( - val: boolean, - nosave?: boolean, -): boolean { - return setConfig("alwaysShowDecimalPlaces", val, nosave); -} - -export function setTypingSpeedUnit( - val: ConfigSchemas.TypingSpeedUnit, - nosave?: boolean, -): boolean { - return setConfig("typingSpeedUnit", val, nosave); -} - -export function setShowOutOfFocusWarning( - val: boolean, - nosave?: boolean, -): boolean { - return setConfig("showOutOfFocusWarning", val, nosave); -} - -//pace caret -export function setPaceCaret( - val: ConfigSchemas.PaceCaret, - nosave?: boolean, -): boolean { - return setConfig("paceCaret", val, nosave); -} - -export function setPaceCaretCustomSpeed( - val: ConfigSchemas.PaceCaretCustomSpeed, - nosave?: boolean, -): boolean { - return setConfig("paceCaretCustomSpeed", val, nosave); -} - -export function setRepeatedPace(pace: boolean, nosave?: boolean): boolean { - return setConfig("repeatedPace", pace, nosave); -} - -//min wpm -export function setMinWpm( - minwpm: ConfigSchemas.MinimumWordsPerMinute, - nosave?: boolean, -): boolean { - return setConfig("minWpm", minwpm, nosave); -} - -export function setMinWpmCustomSpeed( - val: ConfigSchemas.MinWpmCustomSpeed, - nosave?: boolean, -): boolean { - return setConfig("minWpmCustomSpeed", val, nosave); -} - -//min acc -export function setMinAcc( - min: ConfigSchemas.MinimumAccuracy, - nosave?: boolean, -): boolean { - return setConfig("minAcc", min, nosave); -} - -export function setMinAccCustom( - val: ConfigSchemas.MinimumAccuracyCustom, - nosave?: boolean, -): boolean { - return setConfig("minAccCustom", val, nosave); -} - -//min burst -export function setMinBurst( - min: ConfigSchemas.MinimumBurst, - nosave?: boolean, -): boolean { - return setConfig("minBurst", min, nosave); -} - -export function setMinBurstCustomSpeed( - val: ConfigSchemas.MinimumBurstCustomSpeed, - nosave?: boolean, -): boolean { - return setConfig("minBurstCustomSpeed", val, nosave); -} - -//always show words history -export function setAlwaysShowWordsHistory( - val: boolean, - nosave?: boolean, -): boolean { - return setConfig("alwaysShowWordsHistory", val, nosave); -} - -//single list command line -export function setSingleListCommandLine( - option: ConfigSchemas.SingleListCommandLine, - nosave?: boolean, -): boolean { - return setConfig("singleListCommandLine", option, nosave); -} - -//caps lock warning -export function setCapsLockWarning(val: boolean, nosave?: boolean): boolean { - return setConfig("capsLockWarning", val, nosave); -} - -export function setShowAllLines(sal: boolean, nosave?: boolean): boolean { - return setConfig("showAllLines", sal, nosave); -} - -export function setQuickEnd(qe: boolean, nosave?: boolean): boolean { - return setConfig("quickEnd", qe, nosave); -} - -export function setAds(val: ConfigSchemas.Ads, nosave?: boolean): boolean { - return setConfig("ads", val, nosave); -} - -export function setRepeatQuotes( - val: ConfigSchemas.RepeatQuotes, - nosave?: boolean, -): boolean { - return setConfig("repeatQuotes", val, nosave); -} - -//flip colors -export function setFlipTestColors(flip: boolean, nosave?: boolean): boolean { - return setConfig("flipTestColors", flip, nosave); -} - -//extra color -export function setColorfulMode(extra: boolean, nosave?: boolean): boolean { - return setConfig("colorfulMode", extra, nosave); -} - -//strict space -export function setStrictSpace(val: boolean, nosave?: boolean): boolean { - return setConfig("strictSpace", val, nosave); -} - -//opposite shift space -export function setOppositeShiftMode( - val: ConfigSchemas.OppositeShiftMode, - nosave?: boolean, -): boolean { - return setConfig("oppositeShiftMode", val, nosave); -} - -export function setCaretStyle( - caretStyle: ConfigSchemas.CaretStyle, - nosave?: boolean, -): boolean { - return setConfig("caretStyle", caretStyle, nosave); -} - -export function setPaceCaretStyle( - caretStyle: ConfigSchemas.CaretStyle, - nosave?: boolean, -): boolean { - return setConfig("paceCaretStyle", caretStyle, nosave); -} - -export function setShowAverage( - value: ConfigSchemas.ShowAverage, - nosave?: boolean, -): boolean { - return setConfig("showAverage", value, nosave); -} - -export function setHighlightMode( - mode: ConfigSchemas.HighlightMode, - nosave?: boolean, -): boolean { - return setConfig("highlightMode", mode, nosave); -} - -export function setTapeMode( - mode: ConfigSchemas.TapeMode, - nosave?: boolean, -): boolean { - return setConfig("tapeMode", mode, nosave); -} - -export function setTapeMargin( - value: ConfigSchemas.TapeMargin, - nosave?: boolean, -): boolean { - return setConfig("tapeMargin", value, nosave); -} - -export function setHideExtraLetters(val: boolean, nosave?: boolean): boolean { - return setConfig("hideExtraLetters", val, nosave); -} - -export function setTimerStyle( - style: ConfigSchemas.TimerStyle, - nosave?: boolean, -): boolean { - return setConfig("timerStyle", style, nosave); -} - -export function setLiveSpeedStyle( - style: ConfigSchemas.LiveSpeedAccBurstStyle, - nosave?: boolean, -): boolean { - return setConfig("liveSpeedStyle", style, nosave); -} - -export function setLiveAccStyle( - style: ConfigSchemas.LiveSpeedAccBurstStyle, - nosave?: boolean, -): boolean { - return setConfig("liveAccStyle", style, nosave); -} - -export function setLiveBurstStyle( - style: ConfigSchemas.LiveSpeedAccBurstStyle, - nosave?: boolean, -): boolean { - return setConfig("liveBurstStyle", style, nosave); -} - -export function setTimerColor( - color: ConfigSchemas.TimerColor, - nosave?: boolean, -): boolean { - return setConfig("timerColor", color, nosave); -} -export function setTimerOpacity( - opacity: ConfigSchemas.TimerOpacity, - nosave?: boolean, -): boolean { - return setConfig("timerOpacity", opacity, nosave); -} - -//key tips -export function setKeyTips(keyTips: boolean, nosave?: boolean): boolean { - return setConfig("showKeyTips", keyTips, nosave); -} - -//mode -export function setTimeConfig( - time: ConfigSchemas.TimeConfig, - nosave?: boolean, -): boolean { - return setConfig("time", time, nosave); -} - -export function setQuoteLength( - len: ConfigSchemas.QuoteLengthConfig, - nosave?: boolean, -): boolean { - return setConfig("quoteLength", len, nosave); -} - export function setQuoteLengthAll(nosave?: boolean): boolean { return setConfig("quoteLength", [0, 1, 2, 3], nosave); } -export function setWordCount( - wordCount: ConfigSchemas.WordCount, - nosave?: boolean, -): boolean { - return setConfig("words", wordCount, nosave); -} - -//caret -export function setSmoothCaret( - mode: ConfigSchemas.SmoothCaret, - nosave?: boolean, -): boolean { - return setConfig("smoothCaret", mode, nosave); -} - -export function setCodeUnindentOnBackspace( - mode: boolean, - nosave?: boolean, -): boolean { - return setConfig("codeUnindentOnBackspace", mode, nosave); -} - -export function setStartGraphsAtZero(mode: boolean, nosave?: boolean): boolean { - return setConfig("startGraphsAtZero", mode, nosave); -} - -//linescroll -export function setSmoothLineScroll(mode: boolean, nosave?: boolean): boolean { - return setConfig("smoothLineScroll", mode, nosave); -} - -//quick restart -export function setQuickRestartMode( - mode: ConfigSchemas.QuickRestart, - nosave?: boolean, -): boolean { - return setConfig("quickRestart", mode, nosave); -} - -//font family -export function setFontFamily(font: FontName, nosave?: boolean): boolean { - return setConfig("fontFamily", font, nosave); -} - -//freedom -export function setFreedomMode(freedom: boolean, nosave?: boolean): boolean { - return setConfig("freedomMode", freedom, nosave); -} - -export function setConfidenceMode( - cm: ConfigSchemas.ConfidenceMode, - nosave?: boolean, -): boolean { - return setConfig("confidenceMode", cm, nosave); -} - -export function setIndicateTypos( - value: ConfigSchemas.IndicateTypos, - nosave?: boolean, -): boolean { - return setConfig("indicateTypos", value, nosave); -} - -export function setCompositionDisplay( - value: ConfigSchemas.CompositionDisplay, - nosave?: boolean, -): boolean { - return setConfig("compositionDisplay", value, nosave); -} - -export function setAutoSwitchTheme( - boolean: boolean, - nosave?: boolean, -): boolean { - return setConfig("autoSwitchTheme", boolean, nosave); -} - -export function setCustomTheme(boolean: boolean, nosave?: boolean): boolean { - return setConfig("customTheme", boolean, nosave); -} - -export function setTheme( - name: ConfigSchemas.ThemeName, - nosave?: boolean, -): boolean { - return setConfig("theme", name, nosave); -} - -export function setThemeLight( - name: ConfigSchemas.ThemeName, - nosave?: boolean, -): boolean { - return setConfig("themeLight", name, nosave); -} - -export function setThemeDark( - name: ConfigSchemas.ThemeName, - nosave?: boolean, -): boolean { - return setConfig("themeDark", name, nosave); -} - -export function setRandomTheme( - val: ConfigSchemas.RandomTheme, - nosave?: boolean, -): boolean { - return setConfig("randomTheme", val, nosave); -} - -export function setBritishEnglish(val: boolean, nosave?: boolean): boolean { - return setConfig("britishEnglish", val, nosave); -} - -export function setLazyMode(val: boolean, nosave?: boolean): boolean { - return setConfig("lazyMode", val, nosave); -} - -export function setCustomThemeColors( - colors: ConfigSchemas.CustomThemeColors, - nosave?: boolean, -): boolean { - return setConfig("customThemeColors", colors, nosave); -} - -export function setLanguage(language: Language, nosave?: boolean): boolean { - return setConfig("language", language, nosave); -} - -export function setMonkey(monkey: boolean, nosave?: boolean): boolean { - return setConfig("monkey", monkey, nosave); -} - -export function setKeymapMode( - mode: ConfigSchemas.KeymapMode, - nosave?: boolean, -): boolean { - return setConfig("keymapMode", mode, nosave); -} - -export function setKeymapLegendStyle( - style: ConfigSchemas.KeymapLegendStyle, - nosave?: boolean, -): boolean { - return setConfig("keymapLegendStyle", style, nosave); -} - -export function setKeymapStyle( - style: ConfigSchemas.KeymapStyle, - nosave?: boolean, -): boolean { - return setConfig("keymapStyle", style, nosave); -} - -export function setKeymapLayout( - layout: ConfigSchemas.KeymapLayout, - nosave?: boolean, -): boolean { - return setConfig("keymapLayout", layout, nosave); -} - -export function setKeymapShowTopRow( - show: ConfigSchemas.KeymapShowTopRow, - nosave?: boolean, -): boolean { - return setConfig("keymapShowTopRow", show, nosave); -} - -export function setKeymapSize( - keymapSize: ConfigSchemas.KeymapSize, - nosave?: boolean, -): boolean { - return setConfig("keymapSize", keymapSize, nosave); -} - -export function setLayout( - layout: ConfigSchemas.Layout, - nosave?: boolean, -): boolean { - return setConfig("layout", layout, nosave); -} - -export function setFontSize( - fontSize: ConfigSchemas.FontSize, - nosave?: boolean, -): boolean { - return setConfig("fontSize", fontSize, nosave); -} - -export function setMaxLineWidth( - maxLineWidth: ConfigSchemas.MaxLineWidth, - nosave?: boolean, -): boolean { - return setConfig("maxLineWidth", maxLineWidth, nosave); -} - -export function setCustomBackground( - value: ConfigSchemas.CustomBackground, - nosave?: boolean, -): boolean { - return setConfig("customBackground", value, nosave); -} - -export function setCustomLayoutfluid( - value: ConfigSchemas.CustomLayoutFluid, - nosave?: boolean, -): boolean { - return setConfig("customLayoutfluid", value, nosave); -} - -export function setCustomPolyglot( - value: ConfigSchemas.CustomPolyglot, - nosave?: boolean, -): boolean { - return setConfig("customPolyglot", value, nosave); -} - -export function setCustomBackgroundSize( - value: ConfigSchemas.CustomBackgroundSize, - nosave?: boolean, -): boolean { - return setConfig("customBackgroundSize", value, nosave); -} - -export function setCustomBackgroundFilter( - array: ConfigSchemas.CustomBackgroundFilter, - nosave?: boolean, -): boolean { - return setConfig("customBackgroundFilter", array, nosave); -} - -export function setMonkeyPowerLevel( - level: ConfigSchemas.MonkeyPowerLevel, - nosave?: boolean, -): boolean { - return setConfig("monkeyPowerLevel", level, nosave); -} - -export function setBurstHeatmap(value: boolean, nosave?: boolean): boolean { - return setConfig("burstHeatmap", value, nosave); -} - const lastConfigsToApply: Set = new Set([ "keymapMode", "minWpm", @@ -822,7 +240,9 @@ const lastConfigsToApply: Set = new Set([ "funbox", ]); -export async function apply(partialConfig: Partial): Promise { +export async function applyConfig( + partialConfig: Partial, +): Promise { if (partialConfig === undefined || partialConfig === null) return; //migrate old values if needed, remove additional keys and merge with default config @@ -866,8 +286,8 @@ export async function apply(partialConfig: Partial): Promise { ConfigEvent.dispatch("fullConfigChangeFinished"); } -export async function reset(): Promise { - await apply(getDefaultConfig()); +export async function resetConfig(): Promise { + await applyConfig(getDefaultConfig()); await DB.resetConfig(); saveFullConfigToLocalStorage(true); } @@ -876,9 +296,9 @@ export async function loadFromLocalStorage(): Promise { console.log("loading localStorage config"); const newConfig = configLS.get(); if (newConfig === undefined) { - await reset(); + await resetConfig(); } else { - await apply(newConfig); + await applyConfig(newConfig); saveFullConfigToLocalStorage(true); } loadDone(); @@ -897,7 +317,7 @@ export function getConfigChanges(): Partial { return configChanges; } -export async function applyFromJson(json: string): Promise { +export async function applyConfigFromJson(json: string): Promise { try { const parsedConfig = parseJsonWithSchema( json, @@ -911,7 +331,7 @@ export async function applyFromJson(json: string): Promise { }, }, ); - await apply(parsedConfig); + await applyConfig(parsedConfig); saveFullConfigToLocalStorage(); Notifications.add("Done", 1); } catch (e) { @@ -921,9 +341,10 @@ export async function applyFromJson(json: string): Promise { } } -const { promise: loadPromise, resolve: loadDone } = promiseWithResolvers(); +const { promise: configLoadPromise, resolve: loadDone } = + promiseWithResolvers(); -export { loadPromise }; +export { configLoadPromise }; export default config; export const __testing = { configMetadata, diff --git a/frontend/src/ts/controllers/challenge-controller.ts b/frontend/src/ts/controllers/challenge-controller.ts index eb6eccdcba76..1f42debd143b 100644 --- a/frontend/src/ts/controllers/challenge-controller.ts +++ b/frontend/src/ts/controllers/challenge-controller.ts @@ -4,7 +4,7 @@ import * as Notifications from "../elements/notifications"; import * as ManualRestart from "../test/manual-restart-tracker"; import * as CustomText from "../test/custom-text"; import * as Funbox from "../test/funbox/funbox"; -import Config, * as UpdateConfig from "../config"; +import Config, { setConfig } from "../config"; import * as ConfigEvent from "../observables/config-event"; import * as TestState from "../test/test-state"; import * as Loader from "../elements/loader"; @@ -212,7 +212,7 @@ export function verify(result: CompletedEvent): string | null { export async function setup(challengeName: string): Promise { challengeLoading = true; - UpdateConfig.setFunbox([]); + setConfig("funbox", []); const { data: list, error } = await tryCatch(JSONData.getChallengeList()); if (error) { @@ -241,26 +241,26 @@ export async function setup(challengeName: string): Promise { return false; } if (challenge.type === "customTime") { - UpdateConfig.setTimeConfig(challenge.parameters[0] as number, true); - UpdateConfig.setMode("time", true); - UpdateConfig.setDifficulty("normal", true); + setConfig("time", challenge.parameters[0] as number, true); + setConfig("mode", "time", true); + setConfig("difficulty", "normal", true); if (challenge.name === "englishMaster") { - UpdateConfig.setLanguage("english_10k", true); - UpdateConfig.setNumbers(true, true); - UpdateConfig.setPunctuation(true, true); + setConfig("language", "english_10k", true); + setConfig("numbers", true, true); + setConfig("punctuation", true, true); } } else if (challenge.type === "customWords") { - UpdateConfig.setWordCount(challenge.parameters[0] as number, true); - UpdateConfig.setMode("words", true); - UpdateConfig.setDifficulty("normal", true); + setConfig("words", challenge.parameters[0] as number, true); + setConfig("mode", "words", true); + setConfig("difficulty", "normal", 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); - UpdateConfig.setMode("custom", true); - UpdateConfig.setDifficulty("normal", true); + setConfig("mode", "custom", true); + setConfig("difficulty", "normal", true); } else if (challenge.type === "script") { Loader.show(); const response = await fetch( @@ -278,41 +278,41 @@ export async function setup(challengeName: string): Promise { CustomText.setMode("repeat"); CustomText.setLimitMode("word"); CustomText.setPipeDelimiter(false); - UpdateConfig.setMode("custom", true); - UpdateConfig.setDifficulty("normal", true); + setConfig("mode", "custom", true); + setConfig("difficulty", "normal", true); if (challenge.parameters[1] !== null) { - UpdateConfig.setTheme(challenge.parameters[1] as ThemeName); + setConfig("theme", challenge.parameters[1] as ThemeName); } if (challenge.parameters[2] !== null) { void Funbox.activate(challenge.parameters[2] as FunboxName[]); } } else if (challenge.type === "accuracy") { - UpdateConfig.setTimeConfig(0, true); - UpdateConfig.setMode("time", true); - UpdateConfig.setDifficulty("master", true); + setConfig("time", 0, true); + setConfig("mode", "time", true); + setConfig("difficulty", "master", true); } else if (challenge.type === "funbox") { - UpdateConfig.setFunbox(challenge.parameters[0] as FunboxName[], true); - UpdateConfig.setDifficulty("normal", true); + setConfig("funbox", challenge.parameters[0] as FunboxName[], true); + setConfig("difficulty", "normal", true); if (challenge.parameters[1] === "words") { - UpdateConfig.setWordCount(challenge.parameters[2] as number, true); + setConfig("words", challenge.parameters[2] as number, true); } else if (challenge.parameters[1] === "time") { - UpdateConfig.setTimeConfig(challenge.parameters[2] as number, true); + setConfig("time", challenge.parameters[2] as number, true); } - UpdateConfig.setMode(challenge.parameters[1] as Mode, true); + setConfig("mode", challenge.parameters[1] as Mode, true); if (challenge.parameters[3] !== undefined) { - UpdateConfig.setDifficulty(challenge.parameters[3] as Difficulty, true); + setConfig("difficulty", challenge.parameters[3] as Difficulty, 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? - UpdateConfig.setMode("time", true); - UpdateConfig.setTimeConfig(120, true); - UpdateConfig.setLanguage("english_10k", true); - UpdateConfig.setPunctuation(true, true); - UpdateConfig.setStopOnError("word", true); - UpdateConfig.setLayout("semimak", true); - UpdateConfig.setKeymapLayout("overrideSync", true); - UpdateConfig.setKeymapMode("static", true); + 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); } } ManualRestart.set(); diff --git a/frontend/src/ts/controllers/preset-controller.ts b/frontend/src/ts/controllers/preset-controller.ts index 5ab2496ec330..6466e88a0313 100644 --- a/frontend/src/ts/controllers/preset-controller.ts +++ b/frontend/src/ts/controllers/preset-controller.ts @@ -1,5 +1,5 @@ import { Preset } from "@monkeytype/schemas/presets"; -import Config, * as UpdateConfig from "../config"; +import Config, { applyConfig, saveFullConfigToLocalStorage } from "../config"; import * as DB from "../db"; import * as Notifications from "../elements/notifications"; import * as TestLogic from "../test/test-logic"; @@ -16,12 +16,12 @@ export async function apply(_id: string): Promise { } if (isPartialPreset(presetToApply)) { - await UpdateConfig.apply({ + await applyConfig({ ...structuredClone(Config), ...presetToApply.config, }); } else { - await UpdateConfig.apply(presetToApply.config); + await applyConfig(presetToApply.config); } if ( @@ -40,7 +40,7 @@ export async function apply(_id: string): Promise { Notifications.add("Preset applied", 1, { duration: 2, }); - UpdateConfig.saveFullConfigToLocalStorage(); + saveFullConfigToLocalStorage(); } function isPartialPreset(preset: SnapshotPreset): boolean { return preset.settingGroups !== undefined && preset.settingGroups !== null; diff --git a/frontend/src/ts/controllers/theme-controller.ts b/frontend/src/ts/controllers/theme-controller.ts index e05397dc5fbc..0a6817f15257 100644 --- a/frontend/src/ts/controllers/theme-controller.ts +++ b/frontend/src/ts/controllers/theme-controller.ts @@ -3,7 +3,7 @@ import * as ChartController from "./chart-controller"; import * as Misc from "../utils/misc"; import * as Arrays from "../utils/arrays"; import { isColorDark, isColorLight } from "../utils/colors"; -import Config, { setAutoSwitchTheme, setCustomTheme } from "../config"; +import Config, { setConfig } from "../config"; import * as BackgroundFilter from "../elements/custom-background-filter"; import * as ConfigEvent from "../observables/config-event"; import * as DB from "../db"; @@ -144,7 +144,7 @@ export async function loadStyle(name: string): Promise { // const customThemes = DB.getSnapshot().customThemes; // const colors = customThemes.find((e) => e._id === themeId) // ?.colors as string[]; -// UpdateConfig.setCustomThemeColors(colors, nosave); +// UpdateConfig.setConfig("customThemeColors", colors,nosave); // } async function apply( @@ -273,7 +273,7 @@ async function set( await apply(themeIdentifier, undefined, isAutoSwitch); if (!isAutoSwitch && Config.autoSwitchTheme) { - setAutoSwitchTheme(false); + setConfig("autoSwitchTheme", false); Notifications.add("Auto switch theme disabled", 0); } } @@ -352,7 +352,7 @@ export async function randomizeTheme(): Promise { randomTheme = "custom"; } - setCustomTheme(false, true); + setConfig("customTheme", false, 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 fc764871ba32..b8c8ff0eb11d 100644 --- a/frontend/src/ts/elements/custom-background-filter.ts +++ b/frontend/src/ts/elements/custom-background-filter.ts @@ -1,5 +1,5 @@ import { CustomBackgroundFilter } from "@monkeytype/schemas/configs"; -import * as UpdateConfig from "../config"; +import { setConfig } from "../config"; import * as ConfigEvent from "../observables/config-event"; import { debounce } from "throttle-debounce"; import { qs, qsr } from "../utils/dom"; @@ -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; - UpdateConfig.setCustomBackgroundFilter(arr, false); + setConfig("customBackgroundFilter", arr, false); }); ConfigEvent.subscribe((eventKey, eventValue) => { diff --git a/frontend/src/ts/elements/keymap.ts b/frontend/src/ts/elements/keymap.ts index 555bcbfdcd6f..decdebe6fbec 100644 --- a/frontend/src/ts/elements/keymap.ts +++ b/frontend/src/ts/elements/keymap.ts @@ -480,7 +480,7 @@ export async function refresh(): Promise { console.log( "something went wrong when changing layout, resettings: " + e.message, ); - // UpdateConfig.setKeymapLayout("qwerty", true); + // UpdateConfig.setConfig("keymapLayout", "qwerty",true); } } } diff --git a/frontend/src/ts/elements/settings/settings-group.ts b/frontend/src/ts/elements/settings/settings-group.ts index 3651181cac9e..9eecde00ca1a 100644 --- a/frontend/src/ts/elements/settings/settings-group.ts +++ b/frontend/src/ts/elements/settings/settings-group.ts @@ -1,6 +1,6 @@ import { Config as ConfigType, ConfigKey } from "@monkeytype/schemas/configs"; -import Config from "../../config"; +import Config, { setConfig } from "../../config"; import * as Notifications from "../notifications"; import SlimSelect from "slim-select"; import { debounce } from "throttle-debounce"; @@ -32,7 +32,6 @@ export default class SettingsGroup { constructor( configName: K, - configFunction: (param: T, nosave?: boolean) => boolean, mode: Mode, options?: { setCallback?: () => void; @@ -46,7 +45,8 @@ export default class SettingsGroup { ) { this.configName = configName; this.mode = mode; - this.configFunction = configFunction; + this.configFunction = (param, nosave) => + setConfig(configName, param as ConfigType[K], nosave); this.setCallback = options?.setCallback; this.updateCallback = options?.updateCallback; this.validation = options?.validation; diff --git a/frontend/src/ts/elements/settings/theme-picker.ts b/frontend/src/ts/elements/settings/theme-picker.ts index 9ecbfe6e5234..fe166c772732 100644 --- a/frontend/src/ts/elements/settings/theme-picker.ts +++ b/frontend/src/ts/elements/settings/theme-picker.ts @@ -1,4 +1,4 @@ -import Config, * as UpdateConfig from "../../config"; +import Config, { setConfig, saveFullConfigToLocalStorage } from "../../config"; import * as ThemeController from "../../controllers/theme-controller"; import * as Misc from "../../utils/misc"; import * as Colors from "../../utils/colors"; @@ -272,14 +272,17 @@ export function setCustomInputs(noThemeUpdate = false): void { function toggleFavourite(themeName: ThemeName): void { if (Config.favThemes.includes(themeName)) { // already favourite, remove - UpdateConfig.setFavThemes(Config.favThemes.filter((t) => t !== themeName)); + setConfig( + "favThemes", + Config.favThemes.filter((t) => t !== themeName), + ); } else { // add to favourites const newList: ThemeName[] = Config.favThemes; newList.push(themeName); - UpdateConfig.setFavThemes(newList); + setConfig("favThemes", newList); } - UpdateConfig.saveFullConfigToLocalStorage(); + saveFullConfigToLocalStorage(); } function saveCustomThemeColors(): void { @@ -291,7 +294,7 @@ function saveCustomThemeColors(): void { ) as string, ); } - UpdateConfig.setCustomThemeColors(newColors as CustomThemeColors); + setConfig("customThemeColors", newColors as CustomThemeColors); Notifications.add("Custom theme saved", 1); } @@ -344,9 +347,9 @@ $(".pageSettings .section.themes .tabs button").on("click", (e) => { // setCustomInputs(); //test if ($target.attr("data-tab") === "preset") { - UpdateConfig.setCustomTheme(false); + setConfig("customTheme", false); } else { - UpdateConfig.setCustomTheme(true); + setConfig("customTheme", true); } }); @@ -369,7 +372,7 @@ $(".pageSettings").on("click", " .section.themes .customTheme.button", (e) => { return; } - UpdateConfig.setCustomThemeColors(theme.colors); + setConfig("customThemeColors", theme.colors); }); // Handle click on favorite preset theme button @@ -390,7 +393,7 @@ $(".pageSettings").on("click", ".section.themes .theme .favButton", (e) => { $(".pageSettings").on("click", ".section.themes .theme.button", (e) => { const theme = $(e.currentTarget).attr("theme") as ThemeName; if (!$(e.target).hasClass("favButton") && theme !== undefined) { - UpdateConfig.setTheme(theme); + setConfig("theme", theme); } }); diff --git a/frontend/src/ts/event-handlers/footer.ts b/frontend/src/ts/event-handlers/footer.ts index 5f4dc60aff13..8e288c970848 100644 --- a/frontend/src/ts/event-handlers/footer.ts +++ b/frontend/src/ts/event-handlers/footer.ts @@ -1,4 +1,4 @@ -import Config, * as UpdateConfig from "../config"; +import Config, { setConfig } from "../config"; import { isAuthenticated } from "../firebase"; import * as DB from "../db"; import * as Notifications from "../elements/notifications"; @@ -52,7 +52,7 @@ document const e = event as MouseEvent; if (e.shiftKey) { if (Config.customTheme) { - UpdateConfig.setCustomTheme(false); + setConfig("customTheme", false); return; } if ( @@ -60,10 +60,10 @@ document (DB.getSnapshot()?.customThemes?.length ?? 0) < 1 ) { Notifications.add("No custom themes!", 0); - UpdateConfig.setCustomTheme(false); + setConfig("customTheme", false); return; } - UpdateConfig.setCustomTheme(true); + setConfig("customTheme", true); } else { const subgroup = Config.customTheme ? "customThemesList" : "themes"; Commandline.show({ diff --git a/frontend/src/ts/modals/custom-test-duration.ts b/frontend/src/ts/modals/custom-test-duration.ts index fe5197f3b682..244524417a9c 100644 --- a/frontend/src/ts/modals/custom-test-duration.ts +++ b/frontend/src/ts/modals/custom-test-duration.ts @@ -1,4 +1,4 @@ -import Config, * as UpdateConfig from "../config"; +import Config, { setConfig } from "../config"; import * as ManualRestart from "../test/manual-restart-tracker"; import * as TestLogic from "../test/test-logic"; import * as Notifications from "../elements/notifications"; @@ -90,7 +90,7 @@ function apply(): void { const val = parseInput($("#customTestDurationModal input").val() as string); if (val !== null && !isNaN(val) && val >= 0 && isFinite(val)) { - UpdateConfig.setTimeConfig(val); + setConfig("time", val); ManualRestart.set(); TestLogic.restart(); if (val >= 1800) { diff --git a/frontend/src/ts/modals/custom-text.ts b/frontend/src/ts/modals/custom-text.ts index b3396bcadcfa..f7f81c772f59 100644 --- a/frontend/src/ts/modals/custom-text.ts +++ b/frontend/src/ts/modals/custom-text.ts @@ -3,7 +3,7 @@ import * as CustomTextState from "../states/custom-text-name"; import * as ManualRestart from "../test/manual-restart-tracker"; import * as TestLogic from "../test/test-logic"; import * as ChallengeController from "../controllers/challenge-controller"; -import Config, * as UpdateConfig from "../config"; +import Config, { setConfig } from "../config"; import * as Strings from "../utils/strings"; import * as WordFilterPopup from "./word-filter"; import * as CustomGeneratorPopup from "./custom-generator"; @@ -388,7 +388,9 @@ function apply(): void { ChallengeController.clearActive(); ManualRestart.set(); - if (Config.mode !== "custom") UpdateConfig.setMode("custom"); + if (Config.mode !== "custom") { + setConfig("mode", "custom"); + } PractiseWords.resetBefore(); TestLogic.restart(); hide(); diff --git a/frontend/src/ts/modals/custom-word-amount.ts b/frontend/src/ts/modals/custom-word-amount.ts index 2b75dfd12445..b672a8cf0c35 100644 --- a/frontend/src/ts/modals/custom-word-amount.ts +++ b/frontend/src/ts/modals/custom-word-amount.ts @@ -1,4 +1,4 @@ -import Config, * as UpdateConfig from "../config"; +import Config, { setConfig } from "../config"; import * as ManualRestart from "../test/manual-restart-tracker"; import * as TestLogic from "../test/test-logic"; import * as Notifications from "../elements/notifications"; @@ -28,7 +28,7 @@ function apply(): void { ); if (val !== null && !isNaN(val) && val >= 0 && isFinite(val)) { - if (UpdateConfig.setWordCount(val)) { + if (setConfig("words", val)) { ManualRestart.set(); TestLogic.restart(); if (val > 2000) { diff --git a/frontend/src/ts/modals/import-export-settings.ts b/frontend/src/ts/modals/import-export-settings.ts index 17717f91671a..d23ca53a5825 100644 --- a/frontend/src/ts/modals/import-export-settings.ts +++ b/frontend/src/ts/modals/import-export-settings.ts @@ -1,4 +1,4 @@ -import * as UpdateConfig from "../config"; +import { applyConfigFromJson } from "../config"; import AnimatedModal from "../utils/animated-modal"; type State = { @@ -43,7 +43,7 @@ const modal = new AnimatedModal({ void modal.hide(); return; } - await UpdateConfig.applyFromJson(state.value); + await applyConfigFromJson(state.value); void modal.hide(); }); }, diff --git a/frontend/src/ts/modals/mobile-test-config.ts b/frontend/src/ts/modals/mobile-test-config.ts index 92ae57c65859..00dab9fc37a6 100644 --- a/frontend/src/ts/modals/mobile-test-config.ts +++ b/frontend/src/ts/modals/mobile-test-config.ts @@ -1,5 +1,5 @@ import * as TestLogic from "../test/test-logic"; -import Config, * as UpdateConfig from "../config"; +import Config, { setConfig, setQuoteLengthAll } from "../config"; import * as ManualRestart from "../test/manual-restart-tracker"; import * as CustomWordAmountPopup from "./custom-word-amount"; import * as CustomTestDurationPopup from "./custom-test-duration"; @@ -90,7 +90,7 @@ async function setup(modalEl: HTMLElement): Promise { }); } else if (wrd !== undefined) { const wrdNum = parseInt(wrd); - UpdateConfig.setWordCount(wrdNum); + setConfig("words", wrdNum); ManualRestart.set(); TestLogic.restart(); } @@ -103,7 +103,7 @@ async function setup(modalEl: HTMLElement): Promise { const target = e.currentTarget as HTMLElement; const mode = target.getAttribute("data-mode"); if (mode === Config.mode) return; - UpdateConfig.setMode(mode as Mode); + setConfig("mode", mode as Mode); ManualRestart.set(); TestLogic.restart(); }); @@ -121,7 +121,7 @@ async function setup(modalEl: HTMLElement): Promise { }); } else if (time !== undefined) { const timeNum = parseInt(time); - UpdateConfig.setTimeConfig(timeNum); + setConfig("time", timeNum); ManualRestart.set(); TestLogic.restart(); } @@ -135,7 +135,7 @@ async function setup(modalEl: HTMLElement): Promise { const lenAttr = target.getAttribute("data-quoteLength") ?? "0"; if (lenAttr === "all") { - if (UpdateConfig.setQuoteLengthAll()) { + if (setQuoteLengthAll()) { ManualRestart.set(); TestLogic.restart(); } @@ -153,7 +153,7 @@ async function setup(modalEl: HTMLElement): Promise { arr = [len]; } - if (UpdateConfig.setQuoteLength(arr, false)) { + if (setConfig("quoteLength", arr, false)) { ManualRestart.set(); TestLogic.restart(); } @@ -168,13 +168,13 @@ async function setup(modalEl: HTMLElement): Promise { }); modalEl.querySelector(".punctuation")?.addEventListener("click", () => { - UpdateConfig.setPunctuation(!Config.punctuation); + setConfig("punctuation", !Config.punctuation); ManualRestart.set(); TestLogic.restart(); }); modalEl.querySelector(".numbers")?.addEventListener("click", () => { - UpdateConfig.setNumbers(!Config.numbers); + setConfig("numbers", !Config.numbers); ManualRestart.set(); TestLogic.restart(); }); diff --git a/frontend/src/ts/modals/quote-search.ts b/frontend/src/ts/modals/quote-search.ts index 33f371384681..ae7b1eed7b7e 100644 --- a/frontend/src/ts/modals/quote-search.ts +++ b/frontend/src/ts/modals/quote-search.ts @@ -1,4 +1,4 @@ -import Config, * as UpdateConfig from "../config"; +import Config, { setConfig } from "../config"; import * as DB from "../db"; import * as ManualRestart from "../test/manual-restart-tracker"; import * as Notifications from "../elements/notifications"; @@ -363,7 +363,7 @@ function apply(val: number): void { ); } if (val !== null && !isNaN(val) && val >= 0) { - UpdateConfig.setQuoteLength([-2], false); + setConfig("quoteLength", [-2], false); TestState.setSelectedQuoteId(val); ManualRestart.set(); } else { diff --git a/frontend/src/ts/modals/simple-modals.ts b/frontend/src/ts/modals/simple-modals.ts index 367d3ab5db37..a15a831356a9 100644 --- a/frontend/src/ts/modals/simple-modals.ts +++ b/frontend/src/ts/modals/simple-modals.ts @@ -1,7 +1,7 @@ import Ape from "../ape"; import * as AccountController from "../auth"; import * as DB from "../db"; -import * as UpdateConfig from "../config"; +import { resetConfig, setConfig } from "../config"; import * as Notifications from "../elements/notifications"; import * as Settings from "../pages/settings"; import * as ThemePicker from "../elements/settings/theme-picker"; @@ -783,7 +783,7 @@ list.resetAccount = new SimpleModal({ } Notifications.add("Resetting settings...", 0); - await UpdateConfig.reset(); + await resetConfig(); await FileStorage.deleteFile("LocalBackgroundFile"); Notifications.add("Resetting account...", 0); @@ -939,7 +939,7 @@ list.resetSettings = new SimpleModal({ buttonText: "reset", onlineOnly: true, execFn: async (): Promise => { - await UpdateConfig.reset(); + await resetConfig(); await FileStorage.deleteFile("LocalBackgroundFile"); return { status: 1, @@ -1152,7 +1152,7 @@ list.updateCustomTheme = new SimpleModal({ message: "Failed to update custom theme", }; } - UpdateConfig.setCustomThemeColors(newColors as CustomThemeColors); + setConfig("customThemeColors", newColors as CustomThemeColors); void ThemePicker.fillCustomButtons(); return { diff --git a/frontend/src/ts/pages/account.ts b/frontend/src/ts/pages/account.ts index 5ffe525cf07f..07f3fb9e7d6b 100644 --- a/frontend/src/ts/pages/account.ts +++ b/frontend/src/ts/pages/account.ts @@ -2,7 +2,7 @@ import * as DB from "../db"; import * as ResultFilters from "../elements/account/result-filters"; import * as ThemeColors from "../elements/theme-colors"; import * as ChartController from "../controllers/chart-controller"; -import Config, * as UpdateConfig from "../config"; +import Config, { setConfig } from "../config"; import * as MiniResultChartModal from "../modals/mini-result-chart"; import * as PbTables from "../elements/account/pb-tables"; import * as Focus from "../test/focus"; @@ -1035,25 +1035,25 @@ export function updateTagsForResult(resultId: string, tagIds: string[]): void { $(".pageAccount button.toggleResultsOnChart").on("click", () => { const newValue = [...Config.accountChart] as AccountChart; newValue[0] = newValue[0] === "on" ? "off" : "on"; - UpdateConfig.setAccountChart(newValue); + setConfig("accountChart", newValue); }); $(".pageAccount button.toggleAccuracyOnChart").on("click", () => { const newValue = [...Config.accountChart] as AccountChart; newValue[1] = newValue[1] === "on" ? "off" : "on"; - UpdateConfig.setAccountChart(newValue); + setConfig("accountChart", newValue); }); $(".pageAccount button.toggleAverage10OnChart").on("click", () => { const newValue = [...Config.accountChart] as AccountChart; newValue[2] = newValue[2] === "on" ? "off" : "on"; - UpdateConfig.setAccountChart(newValue); + setConfig("accountChart", newValue); }); $(".pageAccount button.toggleAverage100OnChart").on("click", () => { const newValue = [...Config.accountChart] as AccountChart; newValue[3] = newValue[3] === "on" ? "off" : "on"; - UpdateConfig.setAccountChart(newValue); + setConfig("accountChart", newValue); }); $(".pageAccount .loadMoreButton").on("click", () => { diff --git a/frontend/src/ts/pages/settings.ts b/frontend/src/ts/pages/settings.ts index bdb76e5254f0..cd71f5bda552 100644 --- a/frontend/src/ts/pages/settings.ts +++ b/frontend/src/ts/pages/settings.ts @@ -1,5 +1,5 @@ import SettingsGroup from "../elements/settings/settings-group"; -import Config, * as UpdateConfig from "../config"; +import Config, { setConfig, configLoadPromise } from "../config"; import * as Sound from "../controllers/sound-controller"; import * as Misc from "../utils/misc"; import * as Strings from "../utils/strings"; @@ -72,414 +72,183 @@ const StateSchema = z .partial(); async function initGroups(): Promise { - groups["smoothCaret"] = new SettingsGroup( - "smoothCaret", - UpdateConfig.setSmoothCaret, - "button", - ); + groups["smoothCaret"] = new SettingsGroup("smoothCaret", "button"); groups["codeUnindentOnBackspace"] = new SettingsGroup( "codeUnindentOnBackspace", - UpdateConfig.setCodeUnindentOnBackspace, - "button", - ); - groups["difficulty"] = new SettingsGroup( - "difficulty", - UpdateConfig.setDifficulty, - "button", - ); - groups["quickRestart"] = new SettingsGroup( - "quickRestart", - UpdateConfig.setQuickRestartMode, - "button", - ); - groups["showAverage"] = new SettingsGroup( - "showAverage", - UpdateConfig.setShowAverage, "button", ); - groups["keymapMode"] = new SettingsGroup( - "keymapMode", - UpdateConfig.setKeymapMode, - "button", - { - updateCallback: () => { - if (Config.keymapMode === "off") { - $(".pageSettings .section[data-config-name='keymapStyle']").addClass( - "hidden", - ); - $(".pageSettings .section[data-config-name='keymapLayout']").addClass( - "hidden", - ); - $( - ".pageSettings .section[data-config-name='keymapLegendStyle']", - ).addClass("hidden"); - $( - ".pageSettings .section[data-config-name='keymapShowTopRow']", - ).addClass("hidden"); - $(".pageSettings .section[data-config-name='keymapSize']").addClass( - "hidden", - ); - } else { - $( - ".pageSettings .section[data-config-name='keymapStyle']", - ).removeClass("hidden"); - $( - ".pageSettings .section[data-config-name='keymapLayout']", - ).removeClass("hidden"); - $( - ".pageSettings .section[data-config-name='keymapLegendStyle']", - ).removeClass("hidden"); - $( - ".pageSettings .section[data-config-name='keymapShowTopRow']", - ).removeClass("hidden"); - $( - ".pageSettings .section[data-config-name='keymapSize']", - ).removeClass("hidden"); - } - }, + groups["difficulty"] = new SettingsGroup("difficulty", "button"); + groups["quickRestart"] = new SettingsGroup("quickRestart", "button"); + groups["showAverage"] = new SettingsGroup("showAverage", "button"); + groups["keymapMode"] = new SettingsGroup("keymapMode", "button", { + updateCallback: () => { + if (Config.keymapMode === "off") { + $(".pageSettings .section[data-config-name='keymapStyle']").addClass( + "hidden", + ); + $(".pageSettings .section[data-config-name='keymapLayout']").addClass( + "hidden", + ); + $( + ".pageSettings .section[data-config-name='keymapLegendStyle']", + ).addClass("hidden"); + $( + ".pageSettings .section[data-config-name='keymapShowTopRow']", + ).addClass("hidden"); + $(".pageSettings .section[data-config-name='keymapSize']").addClass( + "hidden", + ); + } else { + $(".pageSettings .section[data-config-name='keymapStyle']").removeClass( + "hidden", + ); + $( + ".pageSettings .section[data-config-name='keymapLayout']", + ).removeClass("hidden"); + $( + ".pageSettings .section[data-config-name='keymapLegendStyle']", + ).removeClass("hidden"); + $( + ".pageSettings .section[data-config-name='keymapShowTopRow']", + ).removeClass("hidden"); + $(".pageSettings .section[data-config-name='keymapSize']").removeClass( + "hidden", + ); + } }, - ); - groups["keymapStyle"] = new SettingsGroup( - "keymapStyle", - UpdateConfig.setKeymapStyle, - "button", - ); - groups["keymapLayout"] = new SettingsGroup( - "keymapLayout", - UpdateConfig.setKeymapLayout, - "select", - ); + }); + groups["keymapStyle"] = new SettingsGroup("keymapStyle", "button"); + groups["keymapLayout"] = new SettingsGroup("keymapLayout", "select"); groups["keymapLegendStyle"] = new SettingsGroup( "keymapLegendStyle", - UpdateConfig.setKeymapLegendStyle, - "button", - ); - groups["keymapShowTopRow"] = new SettingsGroup( - "keymapShowTopRow", - UpdateConfig.setKeymapShowTopRow, - "button", - ); - groups["keymapSize"] = new SettingsGroup( - "keymapSize", - UpdateConfig.setKeymapSize, - "range", - ); - groups["showKeyTips"] = new SettingsGroup( - "showKeyTips", - UpdateConfig.setKeyTips, "button", ); - groups["freedomMode"] = new SettingsGroup( - "freedomMode", - UpdateConfig.setFreedomMode, - "button", - { - setCallback: () => { - groups["confidenceMode"]?.updateUI(); - }, + groups["keymapShowTopRow"] = new SettingsGroup("keymapShowTopRow", "button"); + groups["keymapSize"] = new SettingsGroup("keymapSize", "range"); + groups["showKeyTips"] = new SettingsGroup("showKeyTips", "button"); + groups["freedomMode"] = new SettingsGroup("freedomMode", "button", { + setCallback: () => { + groups["confidenceMode"]?.updateUI(); }, - ); - groups["strictSpace"] = new SettingsGroup( - "strictSpace", - UpdateConfig.setStrictSpace, - "button", - ); + }); + groups["strictSpace"] = new SettingsGroup("strictSpace", "button"); groups["oppositeShiftMode"] = new SettingsGroup( "oppositeShiftMode", - UpdateConfig.setOppositeShiftMode, "button", ); - groups["confidenceMode"] = new SettingsGroup( - "confidenceMode", - UpdateConfig.setConfidenceMode, - "button", - { - setCallback: () => { - groups["freedomMode"]?.updateUI(); - groups["stopOnError"]?.updateUI(); - }, + groups["confidenceMode"] = new SettingsGroup("confidenceMode", "button", { + setCallback: () => { + groups["freedomMode"]?.updateUI(); + groups["stopOnError"]?.updateUI(); }, - ); - groups["indicateTypos"] = new SettingsGroup( - "indicateTypos", - UpdateConfig.setIndicateTypos, - "button", - ); + }); + groups["indicateTypos"] = new SettingsGroup("indicateTypos", "button"); groups["compositionDisplay"] = new SettingsGroup( "compositionDisplay", - UpdateConfig.setCompositionDisplay, - "button", - ); - groups["hideExtraLetters"] = new SettingsGroup( - "hideExtraLetters", - UpdateConfig.setHideExtraLetters, - "button", - ); - groups["blindMode"] = new SettingsGroup( - "blindMode", - UpdateConfig.setBlindMode, - "button", - ); - groups["quickEnd"] = new SettingsGroup( - "quickEnd", - UpdateConfig.setQuickEnd, "button", ); - groups["repeatQuotes"] = new SettingsGroup( - "repeatQuotes", - UpdateConfig.setRepeatQuotes, - "button", - ); - groups["ads"] = new SettingsGroup("ads", UpdateConfig.setAds, "button"); + groups["hideExtraLetters"] = new SettingsGroup("hideExtraLetters", "button"); + groups["blindMode"] = new SettingsGroup("blindMode", "button"); + groups["quickEnd"] = new SettingsGroup("quickEnd", "button"); + groups["repeatQuotes"] = new SettingsGroup("repeatQuotes", "button"); + groups["ads"] = new SettingsGroup("ads", "button"); groups["alwaysShowWordsHistory"] = new SettingsGroup( "alwaysShowWordsHistory", - UpdateConfig.setAlwaysShowWordsHistory, - "button", - ); - groups["britishEnglish"] = new SettingsGroup( - "britishEnglish", - UpdateConfig.setBritishEnglish, "button", ); + groups["britishEnglish"] = new SettingsGroup("britishEnglish", "button"); groups["singleListCommandLine"] = new SettingsGroup( "singleListCommandLine", - UpdateConfig.setSingleListCommandLine, - "button", - ); - groups["capsLockWarning"] = new SettingsGroup( - "capsLockWarning", - UpdateConfig.setCapsLockWarning, - "button", - ); - groups["flipTestColors"] = new SettingsGroup( - "flipTestColors", - UpdateConfig.setFlipTestColors, "button", ); + groups["capsLockWarning"] = new SettingsGroup("capsLockWarning", "button"); + groups["flipTestColors"] = new SettingsGroup("flipTestColors", "button"); groups["showOutOfFocusWarning"] = new SettingsGroup( "showOutOfFocusWarning", - UpdateConfig.setShowOutOfFocusWarning, - "button", - ); - groups["colorfulMode"] = new SettingsGroup( - "colorfulMode", - UpdateConfig.setColorfulMode, "button", ); + groups["colorfulMode"] = new SettingsGroup("colorfulMode", "button"); groups["startGraphsAtZero"] = new SettingsGroup( "startGraphsAtZero", - UpdateConfig.setStartGraphsAtZero, - "button", - ); - groups["autoSwitchTheme"] = new SettingsGroup( - "autoSwitchTheme", - UpdateConfig.setAutoSwitchTheme, - "button", - ); - groups["randomTheme"] = new SettingsGroup( - "randomTheme", - UpdateConfig.setRandomTheme, "button", ); - groups["stopOnError"] = new SettingsGroup( - "stopOnError", - UpdateConfig.setStopOnError, - "button", - { - setCallback: () => { - groups["confidenceMode"]?.updateUI(); - }, + groups["autoSwitchTheme"] = new SettingsGroup("autoSwitchTheme", "button"); + groups["randomTheme"] = new SettingsGroup("randomTheme", "button"); + groups["stopOnError"] = new SettingsGroup("stopOnError", "button", { + setCallback: () => { + groups["confidenceMode"]?.updateUI(); }, - ); - groups["soundVolume"] = new SettingsGroup( - "soundVolume", - UpdateConfig.setSoundVolume, - "range", - ); - groups["playTimeWarning"] = new SettingsGroup( - "playTimeWarning", - UpdateConfig.setPlayTimeWarning, - "button", - { - setCallback: () => { - if (Config.playTimeWarning !== "off") void Sound.playTimeWarning(); - }, + }); + groups["soundVolume"] = new SettingsGroup("soundVolume", "range"); + groups["playTimeWarning"] = new SettingsGroup("playTimeWarning", "button", { + setCallback: () => { + if (Config.playTimeWarning !== "off") void Sound.playTimeWarning(); }, - ); - groups["playSoundOnError"] = new SettingsGroup( - "playSoundOnError", - UpdateConfig.setPlaySoundOnError, - "button", - { - setCallback: () => { - if (Config.playSoundOnError !== "off") void Sound.playError(); - }, + }); + groups["playSoundOnError"] = new SettingsGroup("playSoundOnError", "button", { + setCallback: () => { + if (Config.playSoundOnError !== "off") void Sound.playError(); }, - ); - groups["playSoundOnClick"] = new SettingsGroup( - "playSoundOnClick", - UpdateConfig.setPlaySoundOnClick, - "button", - { - setCallback: () => { - if (Config.playSoundOnClick !== "off") void Sound.playClick("KeyQ"); - }, + }); + groups["playSoundOnClick"] = new SettingsGroup("playSoundOnClick", "button", { + setCallback: () => { + if (Config.playSoundOnClick !== "off") void Sound.playClick("KeyQ"); }, - ); - groups["showAllLines"] = new SettingsGroup( - "showAllLines", - UpdateConfig.setShowAllLines, - "button", - ); - groups["paceCaret"] = new SettingsGroup( - "paceCaret", - UpdateConfig.setPaceCaret, - "button", - ); - groups["repeatedPace"] = new SettingsGroup( - "repeatedPace", - UpdateConfig.setRepeatedPace, - "button", - ); - groups["minWpm"] = new SettingsGroup( - "minWpm", - UpdateConfig.setMinWpm, - "button", - ); - groups["minAcc"] = new SettingsGroup( - "minAcc", - UpdateConfig.setMinAcc, - "button", - ); - groups["minBurst"] = new SettingsGroup( - "minBurst", - UpdateConfig.setMinBurst, - "button", - ); - groups["smoothLineScroll"] = new SettingsGroup( - "smoothLineScroll", - UpdateConfig.setSmoothLineScroll, - "button", - ); - groups["lazyMode"] = new SettingsGroup( - "lazyMode", - UpdateConfig.setLazyMode, - "button", - ); - groups["layout"] = new SettingsGroup( - "layout", - UpdateConfig.setLayout, - "select", - ); - groups["language"] = new SettingsGroup( - "language", - UpdateConfig.setLanguage, - "select", - ); - groups["fontSize"] = new SettingsGroup( - "fontSize", - UpdateConfig.setFontSize, - "input", - { validation: { schema: true, inputValueConvert: Number } }, - ); - groups["maxLineWidth"] = new SettingsGroup( - "maxLineWidth", - UpdateConfig.setMaxLineWidth, - "input", - { validation: { schema: true, inputValueConvert: Number } }, - ); - groups["caretStyle"] = new SettingsGroup( - "caretStyle", - UpdateConfig.setCaretStyle, - "button", - ); - groups["paceCaretStyle"] = new SettingsGroup( - "paceCaretStyle", - UpdateConfig.setPaceCaretStyle, - "button", - ); - groups["timerStyle"] = new SettingsGroup( - "timerStyle", - UpdateConfig.setTimerStyle, - "button", - ); - groups["liveSpeedStyle"] = new SettingsGroup( - "liveSpeedStyle", - UpdateConfig.setLiveSpeedStyle, - "button", - ); - groups["liveAccStyle"] = new SettingsGroup( - "liveAccStyle", - UpdateConfig.setLiveAccStyle, - "button", - ); - groups["liveBurstStyle"] = new SettingsGroup( - "liveBurstStyle", - UpdateConfig.setLiveBurstStyle, - "button", - ); - groups["highlightMode"] = new SettingsGroup( - "highlightMode", - UpdateConfig.setHighlightMode, - "button", - ); - groups["tapeMode"] = new SettingsGroup( - "tapeMode", - UpdateConfig.setTapeMode, - "button", - ); - groups["tapeMargin"] = new SettingsGroup( - "tapeMargin", - UpdateConfig.setTapeMargin, - "input", - { validation: { schema: true, inputValueConvert: Number } }, - ); - groups["timerOpacity"] = new SettingsGroup( - "timerOpacity", - UpdateConfig.setTimerOpacity, - "button", - ); - groups["timerColor"] = new SettingsGroup( - "timerColor", - UpdateConfig.setTimerColor, - "button", - ); - groups["fontFamily"] = new SettingsGroup( - "fontFamily", - UpdateConfig.setFontFamily, - "button", - { - updateCallback: () => { - const customButton = $( - ".pageSettings .section[data-config-name='fontFamily'] .buttons button[data-config-value='custom']", - ); - - if ( - $( - ".pageSettings .section[data-config-name='fontFamily'] .buttons .active", - ).length === 0 - ) { - customButton.addClass("active"); - customButton.text(`Custom (${Config.fontFamily.replace(/_/g, " ")})`); - } else { - customButton.text("Custom"); - } - }, + }); + groups["showAllLines"] = new SettingsGroup("showAllLines", "button"); + groups["paceCaret"] = new SettingsGroup("paceCaret", "button"); + groups["repeatedPace"] = new SettingsGroup("repeatedPace", "button"); + groups["minWpm"] = new SettingsGroup("minWpm", "button"); + groups["minAcc"] = new SettingsGroup("minAcc", "button"); + groups["minBurst"] = new SettingsGroup("minBurst", "button"); + groups["smoothLineScroll"] = new SettingsGroup("smoothLineScroll", "button"); + groups["lazyMode"] = new SettingsGroup("lazyMode", "button"); + groups["layout"] = new SettingsGroup("layout", "select"); + groups["language"] = new SettingsGroup("language", "select"); + groups["fontSize"] = new SettingsGroup("fontSize", "input", { + validation: { schema: true, inputValueConvert: Number }, + }); + groups["maxLineWidth"] = new SettingsGroup("maxLineWidth", "input", { + validation: { schema: true, inputValueConvert: Number }, + }); + groups["caretStyle"] = new SettingsGroup("caretStyle", "button"); + groups["paceCaretStyle"] = new SettingsGroup("paceCaretStyle", "button"); + groups["timerStyle"] = new SettingsGroup("timerStyle", "button"); + groups["liveSpeedStyle"] = new SettingsGroup("liveSpeedStyle", "button"); + groups["liveAccStyle"] = new SettingsGroup("liveAccStyle", "button"); + groups["liveBurstStyle"] = new SettingsGroup("liveBurstStyle", "button"); + groups["highlightMode"] = new SettingsGroup("highlightMode", "button"); + groups["tapeMode"] = new SettingsGroup("tapeMode", "button"); + groups["tapeMargin"] = new SettingsGroup("tapeMargin", "input", { + validation: { schema: true, inputValueConvert: Number }, + }); + groups["timerOpacity"] = new SettingsGroup("timerOpacity", "button"); + groups["timerColor"] = new SettingsGroup("timerColor", "button"); + groups["fontFamily"] = new SettingsGroup("fontFamily", "button", { + updateCallback: () => { + const customButton = $( + ".pageSettings .section[data-config-name='fontFamily'] .buttons button[data-config-value='custom']", + ); + + if ( + $( + ".pageSettings .section[data-config-name='fontFamily'] .buttons .active", + ).length === 0 + ) { + customButton.addClass("active"); + customButton.text(`Custom (${Config.fontFamily.replace(/_/g, " ")})`); + } else { + customButton.text("Custom"); + } }, - ); + }); groups["alwaysShowDecimalPlaces"] = new SettingsGroup( "alwaysShowDecimalPlaces", - UpdateConfig.setAlwaysShowDecimalPlaces, - "button", - ); - groups["typingSpeedUnit"] = new SettingsGroup( - "typingSpeedUnit", - UpdateConfig.setTypingSpeedUnit, "button", ); + groups["typingSpeedUnit"] = new SettingsGroup("typingSpeedUnit", "button"); groups["customBackgroundSize"] = new SettingsGroup( "customBackgroundSize", - UpdateConfig.setCustomBackgroundSize, "button", ); } @@ -526,7 +295,7 @@ async function fillSettingsPage(): Promise { data: getThemeDropdownData((theme) => theme.name === Config.themeLight), events: { afterChange: (newVal): void => { - UpdateConfig.setThemeLight(newVal[0]?.value as ThemeName); + setConfig("themeLight", newVal[0]?.value as ThemeName); }, }, }); @@ -537,7 +306,7 @@ async function fillSettingsPage(): Promise { data: getThemeDropdownData((theme) => theme.name === Config.themeDark), events: { afterChange: (newVal): void => { - UpdateConfig.setThemeDark(newVal[0]?.value as ThemeName); + setConfig("themeDark", newVal[0]?.value as ThemeName); }, }, }); @@ -625,7 +394,7 @@ async function fillSettingsPage(): Promise { if ( !areSortedArraysEqual(customLayoutfluid, Config.customLayoutfluid) ) { - void UpdateConfig.setCustomLayoutfluid(customLayoutfluid); + void setConfig("customLayoutfluid", customLayoutfluid); } }, }, @@ -642,7 +411,7 @@ async function fillSettingsPage(): Promise { const customPolyglot = newVal.map((it) => it.value) as Language[]; //checking equal without order, because customPolyglot is not ordered if (!areUnsortedArraysEqual(customPolyglot, Config.customPolyglot)) { - void UpdateConfig.setCustomPolyglot(customPolyglot); + void setConfig("customPolyglot", customPolyglot); } }, }, @@ -1060,7 +829,8 @@ $(".pageSettings .sectionGroupTitle").on("click", (e) => { $( ".pageSettings .section[data-config-name='keymapSize'] .inputAndButton button.save", ).on("click", () => { - const didConfigSave = UpdateConfig.setKeymapSize( + const didConfigSave = setConfig( + "keymapSize", parseFloat( $( ".pageSettings .section[data-config-name='keymapSize'] .inputAndButton input", @@ -1077,7 +847,8 @@ $( $( ".pageSettings .section[data-config-name='keymapSize'] .inputAndButton input", ).on("focusout", () => { - const didConfigSave = UpdateConfig.setKeymapSize( + const didConfigSave = setConfig( + "keymapSize", parseFloat( $( ".pageSettings .section[data-config-name='keymapSize'] .inputAndButton input", @@ -1095,7 +866,8 @@ $( ".pageSettings .section[data-config-name='keymapSize'] .inputAndButton input", ).on("keypress", (e) => { if (e.key === "Enter") { - const didConfigSave = UpdateConfig.setKeymapSize( + const didConfigSave = setConfig( + "keymapSize", parseFloat( $( ".pageSettings .section[data-config-name='keymapSize'] .inputAndButton input", @@ -1247,7 +1019,7 @@ export const page = new PageWithUrlParams({ }, beforeShow: async (options): Promise => { Skeleton.append("pageSettings", "main"); - await UpdateConfig.loadPromise; + await configLoadPromise; await fillSettingsPage(); await update(); // theme UI updates manually to avoid duplication diff --git a/frontend/src/ts/ready.ts b/frontend/src/ts/ready.ts index 742ca75cdd56..d0ca342bc099 100644 --- a/frontend/src/ts/ready.ts +++ b/frontend/src/ts/ready.ts @@ -7,13 +7,13 @@ import * as AccountButton from "./elements/account-button"; import Konami from "konami"; import * as ServerConfiguration from "./ape/server-configuration"; import { getActiveFunboxesWithFunction } from "./test/funbox/list"; -import { loadPromise } from "./config"; +import { configLoadPromise } from "./config"; import { authPromise } from "./firebase"; import { animate } from "animejs"; import { onDocumentReady, qs } from "./utils/dom"; onDocumentReady(async () => { - await loadPromise; + await configLoadPromise; await authPromise; //this line goes back to pretty much the beginning of the project and im pretty sure its here diff --git a/frontend/src/ts/test/funbox/funbox-functions.ts b/frontend/src/ts/test/funbox/funbox-functions.ts index ba1b70a1bd38..b97c55185fc5 100644 --- a/frontend/src/ts/test/funbox/funbox-functions.ts +++ b/frontend/src/ts/test/funbox/funbox-functions.ts @@ -1,6 +1,6 @@ import { FunboxWordsFrequency, Wordset } from "../wordset"; import * as GetText from "../../utils/generate"; -import Config, * as UpdateConfig from "../../config"; +import Config, { setConfig, toggleFunbox } from "../../config"; import * as Misc from "../../utils/misc"; import * as Strings from "../../utils/strings"; import { randomIntFromRange } from "@monkeytype/util/numbers"; @@ -196,7 +196,7 @@ const list: Partial> = { return word; }, rememberSettings(): void { - save("numbers", Config.numbers, UpdateConfig.setNumbers); + save("numbers", Config.numbers); }, getEmulatedChar(event: KeyboardEvent): string | null { if (event.key === "Enter") { @@ -207,18 +207,18 @@ const list: Partial> = { }, simon_says: { applyConfig(): void { - UpdateConfig.setKeymapMode("next", true); + setConfig("keymapMode", "next", true); }, rememberSettings(): void { - save("keymapMode", Config.keymapMode, UpdateConfig.setKeymapMode); + save("keymapMode", Config.keymapMode); }, }, tts: { applyConfig(): void { - UpdateConfig.setKeymapMode("off", true); + setConfig("keymapMode", "off", true); }, rememberSettings(): void { - save("keymapMode", Config.keymapMode, UpdateConfig.setKeymapMode); + save("keymapMode", Config.keymapMode); }, toggleScript(params: string[]): void { if (window.speechSynthesis === undefined) { @@ -233,11 +233,7 @@ const list: Partial> = { return DDR.chart2Word(wordIndex === 0); }, rememberSettings(): void { - save( - "highlightMode", - Config.highlightMode, - UpdateConfig.setHighlightMode, - ); + save("highlightMode", Config.highlightMode); }, getEmulatedChar(event: KeyboardEvent): string | null { const ekey = event.key; @@ -372,25 +368,25 @@ const list: Partial> = { if (Config.layout === "default") { layout = "qwerty"; } - UpdateConfig.setLayout(layout, true); - UpdateConfig.setKeymapLayout("overrideSync", true); + setConfig("layout", layout, true); + setConfig("keymapLayout", "overrideSync", true); }, rememberSettings(): void { - save("keymapMode", Config.keymapMode, UpdateConfig.setKeymapMode); - save("layout", Config.layout, UpdateConfig.setLayout); + save("keymapMode", Config.keymapMode); + save("layout", Config.layout); }, }, layoutfluid: { applyConfig(): void { const layout = Config.customLayoutfluid[0] ?? "qwerty"; - UpdateConfig.setLayout(layout as Layout, true); - UpdateConfig.setKeymapLayout(layout as KeymapLayout, true); + setConfig("layout", layout as Layout, true); + setConfig("keymapLayout", layout as KeymapLayout, true); }, rememberSettings(): void { - save("keymapMode", Config.keymapMode, UpdateConfig.setKeymapMode); - save("layout", Config.layout, UpdateConfig.setLayout); - save("keymapLayout", Config.keymapLayout, UpdateConfig.setKeymapLayout); + save("keymapMode", Config.keymapMode); + save("layout", Config.layout); + save("keymapLayout", Config.keymapLayout); }, handleSpace(): void { if (Config.mode !== "time") { @@ -414,8 +410,8 @@ const list: Partial> = { LayoutfluidFunboxTimer.hide(); } if (mod === wordsPerLayout) { - UpdateConfig.setLayout(layouts[index] as Layout); - UpdateConfig.setKeymapLayout(layouts[index] as KeymapLayout); + setConfig("layout", layouts[index] as Layout); + setConfig("keymapLayout", layouts[index] as KeymapLayout); if (mod > 3) { LayoutfluidFunboxTimer.hide(); } @@ -464,11 +460,7 @@ const list: Partial> = { }, read_ahead_easy: { rememberSettings(): void { - save( - "highlightMode", - Config.highlightMode, - UpdateConfig.setHighlightMode, - ); + save("highlightMode", Config.highlightMode); }, async handleKeydown(event): Promise { await readAheadHandleKeydown(event); @@ -476,11 +468,7 @@ const list: Partial> = { }, read_ahead: { rememberSettings(): void { - save( - "highlightMode", - Config.highlightMode, - UpdateConfig.setHighlightMode, - ); + save("highlightMode", Config.highlightMode); }, async handleKeydown(event): Promise { await readAheadHandleKeydown(event); @@ -488,11 +476,7 @@ const list: Partial> = { }, read_ahead_hard: { rememberSettings(): void { - save( - "highlightMode", - Config.highlightMode, - UpdateConfig.setHighlightMode, - ); + save("highlightMode", Config.highlightMode); }, async handleKeydown(event): Promise { await readAheadHandleKeydown(event); @@ -501,16 +485,16 @@ const list: Partial> = { memory: { applyConfig(): void { $("#wordsWrapper").addClass("hidden"); - UpdateConfig.setShowAllLines(true, true); + setConfig("showAllLines", true, true); if (Config.keymapMode === "next") { - UpdateConfig.setKeymapMode("react", true); + setConfig("keymapMode", "react", true); } }, rememberSettings(): void { - save("mode", Config.mode, UpdateConfig.setMode); - save("showAllLines", Config.showAllLines, UpdateConfig.setShowAllLines); + save("mode", Config.mode); + save("showAllLines", Config.showAllLines); if (Config.keymapMode === "next") { - save("keymapMode", Config.keymapMode, UpdateConfig.setKeymapMode); + save("keymapMode", Config.keymapMode); } }, start(): void { @@ -521,17 +505,13 @@ const list: Partial> = { MemoryTimer.start(Math.round(Math.pow(TestWords.words.length, 1.2))); $("#words").removeClass("hidden"); if (Config.keymapMode === "next") { - UpdateConfig.setKeymapMode("react"); + setConfig("keymapMode", "react"); } }, }, nospace: { rememberSettings(): void { - save( - "highlightMode", - Config.highlightMode, - UpdateConfig.setHighlightMode, - ); + save("highlightMode", Config.highlightMode); }, }, poetry: { @@ -568,7 +548,7 @@ const list: Partial> = { return w; }, rememberSettings(): void { - save("numbers", Config.numbers, UpdateConfig.setNumbers); + save("numbers", Config.numbers); }, }, IPv6: { @@ -587,7 +567,7 @@ const list: Partial> = { return w; }, rememberSettings(): void { - save("numbers", Config.numbers, UpdateConfig.setNumbers); + save("numbers", Config.numbers); }, }, binary: { @@ -603,7 +583,7 @@ const list: Partial> = { return `0x${word}`; }, rememberSettings(): void { - save("punctuation", Config.punctuation, UpdateConfig.setPunctuation); + save("punctuation", Config.punctuation); }, }, zipf: { @@ -658,7 +638,7 @@ const list: Partial> = { duration: 5, }, ); - UpdateConfig.toggleFunbox("crt"); + toggleFunbox("crt"); return; } } @@ -694,7 +674,7 @@ const list: Partial> = { ); if (languages.length === 0) { - UpdateConfig.toggleFunbox("polyglot"); + toggleFunbox("polyglot"); throw new Error( `No valid languages found. Please check your polyglot languages config (${Config.customPolyglot.join( ", ", @@ -704,8 +684,8 @@ const list: Partial> = { if (languages.length === 1) { const lang = languages[0] as LanguageObject; - UpdateConfig.setLanguage(lang.name, true); - UpdateConfig.toggleFunbox("polyglot", true); + setConfig("language", lang.name, true); + toggleFunbox("polyglot", true); Notifications.add( `Disabled polyglot funbox because only one valid language was found. Check your polyglot languages config (${Config.customPolyglot.join( ", ", @@ -729,7 +709,7 @@ const list: Partial> = { ) { const fallbackLanguage = languages[0]?.name ?? (allRightToLeft ? "arabic" : "english"); - UpdateConfig.setLanguage(fallbackLanguage); + setConfig("language", fallbackLanguage); Notifications.add( `Language direction conflict: switched to ${fallbackLanguage} for consistency.`, 0, diff --git a/frontend/src/ts/test/funbox/funbox-memory.ts b/frontend/src/ts/test/funbox/funbox-memory.ts index 4a4dc094809c..886b72b50762 100644 --- a/frontend/src/ts/test/funbox/funbox-memory.ts +++ b/frontend/src/ts/test/funbox/funbox-memory.ts @@ -1,4 +1,6 @@ -import { ConfigValue } from "@monkeytype/schemas/configs"; +import { Config, ConfigKey, ConfigValue } from "@monkeytype/schemas/configs"; + +import { setConfig } from "../../config"; type SetFunction = (param: T, nosave?: boolean) => boolean; @@ -11,14 +13,14 @@ type SettingsMemory = Record>; let settingsMemory: SettingsMemory = {}; -export function save( - settingName: string, - value: T, - setFunction: SetFunction, +export function save( + settingName: T, + value: Config[T], ): void { settingsMemory[settingName] ??= { value, - setFunction: setFunction as SetFunction, + setFunction: (param, noSave?) => + setConfig(settingName, param as Config[T], noSave), }; } diff --git a/frontend/src/ts/test/funbox/funbox.ts b/frontend/src/ts/test/funbox/funbox.ts index 47fd20dd9dbc..58bd506ff322 100644 --- a/frontend/src/ts/test/funbox/funbox.ts +++ b/frontend/src/ts/test/funbox/funbox.ts @@ -3,7 +3,10 @@ import * as Misc from "../../utils/misc"; import * as JSONData from "../../utils/json-data"; import * as Strings from "../../utils/strings"; import * as ManualRestart from "../manual-restart-tracker"; -import Config, * as UpdateConfig from "../../config"; +import Config, { + setConfig, + toggleFunbox as configToggleFunbox, +} from "../../config"; import * as MemoryTimer from "./memory-funbox-timer"; import * as FunboxMemory from "./funbox-memory"; import { HighlightMode, FunboxName } from "@monkeytype/schemas/configs"; @@ -35,7 +38,7 @@ export function setFunbox(funbox: FunboxName[]): boolean { } } FunboxMemory.load(); - UpdateConfig.setFunbox(funbox, false); + setConfig("funbox", funbox, false); return true; } @@ -53,7 +56,7 @@ export function toggleFunbox(funbox: FunboxName): void { return; } FunboxMemory.load(); - UpdateConfig.toggleFunbox(funbox, false); + configToggleFunbox(funbox, false); if (!getActiveFunboxNames().includes(funbox)) { get(funbox).functions?.clearGlobal?.(); @@ -101,7 +104,7 @@ export async function activate( ), -1, ); - UpdateConfig.setFunbox([], true); + setConfig("funbox", [], true); await clear(); return false; } @@ -120,7 +123,7 @@ export async function activate( Misc.createErrorMessage(error, "Failed to activate funbox"), -1, ); - UpdateConfig.setFunbox([], true); + setConfig("funbox", [], true); await clear(); return false; } @@ -131,7 +134,7 @@ export async function activate( "Current language does not support this funbox mode", 0, ); - UpdateConfig.setFunbox([], true); + setConfig("funbox", [], true); await clear(); return; } @@ -149,24 +152,22 @@ export async function activate( if (!check.result) { if (check.forcedConfigs && check.forcedConfigs.length > 0) { if (configKey === "mode") { - UpdateConfig.setMode(check.forcedConfigs[0] as Mode); + setConfig("mode", check.forcedConfigs[0] as Mode); } if (configKey === "words") { - UpdateConfig.setWordCount(check.forcedConfigs[0] as number); + setConfig("words", check.forcedConfigs[0] as number); } if (configKey === "time") { - UpdateConfig.setTimeConfig(check.forcedConfigs[0] as number); + setConfig("time", check.forcedConfigs[0] as number); } if (configKey === "punctuation") { - UpdateConfig.setPunctuation(check.forcedConfigs[0] as boolean); + setConfig("punctuation", check.forcedConfigs[0] as boolean); } if (configKey === "numbers") { - UpdateConfig.setNumbers(check.forcedConfigs[0] as boolean); + setConfig("numbers", check.forcedConfigs[0] as boolean); } if (configKey === "highlightMode") { - UpdateConfig.setHighlightMode( - check.forcedConfigs[0] as HighlightMode, - ); + setConfig("highlightMode", check.forcedConfigs[0] as HighlightMode); } } else { canSetSoFar = false; @@ -187,7 +188,7 @@ export async function activate( -1, ); } - UpdateConfig.setFunbox([], true); + setConfig("funbox", [], true); await clear(); return; } diff --git a/frontend/src/ts/test/practise-words.ts b/frontend/src/ts/test/practise-words.ts index 3ad29d0eced1..0d3a9018f1cb 100644 --- a/frontend/src/ts/test/practise-words.ts +++ b/frontend/src/ts/test/practise-words.ts @@ -1,6 +1,6 @@ import * as TestWords from "./test-words"; import * as Notifications from "../elements/notifications"; -import Config, * as UpdateConfig from "../config"; +import Config, { setConfig } from "../config"; import * as CustomText from "./custom-text"; import * as TestInput from "./test-input"; import * as ConfigEvent from "../observables/config-event"; @@ -148,7 +148,7 @@ export function init( customText = CustomText.getData(); } - UpdateConfig.setMode("custom", true); + setConfig("mode", "custom", 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 365708a7ecaa..c123ba45b8f4 100644 --- a/frontend/src/ts/test/test-logic.ts +++ b/frontend/src/ts/test/test-logic.ts @@ -1,7 +1,7 @@ import Ape from "../ape"; import * as TestUI from "./test-ui"; import * as ManualRestart from "./manual-restart-tracker"; -import Config, * as UpdateConfig from "../config"; +import Config, { setConfig, setQuoteLengthAll, toggleFunbox } from "../config"; import * as Strings from "../utils/strings"; import * as Misc from "../utils/misc"; import * as Arrays from "../utils/arrays"; @@ -252,10 +252,10 @@ export function restart(options = {} as RestartOptions): void { ) { Notifications.add("Reverting to previous settings.", 0); if (PractiseWords.before.punctuation !== null) { - UpdateConfig.setPunctuation(PractiseWords.before.punctuation); + setConfig("punctuation", PractiseWords.before.punctuation); } if (PractiseWords.before.numbers !== null) { - UpdateConfig.setNumbers(PractiseWords.before.numbers); + setConfig("numbers", PractiseWords.before.numbers); } if (PractiseWords.before.customText) { @@ -267,7 +267,7 @@ export function restart(options = {} as RestartOptions): void { ); } - UpdateConfig.setMode(PractiseWords.before.mode); + setConfig("mode", PractiseWords.before.mode); PractiseWords.resetBefore(); } @@ -459,7 +459,7 @@ async function init(): Promise { if (Config.mode === "quote") { if (Config.quoteLength.includes(-3) && !isAuthenticated()) { - UpdateConfig.setQuoteLengthAll(); + setQuoteLengthAll(); } } @@ -497,9 +497,9 @@ async function init(): Promise { important: true, }, ); - UpdateConfig.setLazyMode(false, false); + setConfig("lazyMode", false, false); } else if (rememberLazyMode && anySupportsLazyMode) { - UpdateConfig.setLazyMode(true, true); + setConfig("lazyMode", true, true); } } else { // normal mode @@ -510,9 +510,9 @@ async function init(): Promise { important: true, }); - UpdateConfig.setLazyMode(false, false); + setConfig("lazyMode", false, false); } else if (rememberLazyMode && !language.noLazyMode) { - UpdateConfig.setLazyMode(true, true); + setConfig("lazyMode", true, true); } } @@ -699,7 +699,7 @@ export async function addWord(): Promise { "Error while getting section. Please try again later", -1, ); - UpdateConfig.toggleFunbox(sectionFunbox.name); + toggleFunbox(sectionFunbox.name); restart(); return; } @@ -1511,7 +1511,7 @@ $(".pageTest").on("click", "#testConfig .mode .textButton", (e) => { if ($(e.currentTarget).hasClass("active")) return; const mode = ($(e.currentTarget).attr("mode") ?? "time") as Mode; if (mode === undefined) return; - if (UpdateConfig.setMode(mode)) { + if (setConfig("mode", mode)) { ManualRestart.set(); restart(); } @@ -1521,7 +1521,7 @@ $(".pageTest").on("click", "#testConfig .wordCount .textButton", (e) => { if (TestState.testRestarting) return; const wrd = $(e.currentTarget).attr("wordCount") ?? "15"; if (wrd !== "custom") { - if (UpdateConfig.setWordCount(parseInt(wrd))) { + if (setConfig("words", parseInt(wrd))) { ManualRestart.set(); restart(); } @@ -1532,7 +1532,7 @@ $(".pageTest").on("click", "#testConfig .time .textButton", (e) => { if (TestState.testRestarting) return; const mode = $(e.currentTarget).attr("timeConfig") ?? "10"; if (mode !== "custom") { - if (UpdateConfig.setTimeConfig(parseInt(mode))) { + if (setConfig("time", parseInt(mode))) { ManualRestart.set(); restart(); } @@ -1543,7 +1543,7 @@ $(".pageTest").on("click", "#testConfig .quoteLength .textButton", (e) => { if (TestState.testRestarting) return; const lenAttr = $(e.currentTarget).attr("quoteLength"); if (lenAttr === "all") { - if (UpdateConfig.setQuoteLengthAll()) { + if (setQuoteLengthAll()) { ManualRestart.set(); restart(); } @@ -1559,7 +1559,7 @@ $(".pageTest").on("click", "#testConfig .quoteLength .textButton", (e) => { arr = [len]; } - if (UpdateConfig.setQuoteLength(arr, false)) { + if (setConfig("quoteLength", arr, false)) { ManualRestart.set(); restart(); } @@ -1569,7 +1569,7 @@ $(".pageTest").on("click", "#testConfig .quoteLength .textButton", (e) => { $(".pageTest").on("click", "#testConfig .punctuationMode.textButton", () => { if (TestState.testRestarting) return; - if (UpdateConfig.setPunctuation(!Config.punctuation)) { + if (setConfig("punctuation", !Config.punctuation)) { ManualRestart.set(); restart(); } @@ -1577,7 +1577,7 @@ $(".pageTest").on("click", "#testConfig .punctuationMode.textButton", () => { $(".pageTest").on("click", "#testConfig .numbersMode.textButton", () => { if (TestState.testRestarting) return; - if (UpdateConfig.setNumbers(!Config.numbers)) { + if (setConfig("numbers", !Config.numbers)) { ManualRestart.set(); restart(); } @@ -1598,7 +1598,7 @@ ConfigEvent.subscribe((eventKey, eventValue, nosave) => { (eventValue as string)?.startsWith("arabic") && ArabicLazyMode.get() ) { - UpdateConfig.setLazyMode(true, true); + setConfig("lazyMode", true, true); } restart(); } diff --git a/frontend/src/ts/test/test-timer.ts b/frontend/src/ts/test/test-timer.ts index 823f4a136e2d..55eb5f641c9a 100644 --- a/frontend/src/ts/test/test-timer.ts +++ b/frontend/src/ts/test/test-timer.ts @@ -1,7 +1,7 @@ //most of the code is thanks to //https://stackoverflow.com/questions/29971898/how-to-create-an-accurate-timer-in-javascript -import Config, * as UpdateConfig from "../config"; +import Config, { setConfig } from "../config"; import * as CustomText from "./custom-text"; import * as TimerProgress from "./timer-progress"; import * as LiveSpeed from "./live-speed"; @@ -127,8 +127,8 @@ function layoutfluid(): void { if (Config.layout !== layout && layout !== undefined) { LayoutfluidFunboxTimer.hide(); - UpdateConfig.setLayout(layout as Layout, true); - UpdateConfig.setKeymapLayout(layout as KeymapLayout, true); + setConfig("layout", layout as Layout, true); + setConfig("keymapLayout", layout as KeymapLayout, true); } } if (timerDebug) console.timeEnd("layoutfluid"); diff --git a/frontend/src/ts/test/test-ui.ts b/frontend/src/ts/test/test-ui.ts index e70bce04f8b9..f9bc373dc110 100644 --- a/frontend/src/ts/test/test-ui.ts +++ b/frontend/src/ts/test/test-ui.ts @@ -1,6 +1,6 @@ import * as Notifications from "../elements/notifications"; import * as ThemeColors from "../elements/theme-colors"; -import Config, * as UpdateConfig from "../config"; +import Config, { setConfig } from "../config"; import * as TestWords from "./test-words"; import * as TestInput from "./test-input"; import * as CustomText from "./custom-text"; @@ -1964,7 +1964,7 @@ async function copyToClipboard(content: string): Promise { } $(".pageTest #toggleBurstHeatmap").on("click", async () => { - UpdateConfig.setBurstHeatmap(!Config.burstHeatmap); + setConfig("burstHeatmap", !Config.burstHeatmap); ResultWordHighlight.destroy(); }); diff --git a/frontend/src/ts/test/words-generator.ts b/frontend/src/ts/test/words-generator.ts index bca09c16433f..a8de5f054090 100644 --- a/frontend/src/ts/test/words-generator.ts +++ b/frontend/src/ts/test/words-generator.ts @@ -1,4 +1,4 @@ -import Config, * as UpdateConfig from "../config"; +import Config, { setConfig, setQuoteLengthAll, toggleFunbox } from "../config"; import * as CustomText from "./custom-text"; import { Wordset, FunboxWordsFrequency, withWords } from "./wordset"; import QuotesController, { @@ -329,7 +329,7 @@ async function getFunboxSection(): Promise { const section = await funbox.functions.pullSection(Config.language); if (section === false || section === undefined) { - UpdateConfig.toggleFunbox(funbox.name); + toggleFunbox(funbox.name); throw new Error("Failed to pull section"); } @@ -523,7 +523,7 @@ async function getQuoteWordList( Loader.hide(); if (quotesCollection.length === 0) { - UpdateConfig.setMode("words"); + setConfig("mode", "words"); throw new WordGenError( `No ${Config.language .replace(/_\d*k$/g, "") @@ -537,7 +537,7 @@ async function getQuoteWordList( TestState.selectedQuoteId, ); if (targetQuote === undefined) { - UpdateConfig.setQuoteLengthAll(); + setQuoteLengthAll(); throw new WordGenError( `Quote ${TestState.selectedQuoteId} does not exist`, ); @@ -548,14 +548,14 @@ async function getQuoteWordList( Config.language, ); if (randomQuote === null) { - UpdateConfig.setQuoteLengthAll(); + setQuoteLengthAll(); throw new WordGenError("No favorite quotes found"); } rq = randomQuote; } else { const randomQuote = QuotesController.getRandomQuote(); if (randomQuote === null) { - UpdateConfig.setQuoteLengthAll(); + setQuoteLengthAll(); throw new WordGenError("No quotes found for selected quote length"); } rq = randomQuote; diff --git a/frontend/src/ts/utils/url-handler.ts b/frontend/src/ts/utils/url-handler.ts index cb205a6105e2..0cbab7c8660b 100644 --- a/frontend/src/ts/utils/url-handler.ts +++ b/frontend/src/ts/utils/url-handler.ts @@ -1,5 +1,5 @@ import * as Misc from "./misc"; -import Config, * as UpdateConfig from "../config"; +import Config, { setConfig } from "../config"; import * as Notifications from "../elements/notifications"; import { decompressFromURI } from "lz-ts"; import * as TestState from "../test/test-state"; @@ -117,21 +117,21 @@ export function loadCustomThemeFromUrl(getOverride?: string): void { const oldCustomTheme = Config.customTheme; const oldCustomThemeColors = Config.customThemeColors; try { - UpdateConfig.setCustomThemeColors(colorArray); + setConfig("customThemeColors", colorArray); Notifications.add("Custom theme applied", 1); if (image !== undefined && size !== undefined && filter !== undefined) { - UpdateConfig.setCustomBackground(image); - UpdateConfig.setCustomBackgroundSize(size); - UpdateConfig.setCustomBackgroundFilter(filter); + setConfig("customBackground", image); + setConfig("customBackgroundSize", size); + setConfig("customBackgroundFilter", filter); } - if (!Config.customTheme) UpdateConfig.setCustomTheme(true); + if (!Config.customTheme) setConfig("customTheme", true); } catch (e) { Notifications.add("Something went wrong. Reverting to previous state.", 0); console.error(e); - UpdateConfig.setCustomTheme(oldCustomTheme); - UpdateConfig.setCustomThemeColors(oldCustomThemeColors); + setConfig("customTheme", oldCustomTheme); + setConfig("customThemeColors", oldCustomThemeColors); } } @@ -181,18 +181,18 @@ export function loadTestSettingsFromUrl(getOverride?: string): void { const applied: Record = {}; if (de[0] !== null) { - UpdateConfig.setMode(de[0], true); + setConfig("mode", de[0], true); applied["mode"] = de[0]; } const mode = de[0] ?? Config.mode; if (de[1] !== null) { if (mode === "time") { - UpdateConfig.setTimeConfig(parseInt(de[1], 10), true); + setConfig("time", parseInt(de[1], 10), true); } else if (mode === "words") { - UpdateConfig.setWordCount(parseInt(de[1], 10), true); + setConfig("words", parseInt(de[1], 10), true); } else if (mode === "quote") { - UpdateConfig.setQuoteLength([-2], false); + setConfig("quoteLength", [-2], false); TestState.setSelectedQuoteId(parseInt(de[1], 10)); ManualRestart.set(); } @@ -236,22 +236,22 @@ export function loadTestSettingsFromUrl(getOverride?: string): void { } if (de[3] !== null) { - UpdateConfig.setPunctuation(de[3], true); + setConfig("punctuation", de[3], true); applied["punctuation"] = de[3] ? "on" : "off"; } if (de[4] !== null) { - UpdateConfig.setNumbers(de[4], true); + setConfig("numbers", de[4], true); applied["numbers"] = de[4] ? "on" : "off"; } if (de[5] !== null) { - UpdateConfig.setLanguage(de[5] as Language, true); + setConfig("language", de[5] as Language, true); applied["language"] = de[5]; } if (de[6] !== null) { - UpdateConfig.setDifficulty(de[6], true); + setConfig("difficulty", de[6], true); applied["difficulty"] = de[6]; } @@ -263,7 +263,7 @@ export function loadTestSettingsFromUrl(getOverride?: string): void { } else { val = de[7]; } - UpdateConfig.setFunbox(val, true); + setConfig("funbox", val, true); applied["funbox"] = val.join(", "); } From 67a34e09323ba60b7b2f8b3c954d2a373283f07e Mon Sep 17 00:00:00 2001 From: Miodec Date: Thu, 11 Dec 2025 14:27:50 +0100 Subject: [PATCH 2/4] fix: two line jumps in a row breaking caret positioning --- frontend/src/ts/utils/caret.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/frontend/src/ts/utils/caret.ts b/frontend/src/ts/utils/caret.ts index 3e000a1b6297..c0c31bae2c9c 100644 --- a/frontend/src/ts/utils/caret.ts +++ b/frontend/src/ts/utils/caret.ts @@ -215,6 +215,14 @@ export class Caret { // making sure to use a separate animation queue so that it doesnt // affect the position animations if (this.isMainCaret && options.duration === 0) return; + + // in case we have two line jumps in a row + if (this.readyToResetMarginTop) { + $(this.element).css({ + marginTop: 0, + }); + } + this.readyToResetMarginTop = false; if (options.duration === 0) { From da535a6f307625a40c436b0cc19b994fb18e35f3 Mon Sep 17 00:00:00 2001 From: Jack Date: Thu, 11 Dec 2025 17:12:24 +0100 Subject: [PATCH 3/4] refactor: use dom utils in caret code (@miodec) (#7218) Also adds a few more utils --- frontend/src/ts/test/caret.ts | 6 +- frontend/src/ts/test/pace-caret.ts | 6 +- frontend/src/ts/utils/caret.ts | 209 +++++++++++++++-------------- frontend/src/ts/utils/dom.ts | 50 +++++++ 4 files changed, 159 insertions(+), 112 deletions(-) diff --git a/frontend/src/ts/test/caret.ts b/frontend/src/ts/test/caret.ts index 65aa351ad751..2c3728a8204f 100644 --- a/frontend/src/ts/test/caret.ts +++ b/frontend/src/ts/test/caret.ts @@ -4,6 +4,7 @@ import * as TestState from "../test/test-state"; import { subscribe } from "../observables/config-event"; import { Caret } from "../utils/caret"; import * as CompositionState from "../states/composition"; +import { qsr } from "../utils/dom"; export function stopAnimation(): void { caret.stopBlinking(); @@ -40,10 +41,7 @@ export function updatePosition(noAnim = false): void { }); } -export const caret = new Caret( - document.getElementById("caret") as HTMLElement, - Config.caretStyle, -); +export const caret = new Caret(qsr("#caret"), Config.caretStyle); subscribe((eventKey) => { if (eventKey === "caretStyle") { diff --git a/frontend/src/ts/test/pace-caret.ts b/frontend/src/ts/test/pace-caret.ts index 0176d9da97f5..e9d6b6b83bf3 100644 --- a/frontend/src/ts/test/pace-caret.ts +++ b/frontend/src/ts/test/pace-caret.ts @@ -6,6 +6,7 @@ import * as TestState from "./test-state"; import * as ConfigEvent from "../observables/config-event"; import { getActiveFunboxes } from "./funbox/list"; import { Caret } from "../utils/caret"; +import { qsr } from "../utils/dom"; type Settings = { wpm: number; @@ -22,10 +23,7 @@ let startTimestamp = 0; export let settings: Settings | null = null; -export const caret = new Caret( - document.getElementById("paceCaret") as HTMLElement, - Config.paceCaretStyle, -); +export const caret = new Caret(qsr("#paceCaret"), Config.paceCaretStyle); let lastTestWpm = 0; diff --git a/frontend/src/ts/utils/caret.ts b/frontend/src/ts/utils/caret.ts index c0c31bae2c9c..d782ebf28c7b 100644 --- a/frontend/src/ts/utils/caret.ts +++ b/frontend/src/ts/utils/caret.ts @@ -4,12 +4,11 @@ import * as TestWords from "../test/test-words"; import { getTotalInlineMargin } from "./misc"; import { isWordRightToLeft } from "./strings"; import { requestDebouncedAnimationFrame } from "./debounced-animation-frame"; -import { animate, EasingParam, JSAnimation } from "animejs"; +import { EasingParam, JSAnimation } from "animejs"; +import { ElementWithUtils, qsr } from "./dom"; -const wordsCache = document.querySelector("#words") as HTMLElement; -const wordsWrapperCache = document.querySelector( - "#wordsWrapper", -) as HTMLElement; +const wordsCache = qsr("#words"); +const wordsWrapperCache = qsr("#wordsWrapper"); let lockedMainCaretInTape = true; let caretDebug = false; @@ -31,7 +30,7 @@ export function toggleCaretDebug(): void { export class Caret { private id: string; - private element: HTMLElement; + private element: ElementWithUtils; private style: CaretStyle = "default"; private readyToResetMarginTop: boolean = false; private readyToResetMarginLeft: boolean = false; @@ -42,8 +41,8 @@ export class Caret { private marginTopAnimation: JSAnimation | null = null; private marginLeftAnimation: JSAnimation | null = null; - constructor(element: HTMLElement, style: CaretStyle) { - this.id = element.id; + constructor(element: ElementWithUtils, style: CaretStyle) { + this.id = element.native.id; this.element = element; this.setStyle(style); if (this.id === "caret") { @@ -53,55 +52,49 @@ export class Caret { public setStyle(style: CaretStyle): void { this.style = style; - this.element.style.width = ""; - this.element.classList.remove( - ...[ - "off", - "default", - "underline", - "outline", - "block", - "carrot", - "banana", - "monkey", - ], - ); - this.element.classList.add(style); - } - - public getElement(): HTMLElement { - return this.element; + this.resetWidth(); + this.element.removeClass([ + "off", + "default", + "underline", + "outline", + "block", + "carrot", + "banana", + "monkey", + ]); + this.element.addClass(style); } public show(): void { - this.element.classList.remove("hidden"); - this.element.style.display = ""; + this.element.show(); + this.element.setStyle({ display: "" }); } public hide(): void { - this.element.classList.add("hidden"); + this.element.hide(); } public isHidden(): boolean { - return this.element.classList.contains("hidden"); + return this.element.hasClass("hidden"); } public getWidth(): number { - return this.element.offsetWidth; + return this.element.getOffsetWidth(); } public resetWidth(): void { - this.element.style.width = ""; + this.element.setStyle({ width: "" }); } public getHeight(): number { if (!this.isHidden()) { - return this.element.offsetHeight; + return this.element.getOffsetHeight(); } let height = 0; this.show(); - height = this.element.offsetHeight; + height = this.element.getOffsetHeight(); this.hide(); return height; } @@ -116,31 +109,33 @@ export class Caret { width?: number; }): void { this.posAnimation?.cancel(); - this.element.style.left = `${options.left}px`; - this.element.style.top = `${options.top}px`; + let newStyle: Record = { + left: `${options.left}px`, + top: `${options.top}px`, + }; if (options.width !== undefined) { - this.element.style.width = `${options.width}px`; + newStyle = { ...newStyle, width: `${options.width}px` }; } + this.element.setStyle(newStyle); } public startBlinking(): void { if (Config.smoothCaret !== "off") { - this.element.style.animationName = "caretFlashSmooth"; + this.element.setStyle({ animationName: "caretFlashSmooth" }); } else { - this.element.style.animationName = "caretFlashHard"; + this.element.setStyle({ animationName: "caretFlashHard" }); } } public stopBlinking(): void { - this.element.style.animationName = "none"; - this.element.style.opacity = "1"; + this.element.setStyle({ animationName: "none", opacity: "1" }); } public updateBlinkingAnimation(): void { if (Config.smoothCaret === "off") { - this.element.style.animationName = "caretFlashHard"; + this.element.setStyle({ animationName: "caretFlashHard" }); } else { - this.element.style.animationName = "caretFlashSmooth"; + this.element.setStyle({ animationName: "caretFlashSmooth" }); } } @@ -151,8 +146,7 @@ export class Caret { } public clearMargins(): void { - this.element.style.marginTop = ""; - this.element.style.marginLeft = ""; + this.element.setStyle({ marginTop: "", marginLeft: "" }); this.readyToResetMarginTop = false; this.readyToResetMarginLeft = false; this.cumulativeTapeMarginCorrection = 0; @@ -187,12 +181,12 @@ export class Caret { if (options.duration === 0) { this.marginLeftAnimation?.cancel(); - this.element.style.marginLeft = `${newMarginLeft}px`; + this.element.setStyle({ marginLeft: `${newMarginLeft}px` }); this.readyToResetMarginLeft = true; return; } - this.marginLeftAnimation = animate(this.element, { + this.marginLeftAnimation = this.element.animate({ marginLeft: newMarginLeft, duration: options.duration, ease: options.ease, @@ -218,8 +212,8 @@ export class Caret { // in case we have two line jumps in a row if (this.readyToResetMarginTop) { - $(this.element).css({ - marginTop: 0, + this.element.setStyle({ + marginTop: "0px", }); } @@ -227,12 +221,12 @@ export class Caret { if (options.duration === 0) { this.marginTopAnimation?.cancel(); - this.element.style.marginTop = `${options.newMarginTop}px`; + this.element.setStyle({ marginTop: `${options.newMarginTop}px` }); this.readyToResetMarginTop = true; return; } - this.marginTopAnimation = animate(this.element, { + this.marginTopAnimation = this.element.animate({ marginTop: options.newMarginTop, duration: options.duration, onComplete: () => { @@ -270,7 +264,7 @@ export class Caret { animation["width"] = options.width; } - this.posAnimation = animate(this.element, { + this.posAnimation = this.element.animate({ ...animation, duration: finalDuration, ease: options.easing ?? "inOut(1.25)", @@ -290,10 +284,10 @@ export class Caret { }): void { if (this.style === "off") return; requestDebouncedAnimationFrame(`caret.${this.id}.goTo`, () => { - const word = wordsCache.querySelector( + const word = wordsCache.qs( `.word[data-wordindex="${options.wordIndex}"]`, ); - const letters = word?.querySelectorAll("letter") ?? []; + const letters = word?.qsa("letter") ?? []; const wordText = TestWords.words.get(options.wordIndex); // caret can be either on the left side of the target letter or the right @@ -314,8 +308,7 @@ export class Caret { options.letterIndex = 0; } - let letter = - word?.querySelectorAll("letter")[options.letterIndex]; + let letter = word?.qsa("letter")[options.letterIndex]; if (word === null || letter === undefined) { return; @@ -328,11 +321,11 @@ export class Caret { l.classList.remove("debugCaretTarget2"); l.classList.add("debugCaret"); } - letter?.classList.add("debugCaretTarget"); - this.element.classList.add("debug"); + letter?.addClass("debugCaretTarget"); + this.element.addClass("debug"); } } else { - this.element.classList.remove("debug"); + this.element.removeClass("debug"); } const { left, top, width } = this.getTargetPositionAndWidth({ @@ -349,27 +342,31 @@ export class Caret { // if the margin animation finished, we reset it here by removing the margin // and offsetting the top by the same amount - let currentMarginTop = parseFloat(this.element.style.marginTop || "0"); + let currentMarginTop = parseFloat( + this.element.getStyle().marginTop || "0", + ); if (this.readyToResetMarginTop) { this.readyToResetMarginTop = false; - const currentTop = parseFloat(this.element.style.top || "0"); + const currentTop = parseFloat(this.element.getStyle().top || "0"); - $(this.element).css({ - marginTop: 0, - top: currentTop + currentMarginTop, + this.element.setStyle({ + marginTop: "0px", + top: `${currentTop + currentMarginTop}px`, }); currentMarginTop = 0; } // same for marginLeft - let currentMarginLeft = parseFloat(this.element.style.marginLeft || "0"); + let currentMarginLeft = parseFloat( + this.element.getStyle().marginLeft || "0", + ); if (this.readyToResetMarginLeft) { this.readyToResetMarginLeft = false; - const currentLeft = parseFloat(this.element.style.left || "0"); + const currentLeft = parseFloat(this.element.getStyle().left || "0"); - $(this.element).css({ - marginLeft: 0, - left: currentLeft + currentMarginLeft, + this.element.setStyle({ + marginLeft: "0px", + left: `${currentLeft + currentMarginLeft}px`, }); this.cumulativeTapeMarginCorrection += currentMarginLeft; currentMarginLeft = 0; @@ -398,8 +395,8 @@ export class Caret { } private getTargetPositionAndWidth(options: { - word: HTMLElement; - letter: HTMLElement; + word: ElementWithUtils; + letter: ElementWithUtils; wordText: string; side: "beforeLetter" | "afterLetter"; isLanguageRightToLeft: boolean; @@ -412,9 +409,9 @@ export class Caret { ); //if the letter is not visible, use the closest visible letter - const isLetterVisible = options.letter.offsetWidth > 0; + const isLetterVisible = options.letter.getOffsetWidth() > 0; if (!isLetterVisible) { - const letters = options.word.querySelectorAll("letter"); + const letters = options.word.qsa("letter"); if (letters.length === 0) { throw new Error("Caret getLeftTopWidth: no letters found in word"); } @@ -422,7 +419,7 @@ export class Caret { // ignore letters after the current letter let ignore = true; for (let i = letters.length - 1; i >= 0; i--) { - const loopLetter = letters[i] as HTMLElement; + const loopLetter = letters[i] as ElementWithUtils; if (loopLetter === options.letter) { // at the current letter, stop ignoring, continue to the next ignore = false; @@ -431,20 +428,20 @@ export class Caret { if (ignore) continue; // found the closest visible letter before the current letter - if (loopLetter.offsetWidth > 0) { + if (loopLetter.getOffsetWidth() > 0) { options.letter = loopLetter; break; } } if (caretDebug) { - options.letter.classList.add("debugCaretTarget2"); + options.letter.addClass("debugCaretTarget2"); } } - const spaceWidth = getTotalInlineMargin(options.word); + const spaceWidth = getTotalInlineMargin(options.word.native); let width = spaceWidth; if (this.isFullWidth() && options.side === "beforeLetter") { - width = options.letter.offsetWidth; + width = options.letter.getOffsetWidth(); } let left = 0; @@ -457,38 +454,40 @@ export class Caret { if (this.isFullWidth()) { afterLetterCorrection += spaceWidth * -1; } else { - afterLetterCorrection += options.letter.offsetWidth * -1; + afterLetterCorrection += options.letter.getOffsetWidth() * -1; } } if (Config.tapeMode === "off") { if (!this.isFullWidth()) { - left += options.letter.offsetWidth; + left += options.letter.getOffsetWidth(); } - left += options.letter.offsetLeft; - left += options.word.offsetLeft; + left += options.letter.getOffsetLeft(); + left += options.word.getOffsetLeft(); left += afterLetterCorrection; } else if (Config.tapeMode === "word") { if (!this.isFullWidth()) { - left += options.letter.offsetWidth; + left += options.letter.getOffsetWidth(); } - left += options.word.offsetWidth * -1; - left += options.letter.offsetLeft; + left += options.word.getOffsetWidth() * -1; + left += options.letter.getOffsetLeft(); left += afterLetterCorrection; if (this.isMainCaret && lockedMainCaretInTape) { - left += wordsWrapperCache.offsetWidth * (Config.tapeMargin / 100); + left += + wordsWrapperCache.getOffsetWidth() * (Config.tapeMargin / 100); } else { - left += options.word.offsetLeft; - left += options.word.offsetWidth; + left += options.word.getOffsetLeft(); + left += options.word.getOffsetWidth(); } } else if (Config.tapeMode === "letter") { if (this.isFullWidth()) { left += width * -1; } if (this.isMainCaret && lockedMainCaretInTape) { - left += wordsWrapperCache.offsetWidth * (Config.tapeMargin / 100); + left += + wordsWrapperCache.getOffsetWidth() * (Config.tapeMargin / 100); } else { - left += options.letter.offsetLeft; - left += options.word.offsetLeft; + left += options.letter.getOffsetLeft(); + left += options.word.getOffsetLeft(); left += afterLetterCorrection; left += width; } @@ -496,41 +495,43 @@ export class Caret { } else { let afterLetterCorrection = 0; if (options.side === "afterLetter") { - afterLetterCorrection += options.letter.offsetWidth; + afterLetterCorrection += options.letter.getOffsetWidth(); } if (Config.tapeMode === "off") { - left += options.letter.offsetLeft; - left += options.word.offsetLeft; + left += options.letter.getOffsetLeft(); + left += options.word.getOffsetLeft(); left += afterLetterCorrection; } else if (Config.tapeMode === "word") { - left += options.letter.offsetLeft; + left += options.letter.getOffsetLeft(); left += afterLetterCorrection; if (this.isMainCaret && lockedMainCaretInTape) { - left += wordsWrapperCache.offsetWidth * (Config.tapeMargin / 100); + left += + wordsWrapperCache.getOffsetWidth() * (Config.tapeMargin / 100); } else { - left += options.word.offsetLeft; + left += options.word.getOffsetLeft(); } } else if (Config.tapeMode === "letter") { if (this.isMainCaret && lockedMainCaretInTape) { - left += wordsWrapperCache.offsetWidth * (Config.tapeMargin / 100); + left += + wordsWrapperCache.getOffsetWidth() * (Config.tapeMargin / 100); } else { - left += options.letter.offsetLeft; - left += options.word.offsetLeft; + left += options.letter.getOffsetLeft(); + left += options.word.getOffsetLeft(); left += afterLetterCorrection; } } } //top position - top += options.letter.offsetTop; - top += options.word.offsetTop; + top += options.letter.getOffsetTop(); + top += options.word.getOffsetTop(); if (this.style === "underline") { // if style is underline, add the height of the letter to the top - top += options.letter.offsetHeight; + top += options.letter.getOffsetHeight(); } else { // else center vertically in the letter - top += (options.letter.offsetHeight - this.getHeight()) / 2; + top += (options.letter.getOffsetHeight() - this.getHeight()) / 2; } // also center horizontally diff --git a/frontend/src/ts/utils/dom.ts b/frontend/src/ts/utils/dom.ts index 6befb2eabbde..f47fa6e6b4a4 100644 --- a/frontend/src/ts/utils/dom.ts +++ b/frontend/src/ts/utils/dom.ts @@ -1,3 +1,9 @@ +import { + animate as animejsAnimate, + AnimationParams, + JSAnimation, +} from "animejs"; + /** * Query Selector * @@ -310,6 +316,13 @@ export class ElementWithUtils { return this; } + /** + * Get the element's style object + */ + getStyle(): CSSStyleDeclaration { + return this.native.style; + } + /** * Check if the element is focused */ @@ -464,6 +477,43 @@ export class ElementWithUtils { } return this; } + + /** + * Get the element's width + */ + getOffsetWidth(): number { + return this.native.offsetWidth; + } + + /** + * Get the element's height + */ + getOffsetHeight(): number { + return this.native.offsetHeight; + } + + /** + * Get the element's top offset relative to its offsetParent + */ + getOffsetTop(): number { + return this.native.offsetTop; + } + + /** + * Get the element's left offset relative to its offsetParent + */ + getOffsetLeft(): number { + return this.native.offsetLeft; + } + + /** + * Animate the element using Anime.js + * @param animationParams The Anime.js animation parameters + * @returns The JSAnimation instance created by Anime.js + */ + animate(animationParams: AnimationParams): JSAnimation { + return animejsAnimate(this.native, animationParams); + } } /** From 95bd39a20e6a2df2c32467dafe4e217115e8e478 Mon Sep 17 00:00:00 2001 From: Lorenz De Robles <124335421+lorenzjdr@users.noreply.github.com> Date: Thu, 11 Dec 2025 10:13:49 -0600 Subject: [PATCH 4/4] impr(quotes): add english quote (@lorenzjdr) (#7214) ### Description Added a quote from 1984 by George Orwell. ### Checks - [ ] Adding/modifying Typescript code? - [ ] I have used `qs`,`qsa` or `qsr` instead of JQuery selectors. - [x] Adding quotes? - [ ] Make sure to include translations for the quotes in the description (or another comment) so we can verify their content. - [ ] Adding a language? - Make sure to follow the [languages documentation](https://github.com/monkeytypegame/monkeytype/blob/master/docs/LANGUAGES.md) - [ ] Add language to `packages/schemas/src/languages.ts` - [ ] Add language to exactly one group in `frontend/src/ts/constants/languages.ts` - [ ] Add language json file to `frontend/static/languages` - [ ] Adding a theme? - Make sure to follow the [themes documentation](https://github.com/monkeytypegame/monkeytype/blob/master/docs/THEMES.md) - [ ] Add theme to `packages/schemas/src/themes.ts` - [ ] Add theme to `frontend/src/ts/constants/themes.ts` - [ ] Add theme css file to `frontend/static/themes` - [ ] Add some screenshot of the theme, especially with different test settings (colorful, flip colors) to your pull request - [ ] Adding a layout? - [ ] Make sure to follow the [layouts documentation](https://github.com/monkeytypegame/monkeytype/blob/master/docs/LAYOUTS.md) - [ ] Add layout to `packages/schemas/src/layouts.ts` - [ ] Add layout json file to `frontend/static/layouts` - [ ] Adding a font? - Make sure to follow the [themes documentation](https://github.com/monkeytypegame/monkeytype/blob/master/docs/FONTS.md) - [ ] Add font file to `frontend/static/webfonts` - [ ] Add font to `packages/schemas/src/fonts.ts` - [ ] Add font to `frontend/src/ts/constants/fonts.ts` - [ ] Check if any open issues are related to this PR; if so, be sure to tag them below. - [ ] Make sure the PR title follows the Conventional Commits standard. (https://www.conventionalcommits.org for more info) - [ ] Make sure to include your GitHub username prefixed with @ inside parentheses at the end of the PR title. Closes # --- frontend/static/quotes/english.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frontend/static/quotes/english.json b/frontend/static/quotes/english.json index a3c8192e8975..f75c31058bb1 100644 --- a/frontend/static/quotes/english.json +++ b/frontend/static/quotes/english.json @@ -39105,6 +39105,12 @@ "source": "When Breath Becomes Air", "id": 7730, "length": 111 + }, + { + "text": "In this game that we're playing, we can't win. Some kinds of failure are better than other kinds, that's all.", + "source": "George Orwell, 1984", + "id": 7731, + "length": 109 } ] }