mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2025-09-08 23:56:27 +08:00
refactor: get actual rank with the user data instead of using lbmemory (fehmer) (#5189)
Co-authored-by: Jack <jack@monkeytype.com>
This commit is contained in:
parent
1bb0bdffb0
commit
8eb3206c8a
11 changed files with 125 additions and 66 deletions
|
@ -402,10 +402,13 @@ export async function getUser(
|
|||
|
||||
const isPremium = await UserDAL.checkIfUserIsPremium(uid, userInfo);
|
||||
|
||||
const allTimeLbs = await getAllTimeLbs(uid);
|
||||
|
||||
const userData = {
|
||||
...getRelevantUserInfo(userInfo),
|
||||
inboxUnreadSize: inboxUnreadSize,
|
||||
isPremium,
|
||||
allTimeLbs,
|
||||
};
|
||||
|
||||
return new MonkeyResponse("User data retrieved", userData);
|
||||
|
@ -760,41 +763,13 @@ export async function getProfile(
|
|||
return new MonkeyResponse("Profile retrived: banned user", baseProfile);
|
||||
}
|
||||
|
||||
const allTime15English = await LeaderboardsDAL.getRank(
|
||||
"time",
|
||||
"15",
|
||||
"english",
|
||||
user.uid
|
||||
);
|
||||
|
||||
const allTime60English = await LeaderboardsDAL.getRank(
|
||||
"time",
|
||||
"60",
|
||||
"english",
|
||||
user.uid
|
||||
);
|
||||
|
||||
const allTime15EnglishRank =
|
||||
(allTime15English === false ? null : allTime15English)?.rank ?? null;
|
||||
const allTime60EnglishRank =
|
||||
(allTime60English === false ? null : allTime60English)?.rank ?? null;
|
||||
|
||||
const alltimelbs = {
|
||||
time: {
|
||||
"15": {
|
||||
english: allTime15EnglishRank,
|
||||
},
|
||||
"60": {
|
||||
english: allTime60EnglishRank,
|
||||
},
|
||||
},
|
||||
};
|
||||
const allTimeLbs = await getAllTimeLbs(user.uid);
|
||||
|
||||
const profileData = {
|
||||
...baseProfile,
|
||||
inventory,
|
||||
details: profileDetails,
|
||||
allTimeLbs: alltimelbs,
|
||||
allTimeLbs,
|
||||
uid: user.uid,
|
||||
} as SharedTypes.UserProfile;
|
||||
|
||||
|
@ -937,3 +912,35 @@ export async function revokeAllTokens(
|
|||
removeTokensFromCacheByUid(uid);
|
||||
return new MonkeyResponse("All tokens revoked");
|
||||
}
|
||||
|
||||
async function getAllTimeLbs(uid: string): Promise<SharedTypes.AllTimeLbs> {
|
||||
const allTime15English = await LeaderboardsDAL.getRank(
|
||||
"time",
|
||||
"15",
|
||||
"english",
|
||||
uid
|
||||
);
|
||||
|
||||
const allTime60English = await LeaderboardsDAL.getRank(
|
||||
"time",
|
||||
"60",
|
||||
"english",
|
||||
uid
|
||||
);
|
||||
|
||||
const allTime15EnglishRank =
|
||||
allTime15English === false ? null : allTime15English.rank;
|
||||
const allTime60EnglishRank =
|
||||
allTime60English === false ? null : allTime60English.rank;
|
||||
|
||||
return {
|
||||
time: {
|
||||
"15": {
|
||||
english: allTime15EnglishRank,
|
||||
},
|
||||
"60": {
|
||||
english: allTime60EnglishRank,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
2
backend/src/types/types.d.ts
vendored
2
backend/src/types/types.d.ts
vendored
|
@ -20,7 +20,7 @@ declare namespace MonkeyTypes {
|
|||
|
||||
type DBUser = Omit<
|
||||
SharedTypes.User,
|
||||
"resultFilterPresets" | "tags" | "customThemes" | "isPremium"
|
||||
"resultFilterPresets" | "tags" | "customThemes" | "isPremium" | "allTimeLbs"
|
||||
> & {
|
||||
_id: ObjectId;
|
||||
resultFilterPresets?: WithObjectIdArray<SharedTypes.ResultFilters[]>;
|
||||
|
|
|
@ -230,6 +230,46 @@ describe("format.ts", () => {
|
|||
expect(format.decimals(80.75, { rounding: Math.floor })).toEqual("80");
|
||||
});
|
||||
});
|
||||
|
||||
describe("rank", () => {
|
||||
it("should format with default fallback", () => {
|
||||
const format = getInstance();
|
||||
expect(format.rank(1)).toEqual("1st");
|
||||
expect(format.rank(2)).toEqual("2nd");
|
||||
expect(format.rank(3)).toEqual("3rd");
|
||||
expect(format.rank(4)).toEqual("4th");
|
||||
|
||||
expect(format.rank(11)).toEqual("11th");
|
||||
expect(format.rank(12)).toEqual("12th");
|
||||
expect(format.rank(13)).toEqual("13th");
|
||||
expect(format.rank(14)).toEqual("14th");
|
||||
|
||||
expect(format.rank(21)).toEqual("21st");
|
||||
expect(format.rank(22)).toEqual("22nd");
|
||||
expect(format.rank(23)).toEqual("23rd");
|
||||
expect(format.rank(24)).toEqual("24th");
|
||||
});
|
||||
|
||||
it("should format with fallback", () => {
|
||||
const format = getInstance();
|
||||
|
||||
expect(format.rank(0)).toEqual("0th");
|
||||
expect(format.rank(null)).toEqual("-");
|
||||
expect(format.rank(undefined)).toEqual("-");
|
||||
|
||||
expect(format.rank(0, {})).toEqual("0th");
|
||||
expect(format.rank(null, {})).toEqual("-");
|
||||
expect(format.rank(undefined, {})).toEqual("-");
|
||||
|
||||
expect(format.rank(0, { fallback: "none" })).toEqual("0th");
|
||||
expect(format.rank(null, { fallback: "none" })).toEqual("none");
|
||||
expect(format.rank(undefined, { fallback: "none" })).toEqual("none");
|
||||
|
||||
expect(format.rank(0, { fallback: "" })).toEqual("0th");
|
||||
expect(format.rank(null, { fallback: "" })).toEqual("");
|
||||
expect(format.rank(undefined, { fallback: "" })).toEqual("");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function getInstance(config?: Partial<SharedTypes.Config>): Formatting {
|
||||
|
|
|
@ -35,4 +35,5 @@ export const defaultSnap: MonkeyTypes.Snapshot = {
|
|||
streak: 0,
|
||||
maxStreak: 0,
|
||||
streakHourOffset: undefined,
|
||||
allTimeLbs: { time: { 15: { english: 0 }, 60: { english: 0 } } },
|
||||
};
|
||||
|
|
|
@ -133,6 +133,7 @@ export async function initSnapshot(): Promise<
|
|||
snap.maxStreak = userData?.streak?.maxLength ?? 0;
|
||||
snap.filterPresets = userData.resultFilterPresets ?? [];
|
||||
snap.isPremium = userData?.isPremium;
|
||||
snap.allTimeLbs = userData.allTimeLbs;
|
||||
|
||||
const hourOffset = userData?.streak?.hourOffset;
|
||||
snap.streakHourOffset =
|
||||
|
|
|
@ -8,6 +8,7 @@ import * as EditProfilePopup from "../popups/edit-profile-popup";
|
|||
import * as ActivePage from "../states/active-page";
|
||||
import formatDistanceToNowStrict from "date-fns/formatDistanceToNowStrict";
|
||||
import { getHtmlByUserFlags } from "../controllers/user-flag-controller";
|
||||
import Format from "../utils/format";
|
||||
|
||||
type ProfileViewPaths = "profile" | "account";
|
||||
type UserProfileOrSnapshot = SharedTypes.UserProfile | MonkeyTypes.Snapshot;
|
||||
|
@ -317,25 +318,19 @@ export async function update(
|
|||
} else {
|
||||
profileElement.find(".leaderboardsPositions").removeClass("hidden");
|
||||
|
||||
const lbPos =
|
||||
where === "profile"
|
||||
? (profile as SharedTypes.UserProfile).allTimeLbs
|
||||
: (profile as MonkeyTypes.Snapshot).lbMemory;
|
||||
const t15 = profile.allTimeLbs.time?.["15"]?.["english"] ?? null;
|
||||
const t60 = profile.allTimeLbs.time?.["60"]?.["english"] ?? null;
|
||||
|
||||
const t15 = lbPos?.time?.["15"]?.["english"];
|
||||
const t60 = lbPos?.time?.["60"]?.["english"];
|
||||
|
||||
if (!t15 && !t60) {
|
||||
if (t15 === null && t60 === null) {
|
||||
profileElement.find(".leaderboardsPositions").addClass("hidden");
|
||||
} else {
|
||||
const t15string = t15 ? Misc.getPositionString(t15) : "-";
|
||||
profileElement
|
||||
.find(".leaderboardsPositions .group.t15 .pos")
|
||||
.text(t15string);
|
||||
const t60string = t60 ? Misc.getPositionString(t60) : "-";
|
||||
.text(Format.rank(t15));
|
||||
|
||||
profileElement
|
||||
.find(".leaderboardsPositions .group.t60 .pos")
|
||||
.text(t60string);
|
||||
.text(Format.rank(t60));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ import * as KeymapEvent from "../observables/keymap-event";
|
|||
import * as LayoutfluidFunboxTimer from "../test/funbox/layoutfluid-funbox-timer";
|
||||
import * as Wordset from "./wordset";
|
||||
import * as ArabicLazyMode from "../states/arabic-lazy-mode";
|
||||
import Format from "../utils/format";
|
||||
|
||||
let failReason = "";
|
||||
const koInputVisual = document.getElementById("koInputVisual") as HTMLElement;
|
||||
|
@ -1290,7 +1291,7 @@ async function saveResult(
|
|||
500
|
||||
);
|
||||
$("#result .stats .dailyLeaderboard .bottom").html(
|
||||
Misc.getPositionString(response.data.dailyLeaderboardRank)
|
||||
Format.rank(response.data.dailyLeaderboardRank, { fallback: "" })
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,9 +5,8 @@ import { get as getTypingSpeedUnit } from "../utils/typing-speed-units";
|
|||
export type FormatOptions = {
|
||||
showDecimalPlaces?: boolean;
|
||||
suffix?: string;
|
||||
fallback?: string;
|
||||
rounding?: (val: number) => number;
|
||||
};
|
||||
} & FallbackOptions;
|
||||
|
||||
const FORMAT_DEFAULT_OPTIONS: FormatOptions = {
|
||||
suffix: "",
|
||||
|
@ -16,6 +15,10 @@ const FORMAT_DEFAULT_OPTIONS: FormatOptions = {
|
|||
rounding: Math.round,
|
||||
};
|
||||
|
||||
export type FallbackOptions = {
|
||||
fallback?: string;
|
||||
};
|
||||
|
||||
export class Formatting {
|
||||
constructor(private config: SharedTypes.Config) {}
|
||||
|
||||
|
@ -76,6 +79,29 @@ export class Formatting {
|
|||
}
|
||||
return (formatOptions.rounding ?? Math.round)(value).toString() + suffix;
|
||||
}
|
||||
|
||||
rank(
|
||||
position: number | null | undefined,
|
||||
formatOptions: FallbackOptions = {}
|
||||
): string {
|
||||
const options = { fallback: "-", ...formatOptions };
|
||||
|
||||
if (position === undefined || position === null)
|
||||
return options.fallback ?? "";
|
||||
let numend = "th";
|
||||
const t = position % 10;
|
||||
const h = position % 100;
|
||||
if (t === 1 && h !== 11) {
|
||||
numend = "st";
|
||||
}
|
||||
if (t === 2 && h !== 12) {
|
||||
numend = "nd";
|
||||
}
|
||||
if (t === 3 && h !== 13) {
|
||||
numend = "rd";
|
||||
}
|
||||
return position + numend;
|
||||
}
|
||||
}
|
||||
|
||||
export default new Formatting(Config);
|
||||
|
|
|
@ -802,22 +802,6 @@ export function chart2Word(first: boolean): string {
|
|||
return measure;
|
||||
}
|
||||
|
||||
export function getPositionString(number: number): string {
|
||||
let numend = "th";
|
||||
const t = number % 10;
|
||||
const h = number % 100;
|
||||
if (t === 1 && h !== 11) {
|
||||
numend = "st";
|
||||
}
|
||||
if (t === 2 && h !== 12) {
|
||||
numend = "nd";
|
||||
}
|
||||
if (t === 3 && h !== 13) {
|
||||
numend = "rd";
|
||||
}
|
||||
return number + numend;
|
||||
}
|
||||
|
||||
export function findGetParameter(
|
||||
parameterName: string,
|
||||
getOverride?: string
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
"coverage-gutters.coverageBaseDir": "**/coverage",
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSaveMode": "modifications",
|
||||
"editor.formatOnSave": true
|
||||
"editor.formatOnSave": true,
|
||||
"testing.openTesting": "neverOpen"
|
||||
}
|
||||
}
|
||||
|
|
9
shared-types/types.d.ts
vendored
9
shared-types/types.d.ts
vendored
|
@ -531,6 +531,7 @@ declare namespace SharedTypes {
|
|||
quoteRatings?: UserQuoteRatings;
|
||||
favoriteQuotes?: Record<string, string[]>;
|
||||
lbMemory?: UserLbMemory;
|
||||
allTimeLbs: AllTimeLbs;
|
||||
inventory?: UserInventory;
|
||||
banned?: boolean;
|
||||
lbOptOut?: boolean;
|
||||
|
@ -578,6 +579,7 @@ declare namespace SharedTypes {
|
|||
| "inventory"
|
||||
| "uid"
|
||||
| "isPremium"
|
||||
| "allTimeLbs"
|
||||
> & {
|
||||
typingStats: {
|
||||
completedTests: User["completedTests"];
|
||||
|
@ -587,9 +589,6 @@ declare namespace SharedTypes {
|
|||
streak: UserStreak["length"];
|
||||
maxStreak: UserStreak["maxLength"];
|
||||
details: UserProfileDetails;
|
||||
allTimeLbs: {
|
||||
time: Record<string, Record<string, number | null>>;
|
||||
};
|
||||
personalBests: {
|
||||
time: Pick<
|
||||
Record<`${number}`, SharedTypes.PersonalBest[]>,
|
||||
|
@ -601,4 +600,8 @@ declare namespace SharedTypes {
|
|||
>;
|
||||
};
|
||||
};
|
||||
|
||||
type AllTimeLbs = {
|
||||
time: Record<string, Record<string, number | null>>;
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue