mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2025-09-06 14:46:47 +08:00
parent
8d5d27d314
commit
5e4478c97a
6 changed files with 120 additions and 18 deletions
|
@ -33,6 +33,7 @@ import { migrateConfig } from "./utils/config";
|
|||
import { roundTo1 } from "@monkeytype/util/numbers";
|
||||
import { getDefaultConfig } from "./constants/default-config";
|
||||
import { parseWithSchema as parseJsonWithSchema } from "@monkeytype/util/json";
|
||||
import * as TestState from "./test/test-state";
|
||||
|
||||
const configLS = new LocalStorageWithSchema({
|
||||
key: "config",
|
||||
|
@ -91,8 +92,20 @@ export function saveFullConfigToLocalStorage(noDbCheck = false): void {
|
|||
ConfigEvent.dispatch("saveToLocalStorage", stringified);
|
||||
}
|
||||
|
||||
function isConfigChangeBlocked(): boolean {
|
||||
if (TestState.isActive && config.funbox.includes("no_quit")) {
|
||||
Notifications.add("No quit funbox is active. Please finish the test.", 0, {
|
||||
important: true,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//numbers
|
||||
export function setNumbers(numb: boolean, nosave?: boolean): boolean {
|
||||
if (isConfigChangeBlocked()) return false;
|
||||
|
||||
if (!isConfigValueValidBoolean("numbers", numb)) return false;
|
||||
|
||||
if (!canSetConfigWithCurrentFunboxes("numbers", numb, config.funbox)) {
|
||||
|
@ -111,6 +124,8 @@ export function setNumbers(numb: boolean, nosave?: boolean): boolean {
|
|||
|
||||
//punctuation
|
||||
export function setPunctuation(punc: boolean, nosave?: boolean): boolean {
|
||||
if (isConfigChangeBlocked()) return false;
|
||||
|
||||
if (!isConfigValueValidBoolean("punctuation", punc)) return false;
|
||||
|
||||
if (!canSetConfigWithCurrentFunboxes("punctuation", punc, config.funbox)) {
|
||||
|
@ -128,6 +143,8 @@ export function setPunctuation(punc: boolean, nosave?: boolean): boolean {
|
|||
}
|
||||
|
||||
export function setMode(mode: Mode, nosave?: boolean): boolean {
|
||||
if (isConfigChangeBlocked()) return false;
|
||||
|
||||
if (!isConfigValueValid("mode", mode, ModeSchema)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -224,6 +241,8 @@ export function setDifficulty(
|
|||
diff: ConfigSchemas.Difficulty,
|
||||
nosave?: boolean
|
||||
): boolean {
|
||||
if (isConfigChangeBlocked()) return false;
|
||||
|
||||
if (!isConfigValueValid("difficulty", diff, ConfigSchemas.DifficultySchema)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -260,6 +279,8 @@ export function setFunbox(
|
|||
funbox: ConfigSchemas.Funbox,
|
||||
nosave?: boolean
|
||||
): boolean {
|
||||
if (isConfigChangeBlocked()) return false;
|
||||
|
||||
if (!isConfigValueValid("funbox", funbox, ConfigSchemas.FunboxSchema))
|
||||
return false;
|
||||
|
||||
|
@ -277,6 +298,8 @@ export function setFunbox(
|
|||
}
|
||||
|
||||
export function toggleFunbox(funbox: FunboxName, nosave?: boolean): boolean {
|
||||
if (isConfigChangeBlocked()) return false;
|
||||
|
||||
if (!canSetFunboxWithConfig(funbox, config)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -346,6 +369,8 @@ export function setStopOnError(
|
|||
soe: ConfigSchemas.StopOnError,
|
||||
nosave?: boolean
|
||||
): boolean {
|
||||
if (isConfigChangeBlocked()) return false;
|
||||
|
||||
if (
|
||||
!isConfigValueValid("stop on error", soe, ConfigSchemas.StopOnErrorSchema)
|
||||
) {
|
||||
|
@ -484,6 +509,8 @@ export function setMinWpm(
|
|||
minwpm: ConfigSchemas.MinimumWordsPerMinute,
|
||||
nosave?: boolean
|
||||
): boolean {
|
||||
if (isConfigChangeBlocked()) return false;
|
||||
|
||||
if (
|
||||
!isConfigValueValid(
|
||||
"min speed",
|
||||
|
@ -505,6 +532,8 @@ export function setMinWpmCustomSpeed(
|
|||
val: ConfigSchemas.MinWpmCustomSpeed,
|
||||
nosave?: boolean
|
||||
): boolean {
|
||||
if (isConfigChangeBlocked()) return false;
|
||||
|
||||
if (
|
||||
!isConfigValueValid(
|
||||
"min speed custom",
|
||||
|
@ -527,6 +556,8 @@ export function setMinAcc(
|
|||
min: ConfigSchemas.MinimumAccuracy,
|
||||
nosave?: boolean
|
||||
): boolean {
|
||||
if (isConfigChangeBlocked()) return false;
|
||||
|
||||
if (!isConfigValueValid("min acc", min, ConfigSchemas.MinimumAccuracySchema))
|
||||
return false;
|
||||
|
||||
|
@ -541,6 +572,8 @@ export function setMinAccCustom(
|
|||
val: ConfigSchemas.MinimumAccuracyCustom,
|
||||
nosave?: boolean
|
||||
): boolean {
|
||||
if (isConfigChangeBlocked()) return false;
|
||||
|
||||
//migrate legacy configs
|
||||
if (val > 100) val = 100;
|
||||
if (
|
||||
|
@ -564,6 +597,8 @@ export function setMinBurst(
|
|||
min: ConfigSchemas.MinimumBurst,
|
||||
nosave?: boolean
|
||||
): boolean {
|
||||
if (isConfigChangeBlocked()) return false;
|
||||
|
||||
if (!isConfigValueValid("min burst", min, ConfigSchemas.MinimumBurstSchema)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -579,6 +614,8 @@ export function setMinBurstCustomSpeed(
|
|||
val: ConfigSchemas.MinimumBurstCustomSpeed,
|
||||
nosave?: boolean
|
||||
): boolean {
|
||||
if (isConfigChangeBlocked()) return false;
|
||||
|
||||
if (
|
||||
!isConfigValueValid(
|
||||
"min burst custom speed",
|
||||
|
@ -732,6 +769,8 @@ export function setColorfulMode(extra: boolean, nosave?: boolean): boolean {
|
|||
|
||||
//strict space
|
||||
export function setStrictSpace(val: boolean, nosave?: boolean): boolean {
|
||||
if (isConfigChangeBlocked()) return false;
|
||||
|
||||
if (!isConfigValueValidBoolean("strict space", val)) return false;
|
||||
|
||||
config.strictSpace = val;
|
||||
|
@ -1087,6 +1126,8 @@ export function setTimeConfig(
|
|||
time: ConfigSchemas.TimeConfig,
|
||||
nosave?: boolean
|
||||
): boolean {
|
||||
if (isConfigChangeBlocked()) return false;
|
||||
|
||||
time = isNaN(time) || time < 0 ? getDefaultConfig().time : time;
|
||||
if (!isConfigValueValid("time", time, ConfigSchemas.TimeConfigSchema))
|
||||
return false;
|
||||
|
@ -1107,6 +1148,8 @@ export function setQuoteLength(
|
|||
nosave?: boolean,
|
||||
multipleMode?: boolean
|
||||
): boolean {
|
||||
if (isConfigChangeBlocked()) return false;
|
||||
|
||||
if (Array.isArray(len)) {
|
||||
if (
|
||||
!isConfigValueValid(
|
||||
|
@ -1159,6 +1202,8 @@ export function setWordCount(
|
|||
wordCount: ConfigSchemas.WordCount,
|
||||
nosave?: boolean
|
||||
): boolean {
|
||||
if (isConfigChangeBlocked()) return false;
|
||||
|
||||
wordCount =
|
||||
wordCount < 0 || wordCount > 100000 ? getDefaultConfig().words : wordCount;
|
||||
|
||||
|
@ -1501,6 +1546,8 @@ export function setRandomTheme(
|
|||
}
|
||||
|
||||
export function setBritishEnglish(val: boolean, nosave?: boolean): boolean {
|
||||
if (isConfigChangeBlocked()) return false;
|
||||
|
||||
if (!isConfigValueValidBoolean("british english", val)) return false;
|
||||
|
||||
if (!val) {
|
||||
|
@ -1514,6 +1561,8 @@ export function setBritishEnglish(val: boolean, nosave?: boolean): boolean {
|
|||
}
|
||||
|
||||
export function setLazyMode(val: boolean, nosave?: boolean): boolean {
|
||||
if (isConfigChangeBlocked()) return false;
|
||||
|
||||
if (!isConfigValueValidBoolean("lazy mode", val)) return false;
|
||||
|
||||
if (!val) {
|
||||
|
@ -1566,6 +1615,8 @@ export function setCustomThemeColors(
|
|||
}
|
||||
|
||||
export function setLanguage(language: Language, nosave?: boolean): boolean {
|
||||
if (isConfigChangeBlocked()) return false;
|
||||
|
||||
if (!isConfigValueValid("language", language, LanguageSchema)) return false;
|
||||
|
||||
config.language = language;
|
||||
|
@ -1743,6 +1794,8 @@ export function setLayout(
|
|||
layout: ConfigSchemas.Layout,
|
||||
nosave?: boolean
|
||||
): boolean {
|
||||
if (isConfigChangeBlocked()) return false;
|
||||
|
||||
if (!isConfigValueValid("layout", layout, ConfigSchemas.LayoutSchema))
|
||||
return false;
|
||||
|
||||
|
@ -1850,6 +1903,8 @@ export function setCustomLayoutfluid(
|
|||
value: ConfigSchemas.CustomLayoutFluid,
|
||||
nosave?: boolean
|
||||
): boolean {
|
||||
if (isConfigChangeBlocked()) return false;
|
||||
|
||||
// Remove duplicates
|
||||
const deduped = Array.from(new Set(value));
|
||||
if (
|
||||
|
@ -1873,6 +1928,8 @@ export function setCustomPolyglot(
|
|||
value: ConfigSchemas.CustomPolyglot,
|
||||
nosave?: boolean
|
||||
): boolean {
|
||||
if (isConfigChangeBlocked()) return false;
|
||||
|
||||
// remove duplicates
|
||||
const deduped = Array.from(new Set(value));
|
||||
if (
|
||||
|
|
|
@ -2,6 +2,9 @@ import * as PageController from "./page-controller";
|
|||
import * as TestUI from "../test/test-ui";
|
||||
import * as PageTransition from "../states/page-transition";
|
||||
import { Auth, isAuthenticated } from "../firebase";
|
||||
import { isFunboxActive } from "../test/funbox/list";
|
||||
import * as TestState from "../test/test-state";
|
||||
import * as Notifications from "../elements/notifications";
|
||||
|
||||
//source: https://www.youtube.com/watch?v=OstALBk-jTc
|
||||
// https://www.youtube.com/watch?v=OstALBk-jTc
|
||||
|
@ -157,6 +160,16 @@ export function navigate(
|
|||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const noQuit = isFunboxActive("no_quit");
|
||||
if (TestState.isActive && noQuit) {
|
||||
Notifications.add("No quit funbox is active. Please finish the test.", 0, {
|
||||
important: true,
|
||||
});
|
||||
event?.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
url = url.replace(/\/$/, "");
|
||||
if (url === "") url = "/";
|
||||
|
||||
|
|
|
@ -89,6 +89,15 @@ export function isFunboxActiveWithProperty(property: FunboxProperty): boolean {
|
|||
return getActiveFunboxesWithProperty(property).length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given funbox is active
|
||||
* @param funbox funbox name
|
||||
* @returns true if the funbox is active, false otherwise
|
||||
*/
|
||||
export function isFunboxActive(funbox: FunboxName): boolean {
|
||||
return getActiveFunboxNames().includes(funbox);
|
||||
}
|
||||
|
||||
type MandatoryFunboxFunction<F extends keyof FunboxFunctions> = Exclude<
|
||||
FunboxFunctions[F],
|
||||
undefined
|
||||
|
|
|
@ -69,6 +69,7 @@ import {
|
|||
findSingleActiveFunboxWithFunction,
|
||||
getActiveFunboxes,
|
||||
getActiveFunboxesWithFunction,
|
||||
isFunboxActive,
|
||||
} from "./funbox/list";
|
||||
import { getFunbox } from "@monkeytype/funbox";
|
||||
import * as CompositionState from "../states/composition";
|
||||
|
@ -163,6 +164,15 @@ export function restart(options = {} as RestartOptions): void {
|
|||
options = { ...defaultOptions, ...options };
|
||||
const animationTime = options.noAnim ? 0 : Misc.applyReducedMotion(125);
|
||||
|
||||
const noQuit = isFunboxActive("no_quit");
|
||||
if (TestState.isActive && noQuit) {
|
||||
Notifications.add("No quit funbox is active. Please finish the test.", 0, {
|
||||
important: true,
|
||||
});
|
||||
event?.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if (TestUI.testRestarting || TestUI.resultCalculating) {
|
||||
event?.preventDefault();
|
||||
return;
|
||||
|
@ -1402,18 +1412,20 @@ $(".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;
|
||||
UpdateConfig.setMode(mode);
|
||||
ManualRestart.set();
|
||||
restart();
|
||||
if (UpdateConfig.setMode(mode)) {
|
||||
ManualRestart.set();
|
||||
restart();
|
||||
}
|
||||
});
|
||||
|
||||
$(".pageTest").on("click", "#testConfig .wordCount .textButton", (e) => {
|
||||
if (TestUI.testRestarting) return;
|
||||
const wrd = $(e.currentTarget).attr("wordCount") ?? "15";
|
||||
if (wrd !== "custom") {
|
||||
UpdateConfig.setWordCount(parseInt(wrd));
|
||||
ManualRestart.set();
|
||||
restart();
|
||||
if (UpdateConfig.setWordCount(parseInt(wrd))) {
|
||||
ManualRestart.set();
|
||||
restart();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1421,9 +1433,10 @@ $(".pageTest").on("click", "#testConfig .time .textButton", (e) => {
|
|||
if (TestUI.testRestarting) return;
|
||||
const mode = $(e.currentTarget).attr("timeConfig") ?? "10";
|
||||
if (mode !== "custom") {
|
||||
UpdateConfig.setTimeConfig(parseInt(mode));
|
||||
ManualRestart.set();
|
||||
restart();
|
||||
if (UpdateConfig.setTimeConfig(parseInt(mode))) {
|
||||
ManualRestart.set();
|
||||
restart();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1436,24 +1449,27 @@ $(".pageTest").on("click", "#testConfig .quoteLength .textButton", (e) => {
|
|||
if (len === -1) {
|
||||
len = [0, 1, 2, 3];
|
||||
}
|
||||
UpdateConfig.setQuoteLength(len, false, e.shiftKey);
|
||||
ManualRestart.set();
|
||||
restart();
|
||||
if (UpdateConfig.setQuoteLength(len, false, e.shiftKey)) {
|
||||
ManualRestart.set();
|
||||
restart();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$(".pageTest").on("click", "#testConfig .punctuationMode.textButton", () => {
|
||||
if (TestUI.testRestarting) return;
|
||||
UpdateConfig.setPunctuation(!Config.punctuation);
|
||||
ManualRestart.set();
|
||||
restart();
|
||||
if (UpdateConfig.setPunctuation(!Config.punctuation)) {
|
||||
ManualRestart.set();
|
||||
restart();
|
||||
}
|
||||
});
|
||||
|
||||
$(".pageTest").on("click", "#testConfig .numbersMode.textButton", () => {
|
||||
if (TestUI.testRestarting) return;
|
||||
UpdateConfig.setNumbers(!Config.numbers);
|
||||
ManualRestart.set();
|
||||
restart();
|
||||
if (UpdateConfig.setNumbers(!Config.numbers)) {
|
||||
ManualRestart.set();
|
||||
restart();
|
||||
}
|
||||
});
|
||||
|
||||
$("header").on("click", "nav #startTestButton, #logo", () => {
|
||||
|
|
|
@ -287,6 +287,7 @@ export const FunboxNameSchema = z.enum([
|
|||
"ALL_CAPS",
|
||||
"polyglot",
|
||||
"asl",
|
||||
"no_quit",
|
||||
]);
|
||||
export type FunboxName = z.infer<typeof FunboxNameSchema>;
|
||||
|
||||
|
|
|
@ -461,6 +461,12 @@ const list: Record<FunboxName, FunboxMetadata> = {
|
|||
name: "asl",
|
||||
cssModifications: ["words"],
|
||||
},
|
||||
no_quit: {
|
||||
description: "You can't restart the test.",
|
||||
canGetPb: true,
|
||||
difficultyLevel: 0,
|
||||
name: "no_quit",
|
||||
},
|
||||
};
|
||||
|
||||
export function getFunbox(name: FunboxName): FunboxMetadata;
|
||||
|
|
Loading…
Add table
Reference in a new issue