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:
Christian Fehmer 2024-03-11 20:29:18 +01:00 committed by GitHub
parent 1bb0bdffb0
commit 8eb3206c8a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 125 additions and 66 deletions

View file

@ -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,
},
},
};
}

View file

@ -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[]>;

View file

@ -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 {

View file

@ -35,4 +35,5 @@ export const defaultSnap: MonkeyTypes.Snapshot = {
streak: 0,
maxStreak: 0,
streakHourOffset: undefined,
allTimeLbs: { time: { 15: { english: 0 }, 60: { english: 0 } } },
};

View file

@ -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 =

View file

@ -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));
}
}

View file

@ -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: "" })
);
}

View file

@ -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);

View file

@ -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

View file

@ -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"
}
}

View file

@ -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>>;
};
}