diff --git a/backend/__tests__/dal/leaderboards.spec.ts b/backend/__tests__/dal/leaderboards.spec.ts index 1bb4a8dfc..503b1344e 100644 --- a/backend/__tests__/dal/leaderboards.spec.ts +++ b/backend/__tests__/dal/leaderboards.spec.ts @@ -44,7 +44,7 @@ describe("LeaderboardsDal", () => { "15", "english", 0 - )) as MonkeyTypes.LeaderboardEntry[]; + )) as SharedTypes.LeaderboardEntry[]; //THEN const lb = result.map((it) => _.omit(it, ["_id"])); @@ -70,7 +70,7 @@ describe("LeaderboardsDal", () => { "60", "english", 0 - )) as MonkeyTypes.LeaderboardEntry[]; + )) as SharedTypes.LeaderboardEntry[]; //THEN const lb = result.map((it) => _.omit(it, ["_id"])); diff --git a/backend/src/dal/leaderboards.ts b/backend/src/dal/leaderboards.ts index 7daae50ce..d1cace197 100644 --- a/backend/src/dal/leaderboards.ts +++ b/backend/src/dal/leaderboards.ts @@ -12,12 +12,12 @@ export async function get( language: string, skip: number, limit = 50 -): Promise { +): Promise { if (leaderboardUpdating[`${language}_${mode}_${mode2}`]) return false; if (limit > 50 || limit <= 0) limit = 50; if (skip < 0) skip = 0; const preset = await db - .collection( + .collection( `leaderboards.${language}.${mode}.${mode2}` ) .find() @@ -31,7 +31,7 @@ export async function get( interface GetRankResponse { count: number; rank: number | null; - entry: MonkeyTypes.LeaderboardEntry | null; + entry: SharedTypes.LeaderboardEntry | null; } export async function getRank( @@ -42,7 +42,7 @@ export async function getRank( ): Promise { if (leaderboardUpdating[`${language}_${mode}_${mode2}`]) return false; const entry = await db - .collection( + .collection( `leaderboards.${language}.${mode}.${mode2}` ) .findOne({ uid }); @@ -71,7 +71,7 @@ export async function update( const start1 = performance.now(); const lb = db .collection("users") - .aggregate( + .aggregate( [ { $match: { diff --git a/backend/src/jobs/update-leaderboards.ts b/backend/src/jobs/update-leaderboards.ts index a7e3e7f6d..dbe459c1b 100644 --- a/backend/src/jobs/update-leaderboards.ts +++ b/backend/src/jobs/update-leaderboards.ts @@ -9,14 +9,14 @@ const RECENT_AGE_MILLISECONDS = RECENT_AGE_MINUTES * 60 * 1000; async function getTop10( leaderboardTime: string -): Promise { +): Promise { return (await LeaderboardsDAL.get( "time", leaderboardTime, "english", 0, 10 - )) as MonkeyTypes.LeaderboardEntry[]; //can do that because gettop10 will not be called during an update + )) as SharedTypes.LeaderboardEntry[]; //can do that because gettop10 will not be called during an update } async function updateLeaderboardAndNotifyChanges( diff --git a/backend/src/types/types.d.ts b/backend/src/types/types.d.ts index b2a9260bc..1eafacfb9 100644 --- a/backend/src/types/types.d.ts +++ b/backend/src/types/types.d.ts @@ -139,24 +139,6 @@ declare namespace MonkeyTypes { personalBests: SharedTypes.PersonalBests; } - interface LeaderboardEntry { - _id: ObjectId; - acc: number; - consistency: number; - difficulty: SharedTypes.Config.Difficulty; - lazyMode: boolean; - language: string; - punctuation: boolean; - raw: number; - wpm: number; - timestamp: number; - uid: string; - name: string; - rank: number; - badges?: Badge[]; - badgeId?: number; - } - interface CustomTheme { _id: ObjectId; name: string; diff --git a/frontend/src/ts/ape/endpoints/leaderboards.ts b/frontend/src/ts/ape/endpoints/leaderboards.ts index 5b8c4804c..14570ff37 100644 --- a/frontend/src/ts/ape/endpoints/leaderboards.ts +++ b/frontend/src/ts/ape/endpoints/leaderboards.ts @@ -1,24 +1,13 @@ const BASE_PATH = "/leaderboards"; -interface LeaderboardQuery { - language: string; - mode: SharedTypes.Config.Mode; - mode2: string; - isDaily?: boolean; - daysBefore?: number; -} - -interface LeadeboardQueryWithPagination extends LeaderboardQuery { - skip?: number; - limit?: number; -} - export default class Leaderboards { constructor(private httpClient: Ape.HttpClient) { this.httpClient = httpClient; } - async get(query: LeadeboardQueryWithPagination): Ape.EndpointResponse { + async get( + query: Ape.Leaderboards.QueryWithPagination + ): Ape.EndpointResponse { const { language, mode, @@ -44,7 +33,9 @@ export default class Leaderboards { return await this.httpClient.get(endpointPath, { searchQuery }); } - async getRank(query: LeaderboardQuery): Ape.EndpointResponse { + async getRank( + query: Ape.Leaderboards.Query + ): Ape.EndpointResponse { const { language, mode, mode2, isDaily, daysBefore } = query; const includeDaysBefore = (isDaily ?? false) && (daysBefore ?? 0) > 0; diff --git a/frontend/src/ts/ape/types/leaderboards.d.ts b/frontend/src/ts/ape/types/leaderboards.d.ts new file mode 100644 index 000000000..857c29bc1 --- /dev/null +++ b/frontend/src/ts/ape/types/leaderboards.d.ts @@ -0,0 +1,25 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +// for some reason when using the dot notaion, the types are not being recognized as used +declare namespace Ape.Leaderboards { + interface Query { + language: string; + mode: SharedTypes.Config.Mode; + mode2: string; + isDaily?: boolean; + daysBefore?: number; + } + + interface QueryWithPagination extends Query { + skip?: number; + limit?: number; + } + + type GetLeaderboard = SharedTypes.LeaderboardEntry[]; + + type GetRank = { + minWpm: number; + count: number; + rank: number | null; + entry: SharedTypes.LeaderboardEntry | null; + }; +} diff --git a/frontend/src/ts/elements/leaderboards.ts b/frontend/src/ts/elements/leaderboards.ts index 972fb92a1..37693a868 100644 --- a/frontend/src/ts/elements/leaderboards.ts +++ b/frontend/src/ts/elements/leaderboards.ts @@ -21,21 +21,14 @@ let showingYesterday = false; type LbKey = "15" | "60"; let currentData: { - [key in LbKey]: MonkeyTypes.LeaderboardEntry[]; + [key in LbKey]: SharedTypes.LeaderboardEntry[]; } = { "15": [], "60": [], }; -interface GetRankResponse { - minWpm: number; - count: number; - rank: number | null; - entry: MonkeyTypes.LeaderboardEntry | null; -} - let currentRank: { - [key in LbKey]: GetRankResponse | Record; + [key in LbKey]: Ape.Leaderboards.GetRank | Record; } = { "15": {}, "60": {}, @@ -438,7 +431,7 @@ async function update(): Promise { const timeModes = ["15", "60"]; - const leaderboardRequests = timeModes.map(async (mode2) => { + const lbDataRequests = timeModes.map(async (mode2) => { return Ape.leaderboards.get({ language: currentLanguage, mode: "time", @@ -447,8 +440,11 @@ async function update(): Promise { }); }); + const lbRankRequests: Promise< + Ape.HttpClientResponse + >[] = []; if (Auth?.currentUser) { - leaderboardRequests.push( + lbRankRequests.push( ...timeModes.map(async (mode2) => { return Ape.leaderboards.getRank({ language: currentLanguage, @@ -460,26 +456,28 @@ async function update(): Promise { ); } - const responses = await Promise.all(leaderboardRequests); + const responses = await Promise.all(lbDataRequests); + const rankResponses = await Promise.all(lbRankRequests); - const failedResponse = responses.find((response) => response.status !== 200); - if (failedResponse) { + const atLeastOneFailed = + responses.find((response) => response.status !== 200) || + rankResponses.find((response) => response.status !== 200); + if (atLeastOneFailed) { hideLoader("15"); hideLoader("60"); return Notifications.add( - "Failed to load leaderboards: " + failedResponse.message, + "Failed to load leaderboards: " + atLeastOneFailed.message, -1 ); } - const [lb15Data, lb60Data, lb15Rank, lb60Rank] = responses.map( - (response) => response.data - ); + const [lb15Data, lb60Data] = responses.map((response) => response.data); + const [lb15Rank, lb60Rank] = rankResponses.map((response) => response.data); - currentData["15"] = lb15Data; - currentData["60"] = lb60Data; - currentRank["15"] = lb15Rank; - currentRank["60"] = lb60Rank; + if (lb15Data !== undefined && lb15Data !== null) currentData["15"] = lb15Data; + if (lb60Data !== undefined && lb60Data !== null) currentData["60"] = lb60Data; + if (lb15Rank !== undefined && lb15Rank !== null) currentRank["15"] = lb15Rank; + if (lb60Rank !== undefined && lb60Rank !== null) currentRank["60"] = lb60Rank; const leaderboardKeys: LbKey[] = ["15", "60"]; @@ -535,9 +533,9 @@ async function requestMore(lb: LbKey, prepend = false): Promise { limit: limitVal, ...getDailyLeaderboardQuery(), }); - const data: MonkeyTypes.LeaderboardEntry[] = response.data; + const data = response.data; - if (response.status !== 200 || data.length === 0) { + if (response.status !== 200 || data === null || data.length === 0) { hideLoader(lb); requesting[lb] = false; return; @@ -575,7 +573,7 @@ async function requestNew(lb: LbKey, skip: number): Promise { skip, ...getDailyLeaderboardQuery(), }); - const data: MonkeyTypes.LeaderboardEntry[] = response.data; + const data = response.data; if (response.status === 503) { Notifications.add( @@ -588,7 +586,7 @@ async function requestNew(lb: LbKey, skip: number): Promise { clearBody(lb); currentData[lb] = []; currentAvatars[lb] = []; - if (response.status !== 200 || data.length === 0) { + if (response.status !== 200 || data === null || data.length === 0) { hideLoader(lb); return; } @@ -604,11 +602,14 @@ async function requestNew(lb: LbKey, skip: number): Promise { } async function getAvatarUrls( - data: MonkeyTypes.LeaderboardEntry[] + data: Ape.Leaderboards.GetLeaderboard ): Promise<(string | null)[]> { return Promise.allSettled( data.map(async (entry) => - Misc.getDiscordAvatarUrl(entry.discordId, entry.discordAvatar) + Misc.getDiscordAvatarUrl( + entry.discordId ?? undefined, + entry.discordAvatar ?? undefined + ) ) ).then((promises) => { return promises.map((promise) => { diff --git a/frontend/src/ts/types/types.d.ts b/frontend/src/ts/types/types.d.ts index 4f14395eb..0e80d863a 100644 --- a/frontend/src/ts/types/types.d.ts +++ b/frontend/src/ts/types/types.d.ts @@ -200,29 +200,10 @@ declare namespace MonkeyTypes { interface Leaderboards { time: { - [key in 15 | 60]: LeaderboardEntry[]; + [key in 15 | 60]: SharedTypes.LeaderboardEntry[]; }; } - interface LeaderboardEntry { - difficulty: string; - timestamp: number; - language: string; - wpm: number; - consistency: number | "-"; - punctuation: boolean; - acc: number; - raw: number; - uid?: string; - name: string; - discordId?: string; - discordAvatar?: string; - badgeId?: number; - rank: number; - count?: number; - hidden?: boolean; - } - interface QuoteRatings { [language: string]: { [id: number]: number; diff --git a/shared-types/types.d.ts b/shared-types/types.d.ts index 03f0a7838..4ce4a0ae6 100644 --- a/shared-types/types.d.ts +++ b/shared-types/types.d.ts @@ -438,4 +438,20 @@ declare namespace SharedTypes { name: string; config: SharedTypes.ConfigPreset; } + + interface LeaderboardEntry { + _id: string; + wpm: number; + acc: number; + timestamp: number; + raw: number; + consistency: number | "-"; + uid: string; + name: string; + discordId: string | null | undefined; + discordAvatar: string | null | undefined; + rank: number; + badgeId: number | null; + hidden?: boolean; + } }