mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2025-09-21 22:15:53 +08:00
Update PersonalBests
Interface (#4158) Ferotiq
* update `PersonalBests` interface * removed properties * setting to skeleton if needed * consistency and add skeleton by default * consistency * populate personalBests on user get * simplification + consistency + small fixes * protecting against partial object * removed duplicate * not optional property * ensuring personal bests structure while creating user snapshot * checking pb structure for tags * missing skeleton * required personal bests property * simplify --------- Co-authored-by: Miodec <jack@monkeytype.com>
This commit is contained in:
parent
3f7f77841b
commit
9408322503
10 changed files with 192 additions and 154 deletions
|
@ -224,8 +224,8 @@ describe("UserDal", () => {
|
|||
time: { 20: [mockPersonalBest] },
|
||||
words: {},
|
||||
quote: {},
|
||||
custom: {},
|
||||
zen: {},
|
||||
custom: {},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -331,6 +331,14 @@ export async function getUser(
|
|||
}
|
||||
}
|
||||
|
||||
userInfo.personalBests ??= {
|
||||
time: {},
|
||||
words: {},
|
||||
quote: {},
|
||||
zen: {},
|
||||
custom: {},
|
||||
};
|
||||
|
||||
const agentLog = buildAgentLog(req);
|
||||
Logger.logToDb("user_data_requested", agentLog, uid);
|
||||
|
||||
|
|
|
@ -24,6 +24,13 @@ export async function addUser(
|
|||
email,
|
||||
uid,
|
||||
addedAt: Date.now(),
|
||||
personalBests: {
|
||||
time: {},
|
||||
words: {},
|
||||
quote: {},
|
||||
zen: {},
|
||||
custom: {},
|
||||
},
|
||||
};
|
||||
|
||||
const result = await getUsersCollection().updateOne(
|
||||
|
@ -47,11 +54,11 @@ export async function resetUser(uid: string): Promise<void> {
|
|||
{
|
||||
$set: {
|
||||
personalBests: {
|
||||
custom: {},
|
||||
quote: {},
|
||||
time: {},
|
||||
words: {},
|
||||
quote: {},
|
||||
zen: {},
|
||||
custom: {},
|
||||
},
|
||||
lbPersonalBests: {
|
||||
time: {},
|
||||
|
@ -119,11 +126,11 @@ export async function clearPb(uid: string): Promise<void> {
|
|||
{
|
||||
$set: {
|
||||
personalBests: {
|
||||
custom: {},
|
||||
quote: {},
|
||||
time: {},
|
||||
words: {},
|
||||
quote: {},
|
||||
zen: {},
|
||||
custom: {},
|
||||
},
|
||||
lbPersonalBests: {
|
||||
time: {},
|
||||
|
@ -265,14 +272,27 @@ export async function addTag(
|
|||
name: string
|
||||
): Promise<MonkeyTypes.UserTag> {
|
||||
const _id = new ObjectId();
|
||||
await getUsersCollection().updateOne(
|
||||
{ uid },
|
||||
{ $push: { tags: { _id, name } } }
|
||||
);
|
||||
return {
|
||||
const toPush = {
|
||||
_id,
|
||||
name,
|
||||
personalBests: {
|
||||
time: {},
|
||||
words: {},
|
||||
quote: {},
|
||||
zen: {},
|
||||
custom: {},
|
||||
},
|
||||
};
|
||||
|
||||
await getUsersCollection().updateOne(
|
||||
{ uid },
|
||||
{
|
||||
$push: {
|
||||
tags: toPush,
|
||||
},
|
||||
}
|
||||
);
|
||||
return toPush;
|
||||
}
|
||||
|
||||
export async function getTags(uid: string): Promise<MonkeyTypes.UserTag[]> {
|
||||
|
@ -332,7 +352,17 @@ export async function removeTagPb(uid: string, _id: string): Promise<void> {
|
|||
uid: uid,
|
||||
"tags._id": new ObjectId(_id),
|
||||
},
|
||||
{ $set: { "tags.$.personalBests": {} } }
|
||||
{
|
||||
$set: {
|
||||
"tags.$.personalBests": {
|
||||
time: {},
|
||||
words: {},
|
||||
quote: {},
|
||||
zen: {},
|
||||
custom: {},
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -371,20 +401,18 @@ export async function checkIfPb(
|
|||
return false;
|
||||
}
|
||||
|
||||
let lbPb = user.lbPersonalBests;
|
||||
if (!lbPb) lbPb = { time: {} };
|
||||
user.personalBests ??= {
|
||||
time: {},
|
||||
custom: {},
|
||||
quote: {},
|
||||
words: {},
|
||||
zen: {},
|
||||
};
|
||||
user.lbPersonalBests ??= {
|
||||
time: {},
|
||||
};
|
||||
|
||||
const pb = checkAndUpdatePb(
|
||||
user.personalBests ?? {
|
||||
time: {},
|
||||
custom: {},
|
||||
quote: {},
|
||||
words: {},
|
||||
zen: {},
|
||||
},
|
||||
lbPb,
|
||||
result
|
||||
);
|
||||
const pb = checkAndUpdatePb(user.personalBests, user.lbPersonalBests, result);
|
||||
|
||||
if (!pb.isPb) return false;
|
||||
|
||||
|
@ -430,15 +458,15 @@ export async function checkIfTagPb(
|
|||
const ret: string[] = [];
|
||||
|
||||
for (const tag of tagsToCheck) {
|
||||
const tagPbs: MonkeyTypes.PersonalBests = tag.personalBests ?? {
|
||||
tag.personalBests ??= {
|
||||
time: {},
|
||||
words: {},
|
||||
quote: {},
|
||||
zen: {},
|
||||
custom: {},
|
||||
quote: {},
|
||||
};
|
||||
|
||||
const tagpb = checkAndUpdatePb(tagPbs, undefined, result);
|
||||
const tagpb = checkAndUpdatePb(tag.personalBests, undefined, result);
|
||||
if (tagpb.isPb) {
|
||||
ret.push(tag._id.toHexString());
|
||||
await getUsersCollection().updateOne(
|
||||
|
@ -459,10 +487,10 @@ export async function resetPb(uid: string): Promise<void> {
|
|||
$set: {
|
||||
personalBests: {
|
||||
time: {},
|
||||
custom: {},
|
||||
quote: {},
|
||||
words: {},
|
||||
quote: {},
|
||||
zen: {},
|
||||
custom: {},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
20
backend/src/types/types.d.ts
vendored
20
backend/src/types/types.d.ts
vendored
|
@ -170,7 +170,7 @@ declare namespace MonkeyTypes {
|
|||
lbPersonalBests?: LbPersonalBests;
|
||||
name: string;
|
||||
customThemes?: CustomTheme[];
|
||||
personalBests?: PersonalBests;
|
||||
personalBests: PersonalBests;
|
||||
quoteRatings?: UserQuoteRatings;
|
||||
startedTests?: number;
|
||||
tags?: UserTag[];
|
||||
|
@ -284,7 +284,7 @@ declare namespace MonkeyTypes {
|
|||
interface UserTag {
|
||||
_id: ObjectId;
|
||||
name: string;
|
||||
personalBests?: PersonalBests;
|
||||
personalBests: PersonalBests;
|
||||
}
|
||||
|
||||
interface LeaderboardEntry {
|
||||
|
@ -352,17 +352,11 @@ declare namespace MonkeyTypes {
|
|||
}
|
||||
|
||||
interface PersonalBests {
|
||||
time: {
|
||||
[key: number]: PersonalBest[];
|
||||
};
|
||||
words: {
|
||||
[key: number]: PersonalBest[];
|
||||
};
|
||||
quote: { [quote: string]: PersonalBest[] };
|
||||
custom: { custom?: PersonalBest[] };
|
||||
zen: {
|
||||
zen?: PersonalBest[];
|
||||
};
|
||||
time: Record<number, PersonalBest[]>;
|
||||
words: Record<number, PersonalBest[]>;
|
||||
quote: Record<string, PersonalBest[]>;
|
||||
custom: Partial<Record<"custom", PersonalBest[]>>;
|
||||
zen: Partial<Record<"zen", PersonalBest[]>>;
|
||||
}
|
||||
|
||||
interface ChartData {
|
||||
|
|
|
@ -3,9 +3,9 @@ export const defaultSnap: MonkeyTypes.Snapshot = {
|
|||
personalBests: {
|
||||
time: {},
|
||||
words: {},
|
||||
zen: { zen: [] },
|
||||
quote: { custom: [] },
|
||||
custom: { custom: [] },
|
||||
quote: {},
|
||||
zen: {},
|
||||
custom: {},
|
||||
},
|
||||
name: "",
|
||||
customThemes: [],
|
||||
|
|
|
@ -95,6 +95,18 @@ export async function initSnapshot(): Promise<
|
|||
|
||||
snap.name = userData.name;
|
||||
snap.personalBests = userData.personalBests;
|
||||
snap.personalBests ??= {
|
||||
time: {},
|
||||
words: {},
|
||||
quote: {},
|
||||
zen: {},
|
||||
custom: {},
|
||||
};
|
||||
|
||||
for (const mode of ["time", "words", "quote", "zen", "custom"]) {
|
||||
snap.personalBests[mode as keyof MonkeyTypes.PersonalBests] ??= {};
|
||||
}
|
||||
|
||||
snap.banned = userData.banned;
|
||||
snap.lbOptOut = userData.lbOptOut;
|
||||
snap.verified = userData.verified;
|
||||
|
@ -166,6 +178,17 @@ export async function initSnapshot(): Promise<
|
|||
|
||||
snap.tags.forEach((tag) => {
|
||||
tag.display = tag.name.replaceAll("_", " ");
|
||||
tag.personalBests ??= {
|
||||
time: {},
|
||||
words: {},
|
||||
quote: {},
|
||||
zen: {},
|
||||
custom: {},
|
||||
};
|
||||
|
||||
for (const mode of ["time", "words", "quote", "zen", "custom"]) {
|
||||
tag.personalBests[mode as keyof MonkeyTypes.PersonalBests] ??= {};
|
||||
}
|
||||
});
|
||||
|
||||
snap.tags = snap.tags?.sort((a, b) => {
|
||||
|
@ -577,30 +600,21 @@ export async function saveLocalPB<M extends MonkeyTypes.Mode>(
|
|||
function cont(): void {
|
||||
if (!dbSnapshot) return;
|
||||
let found = false;
|
||||
if (dbSnapshot.personalBests === undefined) {
|
||||
dbSnapshot.personalBests = {
|
||||
time: {},
|
||||
words: {},
|
||||
zen: { zen: [] },
|
||||
quote: { custom: [] },
|
||||
custom: { custom: [] },
|
||||
};
|
||||
}
|
||||
|
||||
if (dbSnapshot.personalBests[mode] === undefined) {
|
||||
if (mode === "zen") {
|
||||
dbSnapshot.personalBests["zen"] = { zen: [] };
|
||||
} else {
|
||||
dbSnapshot.personalBests[mode as Exclude<typeof mode, "zen">] = {
|
||||
custom: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
dbSnapshot.personalBests ??= {
|
||||
time: {},
|
||||
words: {},
|
||||
quote: {},
|
||||
zen: {},
|
||||
custom: {},
|
||||
};
|
||||
|
||||
if (dbSnapshot.personalBests[mode][mode2] === undefined) {
|
||||
dbSnapshot.personalBests[mode][mode2] =
|
||||
[] as unknown as MonkeyTypes.PersonalBests[M][keyof MonkeyTypes.PersonalBests[M]];
|
||||
}
|
||||
dbSnapshot.personalBests[mode] ??= {
|
||||
[mode2]: [],
|
||||
};
|
||||
|
||||
dbSnapshot.personalBests[mode][mode2] ??=
|
||||
[] as unknown as MonkeyTypes.PersonalBests[M][keyof MonkeyTypes.PersonalBests[M]];
|
||||
|
||||
(
|
||||
dbSnapshot.personalBests[mode][
|
||||
|
@ -630,15 +644,15 @@ export async function saveLocalPB<M extends MonkeyTypes.Mode>(
|
|||
mode2
|
||||
] as unknown as MonkeyTypes.PersonalBest[]
|
||||
).push({
|
||||
language: language,
|
||||
difficulty: difficulty,
|
||||
lazyMode: lazyMode,
|
||||
punctuation: punctuation,
|
||||
wpm: wpm,
|
||||
acc: acc,
|
||||
raw: raw,
|
||||
language,
|
||||
difficulty,
|
||||
lazyMode,
|
||||
punctuation,
|
||||
wpm,
|
||||
acc,
|
||||
raw,
|
||||
timestamp: Date.now(),
|
||||
consistency: consistency,
|
||||
consistency,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -666,34 +680,34 @@ export async function getLocalTagPB<M extends MonkeyTypes.Mode>(
|
|||
|
||||
if (filteredtag === undefined) return ret;
|
||||
|
||||
if (filteredtag.personalBests === undefined) {
|
||||
filteredtag.personalBests = {
|
||||
time: {},
|
||||
words: {},
|
||||
zen: { zen: [] },
|
||||
quote: { custom: [] },
|
||||
custom: { custom: [] },
|
||||
};
|
||||
}
|
||||
filteredtag.personalBests ??= {
|
||||
time: {},
|
||||
words: {},
|
||||
quote: {},
|
||||
zen: {},
|
||||
custom: {},
|
||||
};
|
||||
|
||||
try {
|
||||
const personalBests = (filteredtag.personalBests[mode][mode2] ??
|
||||
[]) as MonkeyTypes.PersonalBest[];
|
||||
filteredtag.personalBests[mode] ??= {
|
||||
[mode2]: [],
|
||||
};
|
||||
|
||||
personalBests.forEach((pb) => {
|
||||
if (
|
||||
filteredtag.personalBests[mode][mode2] ??=
|
||||
[] as unknown as MonkeyTypes.PersonalBests[M][keyof MonkeyTypes.PersonalBests[M]];
|
||||
|
||||
const personalBests = (filteredtag.personalBests[mode][mode2] ??
|
||||
[]) as MonkeyTypes.PersonalBest[];
|
||||
|
||||
ret =
|
||||
personalBests.find(
|
||||
(pb) =>
|
||||
pb.punctuation == punctuation &&
|
||||
pb.difficulty == difficulty &&
|
||||
pb.language == language &&
|
||||
(pb.lazyMode === lazyMode ||
|
||||
(pb.lazyMode === undefined && lazyMode === false))
|
||||
) {
|
||||
ret = pb.wpm;
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
)?.wpm ?? 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -721,22 +735,24 @@ export async function saveLocalTagPB<M extends MonkeyTypes.Mode>(
|
|||
(t) => t._id === tagId
|
||||
)[0] as MonkeyTypes.Tag;
|
||||
|
||||
if (!filteredtag.personalBests) {
|
||||
filteredtag.personalBests = {
|
||||
time: {},
|
||||
words: {},
|
||||
zen: { zen: [] },
|
||||
quote: { custom: [] },
|
||||
custom: { custom: [] },
|
||||
};
|
||||
}
|
||||
filteredtag.personalBests ??= {
|
||||
time: {},
|
||||
words: {},
|
||||
quote: {},
|
||||
zen: {},
|
||||
custom: {},
|
||||
};
|
||||
|
||||
filteredtag.personalBests[mode] ??= {
|
||||
[mode2]: [],
|
||||
};
|
||||
|
||||
filteredtag.personalBests[mode][mode2] ??=
|
||||
[] as unknown as MonkeyTypes.PersonalBests[M][keyof MonkeyTypes.PersonalBests[M]];
|
||||
|
||||
try {
|
||||
let found = false;
|
||||
if (filteredtag.personalBests[mode][mode2] === undefined) {
|
||||
filteredtag.personalBests[mode][mode2] =
|
||||
[] as unknown as MonkeyTypes.PersonalBests[M][keyof MonkeyTypes.PersonalBests[M]];
|
||||
}
|
||||
|
||||
(
|
||||
filteredtag.personalBests[mode][
|
||||
mode2
|
||||
|
@ -765,15 +781,15 @@ export async function saveLocalTagPB<M extends MonkeyTypes.Mode>(
|
|||
mode2
|
||||
] as unknown as MonkeyTypes.PersonalBest[]
|
||||
).push({
|
||||
language: language,
|
||||
difficulty: difficulty,
|
||||
lazyMode: lazyMode,
|
||||
punctuation: punctuation,
|
||||
wpm: wpm,
|
||||
acc: acc,
|
||||
raw: raw,
|
||||
language,
|
||||
difficulty,
|
||||
lazyMode,
|
||||
punctuation,
|
||||
wpm,
|
||||
acc,
|
||||
raw,
|
||||
timestamp: Date.now(),
|
||||
consistency: consistency,
|
||||
consistency,
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
|
@ -781,17 +797,10 @@ export async function saveLocalTagPB<M extends MonkeyTypes.Mode>(
|
|||
filteredtag.personalBests = {
|
||||
time: {},
|
||||
words: {},
|
||||
zen: { zen: [] },
|
||||
quote: { custom: [] },
|
||||
custom: { custom: [] },
|
||||
quote: {},
|
||||
zen: {},
|
||||
custom: {},
|
||||
};
|
||||
if (mode === "zen") {
|
||||
filteredtag.personalBests["zen"] = { zen: [] };
|
||||
} else {
|
||||
filteredtag.personalBests[mode as Exclude<typeof mode, "zen">] = {
|
||||
custom: [],
|
||||
};
|
||||
}
|
||||
filteredtag.personalBests[mode][mode2] = [
|
||||
{
|
||||
language: language,
|
||||
|
|
|
@ -101,6 +101,13 @@ async function apply(): Promise<void> {
|
|||
display: propTagName,
|
||||
name: response.data.name,
|
||||
_id: response.data._id,
|
||||
personalBests: {
|
||||
time: {},
|
||||
words: {},
|
||||
quote: {},
|
||||
zen: {},
|
||||
custom: {},
|
||||
},
|
||||
});
|
||||
Settings.update();
|
||||
}
|
||||
|
@ -145,9 +152,9 @@ async function apply(): Promise<void> {
|
|||
tag.personalBests = {
|
||||
time: {},
|
||||
words: {},
|
||||
custom: { custom: [] },
|
||||
zen: { zen: [] },
|
||||
quote: { custom: [] },
|
||||
quote: {},
|
||||
zen: {},
|
||||
custom: {},
|
||||
};
|
||||
}
|
||||
});
|
||||
|
|
|
@ -16,11 +16,9 @@ function update(mode: MonkeyTypes.Mode): void {
|
|||
const snapshot = DB.getSnapshot();
|
||||
if (!snapshot) return;
|
||||
|
||||
const allmode2 = (
|
||||
snapshot.personalBests === undefined
|
||||
? undefined
|
||||
: snapshot.personalBests[mode]
|
||||
) as { [quote: string]: PersonalBest[] } | undefined;
|
||||
const allmode2 = snapshot.personalBests?.[mode] as
|
||||
| Record<string, PersonalBest[]>
|
||||
| undefined;
|
||||
|
||||
if (allmode2 === undefined) return;
|
||||
|
||||
|
|
|
@ -868,9 +868,9 @@ list["clearTagPb"] = new SimplePopup(
|
|||
tag.personalBests = {
|
||||
time: {},
|
||||
words: {},
|
||||
zen: { zen: [] },
|
||||
quote: { custom: [] },
|
||||
custom: { custom: [] },
|
||||
quote: {},
|
||||
zen: {},
|
||||
custom: {},
|
||||
};
|
||||
$(
|
||||
`.pageSettings .section.tags .tagsList .tag[id="${tagId}"] .clearPbButton`
|
||||
|
@ -949,9 +949,9 @@ list["resetPersonalBests"] = new SimplePopup(
|
|||
snapshot.personalBests = {
|
||||
time: {},
|
||||
words: {},
|
||||
zen: { zen: [] },
|
||||
quote: { custom: [] },
|
||||
custom: { custom: [] },
|
||||
quote: {},
|
||||
zen: {},
|
||||
custom: {},
|
||||
};
|
||||
} catch (e) {
|
||||
Loader.hide();
|
||||
|
|
20
frontend/src/ts/types/types.d.ts
vendored
20
frontend/src/ts/types/types.d.ts
vendored
|
@ -295,24 +295,18 @@ declare namespace MonkeyTypes {
|
|||
}
|
||||
|
||||
interface PersonalBests {
|
||||
time: {
|
||||
[key: number]: PersonalBest[];
|
||||
};
|
||||
words: {
|
||||
[key: number]: PersonalBest[];
|
||||
};
|
||||
quote: { [quote: string]: PersonalBest[] };
|
||||
custom: { custom: PersonalBest[] };
|
||||
zen: {
|
||||
zen: PersonalBest[];
|
||||
};
|
||||
time: Record<number, PersonalBest[]>;
|
||||
words: Record<number, PersonalBest[]>;
|
||||
quote: Record<string, PersonalBest[]>;
|
||||
custom: Partial<Record<"custom", PersonalBest[]>>;
|
||||
zen: Partial<Record<"zen", PersonalBest[]>>;
|
||||
}
|
||||
|
||||
interface Tag {
|
||||
_id: string;
|
||||
name: string;
|
||||
display: string;
|
||||
personalBests?: PersonalBests;
|
||||
personalBests: PersonalBests;
|
||||
active?: boolean;
|
||||
}
|
||||
|
||||
|
@ -550,7 +544,7 @@ declare namespace MonkeyTypes {
|
|||
quoteRatings?: QuoteRatings;
|
||||
results?: Result<Mode>[];
|
||||
verified?: boolean;
|
||||
personalBests?: PersonalBests;
|
||||
personalBests: PersonalBests;
|
||||
name: string;
|
||||
customThemes: CustomTheme[];
|
||||
presets?: Preset[];
|
||||
|
|
Loading…
Add table
Reference in a new issue