mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2025-12-29 19:32:26 +08:00
Merge branch 'master' into batchsetconfig
This commit is contained in:
commit
122178eac9
33 changed files with 312 additions and 132 deletions
|
|
@ -212,7 +212,9 @@ describe("Config", () => {
|
|||
replaceConfig({ numbers: false });
|
||||
|
||||
//WHEN
|
||||
Config.setConfig("numbers", true, true);
|
||||
Config.setConfig("numbers", true, {
|
||||
nosave: true,
|
||||
});
|
||||
|
||||
//THEN
|
||||
//wait for debounce
|
||||
|
|
@ -227,7 +229,9 @@ describe("Config", () => {
|
|||
replaceConfig({ numbers: false });
|
||||
|
||||
//WHEN
|
||||
Config.setConfig("numbers", true, true);
|
||||
Config.setConfig("numbers", true, {
|
||||
nosave: true,
|
||||
});
|
||||
|
||||
//THEN
|
||||
|
||||
|
|
@ -241,21 +245,21 @@ describe("Config", () => {
|
|||
|
||||
it("triggers resize if property is set", () => {
|
||||
///WHEN
|
||||
Config.setConfig("maxLineWidth", 50, false);
|
||||
Config.setConfig("maxLineWidth", 50);
|
||||
|
||||
expect(miscTriggerResizeMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not triggers resize if property is not set", () => {
|
||||
///WHEN
|
||||
Config.setConfig("startGraphsAtZero", true, false);
|
||||
Config.setConfig("startGraphsAtZero", true);
|
||||
|
||||
expect(miscTriggerResizeMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not triggers resize if property on nosave", () => {
|
||||
///WHEN
|
||||
Config.setConfig("maxLineWidth", 50, true);
|
||||
Config.setConfig("maxLineWidth", 50, { nosave: true });
|
||||
|
||||
expect(miscTriggerResizeMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ vi.mock("../src/ts/utils/dom", () => {
|
|||
getOffsetTop: vi.fn().mockReturnValue(0),
|
||||
getOffsetLeft: vi.fn().mockReturnValue(0),
|
||||
animate: vi.fn().mockResolvedValue(null),
|
||||
promiseAnimate: vi.fn().mockResolvedValue(null),
|
||||
native: document.createElement("div"),
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -56,8 +56,12 @@ describe("url-handler", () => {
|
|||
loadTestSettingsFromUrl("");
|
||||
|
||||
//THEN
|
||||
expect(setConfigMock).toHaveBeenCalledWith("mode", "time", true);
|
||||
expect(setConfigMock).toHaveBeenCalledWith("time", 60, true);
|
||||
expect(setConfigMock).toHaveBeenCalledWith("mode", "time", {
|
||||
nosave: true,
|
||||
});
|
||||
expect(setConfigMock).toHaveBeenCalledWith("time", 60, {
|
||||
nosave: true,
|
||||
});
|
||||
expect(restartTestMock).toHaveBeenCalled();
|
||||
});
|
||||
it("sets time", () => {
|
||||
|
|
@ -70,8 +74,12 @@ describe("url-handler", () => {
|
|||
loadTestSettingsFromUrl("");
|
||||
|
||||
//THEN
|
||||
expect(setConfigMock).toHaveBeenCalledWith("mode", "time", true);
|
||||
expect(setConfigMock).toHaveBeenCalledWith("time", 30, true);
|
||||
expect(setConfigMock).toHaveBeenCalledWith("mode", "time", {
|
||||
nosave: true,
|
||||
});
|
||||
expect(setConfigMock).toHaveBeenCalledWith("time", 30, {
|
||||
nosave: true,
|
||||
});
|
||||
expect(restartTestMock).toHaveBeenCalled();
|
||||
});
|
||||
it("sets word count", () => {
|
||||
|
|
@ -84,8 +92,12 @@ describe("url-handler", () => {
|
|||
loadTestSettingsFromUrl("");
|
||||
|
||||
//THEN
|
||||
expect(setConfigMock).toHaveBeenCalledWith("mode", "words", true);
|
||||
expect(setConfigMock).toHaveBeenCalledWith("words", 50, true);
|
||||
expect(setConfigMock).toHaveBeenCalledWith("mode", "words", {
|
||||
nosave: true,
|
||||
});
|
||||
expect(setConfigMock).toHaveBeenCalledWith("words", 50, {
|
||||
nosave: true,
|
||||
});
|
||||
expect(restartTestMock).toHaveBeenCalled();
|
||||
});
|
||||
it("sets quote length", () => {
|
||||
|
|
@ -98,8 +110,10 @@ describe("url-handler", () => {
|
|||
loadTestSettingsFromUrl("");
|
||||
|
||||
//THEN
|
||||
expect(setConfigMock).toHaveBeenCalledWith("mode", "quote", true);
|
||||
expect(setConfigMock).toHaveBeenCalledWith("quoteLength", [-2], false);
|
||||
expect(setConfigMock).toHaveBeenCalledWith("mode", "quote", {
|
||||
nosave: true,
|
||||
});
|
||||
expect(setConfigMock).toHaveBeenCalledWith("quoteLength", [-2]);
|
||||
expect(setSelectedQuoteIdMock).toHaveBeenCalledWith(512);
|
||||
expect(restartTestMock).toHaveBeenCalled();
|
||||
});
|
||||
|
|
@ -111,7 +125,9 @@ describe("url-handler", () => {
|
|||
loadTestSettingsFromUrl("");
|
||||
|
||||
//THEN
|
||||
expect(setConfigMock).toHaveBeenCalledWith("punctuation", true, true);
|
||||
expect(setConfigMock).toHaveBeenCalledWith("punctuation", true, {
|
||||
nosave: true,
|
||||
});
|
||||
expect(restartTestMock).toHaveBeenCalled();
|
||||
});
|
||||
it("sets numbers", () => {
|
||||
|
|
@ -122,7 +138,9 @@ describe("url-handler", () => {
|
|||
loadTestSettingsFromUrl("");
|
||||
|
||||
//THEN
|
||||
expect(setConfigMock).toHaveBeenCalledWith("numbers", false, true);
|
||||
expect(setConfigMock).toHaveBeenCalledWith("numbers", false, {
|
||||
nosave: true,
|
||||
});
|
||||
expect(restartTestMock).toHaveBeenCalled();
|
||||
});
|
||||
it("sets language", () => {
|
||||
|
|
@ -133,7 +151,9 @@ describe("url-handler", () => {
|
|||
loadTestSettingsFromUrl("");
|
||||
|
||||
//THEN
|
||||
expect(setConfigMock).toHaveBeenCalledWith("language", "english", true);
|
||||
expect(setConfigMock).toHaveBeenCalledWith("language", "english", {
|
||||
nosave: true,
|
||||
});
|
||||
expect(restartTestMock).toHaveBeenCalled();
|
||||
});
|
||||
it("sets difficulty", () => {
|
||||
|
|
@ -144,7 +164,9 @@ describe("url-handler", () => {
|
|||
loadTestSettingsFromUrl("");
|
||||
|
||||
//THEN
|
||||
expect(setConfigMock).toHaveBeenCalledWith("difficulty", "master", true);
|
||||
expect(setConfigMock).toHaveBeenCalledWith("difficulty", "master", {
|
||||
nosave: true,
|
||||
});
|
||||
expect(restartTestMock).toHaveBeenCalled();
|
||||
});
|
||||
it("sets funbox", () => {
|
||||
|
|
@ -160,7 +182,9 @@ describe("url-handler", () => {
|
|||
expect(setConfigMock).toHaveBeenCalledWith(
|
||||
"funbox",
|
||||
["crt", "choo_choo"],
|
||||
true,
|
||||
{
|
||||
nosave: true,
|
||||
},
|
||||
);
|
||||
expect(restartTestMock).toHaveBeenCalled();
|
||||
});
|
||||
|
|
@ -177,7 +201,9 @@ describe("url-handler", () => {
|
|||
expect(setConfigMock).toHaveBeenCalledWith(
|
||||
"funbox",
|
||||
["crt", "choo_choo"],
|
||||
true,
|
||||
{
|
||||
nosave: true,
|
||||
},
|
||||
);
|
||||
expect(restartTestMock).toHaveBeenCalled();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -94,12 +94,10 @@ export function batchSetConfig(
|
|||
): Partial<Record<keyof Config, boolean>> {
|
||||
let ret: Partial<Record<keyof Config, boolean>> = {};
|
||||
for (const key of typedKeys(keyValues)) {
|
||||
ret[key] = setConfig(
|
||||
key,
|
||||
keyValues[key] as Config[keyof Config],
|
||||
true,
|
||||
true,
|
||||
);
|
||||
ret[key] = setConfig(key, keyValues[key] as Config[keyof Config], {
|
||||
nosave: true,
|
||||
batched: true,
|
||||
});
|
||||
//@ts-expect-error this is fine
|
||||
configToSend[key] = config[key];
|
||||
}
|
||||
|
|
@ -136,8 +134,10 @@ export function batchSetConfig(
|
|||
export function setConfig<T extends keyof Config>(
|
||||
key: T,
|
||||
value: Config[T],
|
||||
nosave: boolean = false,
|
||||
batched = false,
|
||||
options?: {
|
||||
nosave?: boolean;
|
||||
batched?: boolean;
|
||||
},
|
||||
): boolean {
|
||||
const metadata = configMetadata[key] as ConfigMetadataObject[T];
|
||||
if (metadata === undefined) {
|
||||
|
|
@ -214,7 +214,7 @@ export function setConfig<T extends keyof Config>(
|
|||
continue; // no need to set if the value is already the same
|
||||
}
|
||||
|
||||
const set = setConfig(targetKey, targetValue, nosave);
|
||||
const set = setConfig(targetKey, targetValue, options);
|
||||
if (!set) {
|
||||
throw new Error(
|
||||
`Failed to set config key "${targetKey}" with value "${targetValue}" for ${metadata.displayString} config override.`,
|
||||
|
|
@ -224,24 +224,24 @@ export function setConfig<T extends keyof Config>(
|
|||
}
|
||||
|
||||
config[key] = value;
|
||||
|
||||
if (!nosave) saveToLocalStorage(key, nosave);
|
||||
if (!batched) {
|
||||
if (!options?.nosave) saveToLocalStorage(key, options?.nosave);
|
||||
if (!options?.batched) {
|
||||
// @ts-expect-error i can't figure this out
|
||||
ConfigEvent.dispatch({
|
||||
key: key,
|
||||
newValue: value,
|
||||
nosave,
|
||||
nosave: options?.nosave ?? false,
|
||||
previousValue: previousValue as Config[T],
|
||||
});
|
||||
|
||||
if (metadata.triggerResize && !nosave) {
|
||||
if (metadata.triggerResize && !options?.nosave) {
|
||||
triggerResize();
|
||||
}
|
||||
}
|
||||
|
||||
metadata.afterSet?.({ nosave: nosave || false, currentConfig: config });
|
||||
|
||||
metadata.afterSet?.({
|
||||
nosave: options?.nosave ?? false,
|
||||
currentConfig: config,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -280,7 +280,9 @@ export function toggleFunbox(funbox: FunboxName, nosave?: boolean): boolean {
|
|||
}
|
||||
|
||||
export function setQuoteLengthAll(nosave?: boolean): boolean {
|
||||
return setConfig("quoteLength", [0, 1, 2, 3], nosave);
|
||||
return setConfig("quoteLength", [0, 1, 2, 3], {
|
||||
nosave,
|
||||
});
|
||||
}
|
||||
|
||||
const lastConfigsToApply: Set<keyof Config> = new Set([
|
||||
|
|
@ -341,7 +343,10 @@ export async function applyConfig(
|
|||
for (const configKey of [...firstKeys, ...lastConfigsToApply]) {
|
||||
const configValue = fullConfig[configKey];
|
||||
|
||||
const set = setConfig(configKey, configValue, true, true);
|
||||
const set = setConfig(configKey, configValue, {
|
||||
nosave: true,
|
||||
batched: true,
|
||||
});
|
||||
|
||||
if (!set) {
|
||||
configKeysToReset.push(configKey);
|
||||
|
|
|
|||
|
|
@ -241,26 +241,48 @@ export async function setup(challengeName: string): Promise<boolean> {
|
|||
return false;
|
||||
}
|
||||
if (challenge.type === "customTime") {
|
||||
setConfig("time", challenge.parameters[0] as number, true);
|
||||
setConfig("mode", "time", true);
|
||||
setConfig("difficulty", "normal", true);
|
||||
setConfig("time", challenge.parameters[0] as number, {
|
||||
nosave: true,
|
||||
});
|
||||
setConfig("mode", "time", {
|
||||
nosave: true,
|
||||
});
|
||||
setConfig("difficulty", "normal", {
|
||||
nosave: true,
|
||||
});
|
||||
if (challenge.name === "englishMaster") {
|
||||
setConfig("language", "english_10k", true);
|
||||
setConfig("numbers", true, true);
|
||||
setConfig("punctuation", true, true);
|
||||
setConfig("language", "english_10k", {
|
||||
nosave: true,
|
||||
});
|
||||
setConfig("numbers", true, {
|
||||
nosave: true,
|
||||
});
|
||||
setConfig("punctuation", true, {
|
||||
nosave: true,
|
||||
});
|
||||
}
|
||||
} else if (challenge.type === "customWords") {
|
||||
setConfig("words", challenge.parameters[0] as number, true);
|
||||
setConfig("mode", "words", true);
|
||||
setConfig("difficulty", "normal", true);
|
||||
setConfig("words", challenge.parameters[0] as number, {
|
||||
nosave: true,
|
||||
});
|
||||
setConfig("mode", "words", {
|
||||
nosave: true,
|
||||
});
|
||||
setConfig("difficulty", "normal", {
|
||||
nosave: true,
|
||||
});
|
||||
} else if (challenge.type === "customText") {
|
||||
CustomText.setText((challenge.parameters[0] as string).split(" "));
|
||||
CustomText.setMode(challenge.parameters[1] as CustomTextMode);
|
||||
CustomText.setLimitValue(challenge.parameters[2] as number);
|
||||
CustomText.setLimitMode(challenge.parameters[3] as CustomTextLimitMode);
|
||||
CustomText.setPipeDelimiter(challenge.parameters[4] as boolean);
|
||||
setConfig("mode", "custom", true);
|
||||
setConfig("difficulty", "normal", true);
|
||||
setConfig("mode", "custom", {
|
||||
nosave: true,
|
||||
});
|
||||
setConfig("difficulty", "normal", {
|
||||
nosave: true,
|
||||
});
|
||||
} else if (challenge.type === "script") {
|
||||
Loader.show();
|
||||
const response = await fetch(
|
||||
|
|
@ -278,8 +300,12 @@ export async function setup(challengeName: string): Promise<boolean> {
|
|||
CustomText.setMode("repeat");
|
||||
CustomText.setLimitMode("word");
|
||||
CustomText.setPipeDelimiter(false);
|
||||
setConfig("mode", "custom", true);
|
||||
setConfig("difficulty", "normal", true);
|
||||
setConfig("mode", "custom", {
|
||||
nosave: true,
|
||||
});
|
||||
setConfig("difficulty", "normal", {
|
||||
nosave: true,
|
||||
});
|
||||
if (challenge.parameters[1] !== null) {
|
||||
setConfig("theme", challenge.parameters[1] as ThemeName);
|
||||
}
|
||||
|
|
@ -287,32 +313,66 @@ export async function setup(challengeName: string): Promise<boolean> {
|
|||
void Funbox.activate(challenge.parameters[2] as FunboxName[]);
|
||||
}
|
||||
} else if (challenge.type === "accuracy") {
|
||||
setConfig("time", 0, true);
|
||||
setConfig("mode", "time", true);
|
||||
setConfig("difficulty", "master", true);
|
||||
setConfig("time", 0, {
|
||||
nosave: true,
|
||||
});
|
||||
setConfig("mode", "time", {
|
||||
nosave: true,
|
||||
});
|
||||
setConfig("difficulty", "master", {
|
||||
nosave: true,
|
||||
});
|
||||
} else if (challenge.type === "funbox") {
|
||||
setConfig("funbox", challenge.parameters[0] as FunboxName[], true);
|
||||
setConfig("difficulty", "normal", true);
|
||||
setConfig("funbox", challenge.parameters[0] as FunboxName[], {
|
||||
nosave: true,
|
||||
});
|
||||
setConfig("difficulty", "normal", {
|
||||
nosave: true,
|
||||
});
|
||||
if (challenge.parameters[1] === "words") {
|
||||
setConfig("words", challenge.parameters[2] as number, true);
|
||||
setConfig("words", challenge.parameters[2] as number, {
|
||||
nosave: true,
|
||||
});
|
||||
} else if (challenge.parameters[1] === "time") {
|
||||
setConfig("time", challenge.parameters[2] as number, true);
|
||||
setConfig("time", challenge.parameters[2] as number, {
|
||||
nosave: true,
|
||||
});
|
||||
}
|
||||
setConfig("mode", challenge.parameters[1] as Mode, true);
|
||||
setConfig("mode", challenge.parameters[1] as Mode, {
|
||||
nosave: true,
|
||||
});
|
||||
if (challenge.parameters[3] !== undefined) {
|
||||
setConfig("difficulty", challenge.parameters[3] as Difficulty, true);
|
||||
setConfig("difficulty", challenge.parameters[3] as Difficulty, {
|
||||
nosave: true,
|
||||
});
|
||||
}
|
||||
} else if (challenge.type === "special") {
|
||||
if (challenge.name === "semimak") {
|
||||
// so can you make a link that sets up 120s, 10k, punct, stop on word, and semimak as the layout?
|
||||
setConfig("mode", "time", true);
|
||||
setConfig("time", 120, true);
|
||||
setConfig("language", "english_10k", true);
|
||||
setConfig("punctuation", true, true);
|
||||
setConfig("stopOnError", "word", true);
|
||||
setConfig("layout", "semimak", true);
|
||||
setConfig("keymapLayout", "overrideSync", true);
|
||||
setConfig("keymapMode", "static", true);
|
||||
setConfig("mode", "time", {
|
||||
nosave: true,
|
||||
});
|
||||
setConfig("time", 120, {
|
||||
nosave: true,
|
||||
});
|
||||
setConfig("language", "english_10k", {
|
||||
nosave: true,
|
||||
});
|
||||
setConfig("punctuation", true, {
|
||||
nosave: true,
|
||||
});
|
||||
setConfig("stopOnError", "word", {
|
||||
nosave: true,
|
||||
});
|
||||
setConfig("layout", "semimak", {
|
||||
nosave: true,
|
||||
});
|
||||
setConfig("keymapLayout", "overrideSync", {
|
||||
nosave: true,
|
||||
});
|
||||
setConfig("keymapMode", "static", {
|
||||
nosave: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
ManualRestart.set();
|
||||
|
|
|
|||
|
|
@ -59,14 +59,14 @@ async function showSyncLoading({
|
|||
loadingOptions: LoadingOptions[];
|
||||
totalDuration: number;
|
||||
}): Promise<void> {
|
||||
PageLoading.page.element.removeClass("hidden").css("opacity", 0);
|
||||
PageLoading.page.element.show().setStyle({ opacity: "0" });
|
||||
await PageLoading.page.beforeShow({});
|
||||
|
||||
const fillDivider = loadingOptions.length;
|
||||
const fillOffset = 100 / fillDivider;
|
||||
|
||||
//void here to run the loading promise as soon as possible
|
||||
void Misc.promiseAnimate(PageLoading.page.element[0] as HTMLElement, {
|
||||
void PageLoading.page.element.promiseAnimate({
|
||||
opacity: "1",
|
||||
duration: totalDuration / 2,
|
||||
});
|
||||
|
|
@ -97,13 +97,13 @@ async function showSyncLoading({
|
|||
}
|
||||
}
|
||||
|
||||
await Misc.promiseAnimate(PageLoading.page.element[0] as HTMLElement, {
|
||||
await PageLoading.page.element.promiseAnimate({
|
||||
opacity: "0",
|
||||
duration: totalDuration / 2,
|
||||
});
|
||||
|
||||
await PageLoading.page.afterHide();
|
||||
PageLoading.page.element.addClass("hidden");
|
||||
PageLoading.page.element.hide();
|
||||
}
|
||||
|
||||
// Global abort controller for keyframe promises
|
||||
|
|
@ -206,12 +206,12 @@ export async function change(
|
|||
|
||||
//previous page
|
||||
await previousPage?.beforeHide?.();
|
||||
previousPage.element.removeClass("hidden").css("opacity", 1);
|
||||
await Misc.promiseAnimate(previousPage.element[0] as HTMLElement, {
|
||||
previousPage.element.show().setStyle({ opacity: "1" });
|
||||
await previousPage.element.promiseAnimate({
|
||||
opacity: "0",
|
||||
duration: totalDuration / 2,
|
||||
});
|
||||
previousPage.element.addClass("hidden");
|
||||
previousPage.element.hide();
|
||||
await previousPage?.afterHide();
|
||||
|
||||
// we need to evaluate and store next page loading mode in case options.loadingOptions.loadingMode is sync
|
||||
|
|
@ -281,8 +281,8 @@ export async function change(
|
|||
});
|
||||
}
|
||||
|
||||
nextPage.element.removeClass("hidden").css("opacity", 0);
|
||||
await Misc.promiseAnimate(nextPage.element[0] as HTMLElement, {
|
||||
nextPage.element.show().setStyle({ opacity: "0" });
|
||||
await nextPage.element.promiseAnimate({
|
||||
opacity: "1",
|
||||
duration: totalDuration / 2,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -352,7 +352,9 @@ export async function randomizeTheme(): Promise<void> {
|
|||
randomTheme = "custom";
|
||||
}
|
||||
|
||||
setConfig("customTheme", false, true);
|
||||
setConfig("customTheme", false, {
|
||||
nosave: true,
|
||||
});
|
||||
await apply(randomTheme, colorsOverride);
|
||||
|
||||
if (randomThemeIndex >= themesList.length) {
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ const debouncedSave = debounce(2000, async () => {
|
|||
const arr = Object.keys(filters).map(
|
||||
(filterKey) => filters[filterKey as keyof typeof filters].value,
|
||||
) as CustomBackgroundFilter;
|
||||
setConfig("customBackgroundFilter", arr, false);
|
||||
setConfig("customBackgroundFilter", arr);
|
||||
});
|
||||
|
||||
ConfigEvent.subscribe(({ key, newValue }) => {
|
||||
|
|
|
|||
|
|
@ -294,7 +294,7 @@ export function handleConfigInput<T extends ConfigKey>({
|
|||
if (Config[configName] === value) {
|
||||
return;
|
||||
}
|
||||
const didConfigSave = setConfig(configName, value, false);
|
||||
const didConfigSave = setConfig(configName, value);
|
||||
|
||||
if (didConfigSave) {
|
||||
Notifications.add("Saved", 1, {
|
||||
|
|
|
|||
|
|
@ -46,7 +46,9 @@ export default class SettingsGroup<K extends ConfigKey, T = ConfigType[K]> {
|
|||
this.configName = configName;
|
||||
this.mode = mode;
|
||||
this.configFunction = (param, nosave) =>
|
||||
setConfig(configName, param as ConfigType[K], nosave);
|
||||
setConfig(configName, param as ConfigType[K], {
|
||||
nosave: nosave ?? false,
|
||||
});
|
||||
this.setCallback = options?.setCallback;
|
||||
this.updateCallback = options?.updateCallback;
|
||||
this.validation = options?.validation;
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ async function setup(modalEl: HTMLElement): Promise<void> {
|
|||
arr = [len];
|
||||
}
|
||||
|
||||
if (setConfig("quoteLength", arr, false)) {
|
||||
if (setConfig("quoteLength", arr)) {
|
||||
ManualRestart.set();
|
||||
TestLogic.restart();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -363,7 +363,7 @@ function apply(val: number): void {
|
|||
);
|
||||
}
|
||||
if (val !== null && !isNaN(val) && val >= 0) {
|
||||
setConfig("quoteLength", [-2], false);
|
||||
setConfig("quoteLength", [-2]);
|
||||
TestState.setSelectedQuoteId(val);
|
||||
ManualRestart.set();
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import Page from "./page";
|
||||
import * as Skeleton from "../utils/skeleton";
|
||||
import { qsr } from "../utils/dom";
|
||||
|
||||
export const page = new Page({
|
||||
id: "404",
|
||||
element: $(".page.page404"),
|
||||
element: qsr(".page.page404"),
|
||||
path: "/404",
|
||||
afterHide: async (): Promise<void> => {
|
||||
Skeleton.remove("page404");
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import * as Skeleton from "../utils/skeleton";
|
|||
import { TypingStats, SpeedHistogram } from "@monkeytype/schemas/public";
|
||||
import { getNumberWithMagnitude, numberWithSpaces } from "../utils/numbers";
|
||||
import { tryCatch } from "@monkeytype/util/trycatch";
|
||||
import { qsr } from "../utils/dom";
|
||||
|
||||
function reset(): void {
|
||||
$(".pageAbout .contributors").empty();
|
||||
|
|
@ -199,7 +200,7 @@ function getHistogramDataBucketed(data: Record<string, number>): {
|
|||
|
||||
export const page = new Page({
|
||||
id: "about",
|
||||
element: $(".page.pageAbout"),
|
||||
element: qsr(".page.pageAbout"),
|
||||
path: "/about",
|
||||
afterHide: async (): Promise<void> => {
|
||||
reset();
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import * as BlockedUserTable from "../elements/account-settings/blocked-user-tab
|
|||
import * as Notifications from "../elements/notifications";
|
||||
import { z } from "zod";
|
||||
import * as AuthEvent from "../observables/auth-event";
|
||||
import { qsr } from "../utils/dom";
|
||||
|
||||
const pageElement = $(".page.pageAccountSettings");
|
||||
|
||||
|
|
@ -229,7 +230,7 @@ AuthEvent.subscribe((event) => {
|
|||
export const page = new PageWithUrlParams({
|
||||
id: "accountSettings",
|
||||
display: "Account Settings",
|
||||
element: pageElement,
|
||||
element: qsr(".page.pageAccountSettings"),
|
||||
path: "/account-settings",
|
||||
urlParamsSchema: UrlParameterSchema,
|
||||
afterHide: async (): Promise<void> => {
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import { SnapshotResult } from "../constants/default-snapshot";
|
|||
import Ape from "../ape";
|
||||
import { AccountChart } from "@monkeytype/schemas/configs";
|
||||
import { SortedTableWithLimit } from "../utils/sorted-table";
|
||||
import { qsr } from "../utils/dom";
|
||||
|
||||
let filterDebug = false;
|
||||
//toggle filterdebug
|
||||
|
|
@ -1195,7 +1196,7 @@ ConfigEvent.subscribe(({ key }) => {
|
|||
|
||||
export const page = new Page<undefined>({
|
||||
id: "account",
|
||||
element: $(".page.pageAccount"),
|
||||
element: qsr(".page.pageAccount"),
|
||||
path: "/account",
|
||||
loadingOptions: {
|
||||
loadingMode: () => {
|
||||
|
|
|
|||
|
|
@ -30,8 +30,7 @@ import { Friend, UserNameSchema } from "@monkeytype/schemas/users";
|
|||
import * as Loader from "../elements/loader";
|
||||
import { LocalStorageWithSchema } from "../utils/local-storage-with-schema";
|
||||
import { remoteValidation } from "../utils/remote-validation";
|
||||
|
||||
const pageElement = $(".page.pageFriends");
|
||||
import { qsr } from "../utils/dom";
|
||||
|
||||
let friendsTable: SortedTable<Friend> | undefined = undefined;
|
||||
|
||||
|
|
@ -499,7 +498,7 @@ function update(): void {
|
|||
export const page = new Page<undefined>({
|
||||
id: "friends",
|
||||
display: "Friends",
|
||||
element: pageElement,
|
||||
element: qsr(".page.pageFriends"),
|
||||
path: "/friends",
|
||||
loadingOptions: {
|
||||
loadingMode: () => {
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ import { isSafeNumber } from "@monkeytype/util/numbers";
|
|||
import { Mode, Mode2, ModeSchema } from "@monkeytype/schemas/shared";
|
||||
import * as ServerConfiguration from "../ape/server-configuration";
|
||||
import { getAvatarElement } from "../utils/discord-avatar";
|
||||
import { qsr } from "../utils/dom";
|
||||
|
||||
const LeaderboardTypeSchema = z.enum(["allTime", "weekly", "daily"]);
|
||||
type LeaderboardType = z.infer<typeof LeaderboardTypeSchema>;
|
||||
|
|
@ -1489,7 +1490,7 @@ $(".page.pageLeaderboards .buttonGroup.friendsOnlyButtons").on(
|
|||
|
||||
export const page = new PageWithUrlParams({
|
||||
id: "leaderboards",
|
||||
element: $(".page.pageLeaderboards"),
|
||||
element: qsr(".page.pageLeaderboards"),
|
||||
path: "/leaderboards",
|
||||
urlParamsSchema: UrlParameterSchema,
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import Page from "./page";
|
||||
import * as Skeleton from "../utils/skeleton";
|
||||
import { promiseAnimate } from "../utils/misc";
|
||||
import { qsr } from "../utils/dom";
|
||||
|
||||
const pageEl = $(".page.pageLoading");
|
||||
const barEl = pageEl.find(".bar");
|
||||
|
|
@ -45,7 +46,7 @@ export async function showBar(): Promise<void> {
|
|||
|
||||
export const page = new Page({
|
||||
id: "loading",
|
||||
element: pageEl,
|
||||
element: qsr(".page.pageLoading"),
|
||||
path: "/",
|
||||
afterHide: async (): Promise<void> => {
|
||||
Skeleton.remove("pageLoading");
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ new ValidatedHtmlInputElement(passwordVerifyInputEl, {
|
|||
|
||||
export const page = new Page({
|
||||
id: "login",
|
||||
element: $(".page.pageLogin"),
|
||||
element: qsr(".page.pageLogin"),
|
||||
path: "/login",
|
||||
afterHide: async (): Promise<void> => {
|
||||
hidePreloader();
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import {
|
|||
safeParse as parseUrlSearchParams,
|
||||
serialize as serializeUrlSearchParams,
|
||||
} from "zod-urlsearchparams";
|
||||
import { ElementWithUtils } from "../utils/dom";
|
||||
|
||||
export type PageName =
|
||||
| "loading"
|
||||
|
|
@ -69,7 +70,7 @@ export type LoadingOptions = {
|
|||
type PageProperties<T> = {
|
||||
id: PageName;
|
||||
display?: string;
|
||||
element: JQuery;
|
||||
element: ElementWithUtils;
|
||||
path: string;
|
||||
loadingOptions?: LoadingOptions;
|
||||
beforeHide?: () => Promise<void>;
|
||||
|
|
@ -84,7 +85,7 @@ async function empty(): Promise<void> {
|
|||
export default class Page<T> {
|
||||
public id: PageName;
|
||||
public display: string | undefined;
|
||||
public element: JQuery;
|
||||
public element: ElementWithUtils;
|
||||
public pathname: string;
|
||||
public loadingOptions: LoadingOptions | undefined;
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ function disableButton(): void {
|
|||
|
||||
export const page = new Page({
|
||||
id: "profileSearch",
|
||||
element: $(".page.pageProfileSearch"),
|
||||
element: qsr(".page.pageProfileSearch"),
|
||||
path: "/profile",
|
||||
afterHide: async (): Promise<void> => {
|
||||
Skeleton.remove("pageProfileSearch");
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import * as TestActivity from "../elements/test-activity";
|
|||
import { TestActivityCalendar } from "../elements/test-activity-calendar";
|
||||
import { getFirstDayOfTheWeek } from "../utils/date-and-time";
|
||||
import { addFriend } from "./friends";
|
||||
import { qsr } from "../utils/dom";
|
||||
|
||||
const firstDayOfTheWeek = getFirstDayOfTheWeek();
|
||||
|
||||
|
|
@ -260,7 +261,7 @@ $(".page.pageProfile").on("click", ".profile .addFriendButton", async () => {
|
|||
|
||||
export const page = new Page<undefined | UserProfile>({
|
||||
id: "profile",
|
||||
element: $(".page.pageProfile"),
|
||||
element: qsr(".page.pageProfile"),
|
||||
path: "/profile",
|
||||
afterHide: async (): Promise<void> => {
|
||||
Skeleton.remove("pageProfile");
|
||||
|
|
|
|||
|
|
@ -1011,7 +1011,7 @@ AuthEvent.subscribe((event) => {
|
|||
|
||||
export const page = new PageWithUrlParams({
|
||||
id: "settings",
|
||||
element: $(".page.pageSettings"),
|
||||
element: qsr(".page.pageSettings"),
|
||||
path: "/settings",
|
||||
urlParamsSchema: StateSchema,
|
||||
afterHide: async (): Promise<void> => {
|
||||
|
|
|
|||
|
|
@ -9,10 +9,11 @@ import * as Keymap from "../elements/keymap";
|
|||
import * as TestConfig from "../test/test-config";
|
||||
import * as ScrollToTop from "../elements/scroll-to-top";
|
||||
import { blurInputElement } from "../input/input-element";
|
||||
import { qsr } from "../utils/dom";
|
||||
|
||||
export const page = new Page({
|
||||
id: "test",
|
||||
element: $(".page.pageTest"),
|
||||
element: qsr(".page.pageTest"),
|
||||
path: "/",
|
||||
beforeHide: async (): Promise<void> => {
|
||||
blurInputElement();
|
||||
|
|
|
|||
|
|
@ -207,7 +207,9 @@ const list: Partial<Record<FunboxName, FunboxFunctions>> = {
|
|||
},
|
||||
simon_says: {
|
||||
applyConfig(): void {
|
||||
setConfig("keymapMode", "next", true);
|
||||
setConfig("keymapMode", "next", {
|
||||
nosave: true,
|
||||
});
|
||||
},
|
||||
rememberSettings(): void {
|
||||
save("keymapMode", Config.keymapMode);
|
||||
|
|
@ -215,7 +217,9 @@ const list: Partial<Record<FunboxName, FunboxFunctions>> = {
|
|||
},
|
||||
tts: {
|
||||
applyConfig(): void {
|
||||
setConfig("keymapMode", "off", true);
|
||||
setConfig("keymapMode", "off", {
|
||||
nosave: true,
|
||||
});
|
||||
},
|
||||
rememberSettings(): void {
|
||||
save("keymapMode", Config.keymapMode);
|
||||
|
|
@ -368,8 +372,12 @@ const list: Partial<Record<FunboxName, FunboxFunctions>> = {
|
|||
if (Config.layout === "default") {
|
||||
layout = "qwerty";
|
||||
}
|
||||
setConfig("layout", layout, true);
|
||||
setConfig("keymapLayout", "overrideSync", true);
|
||||
setConfig("layout", layout, {
|
||||
nosave: true,
|
||||
});
|
||||
setConfig("keymapLayout", "overrideSync", {
|
||||
nosave: true,
|
||||
});
|
||||
},
|
||||
rememberSettings(): void {
|
||||
save("keymapMode", Config.keymapMode);
|
||||
|
|
@ -380,8 +388,12 @@ const list: Partial<Record<FunboxName, FunboxFunctions>> = {
|
|||
applyConfig(): void {
|
||||
const layout = Config.customLayoutfluid[0] ?? "qwerty";
|
||||
|
||||
setConfig("layout", layout as Layout, true);
|
||||
setConfig("keymapLayout", layout as KeymapLayout, true);
|
||||
setConfig("layout", layout as Layout, {
|
||||
nosave: true,
|
||||
});
|
||||
setConfig("keymapLayout", layout as KeymapLayout, {
|
||||
nosave: true,
|
||||
});
|
||||
},
|
||||
rememberSettings(): void {
|
||||
save("keymapMode", Config.keymapMode);
|
||||
|
|
@ -485,9 +497,13 @@ const list: Partial<Record<FunboxName, FunboxFunctions>> = {
|
|||
memory: {
|
||||
applyConfig(): void {
|
||||
$("#wordsWrapper").addClass("hidden");
|
||||
setConfig("showAllLines", true, true);
|
||||
setConfig("showAllLines", true, {
|
||||
nosave: true,
|
||||
});
|
||||
if (Config.keymapMode === "next") {
|
||||
setConfig("keymapMode", "react", true);
|
||||
setConfig("keymapMode", "react", {
|
||||
nosave: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
rememberSettings(): void {
|
||||
|
|
@ -684,7 +700,9 @@ const list: Partial<Record<FunboxName, FunboxFunctions>> = {
|
|||
|
||||
if (languages.length === 1) {
|
||||
const lang = languages[0] as LanguageObject;
|
||||
setConfig("language", lang.name, true);
|
||||
setConfig("language", lang.name, {
|
||||
nosave: true,
|
||||
});
|
||||
toggleFunbox("polyglot", true);
|
||||
Notifications.add(
|
||||
`Disabled polyglot funbox because only one valid language was found. Check your polyglot languages config (${Config.customPolyglot.join(
|
||||
|
|
|
|||
|
|
@ -20,7 +20,9 @@ export function save<T extends ConfigKey>(
|
|||
settingsMemory[settingName] ??= {
|
||||
value,
|
||||
setFunction: (param, noSave?) =>
|
||||
setConfig(settingName, param as Config[T], noSave),
|
||||
setConfig(settingName, param as Config[T], {
|
||||
nosave: noSave ?? false,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export function setFunbox(funbox: FunboxName[]): boolean {
|
|||
}
|
||||
}
|
||||
FunboxMemory.load();
|
||||
setConfig("funbox", funbox, false);
|
||||
setConfig("funbox", funbox);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -104,7 +104,9 @@ export async function activate(
|
|||
),
|
||||
-1,
|
||||
);
|
||||
setConfig("funbox", [], true);
|
||||
setConfig("funbox", [], {
|
||||
nosave: true,
|
||||
});
|
||||
await clear();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -123,7 +125,9 @@ export async function activate(
|
|||
Misc.createErrorMessage(error, "Failed to activate funbox"),
|
||||
-1,
|
||||
);
|
||||
setConfig("funbox", [], true);
|
||||
setConfig("funbox", [], {
|
||||
nosave: true,
|
||||
});
|
||||
await clear();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -134,7 +138,9 @@ export async function activate(
|
|||
"Current language does not support this funbox mode",
|
||||
0,
|
||||
);
|
||||
setConfig("funbox", [], true);
|
||||
setConfig("funbox", [], {
|
||||
nosave: true,
|
||||
});
|
||||
await clear();
|
||||
return;
|
||||
}
|
||||
|
|
@ -188,7 +194,9 @@ export async function activate(
|
|||
-1,
|
||||
);
|
||||
}
|
||||
setConfig("funbox", [], true);
|
||||
setConfig("funbox", [], {
|
||||
nosave: true,
|
||||
});
|
||||
await clear();
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -148,7 +148,9 @@ export function init(
|
|||
customText = CustomText.getData();
|
||||
}
|
||||
|
||||
setConfig("mode", "custom", true);
|
||||
setConfig("mode", "custom", {
|
||||
nosave: true,
|
||||
});
|
||||
CustomText.setPipeDelimiter(true);
|
||||
CustomText.setText(newCustomText);
|
||||
CustomText.setLimitMode("section");
|
||||
|
|
|
|||
|
|
@ -497,9 +497,11 @@ async function init(): Promise<boolean> {
|
|||
important: true,
|
||||
},
|
||||
);
|
||||
setConfig("lazyMode", false, false);
|
||||
setConfig("lazyMode", false);
|
||||
} else if (rememberLazyMode && anySupportsLazyMode) {
|
||||
setConfig("lazyMode", true, true);
|
||||
setConfig("lazyMode", true, {
|
||||
nosave: true,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// normal mode
|
||||
|
|
@ -510,9 +512,11 @@ async function init(): Promise<boolean> {
|
|||
important: true,
|
||||
});
|
||||
|
||||
setConfig("lazyMode", false, false);
|
||||
setConfig("lazyMode", false);
|
||||
} else if (rememberLazyMode && !language.noLazyMode) {
|
||||
setConfig("lazyMode", true, true);
|
||||
setConfig("lazyMode", true, {
|
||||
nosave: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1559,7 +1563,7 @@ $(".pageTest").on("click", "#testConfig .quoteLength .textButton", (e) => {
|
|||
arr = [len];
|
||||
}
|
||||
|
||||
if (setConfig("quoteLength", arr, false)) {
|
||||
if (setConfig("quoteLength", arr)) {
|
||||
ManualRestart.set();
|
||||
restart();
|
||||
}
|
||||
|
|
@ -1595,7 +1599,9 @@ ConfigEvent.subscribe(({ key, newValue, nosave }) => {
|
|||
if (key === "language") {
|
||||
//automatically enable lazy mode for arabic
|
||||
if ((newValue as string)?.startsWith("arabic") && ArabicLazyMode.get()) {
|
||||
setConfig("lazyMode", true, true);
|
||||
setConfig("lazyMode", true, {
|
||||
nosave: true,
|
||||
});
|
||||
}
|
||||
restart();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,8 +127,12 @@ function layoutfluid(): void {
|
|||
|
||||
if (Config.layout !== layout && layout !== undefined) {
|
||||
LayoutfluidFunboxTimer.hide();
|
||||
setConfig("layout", layout as Layout, true);
|
||||
setConfig("keymapLayout", layout as KeymapLayout, true);
|
||||
setConfig("layout", layout as Layout, {
|
||||
nosave: true,
|
||||
});
|
||||
setConfig("keymapLayout", layout as KeymapLayout, {
|
||||
nosave: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (timerDebug) console.timeEnd("layoutfluid");
|
||||
|
|
|
|||
|
|
@ -514,6 +514,22 @@ export class ElementWithUtils<T extends HTMLElement = HTMLElement> {
|
|||
animate(animationParams: AnimationParams): JSAnimation {
|
||||
return animejsAnimate(this.native, animationParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Animate the element using Anime.js
|
||||
* @param animationParams The Anime.js animation parameters
|
||||
*/
|
||||
async promiseAnimate(animationParams: AnimationParams): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
animejsAnimate(this.native, {
|
||||
...animationParams,
|
||||
onComplete: (self, e) => {
|
||||
animationParams.onComplete?.(self, e);
|
||||
resolve();
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -181,18 +181,24 @@ export function loadTestSettingsFromUrl(getOverride?: string): void {
|
|||
const applied: Record<string, string> = {};
|
||||
|
||||
if (de[0] !== null) {
|
||||
setConfig("mode", de[0], true);
|
||||
setConfig("mode", de[0], {
|
||||
nosave: true,
|
||||
});
|
||||
applied["mode"] = de[0];
|
||||
}
|
||||
|
||||
const mode = de[0] ?? Config.mode;
|
||||
if (de[1] !== null) {
|
||||
if (mode === "time") {
|
||||
setConfig("time", parseInt(de[1], 10), true);
|
||||
setConfig("time", parseInt(de[1], 10), {
|
||||
nosave: true,
|
||||
});
|
||||
} else if (mode === "words") {
|
||||
setConfig("words", parseInt(de[1], 10), true);
|
||||
setConfig("words", parseInt(de[1], 10), {
|
||||
nosave: true,
|
||||
});
|
||||
} else if (mode === "quote") {
|
||||
setConfig("quoteLength", [-2], false);
|
||||
setConfig("quoteLength", [-2]);
|
||||
TestState.setSelectedQuoteId(parseInt(de[1], 10));
|
||||
ManualRestart.set();
|
||||
}
|
||||
|
|
@ -236,22 +242,30 @@ export function loadTestSettingsFromUrl(getOverride?: string): void {
|
|||
}
|
||||
|
||||
if (de[3] !== null) {
|
||||
setConfig("punctuation", de[3], true);
|
||||
setConfig("punctuation", de[3], {
|
||||
nosave: true,
|
||||
});
|
||||
applied["punctuation"] = de[3] ? "on" : "off";
|
||||
}
|
||||
|
||||
if (de[4] !== null) {
|
||||
setConfig("numbers", de[4], true);
|
||||
setConfig("numbers", de[4], {
|
||||
nosave: true,
|
||||
});
|
||||
applied["numbers"] = de[4] ? "on" : "off";
|
||||
}
|
||||
|
||||
if (de[5] !== null) {
|
||||
setConfig("language", de[5] as Language, true);
|
||||
setConfig("language", de[5] as Language, {
|
||||
nosave: true,
|
||||
});
|
||||
applied["language"] = de[5];
|
||||
}
|
||||
|
||||
if (de[6] !== null) {
|
||||
setConfig("difficulty", de[6], true);
|
||||
setConfig("difficulty", de[6], {
|
||||
nosave: true,
|
||||
});
|
||||
applied["difficulty"] = de[6];
|
||||
}
|
||||
|
||||
|
|
@ -263,7 +277,9 @@ export function loadTestSettingsFromUrl(getOverride?: string): void {
|
|||
} else {
|
||||
val = de[7];
|
||||
}
|
||||
setConfig("funbox", val, true);
|
||||
setConfig("funbox", val, {
|
||||
nosave: true,
|
||||
});
|
||||
applied["funbox"] = val.join(", ");
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue