diff --git a/backend/src/api/controllers/result.ts b/backend/src/api/controllers/result.ts index 4e7cacdaaf2b..3e49e94b5792 100644 --- a/backend/src/api/controllers/result.ts +++ b/backend/src/api/controllers/result.ts @@ -121,6 +121,7 @@ export async function getResults( limit, offset, }); + void addLog( "user_results_requested", { @@ -132,7 +133,10 @@ export async function getResults( uid, ); - return new MonkeyResponse("Results retrieved", replaceObjectIds(results)); + return new MonkeyResponse( + "Results retrieved", + replaceObjectIds(results) as GetResultsResponse["data"], + ); } export async function getResultById( @@ -142,7 +146,11 @@ export async function getResultById( const { resultId } = req.params; const result = await ResultDAL.getResult(uid, resultId); - return new MonkeyResponse("Result retrieved", replaceObjectId(result)); + + return new MonkeyResponse( + "Result retrieved", + replaceObjectId(result) as GetResultByIdResponse["data"], + ); } export async function getLastResult( @@ -150,7 +158,11 @@ export async function getLastResult( ): Promise { const { uid } = req.ctx.decodedToken; const result = await ResultDAL.getLastResult(uid); - return new MonkeyResponse("Result retrieved", replaceObjectId(result)); + + return new MonkeyResponse( + "Result retrieved", + replaceObjectId(result) as GetLastResultResponse["data"], + ); } export async function deleteAll(req: MonkeyRequest): Promise { @@ -201,6 +213,8 @@ export async function addResult( const completedEvent = req.body.result; completedEvent.uid = uid; + const isPractice = completedEvent.mode === "practice"; + if (isTestTooShort(completedEvent)) { const status = MonkeyStatusCodes.TEST_TOO_SHORT; throw new MonkeyError(status.code, status.message); @@ -437,7 +451,7 @@ export async function addResult( let isPb = false; let tagPbs: string[] = []; - if (!completedEvent.bailedOut) { + if (!completedEvent.bailedOut && !isPractice) { [isPb, tagPbs] = await Promise.all([ UserDAL.checkIfPb(uid, user, completedEvent), UserDAL.checkIfTagPb(uid, user, completedEvent), @@ -629,27 +643,32 @@ export async function addResult( dbresult.keyDurationStats = keyDurationStats; } - const addedResult = await ResultDAL.addResult(uid, dbresult); + let insertedId: string | undefined; + + if (!isPractice) { + const addedResult = await ResultDAL.addResult(uid, dbresult); + insertedId = addedResult.insertedId.toHexString(); + } await UserDAL.incrementXp(uid, xpGained.xp); await UserDAL.incrementTestActivity(user, completedEvent.timestamp); - if (isPb) { + if (isPb && insertedId !== undefined) { void addLog( "user_new_pb", - `${completedEvent.mode + " " + completedEvent.mode2} ${ + `${completedEvent.mode} ${completedEvent.mode2} ${ completedEvent.wpm } ${completedEvent.acc}% ${completedEvent.rawWpm} ${ completedEvent.consistency - }% (${addedResult.insertedId})`, + }% (${insertedId})`, uid, ); } const data: PostResultResponse = { + insertedId: insertedId ?? "practice", isPb, tagPbs, - insertedId: addedResult.insertedId.toHexString(), xp: xpGained.xp, dailyXpBonus: xpGained.dailyBonus ?? false, xpBreakdown: xpGained.breakdown ?? {}, @@ -703,7 +722,7 @@ async function calculateXp( funboxBonus: funboxBonusConfiguration, } = xpConfiguration; - if (mode === "zen" || !enabled) { + if (mode === "zen" || mode === "practice" || !enabled) { return { xp: 0, }; diff --git a/frontend/src/ts/constants/default-result-filters.ts b/frontend/src/ts/constants/default-result-filters.ts index d057e7a6e3aa..29513beb7775 100644 --- a/frontend/src/ts/constants/default-result-filters.ts +++ b/frontend/src/ts/constants/default-result-filters.ts @@ -20,7 +20,9 @@ const object: ResultFilters = { quote: true, zen: true, custom: true, + practice: false, // practice results are never part of stats }, + words: { "10": true, "25": true, diff --git a/packages/schemas/src/shared.ts b/packages/schemas/src/shared.ts index 7a1f5b570d07..c8fd66bcdcd5 100644 --- a/packages/schemas/src/shared.ts +++ b/packages/schemas/src/shared.ts @@ -22,6 +22,7 @@ export const PersonalBestSchema = z.object({ export type PersonalBest = z.infer; //used by user and config + export const PersonalBestsSchema = z.object({ time: z.record( StringNumberSchema.describe("Number of seconds as string"), @@ -34,7 +35,11 @@ export const PersonalBestsSchema = z.object({ quote: z.record(StringNumberSchema, z.array(PersonalBestSchema)), custom: z.record(z.literal("custom"), z.array(PersonalBestSchema)), zen: z.record(z.literal("zen"), z.array(PersonalBestSchema)), + + // practice mode: intentionally empty, never tracked + practice: z.never(), }); + export type PersonalBests = z.infer; export const DefaultWordsModeSchema = z.union([