mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2025-10-18 11:36:13 +08:00
refactor: cleanup schema and types for CustomText (@fehmer) (#6605)
This commit is contained in:
parent
1cada77ea8
commit
47de0c8a40
11 changed files with 59 additions and 67 deletions
|
@ -251,7 +251,7 @@ describe("url-handler", () => {
|
|||
|
||||
//THEN
|
||||
expect(addNotificationMock).toHaveBeenCalledWith(
|
||||
`Failed to load test settings from URL: JSON does not match schema: \"0\" invalid enum value. expected 'time' | 'words' | 'quote' | 'custom' | 'zen', received 'invalidmode', \"1\" needs to be a number or a number represented as a string e.g. \"10\"., \"2.text\" expected array, received string, \"2.mode\" invalid enum value. expected 'repeat' | 'random' | 'shuffle', received 'invalid', \"2.limit\" expected object, received string, \"2.pipeDelimiter\" expected boolean, received string, \"3\" expected boolean, received string, \"4\" expected boolean, received string, \"6\" invalid enum value. expected 'normal' | 'expert' | 'master', received 'invalid', \"7\" invalid input`,
|
||||
`Failed to load test settings from URL: JSON does not match schema: \"0\" invalid enum value. expected 'time' | 'words' | 'quote' | 'custom' | 'zen', received 'invalidmode', \"1\" needs to be a number or a number represented as a string e.g. \"10\"., \"2.mode\" invalid enum value. expected 'repeat' | 'random' | 'shuffle', received 'invalid', \"2.pipeDelimiter\" expected boolean, received string, \"2.limit\" expected object, received string, \"2.text\" expected array, received string, \"3\" expected boolean, received string, \"4\" expected boolean, received string, \"6\" invalid enum value. expected 'normal' | 'expert' | 'master', received 'invalid', \"7\" invalid input`,
|
||||
0
|
||||
);
|
||||
});
|
||||
|
|
|
@ -42,6 +42,7 @@ import {
|
|||
getActiveFunboxNames,
|
||||
} from "../test/funbox/list";
|
||||
import { tryCatchSync } from "@monkeytype/util/trycatch";
|
||||
import { canQuickRestart } from "../utils/quick-restart";
|
||||
|
||||
let dontInsertSpace = false;
|
||||
let correctShiftUsed = true;
|
||||
|
@ -1073,7 +1074,7 @@ $(document).on("keydown", async (event) => {
|
|||
if (Config.mode === "zen") {
|
||||
void TestLogic.finish();
|
||||
} else if (
|
||||
!Misc.canQuickRestart(
|
||||
!canQuickRestart(
|
||||
Config.mode,
|
||||
Config.words,
|
||||
Config.time,
|
||||
|
|
|
@ -16,7 +16,7 @@ function getCheckboxValue(checkbox: string): boolean {
|
|||
type SharedTestSettings = [
|
||||
Mode | null,
|
||||
Mode2<Mode> | null,
|
||||
CustomText.CustomTextData | null,
|
||||
CustomText.CustomTextSettings | null,
|
||||
boolean | null,
|
||||
boolean | null,
|
||||
string | null,
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
import {
|
||||
CustomTextLimitMode,
|
||||
CustomTextLimitModeSchema,
|
||||
CustomTextMode,
|
||||
CustomTextModeSchema,
|
||||
} from "@monkeytype/contracts/schemas/util";
|
||||
import { LocalStorageWithSchema } from "../utils/local-storage-with-schema";
|
||||
import { z } from "zod";
|
||||
import { CustomTextDataWithTextLen } from "@monkeytype/contracts/schemas/results";
|
||||
import { CompletedEventCustomTextSchema } from "@monkeytype/contracts/schemas/results";
|
||||
import { deepClone } from "../utils/misc";
|
||||
|
||||
const CustomTextObjectSchema = z.record(z.string(), z.string());
|
||||
|
@ -30,11 +28,10 @@ const customTextLongLS = new LocalStorageWithSchema({
|
|||
fallback: {},
|
||||
});
|
||||
|
||||
export const CustomTextSettingsSchema = z.object({
|
||||
export const CustomTextSettingsSchema = CompletedEventCustomTextSchema.omit({
|
||||
textLen: true,
|
||||
}).extend({
|
||||
text: z.array(z.string()).min(1),
|
||||
mode: CustomTextModeSchema,
|
||||
limit: z.object({ value: z.number(), mode: CustomTextLimitModeSchema }),
|
||||
pipeDelimiter: z.boolean(),
|
||||
});
|
||||
|
||||
export type CustomTextSettings = z.infer<typeof CustomTextSettingsSchema>;
|
||||
|
@ -139,17 +136,8 @@ export function setPipeDelimiter(val: boolean): void {
|
|||
});
|
||||
}
|
||||
|
||||
export type CustomTextData = Omit<CustomTextDataWithTextLen, "textLen"> & {
|
||||
text: string[];
|
||||
};
|
||||
|
||||
export function getData(): CustomTextData {
|
||||
return {
|
||||
text: getText(),
|
||||
mode: getMode(),
|
||||
limit: getLimit(),
|
||||
pipeDelimiter: getPipeDelimiter(),
|
||||
};
|
||||
export function getData(): CustomTextSettings {
|
||||
return customTextSettings.get();
|
||||
}
|
||||
|
||||
export function getCustomText(name: string, long = false): string[] {
|
||||
|
|
|
@ -11,7 +11,7 @@ type Before = {
|
|||
mode: Mode | null;
|
||||
punctuation: boolean | null;
|
||||
numbers: boolean | null;
|
||||
customText: CustomText.CustomTextData | null;
|
||||
customText: CustomText.CustomTextSettings | null;
|
||||
};
|
||||
|
||||
export const before: Before = {
|
||||
|
|
|
@ -41,6 +41,7 @@ import { getActiveFunboxes, isFunboxActiveWithProperty } from "./funbox/list";
|
|||
import { getFunbox } from "@monkeytype/funbox";
|
||||
import { SnapshotUserTag } from "../constants/default-snapshot";
|
||||
import { Language } from "@monkeytype/contracts/schemas/languages";
|
||||
import { canQuickRestart as canQuickRestartFn } from "../utils/quick-restart";
|
||||
|
||||
let result: CompletedEvent;
|
||||
let maxChartVal: number;
|
||||
|
@ -966,7 +967,7 @@ export async function update(
|
|||
Misc.applyReducedMotion(125)
|
||||
);
|
||||
|
||||
const canQuickRestart = Misc.canQuickRestart(
|
||||
const canQuickRestart = canQuickRestartFn(
|
||||
Config.mode,
|
||||
Config.words,
|
||||
Config.time,
|
||||
|
|
|
@ -62,7 +62,7 @@ import { QuoteLength } from "@monkeytype/contracts/schemas/configs";
|
|||
import { Mode } from "@monkeytype/contracts/schemas/shared";
|
||||
import {
|
||||
CompletedEvent,
|
||||
CustomTextDataWithTextLen,
|
||||
CompletedEventCustomText,
|
||||
} from "@monkeytype/contracts/schemas/results";
|
||||
import * as XPBar from "../elements/xp-bar";
|
||||
import {
|
||||
|
@ -78,6 +78,7 @@ import { tryCatch } from "@monkeytype/util/trycatch";
|
|||
import { captureException } from "../sentry";
|
||||
import * as Loader from "../elements/loader";
|
||||
import * as TestInitFailed from "../elements/test-init-failed";
|
||||
import { canQuickRestart } from "../utils/quick-restart";
|
||||
|
||||
let failReason = "";
|
||||
const koInputVisual = document.getElementById("koInputVisual") as HTMLElement;
|
||||
|
@ -170,7 +171,7 @@ export function restart(options = {} as RestartOptions): void {
|
|||
if (!ManualRestart.get()) {
|
||||
if (Config.mode !== "zen") event?.preventDefault();
|
||||
if (
|
||||
!Misc.canQuickRestart(
|
||||
!canQuickRestart(
|
||||
Config.mode,
|
||||
Config.words,
|
||||
Config.time,
|
||||
|
@ -801,7 +802,7 @@ function buildCompletedEvent(
|
|||
const wpmCons = Numbers.roundTo2(Numbers.kogasa(stddev3 / avg3));
|
||||
const wpmConsistency = isNaN(wpmCons) ? 0 : wpmCons;
|
||||
|
||||
let customText: CustomTextDataWithTextLen | undefined = undefined;
|
||||
let customText: CompletedEventCustomText | undefined = undefined;
|
||||
if (Config.mode === "custom") {
|
||||
const temp = CustomText.getData();
|
||||
customText = {
|
||||
|
|
|
@ -6,8 +6,9 @@ import * as ConfigEvent from "./observables/config-event";
|
|||
import { debounce, throttle } from "throttle-debounce";
|
||||
import * as TestUI from "./test/test-ui";
|
||||
import { get as getActivePage } from "./states/active-page";
|
||||
import { canQuickRestart, isDevEnvironment } from "./utils/misc";
|
||||
import { isDevEnvironment } from "./utils/misc";
|
||||
import { isCustomTextLong } from "./states/custom-text-name";
|
||||
import { canQuickRestart } from "./utils/quick-restart";
|
||||
|
||||
let isPreviewingFont = false;
|
||||
export function previewFontFamily(font: string): void {
|
||||
|
|
|
@ -7,10 +7,7 @@ import {
|
|||
Mode2,
|
||||
PersonalBests,
|
||||
} from "@monkeytype/contracts/schemas/shared";
|
||||
import {
|
||||
CustomTextDataWithTextLen,
|
||||
Result,
|
||||
} from "@monkeytype/contracts/schemas/results";
|
||||
import { Result } from "@monkeytype/contracts/schemas/results";
|
||||
import { z } from "zod";
|
||||
|
||||
export function whorf(speed: number, wordlen: number): number {
|
||||
|
@ -195,39 +192,6 @@ export function isUsernameValid(name: string): boolean {
|
|||
return /^[0-9a-zA-Z_.-]+$/.test(name);
|
||||
}
|
||||
|
||||
export function canQuickRestart(
|
||||
mode: string,
|
||||
words: number,
|
||||
time: number,
|
||||
CustomText: Omit<CustomTextDataWithTextLen, "textLen">,
|
||||
customTextIsLong: boolean
|
||||
): boolean {
|
||||
const wordsLong = mode === "words" && (words >= 1000 || words === 0);
|
||||
const timeLong = mode === "time" && (time >= 900 || time === 0);
|
||||
const customTextLong = mode === "custom" && customTextIsLong;
|
||||
|
||||
const customTextRandomWordsLong =
|
||||
mode === "custom" &&
|
||||
(CustomText.limit.mode === "word" || CustomText.limit.mode === "section") &&
|
||||
(CustomText.limit.value >= 1000 || CustomText.limit.value === 0);
|
||||
const customTextRandomTimeLong =
|
||||
mode === "custom" &&
|
||||
CustomText.limit.mode === "time" &&
|
||||
(CustomText.limit.value >= 900 || CustomText.limit.value === 0);
|
||||
|
||||
if (
|
||||
wordsLong ||
|
||||
timeLong ||
|
||||
customTextLong ||
|
||||
customTextRandomWordsLong ||
|
||||
customTextRandomTimeLong
|
||||
) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export function clearTimeouts(timeouts: (number | NodeJS.Timeout)[]): void {
|
||||
timeouts.forEach((to) => {
|
||||
if (typeof to === "number") clearTimeout(to);
|
||||
|
|
34
frontend/src/ts/utils/quick-restart.ts
Normal file
34
frontend/src/ts/utils/quick-restart.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import { CustomTextSettings } from "../test/custom-text";
|
||||
|
||||
export function canQuickRestart(
|
||||
mode: string,
|
||||
words: number,
|
||||
time: number,
|
||||
CustomText: CustomTextSettings,
|
||||
customTextIsLong: boolean
|
||||
): boolean {
|
||||
const wordsLong = mode === "words" && (words >= 1000 || words === 0);
|
||||
const timeLong = mode === "time" && (time >= 900 || time === 0);
|
||||
const customTextLong = mode === "custom" && customTextIsLong;
|
||||
|
||||
const customTextRandomWordsLong =
|
||||
mode === "custom" &&
|
||||
(CustomText.limit.mode === "word" || CustomText.limit.mode === "section") &&
|
||||
(CustomText.limit.value >= 1000 || CustomText.limit.value === 0);
|
||||
const customTextRandomTimeLong =
|
||||
mode === "custom" &&
|
||||
CustomText.limit.mode === "time" &&
|
||||
(CustomText.limit.value >= 900 || CustomText.limit.value === 0);
|
||||
|
||||
if (
|
||||
wordsLong ||
|
||||
timeLong ||
|
||||
customTextLong ||
|
||||
customTextRandomWordsLong ||
|
||||
customTextRandomTimeLong
|
||||
) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -30,7 +30,7 @@ export const KeyStatsSchema = z.object({
|
|||
});
|
||||
export type KeyStats = z.infer<typeof KeyStatsSchema>;
|
||||
|
||||
export const CustomTextSchema = z.object({
|
||||
export const CompletedEventCustomTextSchema = z.object({
|
||||
textLen: z.number().int().nonnegative(),
|
||||
mode: CustomTextModeSchema,
|
||||
pipeDelimiter: z.boolean(),
|
||||
|
@ -39,7 +39,9 @@ export const CustomTextSchema = z.object({
|
|||
value: z.number().nonnegative(),
|
||||
}),
|
||||
});
|
||||
export type CustomTextDataWithTextLen = z.infer<typeof CustomTextSchema>;
|
||||
export type CompletedEventCustomText = z.infer<
|
||||
typeof CompletedEventCustomTextSchema
|
||||
>;
|
||||
|
||||
export const CharStatsSchema = z.tuple([
|
||||
z.number().int().nonnegative(),
|
||||
|
@ -120,7 +122,7 @@ export const CompletedEventSchema = ResultBaseSchema.required({
|
|||
.extend({
|
||||
charTotal: z.number().int().nonnegative(),
|
||||
challenge: token().max(100).optional(),
|
||||
customText: CustomTextSchema.optional(),
|
||||
customText: CompletedEventCustomTextSchema.optional(),
|
||||
hash: token().max(100),
|
||||
keyDuration: z.array(z.number().nonnegative()).or(z.literal("toolong")),
|
||||
keySpacing: z.array(z.number().nonnegative()).or(z.literal("toolong")),
|
||||
|
|
Loading…
Add table
Reference in a new issue