Skip to content

Commit 5e4478c

Browse files
authored
feat(funbox): add no quit (@Miodec) (monkeytypegame#6741)
Brrrr
1 parent 8d5d27d commit 5e4478c

File tree

6 files changed

+120
-18
lines changed

6 files changed

+120
-18
lines changed

frontend/src/ts/config.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { migrateConfig } from "./utils/config";
3333
import { roundTo1 } from "@monkeytype/util/numbers";
3434
import { getDefaultConfig } from "./constants/default-config";
3535
import { parseWithSchema as parseJsonWithSchema } from "@monkeytype/util/json";
36+
import * as TestState from "./test/test-state";
3637

3738
const configLS = new LocalStorageWithSchema({
3839
key: "config",
@@ -91,8 +92,20 @@ export function saveFullConfigToLocalStorage(noDbCheck = false): void {
9192
ConfigEvent.dispatch("saveToLocalStorage", stringified);
9293
}
9394

95+
function isConfigChangeBlocked(): boolean {
96+
if (TestState.isActive && config.funbox.includes("no_quit")) {
97+
Notifications.add("No quit funbox is active. Please finish the test.", 0, {
98+
important: true,
99+
});
100+
return true;
101+
}
102+
return false;
103+
}
104+
94105
//numbers
95106
export function setNumbers(numb: boolean, nosave?: boolean): boolean {
107+
if (isConfigChangeBlocked()) return false;
108+
96109
if (!isConfigValueValidBoolean("numbers", numb)) return false;
97110

98111
if (!canSetConfigWithCurrentFunboxes("numbers", numb, config.funbox)) {
@@ -111,6 +124,8 @@ export function setNumbers(numb: boolean, nosave?: boolean): boolean {
111124

112125
//punctuation
113126
export function setPunctuation(punc: boolean, nosave?: boolean): boolean {
127+
if (isConfigChangeBlocked()) return false;
128+
114129
if (!isConfigValueValidBoolean("punctuation", punc)) return false;
115130

116131
if (!canSetConfigWithCurrentFunboxes("punctuation", punc, config.funbox)) {
@@ -128,6 +143,8 @@ export function setPunctuation(punc: boolean, nosave?: boolean): boolean {
128143
}
129144

130145
export function setMode(mode: Mode, nosave?: boolean): boolean {
146+
if (isConfigChangeBlocked()) return false;
147+
131148
if (!isConfigValueValid("mode", mode, ModeSchema)) {
132149
return false;
133150
}
@@ -224,6 +241,8 @@ export function setDifficulty(
224241
diff: ConfigSchemas.Difficulty,
225242
nosave?: boolean
226243
): boolean {
244+
if (isConfigChangeBlocked()) return false;
245+
227246
if (!isConfigValueValid("difficulty", diff, ConfigSchemas.DifficultySchema)) {
228247
return false;
229248
}
@@ -260,6 +279,8 @@ export function setFunbox(
260279
funbox: ConfigSchemas.Funbox,
261280
nosave?: boolean
262281
): boolean {
282+
if (isConfigChangeBlocked()) return false;
283+
263284
if (!isConfigValueValid("funbox", funbox, ConfigSchemas.FunboxSchema))
264285
return false;
265286

@@ -277,6 +298,8 @@ export function setFunbox(
277298
}
278299

279300
export function toggleFunbox(funbox: FunboxName, nosave?: boolean): boolean {
301+
if (isConfigChangeBlocked()) return false;
302+
280303
if (!canSetFunboxWithConfig(funbox, config)) {
281304
return false;
282305
}
@@ -346,6 +369,8 @@ export function setStopOnError(
346369
soe: ConfigSchemas.StopOnError,
347370
nosave?: boolean
348371
): boolean {
372+
if (isConfigChangeBlocked()) return false;
373+
349374
if (
350375
!isConfigValueValid("stop on error", soe, ConfigSchemas.StopOnErrorSchema)
351376
) {
@@ -484,6 +509,8 @@ export function setMinWpm(
484509
minwpm: ConfigSchemas.MinimumWordsPerMinute,
485510
nosave?: boolean
486511
): boolean {
512+
if (isConfigChangeBlocked()) return false;
513+
487514
if (
488515
!isConfigValueValid(
489516
"min speed",
@@ -505,6 +532,8 @@ export function setMinWpmCustomSpeed(
505532
val: ConfigSchemas.MinWpmCustomSpeed,
506533
nosave?: boolean
507534
): boolean {
535+
if (isConfigChangeBlocked()) return false;
536+
508537
if (
509538
!isConfigValueValid(
510539
"min speed custom",
@@ -527,6 +556,8 @@ export function setMinAcc(
527556
min: ConfigSchemas.MinimumAccuracy,
528557
nosave?: boolean
529558
): boolean {
559+
if (isConfigChangeBlocked()) return false;
560+
530561
if (!isConfigValueValid("min acc", min, ConfigSchemas.MinimumAccuracySchema))
531562
return false;
532563

@@ -541,6 +572,8 @@ export function setMinAccCustom(
541572
val: ConfigSchemas.MinimumAccuracyCustom,
542573
nosave?: boolean
543574
): boolean {
575+
if (isConfigChangeBlocked()) return false;
576+
544577
//migrate legacy configs
545578
if (val > 100) val = 100;
546579
if (
@@ -564,6 +597,8 @@ export function setMinBurst(
564597
min: ConfigSchemas.MinimumBurst,
565598
nosave?: boolean
566599
): boolean {
600+
if (isConfigChangeBlocked()) return false;
601+
567602
if (!isConfigValueValid("min burst", min, ConfigSchemas.MinimumBurstSchema)) {
568603
return false;
569604
}
@@ -579,6 +614,8 @@ export function setMinBurstCustomSpeed(
579614
val: ConfigSchemas.MinimumBurstCustomSpeed,
580615
nosave?: boolean
581616
): boolean {
617+
if (isConfigChangeBlocked()) return false;
618+
582619
if (
583620
!isConfigValueValid(
584621
"min burst custom speed",
@@ -732,6 +769,8 @@ export function setColorfulMode(extra: boolean, nosave?: boolean): boolean {
732769

733770
//strict space
734771
export function setStrictSpace(val: boolean, nosave?: boolean): boolean {
772+
if (isConfigChangeBlocked()) return false;
773+
735774
if (!isConfigValueValidBoolean("strict space", val)) return false;
736775

737776
config.strictSpace = val;
@@ -1087,6 +1126,8 @@ export function setTimeConfig(
10871126
time: ConfigSchemas.TimeConfig,
10881127
nosave?: boolean
10891128
): boolean {
1129+
if (isConfigChangeBlocked()) return false;
1130+
10901131
time = isNaN(time) || time < 0 ? getDefaultConfig().time : time;
10911132
if (!isConfigValueValid("time", time, ConfigSchemas.TimeConfigSchema))
10921133
return false;
@@ -1107,6 +1148,8 @@ export function setQuoteLength(
11071148
nosave?: boolean,
11081149
multipleMode?: boolean
11091150
): boolean {
1151+
if (isConfigChangeBlocked()) return false;
1152+
11101153
if (Array.isArray(len)) {
11111154
if (
11121155
!isConfigValueValid(
@@ -1159,6 +1202,8 @@ export function setWordCount(
11591202
wordCount: ConfigSchemas.WordCount,
11601203
nosave?: boolean
11611204
): boolean {
1205+
if (isConfigChangeBlocked()) return false;
1206+
11621207
wordCount =
11631208
wordCount < 0 || wordCount > 100000 ? getDefaultConfig().words : wordCount;
11641209

@@ -1501,6 +1546,8 @@ export function setRandomTheme(
15011546
}
15021547

15031548
export function setBritishEnglish(val: boolean, nosave?: boolean): boolean {
1549+
if (isConfigChangeBlocked()) return false;
1550+
15041551
if (!isConfigValueValidBoolean("british english", val)) return false;
15051552

15061553
if (!val) {
@@ -1514,6 +1561,8 @@ export function setBritishEnglish(val: boolean, nosave?: boolean): boolean {
15141561
}
15151562

15161563
export function setLazyMode(val: boolean, nosave?: boolean): boolean {
1564+
if (isConfigChangeBlocked()) return false;
1565+
15171566
if (!isConfigValueValidBoolean("lazy mode", val)) return false;
15181567

15191568
if (!val) {
@@ -1566,6 +1615,8 @@ export function setCustomThemeColors(
15661615
}
15671616

15681617
export function setLanguage(language: Language, nosave?: boolean): boolean {
1618+
if (isConfigChangeBlocked()) return false;
1619+
15691620
if (!isConfigValueValid("language", language, LanguageSchema)) return false;
15701621

15711622
config.language = language;
@@ -1743,6 +1794,8 @@ export function setLayout(
17431794
layout: ConfigSchemas.Layout,
17441795
nosave?: boolean
17451796
): boolean {
1797+
if (isConfigChangeBlocked()) return false;
1798+
17461799
if (!isConfigValueValid("layout", layout, ConfigSchemas.LayoutSchema))
17471800
return false;
17481801

@@ -1850,6 +1903,8 @@ export function setCustomLayoutfluid(
18501903
value: ConfigSchemas.CustomLayoutFluid,
18511904
nosave?: boolean
18521905
): boolean {
1906+
if (isConfigChangeBlocked()) return false;
1907+
18531908
// Remove duplicates
18541909
const deduped = Array.from(new Set(value));
18551910
if (
@@ -1873,6 +1928,8 @@ export function setCustomPolyglot(
18731928
value: ConfigSchemas.CustomPolyglot,
18741929
nosave?: boolean
18751930
): boolean {
1931+
if (isConfigChangeBlocked()) return false;
1932+
18761933
// remove duplicates
18771934
const deduped = Array.from(new Set(value));
18781935
if (

frontend/src/ts/controllers/route-controller.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import * as PageController from "./page-controller";
22
import * as TestUI from "../test/test-ui";
33
import * as PageTransition from "../states/page-transition";
44
import { Auth, isAuthenticated } from "../firebase";
5+
import { isFunboxActive } from "../test/funbox/list";
6+
import * as TestState from "../test/test-state";
7+
import * as Notifications from "../elements/notifications";
58

69
//source: https://www.youtube.com/watch?v=OstALBk-jTc
710
// https://www.youtube.com/watch?v=OstALBk-jTc
@@ -157,6 +160,16 @@ export function navigate(
157160
);
158161
return;
159162
}
163+
164+
const noQuit = isFunboxActive("no_quit");
165+
if (TestState.isActive && noQuit) {
166+
Notifications.add("No quit funbox is active. Please finish the test.", 0, {
167+
important: true,
168+
});
169+
event?.preventDefault();
170+
return;
171+
}
172+
160173
url = url.replace(/\/$/, "");
161174
if (url === "") url = "/";
162175

frontend/src/ts/test/funbox/list.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,15 @@ export function isFunboxActiveWithProperty(property: FunboxProperty): boolean {
8989
return getActiveFunboxesWithProperty(property).length > 0;
9090
}
9191

92+
/**
93+
* Check if the given funbox is active
94+
* @param funbox funbox name
95+
* @returns true if the funbox is active, false otherwise
96+
*/
97+
export function isFunboxActive(funbox: FunboxName): boolean {
98+
return getActiveFunboxNames().includes(funbox);
99+
}
100+
92101
type MandatoryFunboxFunction<F extends keyof FunboxFunctions> = Exclude<
93102
FunboxFunctions[F],
94103
undefined

frontend/src/ts/test/test-logic.ts

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ import {
6969
findSingleActiveFunboxWithFunction,
7070
getActiveFunboxes,
7171
getActiveFunboxesWithFunction,
72+
isFunboxActive,
7273
} from "./funbox/list";
7374
import { getFunbox } from "@monkeytype/funbox";
7475
import * as CompositionState from "../states/composition";
@@ -163,6 +164,15 @@ export function restart(options = {} as RestartOptions): void {
163164
options = { ...defaultOptions, ...options };
164165
const animationTime = options.noAnim ? 0 : Misc.applyReducedMotion(125);
165166

167+
const noQuit = isFunboxActive("no_quit");
168+
if (TestState.isActive && noQuit) {
169+
Notifications.add("No quit funbox is active. Please finish the test.", 0, {
170+
important: true,
171+
});
172+
event?.preventDefault();
173+
return;
174+
}
175+
166176
if (TestUI.testRestarting || TestUI.resultCalculating) {
167177
event?.preventDefault();
168178
return;
@@ -1402,28 +1412,31 @@ $(".pageTest").on("click", "#testConfig .mode .textButton", (e) => {
14021412
if ($(e.currentTarget).hasClass("active")) return;
14031413
const mode = ($(e.currentTarget).attr("mode") ?? "time") as Mode;
14041414
if (mode === undefined) return;
1405-
UpdateConfig.setMode(mode);
1406-
ManualRestart.set();
1407-
restart();
1415+
if (UpdateConfig.setMode(mode)) {
1416+
ManualRestart.set();
1417+
restart();
1418+
}
14081419
});
14091420

14101421
$(".pageTest").on("click", "#testConfig .wordCount .textButton", (e) => {
14111422
if (TestUI.testRestarting) return;
14121423
const wrd = $(e.currentTarget).attr("wordCount") ?? "15";
14131424
if (wrd !== "custom") {
1414-
UpdateConfig.setWordCount(parseInt(wrd));
1415-
ManualRestart.set();
1416-
restart();
1425+
if (UpdateConfig.setWordCount(parseInt(wrd))) {
1426+
ManualRestart.set();
1427+
restart();
1428+
}
14171429
}
14181430
});
14191431

14201432
$(".pageTest").on("click", "#testConfig .time .textButton", (e) => {
14211433
if (TestUI.testRestarting) return;
14221434
const mode = $(e.currentTarget).attr("timeConfig") ?? "10";
14231435
if (mode !== "custom") {
1424-
UpdateConfig.setTimeConfig(parseInt(mode));
1425-
ManualRestart.set();
1426-
restart();
1436+
if (UpdateConfig.setTimeConfig(parseInt(mode))) {
1437+
ManualRestart.set();
1438+
restart();
1439+
}
14271440
}
14281441
});
14291442

@@ -1436,24 +1449,27 @@ $(".pageTest").on("click", "#testConfig .quoteLength .textButton", (e) => {
14361449
if (len === -1) {
14371450
len = [0, 1, 2, 3];
14381451
}
1439-
UpdateConfig.setQuoteLength(len, false, e.shiftKey);
1440-
ManualRestart.set();
1441-
restart();
1452+
if (UpdateConfig.setQuoteLength(len, false, e.shiftKey)) {
1453+
ManualRestart.set();
1454+
restart();
1455+
}
14421456
}
14431457
});
14441458

14451459
$(".pageTest").on("click", "#testConfig .punctuationMode.textButton", () => {
14461460
if (TestUI.testRestarting) return;
1447-
UpdateConfig.setPunctuation(!Config.punctuation);
1448-
ManualRestart.set();
1449-
restart();
1461+
if (UpdateConfig.setPunctuation(!Config.punctuation)) {
1462+
ManualRestart.set();
1463+
restart();
1464+
}
14501465
});
14511466

14521467
$(".pageTest").on("click", "#testConfig .numbersMode.textButton", () => {
14531468
if (TestUI.testRestarting) return;
1454-
UpdateConfig.setNumbers(!Config.numbers);
1455-
ManualRestart.set();
1456-
restart();
1469+
if (UpdateConfig.setNumbers(!Config.numbers)) {
1470+
ManualRestart.set();
1471+
restart();
1472+
}
14571473
});
14581474

14591475
$("header").on("click", "nav #startTestButton, #logo", () => {

packages/contracts/src/schemas/configs.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ export const FunboxNameSchema = z.enum([
287287
"ALL_CAPS",
288288
"polyglot",
289289
"asl",
290+
"no_quit",
290291
]);
291292
export type FunboxName = z.infer<typeof FunboxNameSchema>;
292293

packages/funbox/src/list.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,12 @@ const list: Record<FunboxName, FunboxMetadata> = {
461461
name: "asl",
462462
cssModifications: ["words"],
463463
},
464+
no_quit: {
465+
description: "You can't restart the test.",
466+
canGetPb: true,
467+
difficultyLevel: 0,
468+
name: "no_quit",
469+
},
464470
};
465471

466472
export function getFunbox(name: FunboxName): FunboxMetadata;

0 commit comments

Comments
 (0)