Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
100 commits
Select commit Hold shift + click to select a range
2c540a1
lb page
Miodec Aug 30, 2024
2d22a90
style(settings): rework section links to follow website style
Miodec Feb 6, 2025
dd9b937
Merge branch 'master' into lb
Miodec Feb 6, 2025
41dbac8
missing page
Miodec Feb 6, 2025
87b76c0
style
Miodec Feb 6, 2025
bfd5b5b
disabled button, font size
Miodec Feb 6, 2025
a2811b7
margin
Miodec Feb 6, 2025
2bccf89
left sidebar
Miodec Feb 6, 2025
882ee34
media queries
Miodec Feb 6, 2025
50069b8
initial logic
Miodec Feb 6, 2025
fedcd84
Merge branch 'master' into lb
Miodec Feb 8, 2025
ffa8706
return count when getting leaderboards
Miodec Feb 8, 2025
02b52b1
pagination
Miodec Feb 9, 2025
240d79d
jump buttons
Miodec Feb 9, 2025
4e1ac4b
handle no data
Miodec Feb 9, 2025
1f6a9d6
pull user data
Miodec Feb 9, 2025
00182c5
missing classes
Miodec Feb 9, 2025
79c200d
memory diff
Miodec Feb 9, 2025
41232ce
no default data
Miodec Feb 9, 2025
acc5bcb
avatars, crown for first place, highlight user, update timer
Miodec Feb 9, 2025
f13c22b
disable if on current page
Miodec Feb 9, 2025
da7cfd9
handle go to user page
Miodec Feb 9, 2025
b81a071
handle wide, handle narrow, new big user apporach
Miodec Feb 9, 2025
b9e4461
fix error when using responsive mode
Miodec Feb 9, 2025
8c9b4eb
handle discord avatars
Miodec Feb 9, 2025
9b24574
special cases for the user element
Miodec Feb 9, 2025
21dda62
missing rank
Miodec Feb 9, 2025
1338988
warning
Miodec Feb 9, 2025
269890c
daily
Miodec Feb 9, 2025
b4d6525
dynamcally build daily language buttons
Miodec Feb 9, 2025
c7aac89
fix error when 200
Miodec Feb 9, 2025
c4ca8e9
show goat if rank 1
Miodec Feb 9, 2025
40c6a90
not qualified warning
Miodec Feb 9, 2025
b450409
put state into get
Miodec Feb 9, 2025
ccf6573
nav button
Miodec Feb 9, 2025
4675887
no dynamic buttons for now
Miodec Feb 9, 2025
9fb9d5d
rename props, add mode
Miodec Feb 9, 2025
d03eeff
no need for a if check
Miodec Feb 9, 2025
c8336d2
rename
Miodec Feb 9, 2025
f0d08bb
rename
Miodec Feb 9, 2025
a127dcc
more renaming
Miodec Feb 9, 2025
0f36fdb
more renaming
Miodec Feb 9, 2025
2542c39
rename
Miodec Feb 9, 2025
795a87d
more rename, daily mode
Miodec Feb 9, 2025
acae970
fix divider
Miodec Feb 9, 2025
a4c9747
fix pagination
Miodec Feb 9, 2025
167145a
fix media query
Miodec Feb 9, 2025
2677c14
missing return param
Miodec Feb 9, 2025
d4bc71e
fix return type
Miodec Feb 9, 2025
98a424e
handle weekly lb
Miodec Feb 9, 2025
ae411a9
add ispremium to xp lb entry
Miodec Feb 9, 2025
b3f0c2b
missing param
Miodec Feb 9, 2025
968d720
unnecessary state
Miodec Feb 9, 2025
b884fd6
unnecessary return
Miodec Feb 9, 2025
c3ee25c
fix schema
Miodec Feb 9, 2025
835d02c
handle big user for weekly
Miodec Feb 9, 2025
4e0727d
use flex instead
Miodec Feb 9, 2025
77c4e3f
keep avatars while the lb page is open
Miodec Feb 9, 2025
bca20c7
only fill user when data changes
Miodec Feb 9, 2025
587c2f8
yeet
Miodec Feb 9, 2025
c5fa137
yeet once more
Miodec Feb 9, 2025
27d3f08
center warning
Miodec Feb 10, 2025
9ad3f15
show no data
Miodec Feb 10, 2025
3bba2c0
move skip/limit calculations to dal
Miodec Feb 10, 2025
fc19286
fix unit tests
Miodec Feb 10, 2025
968f4db
fix build
Miodec Feb 10, 2025
ec75b6b
rename
Miodec Feb 10, 2025
09f9473
remove comment
Miodec Feb 10, 2025
eebd9f1
move minwpm from rank to data response
Miodec Feb 10, 2025
5111fe9
only respond with the entry for the rank endpoints
Miodec Feb 10, 2025
eaad4ac
fix tests
Miodec Feb 10, 2025
01f8401
organise schemas
Miodec Feb 10, 2025
1e19cce
fix tests
Miodec Feb 10, 2025
7c3b532
fix types
Miodec Feb 10, 2025
d512042
fix tests
Miodec Feb 10, 2025
c48ac9c
move minwpm to its own getter, change return type of get results
Miodec Feb 10, 2025
c0e7297
remove questionmark
Miodec Feb 10, 2025
7c03931
add relative time display for last activity in leaderboard
Miodec Feb 11, 2025
c680a0f
reset leaderboard page state on button click
Miodec Feb 11, 2025
a45345d
convert from page id to page and back
Miodec Feb 11, 2025
5274eae
correct page index calculation in readGetParameters function
Miodec Feb 11, 2025
dd88127
update next page button state condition to handle edge case
Miodec Feb 11, 2025
6adaab0
show title and buttons in leaderboard on content update
Miodec Feb 11, 2025
1200675
go to last page if on a page that doesnt exist
Miodec Feb 11, 2025
1ac675c
add navigation buttons to leaderboard page
Miodec Feb 11, 2025
5b8698e
remove validation from DAL
Miodec Feb 11, 2025
78eb1e7
ensure page state does not go below zero in leaderboard navigation
Miodec Feb 11, 2025
bcbdc35
remove unused state.mode2 assignment in leaderboard type change
Miodec Feb 11, 2025
482a6ef
add title subtext
Miodec Feb 11, 2025
94b16d4
add buttons to show yesterday / last week
Miodec Feb 11, 2025
f2a1a25
fix build error
Miodec Feb 11, 2025
0615509
no need for language in alltime
Miodec Feb 11, 2025
d237555
rename
Miodec Feb 11, 2025
5ef5b47
remove unused style
Miodec Feb 11, 2025
7b29681
add divider if biguser is hidden
Miodec Feb 11, 2025
a10b4be
remove unnecessary params
Miodec Feb 11, 2025
862b27e
start with blank params
Miodec Feb 11, 2025
abb46e2
fix tests
Miodec Feb 12, 2025
9fd12e7
Merge branch 'master' into lb
Miodec Feb 12, 2025
b4c0bf6
fix
Miodec Feb 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
261 changes: 158 additions & 103 deletions backend/__tests__/api/controllers/leaderboard.spec.ts

Large diffs are not rendered by default.

13 changes: 9 additions & 4 deletions backend/__tests__/api/controllers/user.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ import { ObjectId } from "mongodb";
import { PersonalBest } from "@monkeytype/contracts/schemas/shared";
import { pb } from "../../dal/leaderboards.spec";
import { mockAuthenticateWithApeKey } from "../../__testData__/auth";
import { LeaderboardRank } from "@monkeytype/contracts/schemas/leaderboards";
import { randomUUID } from "node:crypto";
import _ from "lodash";
import { MonkeyMail, UserStreak } from "@monkeytype/contracts/schemas/users";
import { isFirebaseError } from "../../../src/utils/error";
import { LeaderboardEntry } from "@monkeytype/contracts/schemas/leaderboards";

const mockApp = request(app);
const configuration = Configuration.getCachedConfiguration();
Expand Down Expand Up @@ -2696,6 +2696,7 @@ describe("user controller test", () => {
const getUserByNameMock = vi.spyOn(UserDal, "getUserByName");
const checkIfUserIsPremiumMock = vi.spyOn(UserDal, "checkIfUserIsPremium");
const leaderboardGetRankMock = vi.spyOn(LeaderboardDal, "getRank");
const leaderboardGetCountMock = vi.spyOn(LeaderboardDal, "getCount");

const foundUser: Partial<UserDal.DBUser> = {
_id: new ObjectId(),
Expand Down Expand Up @@ -2747,15 +2748,17 @@ describe("user controller test", () => {
getUserByNameMock.mockReset();
checkIfUserIsPremiumMock.mockReset().mockResolvedValue(true);
leaderboardGetRankMock.mockReset();
leaderboardGetCountMock.mockReset();
await enableProfiles(true);
});

it("should get by name without authentication", async () => {
//GIVEN
getUserByNameMock.mockResolvedValue(foundUser as any);

const rank: LeaderboardRank = { count: 100, rank: 24 };
const rank = { rank: 24 } as LeaderboardEntry;
leaderboardGetRankMock.mockResolvedValue(rank);
leaderboardGetCountMock.mockResolvedValue(100);

//WHEN
const { body } = await mockApp.get("/users/bob/profile").expect(200);
Expand Down Expand Up @@ -2815,8 +2818,9 @@ describe("user controller test", () => {
banned: true,
} as any);

const rank: LeaderboardRank = { count: 100, rank: 24 };
const rank = { rank: 24 } as LeaderboardEntry;
leaderboardGetRankMock.mockResolvedValue(rank);
leaderboardGetCountMock.mockResolvedValue(100);

//WHEN
const { body } = await mockApp.get("/users/bob/profile").expect(200);
Expand Down Expand Up @@ -2865,8 +2869,9 @@ describe("user controller test", () => {
const uid = foundUser.uid;
getUserMock.mockResolvedValue(foundUser as any);

const rank: LeaderboardRank = { count: 100, rank: 24 };
const rank = { rank: 24 } as LeaderboardEntry;
leaderboardGetRankMock.mockResolvedValue(rank);
leaderboardGetCountMock.mockResolvedValue(100);

//WHEN
const { body } = await mockApp
Expand Down
23 changes: 15 additions & 8 deletions backend/__tests__/dal/leaderboards.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ describe("LeaderboardsDal", () => {

//WHEN
await LeaderboardsDal.update("time", "15", "english");
const result = await LeaderboardsDal.get("time", "15", "english", 0);
const result = await LeaderboardsDal.get("time", "15", "english", 0, 50);

//THEN
expect(result).toHaveLength(1);
Expand All @@ -50,7 +50,8 @@ describe("LeaderboardsDal", () => {
"time",
"15",
"english",
0
0,
50
)) as DBLeaderboardEntry[];

//THEN
Expand All @@ -76,7 +77,8 @@ describe("LeaderboardsDal", () => {
"time",
"60",
"english",
0
0,
50
)) as LeaderboardsDal.DBLeaderboardEntry[];

//THEN
Expand All @@ -102,7 +104,8 @@ describe("LeaderboardsDal", () => {
"time",
"60",
"english",
0
0,
50
)) as DBLeaderboardEntry[];

//THEN
Expand All @@ -125,7 +128,8 @@ describe("LeaderboardsDal", () => {
"time",
"15",
"english",
0
0,
50
)) as DBLeaderboardEntry[];

//THEN
Expand Down Expand Up @@ -187,7 +191,8 @@ describe("LeaderboardsDal", () => {
"time",
"15",
"english",
0
0,
50
)) as DBLeaderboardEntry[];

//THEN
Expand Down Expand Up @@ -223,7 +228,8 @@ describe("LeaderboardsDal", () => {
"time",
"15",
"english",
0
0,
50
)) as DBLeaderboardEntry[];

//THEN
Expand Down Expand Up @@ -255,7 +261,8 @@ describe("LeaderboardsDal", () => {
"time",
"15",
"english",
0
0,
50
)) as DBLeaderboardEntry[];

//THEN
Expand Down
2 changes: 1 addition & 1 deletion backend/redis-scripts/get-results.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ for _, user_id in ipairs(scores_in_range) do
end
end

return {results, scores}
return {results, scores}
73 changes: 45 additions & 28 deletions backend/src/api/controllers/leaderboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import * as WeeklyXpLeaderboard from "../../services/weekly-xp-leaderboard";
import {
GetDailyLeaderboardQuery,
GetDailyLeaderboardRankQuery,
GetDailyLeaderboardResponse,
GetLeaderboardDailyRankResponse,
GetLeaderboardQuery,
GetLeaderboardRankQuery,
GetLeaderboardRankResponse,
GetLeaderboardResponse as GetLeaderboardResponse,
GetWeeklyXpLeaderboardQuery,
GetWeeklyXpLeaderboardRankResponse,
GetWeeklyXpLeaderboardResponse,
LanguageAndModeQuery,
} from "@monkeytype/contracts/leaderboards";
import { Configuration } from "@monkeytype/contracts/schemas/configuration";
import {
Expand All @@ -27,14 +28,14 @@ import { MonkeyRequest } from "../types";
export async function getLeaderboard(
req: MonkeyRequest<GetLeaderboardQuery>
): Promise<GetLeaderboardResponse> {
const { language, mode, mode2, skip = 0, limit = 50 } = req.query;
const { language, mode, mode2, page, pageSize } = req.query;

const leaderboard = await LeaderboardsDAL.get(
mode,
mode2,
language,
skip,
limit
page,
pageSize
);

if (leaderboard === false) {
Expand All @@ -44,13 +45,18 @@ export async function getLeaderboard(
);
}

const count = await LeaderboardsDAL.getCount(mode, mode2, language);
const normalizedLeaderboard = leaderboard.map((it) => _.omit(it, ["_id"]));

return new MonkeyResponse("Leaderboard retrieved", normalizedLeaderboard);
return new MonkeyResponse("Leaderboard retrieved", {
count,
entries: normalizedLeaderboard,
pageSize,
});
}

export async function getRankFromLeaderboard(
req: MonkeyRequest<LanguageAndModeQuery>
req: MonkeyRequest<GetLeaderboardRankQuery>
): Promise<GetLeaderboardRankResponse> {
const { language, mode, mode2 } = req.query;
const { uid } = req.ctx.decodedToken;
Expand Down Expand Up @@ -91,25 +97,33 @@ function getDailyLeaderboardWithError(

export async function getDailyLeaderboard(
req: MonkeyRequest<GetDailyLeaderboardQuery>
): Promise<GetLeaderboardResponse> {
const { skip = 0, limit = 50 } = req.query;
): Promise<GetDailyLeaderboardResponse> {
const { page, pageSize } = req.query;

const dailyLeaderboard = getDailyLeaderboardWithError(
req.query,
req.ctx.configuration.dailyLeaderboards
);

const minRank = skip;
const maxRank = minRank + limit - 1;

const topResults = await dailyLeaderboard.getResults(
minRank,
maxRank,
const results = await dailyLeaderboard.getResults(
page,
pageSize,
req.ctx.configuration.dailyLeaderboards,
req.ctx.configuration.users.premium.enabled
);

return new MonkeyResponse("Daily leaderboard retrieved", topResults);
const minWpm = await dailyLeaderboard.getMinWpm(
req.ctx.configuration.dailyLeaderboards
);

const count = await dailyLeaderboard.getCount();

return new MonkeyResponse("Daily leaderboard retrieved", {
entries: results,
minWpm,
count,
pageSize,
});
}

export async function getDailyLeaderboardRank(
Expand All @@ -131,8 +145,8 @@ export async function getDailyLeaderboardRank(
}

function getWeeklyXpLeaderboardWithError(
{ weeksBefore }: GetWeeklyXpLeaderboardQuery,
config: Configuration["leaderboards"]["weeklyXp"]
config: Configuration["leaderboards"]["weeklyXp"],
weeksBefore?: number
): WeeklyXpLeaderboard.WeeklyXpLeaderboard {
const customTimestamp =
weeksBefore === undefined
Expand All @@ -150,22 +164,26 @@ function getWeeklyXpLeaderboardWithError(
export async function getWeeklyXpLeaderboardResults(
req: MonkeyRequest<GetWeeklyXpLeaderboardQuery>
): Promise<GetWeeklyXpLeaderboardResponse> {
const { skip = 0, limit = 50 } = req.query;

const minRank = skip;
const maxRank = minRank + limit - 1;
const { page, pageSize, weeksBefore } = req.query;

const weeklyXpLeaderboard = getWeeklyXpLeaderboardWithError(
req.query,
req.ctx.configuration.leaderboards.weeklyXp
req.ctx.configuration.leaderboards.weeklyXp,
weeksBefore
);
const results = await weeklyXpLeaderboard.getResults(
minRank,
maxRank,
req.ctx.configuration.leaderboards.weeklyXp
page,
pageSize,
req.ctx.configuration.leaderboards.weeklyXp,
req.ctx.configuration.users.premium.enabled
);

return new MonkeyResponse("Weekly xp leaderboard retrieved", results);
const count = await weeklyXpLeaderboard.getCount();

return new MonkeyResponse("Weekly xp leaderboard retrieved", {
entries: results,
count,
pageSize,
});
}

export async function getWeeklyXpLeaderboardRank(
Expand All @@ -174,7 +192,6 @@ export async function getWeeklyXpLeaderboardRank(
const { uid } = req.ctx.decodedToken;

const weeklyXpLeaderboard = getWeeklyXpLeaderboardWithError(
{},
req.ctx.configuration.leaderboards.weeklyXp
);
const rankEntry = await weeklyXpLeaderboard.getRank(
Expand Down
1 change: 1 addition & 0 deletions backend/src/api/controllers/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,7 @@ export async function addResult(
discordId: user.discordId,
badgeId: selectedBadgeId,
lastActivityTimestamp: Date.now(),
isPremium,
},
xpGained: xpGained.xp,
timeTypedSeconds: totalDurationTypedSeconds,
Expand Down
20 changes: 16 additions & 4 deletions backend/src/api/controllers/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1071,27 +1071,39 @@ async function getAllTimeLbs(uid: string): Promise<AllTimeLbs> {
uid
);

const allTime15EnglishCount = await LeaderboardsDAL.getCount(
"time",
"15",
"english"
);

const allTime60English = await LeaderboardsDAL.getRank(
"time",
"60",
"english",
uid
);

const allTime60EnglishCount = await LeaderboardsDAL.getCount(
"time",
"60",
"english"
);

const english15 =
allTime15English === false
allTime15English === false || allTime15English === null
? undefined
: {
rank: allTime15English.rank,
count: allTime15English.count,
count: allTime15EnglishCount,
};

const english60 =
allTime60English === false
allTime60English === false || allTime60English === null
? undefined
: {
rank: allTime60English.rank,
count: allTime60English.count,
count: allTime60EnglishCount,
};

return {
Expand Down
Loading