From 10f4a13c1fb7a8b44c73d7d6fed0fa645c2a69a9 Mon Sep 17 00:00:00 2001 From: Miodec Date: Sat, 20 Dec 2025 13:55:08 +0100 Subject: [PATCH 1/8] chore: move ui code out of the test-logic file --- frontend/src/ts/test/result.ts | 6 +++++- frontend/src/ts/test/test-logic.ts | 6 ------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/frontend/src/ts/test/result.ts b/frontend/src/ts/test/result.ts index 5d352af7b7d2..eb2a3d471925 100644 --- a/frontend/src/ts/test/result.ts +++ b/frontend/src/ts/test/result.ts @@ -1064,10 +1064,14 @@ export async function update( } else { $("main #result .stats").removeClass("hidden"); $("main #result .chart").removeClass("hidden"); - // $("main #result #resultWordsHistory").removeClass("hidden"); if (!isAuthenticated()) { $("main #result .loginTip").removeClass("hidden"); + $("main #result #rateQuoteButton").addClass("hidden"); + $("main #result #reportQuoteButton").addClass("hidden"); + } else { + $("main #result #reportQuoteButton").removeClass("hidden"); } + $("main #result .stats .dailyLeaderboard").addClass("hidden"); $("main #result #showWordHistoryButton").removeClass("hidden"); $("main #result #watchReplayButton").removeClass("hidden"); $("main #result #saveScreenshotButton").removeClass("hidden"); diff --git a/frontend/src/ts/test/test-logic.ts b/frontend/src/ts/test/test-logic.ts index 53fcdb878e6b..0187092758b4 100644 --- a/frontend/src/ts/test/test-logic.ts +++ b/frontend/src/ts/test/test-logic.ts @@ -1134,17 +1134,11 @@ export async function finish(difficultyFailed = false): Promise { Result.updateTodayTracker(); if (!isAuthenticated()) { - $(".pageTest #result #rateQuoteButton").addClass("hidden"); - $(".pageTest #result #reportQuoteButton").addClass("hidden"); void AnalyticsController.log("testCompletedNoLogin"); if (!dontSave) notSignedInLastResult = completedEvent; dontSave = true; - } else { - $(".pageTest #result #reportQuoteButton").removeClass("hidden"); } - $("#result .stats .dailyLeaderboard").addClass("hidden"); - TestStats.setLastResult(structuredClone(completedEvent)); if (!ConnectionState.get()) { From 1f4e616d74653bd4edcdc746405baf28b4644fb9 Mon Sep 17 00:00:00 2001 From: Miodec Date: Sat, 20 Dec 2025 14:00:12 +0100 Subject: [PATCH 2/8] refactor: move code around --- frontend/src/ts/test/result.ts | 6 ++++++ frontend/src/ts/test/test-logic.ts | 8 ++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/frontend/src/ts/test/result.ts b/frontend/src/ts/test/result.ts index eb2a3d471925..b32fe1c89538 100644 --- a/frontend/src/ts/test/result.ts +++ b/frontend/src/ts/test/result.ts @@ -46,6 +46,7 @@ import { LocalStorageWithSchema } from "../utils/local-storage-with-schema"; import { z } from "zod"; import * as TestState from "./test-state"; import { blurInputElement } from "../input/input-element"; +import * as ConnectionState from "../states/connection"; let result: CompletedEvent; let maxChartVal: number; @@ -1006,6 +1007,11 @@ export async function update( } else { $("#result #watchVideoAdButton").removeClass("hidden"); } + + if (!ConnectionState.get()) { + ConnectionState.showOfflineBanner(); + } + updateWpmAndAcc(); updateConsistency(); updateTime(); diff --git a/frontend/src/ts/test/test-logic.ts b/frontend/src/ts/test/test-logic.ts index 0187092758b4..1a41860b0405 100644 --- a/frontend/src/ts/test/test-logic.ts +++ b/frontend/src/ts/test/test-logic.ts @@ -982,6 +982,8 @@ export async function finish(difficultyFailed = false): Promise { const completedEvent = structuredClone(ce) as CompletedEvent; + TestStats.setLastResult(structuredClone(completedEvent)); + ///////// completed event ready //afk check @@ -1139,12 +1141,6 @@ export async function finish(difficultyFailed = false): Promise { dontSave = true; } - TestStats.setLastResult(structuredClone(completedEvent)); - - if (!ConnectionState.get()) { - ConnectionState.showOfflineBanner(); - } - await Result.update( completedEvent, difficultyFailed, From 2cba7576bd3ef37283fd01889c76122f865fa66a Mon Sep 17 00:00:00 2001 From: Miodec Date: Sat, 20 Dec 2025 14:02:49 +0100 Subject: [PATCH 3/8] chore: move ui code to result --- frontend/src/ts/test/result.ts | 25 ++++++++++++++++++++++++- frontend/src/ts/test/test-logic.ts | 28 ---------------------------- 2 files changed, 24 insertions(+), 29 deletions(-) diff --git a/frontend/src/ts/test/result.ts b/frontend/src/ts/test/result.ts index b32fe1c89538..377f93c39bbf 100644 --- a/frontend/src/ts/test/result.ts +++ b/frontend/src/ts/test/result.ts @@ -1083,8 +1083,31 @@ export async function update( $("main #result #saveScreenshotButton").removeClass("hidden"); } - TestConfig.hide(); + if (res.wpm === 0 && !difficultyFailed && res.testDuration >= 5) { + const roundedTime = Math.round(res.testDuration); + + const messages = [ + `Congratulations. You just wasted ${roundedTime} seconds of your life by typing nothing. Be proud of yourself.`, + `Bravo! You've managed to waste ${roundedTime} seconds and accomplish exactly zero. A true productivity icon.`, + `That was ${roundedTime} seconds of absolutely legendary idleness. History will remember this moment.`, + `Wow, ${roundedTime} seconds of typing... nothing. Bold. Mysterious. Completely useless.`, + `Thank you for those ${roundedTime} seconds of utter nothingness. The keyboard needed the break.`, + `A breathtaking display of inactivity. ${roundedTime} seconds of absolutely nothing. Powerful.`, + `You just gave ${roundedTime} seconds of your life to the void. And the void says thanks.`, + `Stunning. ${roundedTime} seconds of intense... whatever that wasn't. Keep it up, champ.`, + `Is it performance art? A protest? Or just ${roundedTime} seconds of glorious nothing? We may never know.`, + `You typed nothing for ${roundedTime} seconds. And in that moment, you became legend.`, + ]; + + showConfetti(); + Notifications.add(Arrays.randomElementFromArray(messages), 0, { + customTitle: "Nice", + duration: 15, + important: true, + }); + } + TestConfig.hide(); Focus.set(false); const canQuickRestart = canQuickRestartFn( diff --git a/frontend/src/ts/test/test-logic.ts b/frontend/src/ts/test/test-logic.ts index 1a41860b0405..ef72d5547000 100644 --- a/frontend/src/ts/test/test-logic.ts +++ b/frontend/src/ts/test/test-logic.ts @@ -1163,34 +1163,6 @@ export async function finish(difficultyFailed = false): Promise { completedEvent.keyDuration = "toolong"; } - if ( - completedEvent.wpm === 0 && - !difficultyFailed && - completedEvent.testDuration >= 5 - ) { - const roundedTime = Math.round(completedEvent.testDuration); - - const messages = [ - `Congratulations. You just wasted ${roundedTime} seconds of your life by typing nothing. Be proud of yourself.`, - `Bravo! You've managed to waste ${roundedTime} seconds and accomplish exactly zero. A true productivity icon.`, - `That was ${roundedTime} seconds of absolutely legendary idleness. History will remember this moment.`, - `Wow, ${roundedTime} seconds of typing... nothing. Bold. Mysterious. Completely useless.`, - `Thank you for those ${roundedTime} seconds of utter nothingness. The keyboard needed the break.`, - `A breathtaking display of inactivity. ${roundedTime} seconds of absolutely nothing. Powerful.`, - `You just gave ${roundedTime} seconds of your life to the void. And the void says thanks.`, - `Stunning. ${roundedTime} seconds of intense... whatever that wasn't. Keep it up, champ.`, - `Is it performance art? A protest? Or just ${roundedTime} seconds of glorious nothing? We may never know.`, - `You typed nothing for ${roundedTime} seconds. And in that moment, you became legend.`, - ]; - - Result.showConfetti(); - Notifications.add(Arrays.randomElementFromArray(messages), 0, { - customTitle: "Nice", - duration: 15, - important: true, - }); - } - if (dontSave) { void AnalyticsController.log("testCompletedInvalid"); return; From 5f506281102e11a2e22a872ef6e38ea66b6a1114 Mon Sep 17 00:00:00 2001 From: Miodec Date: Sat, 20 Dec 2025 14:45:07 +0100 Subject: [PATCH 4/8] chore: remove unnecessary code --- frontend/src/ts/test/test-logic.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/frontend/src/ts/test/test-logic.ts b/frontend/src/ts/test/test-logic.ts index ef72d5547000..3655a962e5b0 100644 --- a/frontend/src/ts/test/test-logic.ts +++ b/frontend/src/ts/test/test-logic.ts @@ -1152,11 +1152,6 @@ export async function finish(difficultyFailed = false): Promise { dontSave, ); - if (completedEvent.chartData !== "toolong") { - // @ts-expect-error TODO: check if this is needed - delete completedEvent.chartData.unsmoothedRaw; - } - if (completedEvent.testDuration > 122) { completedEvent.chartData = "toolong"; completedEvent.keySpacing = "toolong"; From 4fae28c0f6ab1929cded251393fd75656c9712e7 Mon Sep 17 00:00:00 2001 From: Miodec Date: Sat, 20 Dec 2025 15:25:47 +0100 Subject: [PATCH 5/8] chore: move result code to result file --- frontend/src/ts/test/result.ts | 2 ++ frontend/src/ts/test/test-logic.ts | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/ts/test/result.ts b/frontend/src/ts/test/result.ts index 377f93c39bbf..24905cad853b 100644 --- a/frontend/src/ts/test/result.ts +++ b/frontend/src/ts/test/result.ts @@ -47,6 +47,7 @@ import { z } from "zod"; import * as TestState from "./test-state"; import { blurInputElement } from "../input/input-element"; import * as ConnectionState from "../states/connection"; +import { currentQuote } from "./test-words"; let result: CompletedEvent; let maxChartVal: number; @@ -1075,6 +1076,7 @@ export async function update( $("main #result #rateQuoteButton").addClass("hidden"); $("main #result #reportQuoteButton").addClass("hidden"); } else { + updateRateQuote(currentQuote); $("main #result #reportQuoteButton").removeClass("hidden"); } $("main #result .stats .dailyLeaderboard").addClass("hidden"); diff --git a/frontend/src/ts/test/test-logic.ts b/frontend/src/ts/test/test-logic.ts index 3655a962e5b0..5a4191df415f 100644 --- a/frontend/src/ts/test/test-logic.ts +++ b/frontend/src/ts/test/test-logic.ts @@ -1175,8 +1175,6 @@ export async function finish(difficultyFailed = false): Promise { completedEvent.uid = user.uid; - Result.updateRateQuote(TestWords.currentQuote); - if (!completedEvent.bailedOut) { const challenge = ChallengeContoller.verify(completedEvent); if (challenge !== null) completedEvent.challenge = challenge; From 11c2bff300faeba369efff3c4789bf48c9d0a5cd Mon Sep 17 00:00:00 2001 From: Miodec Date: Sat, 20 Dec 2025 15:27:42 +0100 Subject: [PATCH 6/8] chore: reorder --- frontend/src/ts/test/test-logic.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/src/ts/test/test-logic.ts b/frontend/src/ts/test/test-logic.ts index 5a4191df415f..e33bfc5ab108 100644 --- a/frontend/src/ts/test/test-logic.ts +++ b/frontend/src/ts/test/test-logic.ts @@ -1152,12 +1152,6 @@ export async function finish(difficultyFailed = false): Promise { dontSave, ); - if (completedEvent.testDuration > 122) { - completedEvent.chartData = "toolong"; - completedEvent.keySpacing = "toolong"; - completedEvent.keyDuration = "toolong"; - } - if (dontSave) { void AnalyticsController.log("testCompletedInvalid"); return; @@ -1175,6 +1169,12 @@ export async function finish(difficultyFailed = false): Promise { completedEvent.uid = user.uid; + if (completedEvent.testDuration > 122) { + completedEvent.chartData = "toolong"; + completedEvent.keySpacing = "toolong"; + completedEvent.keyDuration = "toolong"; + } + if (!completedEvent.bailedOut) { const challenge = ChallengeContoller.verify(completedEvent); if (challenge !== null) completedEvent.challenge = challenge; From 58b6162b1145705f3853ceccdfd9791042ab277d Mon Sep 17 00:00:00 2001 From: Miodec Date: Sat, 20 Dec 2025 17:56:10 +0100 Subject: [PATCH 7/8] chore: missing setInvalid calls --- frontend/src/ts/test/test-logic.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frontend/src/ts/test/test-logic.ts b/frontend/src/ts/test/test-logic.ts index e33bfc5ab108..99b4f64e299a 100644 --- a/frontend/src/ts/test/test-logic.ts +++ b/frontend/src/ts/test/test-logic.ts @@ -1013,9 +1013,11 @@ export async function finish(difficultyFailed = false): Promise { dontSave = true; } else if (afkDetected) { Notifications.add("Test invalid - AFK detected", 0); + TestStats.setInvalid(); dontSave = true; } else if (TestState.isRepeated) { Notifications.add("Test invalid - repeated", 0); + TestStats.setInvalid(); dontSave = true; } else if ( completedEvent.testDuration < 1 || @@ -1037,6 +1039,7 @@ export async function finish(difficultyFailed = false): Promise { (Config.mode === "zen" && completedEvent.testDuration < 15) ) { Notifications.add("Test invalid - too short", 0); + TestStats.setInvalid(); tooShort = true; dontSave = true; } else if ( From d11bdaa710328dc52bab918a87ed30dd3f90c0ed Mon Sep 17 00:00:00 2001 From: Miodec Date: Sat, 20 Dec 2025 18:08:35 +0100 Subject: [PATCH 8/8] refactor: result saving code flow --- frontend/src/ts/test/test-logic.ts | 84 ++++++++++++++++-------------- 1 file changed, 44 insertions(+), 40 deletions(-) diff --git a/frontend/src/ts/test/test-logic.ts b/frontend/src/ts/test/test-logic.ts index 99b4f64e299a..28206df798bf 100644 --- a/frontend/src/ts/test/test-logic.ts +++ b/frontend/src/ts/test/test-logic.ts @@ -1138,13 +1138,48 @@ export async function finish(difficultyFailed = false): Promise { ); Result.updateTodayTracker(); - if (!isAuthenticated()) { + let savingResultPromise: ReturnType = + Promise.resolve(null); + const user = getAuthenticatedUser(); + if (user !== null) { + // logged in + if (dontSave) { + void AnalyticsController.log("testCompletedInvalid"); + } else { + TestStats.resetIncomplete(); + + if (completedEvent.testDuration > 122) { + completedEvent.chartData = "toolong"; + completedEvent.keySpacing = "toolong"; + completedEvent.keyDuration = "toolong"; + } + + if (!completedEvent.bailedOut) { + const challenge = ChallengeContoller.verify(completedEvent); + if (challenge !== null) completedEvent.challenge = challenge; + } + + completedEvent.uid = user.uid; + completedEvent.hash = objectHash(completedEvent); + + savingResultPromise = saveResult(completedEvent, false); + void savingResultPromise.then((response) => { + if (response && response.status === 200) { + void AnalyticsController.log("testCompleted"); + } + }); + } + } else { + // logged out void AnalyticsController.log("testCompletedNoLogin"); - if (!dontSave) notSignedInLastResult = completedEvent; + if (!dontSave) { + // if its valid save it for later + notSignedInLastResult = completedEvent; + } dontSave = true; } - await Result.update( + const resultUpdatePromise = Result.update( completedEvent, difficultyFailed, failReason, @@ -1155,43 +1190,13 @@ export async function finish(difficultyFailed = false): Promise { dontSave, ); - if (dontSave) { - void AnalyticsController.log("testCompletedInvalid"); - return; - } - - // because of the dont save check above, we know the user is signed in - // we check here again so that typescript doesnt complain - const user = getAuthenticatedUser(); - if (!user) { - return; - } - - // user is logged in - TestStats.resetIncomplete(); - - completedEvent.uid = user.uid; - - if (completedEvent.testDuration > 122) { - completedEvent.chartData = "toolong"; - completedEvent.keySpacing = "toolong"; - completedEvent.keyDuration = "toolong"; - } - - if (!completedEvent.bailedOut) { - const challenge = ChallengeContoller.verify(completedEvent); - if (challenge !== null) completedEvent.challenge = challenge; - } - - completedEvent.hash = objectHash(completedEvent); - - await saveResult(completedEvent, false); + await Promise.all([savingResultPromise, resultUpdatePromise]); } async function saveResult( completedEvent: CompletedEvent, isRetrying: boolean, -): Promise { +): Promise>> { AccountButton.loading(true); if (!TestState.savingEnabled) { @@ -1201,7 +1206,7 @@ async function saveResult( important: true, }); AccountButton.loading(false); - return; + return null; } if (!ConnectionState.get()) { @@ -1216,7 +1221,7 @@ async function saveResult( if (!isRetrying) { retrySaving.completedEvent = completedEvent; } - return; + return null; } const response = await Ape.results.add({ body: { result: completedEvent } }); @@ -1244,7 +1249,7 @@ async function saveResult( "Looks like your result data is using an incorrect schema. Please refresh the page to download the new update. If the problem persists, please contact support."; } Notifications.add("Failed to save result", -1, { response }); - return; + return response; } const data = response.body.data; @@ -1285,8 +1290,6 @@ async function saveResult( dataToSave.result = result; } - void AnalyticsController.log("testCompleted"); - if (data.isPb !== undefined && data.isPb) { //new pb const localPb = await DB.getLocalPB( @@ -1335,6 +1338,7 @@ async function saveResult( Notifications.add("Result saved", 1, { important: true }); } DB.saveLocalResult(dataToSave); + return response; } export function fail(reason: string): void {