mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2025-11-07 19:38:09 +08:00
impr(leaderboard): daily mode/language buttons based on backend configuration (@fehmer) (#6713)
Co-authored-by: Miodec <jack@monkeytype.com>
This commit is contained in:
parent
5aec2c9a17
commit
a4de8dfda6
7 changed files with 326 additions and 112 deletions
|
|
@ -139,28 +139,29 @@ describe("Loaderboard Controller", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it("should get for mode", async () => {
|
||||
getLeaderboardMock.mockResolvedValue([]);
|
||||
for (const mode of ["time", "words", "quote", "zen", "custom"]) {
|
||||
const response = await mockApp
|
||||
.get("/leaderboards")
|
||||
.query({ language: "english", mode, mode2: "custom" });
|
||||
expect(response.status, "for mode " + mode).toEqual(200);
|
||||
}
|
||||
describe("should get for modes", async () => {
|
||||
beforeEach(() => {
|
||||
getLeaderboardMock.mockResolvedValue([]);
|
||||
});
|
||||
|
||||
const testCases = [
|
||||
{ mode: "time", mode2: "15", language: "english", expectStatus: 200 },
|
||||
{ mode: "time", mode2: "60", language: "english", expectStatus: 200 },
|
||||
{ mode: "time", mode2: "30", language: "english", expectStatus: 404 },
|
||||
{ mode: "words", mode2: "15", language: "english", expectStatus: 404 },
|
||||
{ mode: "time", mode2: "15", language: "spanish", expectStatus: 404 },
|
||||
];
|
||||
it.for(testCases)(
|
||||
`expect $expectStatus for mode $mode, mode2 $mode2, lang $language`,
|
||||
async ({ mode, mode2, language, expectStatus }) => {
|
||||
await mockApp
|
||||
.get("/leaderboards")
|
||||
.query({ language, mode, mode2 })
|
||||
.expect(expectStatus);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it("should get for mode2", async () => {
|
||||
getLeaderboardMock.mockResolvedValue([]);
|
||||
for (const mode2 of allModes) {
|
||||
const response = await mockApp.get("/leaderboards").query({
|
||||
language: "english",
|
||||
mode: "words",
|
||||
mode2,
|
||||
});
|
||||
|
||||
expect(response.status, "for mode2 " + mode2).toEqual(200);
|
||||
}
|
||||
});
|
||||
it("fails for missing query", async () => {
|
||||
const { body } = await mockApp.get("/leaderboards").expect(422);
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,14 @@ export async function getLeaderboard(
|
|||
): Promise<GetLeaderboardResponse> {
|
||||
const { language, mode, mode2, page, pageSize } = req.query;
|
||||
|
||||
if (
|
||||
mode !== "time" ||
|
||||
(mode2 !== "15" && mode2 !== "60") ||
|
||||
language !== "english"
|
||||
) {
|
||||
throw new MonkeyError(404, "There is no leaderboard for this mode");
|
||||
}
|
||||
|
||||
const leaderboard = await LeaderboardsDAL.get(
|
||||
mode,
|
||||
mode2,
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<div class="sideButtons">
|
||||
<div class="buttonGroup typeButtons">
|
||||
<button data-type="allTime">
|
||||
<i class="fas fa-globe-americas"></i>
|
||||
|
|
@ -172,7 +172,7 @@
|
|||
</button>
|
||||
</div>
|
||||
<div class="divider hidden"></div>
|
||||
<div class="buttonGroup hidden secondary modeButtons">
|
||||
<div class="buttonGroup hidden modeButtons">
|
||||
<button data-mode="15">
|
||||
<i class="fas fa-clock"></i>
|
||||
time 15
|
||||
|
|
@ -182,8 +182,8 @@
|
|||
time 60
|
||||
</button>
|
||||
</div>
|
||||
<div class="divider divider2 hidden"></div>
|
||||
<div class="buttonGroup hidden secondary languageButtons">
|
||||
<div class="divider2 hidden"></div>
|
||||
<div class="buttonGroup hidden languageButtons">
|
||||
<button data-language="english">
|
||||
<i class="fas fa-globe"></i>
|
||||
english
|
||||
|
|
|
|||
|
|
@ -279,7 +279,7 @@
|
|||
color: var(--sub-color);
|
||||
padding: 1em;
|
||||
}
|
||||
.buttons {
|
||||
.sideButtons {
|
||||
align-content: start;
|
||||
align-items: start;
|
||||
grid-area: buttons;
|
||||
|
|
@ -294,7 +294,8 @@
|
|||
justify-content: start;
|
||||
padding-left: 0.75em;
|
||||
}
|
||||
.divider {
|
||||
.divider,
|
||||
.divider2 {
|
||||
background: var(--bg-color);
|
||||
width: 100%;
|
||||
height: 0.25em;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
import { Configuration } from "@monkeytype/contracts/schemas/configuration";
|
||||
import Ape from ".";
|
||||
import { promiseWithResolvers } from "../utils/misc";
|
||||
|
||||
let config: Configuration | undefined = undefined;
|
||||
|
||||
const { promise: configPromise, resolve } = promiseWithResolvers<boolean>();
|
||||
|
||||
export { configPromise };
|
||||
|
||||
export function get(): Configuration | undefined {
|
||||
return config;
|
||||
}
|
||||
|
|
@ -15,5 +20,6 @@ export async function sync(): Promise<void> {
|
|||
return;
|
||||
} else {
|
||||
config = response.body.data ?? undefined;
|
||||
resolve(true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ import {
|
|||
LanguageSchema,
|
||||
} from "@monkeytype/contracts/schemas/languages";
|
||||
import { isSafeNumber } from "@monkeytype/util/numbers";
|
||||
import { Mode, Mode2, ModeSchema } from "@monkeytype/contracts/schemas/shared";
|
||||
import * as ServerConfiguration from "../ape/server-configuration";
|
||||
|
||||
const LeaderboardTypeSchema = z.enum(["allTime", "weekly", "daily"]);
|
||||
|
|
@ -56,6 +57,7 @@ type AllTimeState = {
|
|||
type: "allTime";
|
||||
mode: "time";
|
||||
mode2: "15" | "60";
|
||||
language: "english";
|
||||
data: LeaderboardEntry[] | null;
|
||||
count: number;
|
||||
userData: LeaderboardEntry | null;
|
||||
|
|
@ -71,8 +73,8 @@ type WeeklyState = {
|
|||
|
||||
type DailyState = {
|
||||
type: "daily";
|
||||
mode: "time";
|
||||
mode2: "15" | "60";
|
||||
mode: Mode;
|
||||
mode2: Mode2<DailyState["mode"]>;
|
||||
yesterday: boolean;
|
||||
minWpm: number;
|
||||
language: Language;
|
||||
|
|
@ -98,6 +100,7 @@ const state = {
|
|||
loading: true,
|
||||
updating: false,
|
||||
type: "allTime",
|
||||
mode: "time",
|
||||
mode2: "15",
|
||||
data: null,
|
||||
userData: null,
|
||||
|
|
@ -111,7 +114,8 @@ const state = {
|
|||
|
||||
const SelectorSchema = z.object({
|
||||
type: LeaderboardTypeSchema,
|
||||
mode2: z.enum(["15", "60"]).optional(),
|
||||
mode: ModeSchema.optional(),
|
||||
mode2: z.string().optional(),
|
||||
language: LanguageSchema.optional(),
|
||||
yesterday: z.boolean().optional(),
|
||||
lastWeek: z.boolean().optional(),
|
||||
|
|
@ -128,6 +132,25 @@ const selectorLS = new LocalStorageWithSchema({
|
|||
fallback: { type: "allTime", mode2: "15" },
|
||||
});
|
||||
|
||||
type LanguagesByModeByMode2 = Partial<
|
||||
Record<Mode, Record<string /*mode2*/, Language[]>>
|
||||
>;
|
||||
|
||||
type ValidLeaderboards = {
|
||||
allTime: LanguagesByModeByMode2;
|
||||
daily: LanguagesByModeByMode2;
|
||||
};
|
||||
|
||||
const validLeaderboards: ValidLeaderboards = {
|
||||
allTime: {
|
||||
time: {
|
||||
"15": ["english"],
|
||||
"60": ["english"],
|
||||
},
|
||||
},
|
||||
daily: {},
|
||||
};
|
||||
|
||||
function updateTitle(): void {
|
||||
const type =
|
||||
state.type === "allTime"
|
||||
|
|
@ -137,17 +160,11 @@ function updateTitle(): void {
|
|||
: "Daily";
|
||||
|
||||
const language =
|
||||
state.type === "daily"
|
||||
? capitalizeFirstLetter(state.language)
|
||||
: state.type === "allTime"
|
||||
? "English"
|
||||
: "";
|
||||
state.type !== "weekly" ? capitalizeFirstLetter(state.language) : "";
|
||||
|
||||
const mode =
|
||||
state.type === "allTime"
|
||||
? ` Time ${state.mode2}`
|
||||
: state.type === "daily"
|
||||
? ` Time ${state.mode2}`
|
||||
state.type !== "weekly"
|
||||
? ` ${capitalizeFirstLetter(state.mode)} ${state.mode2}`
|
||||
: "";
|
||||
|
||||
state.title = `${type} ${language} ${mode} Leaderboard`;
|
||||
|
|
@ -273,7 +290,7 @@ async function requestData(update = false): Promise<void> {
|
|||
Ape.leaderboards.getDailyRank,
|
||||
{
|
||||
language: state.language,
|
||||
mode: "time",
|
||||
mode: state.mode,
|
||||
mode2: state.mode2,
|
||||
daysBefore: state.yesterday ? 1 : undefined,
|
||||
}
|
||||
|
|
@ -326,11 +343,16 @@ async function requestData(update = false): Promise<void> {
|
|||
}
|
||||
} else {
|
||||
state.data = null;
|
||||
state.error = "Something went wrong";
|
||||
Notifications.add(
|
||||
"Failed to get leaderboard: " + dataResponse.body.message,
|
||||
-1
|
||||
);
|
||||
|
||||
if (dataResponse.status === 404) {
|
||||
state.error = "No leaderboard found";
|
||||
} else {
|
||||
state.error = "Something went wrong";
|
||||
Notifications.add(
|
||||
"Failed to get leaderboard: " + dataResponse.body.message,
|
||||
-1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (state.userData === null && rankResponse !== undefined) {
|
||||
|
|
@ -919,34 +941,77 @@ function updateContent(): void {
|
|||
}
|
||||
}
|
||||
|
||||
function updateSideButtons(): void {
|
||||
updateTypeButtons();
|
||||
updateModeButtons();
|
||||
updateLanguageButtons();
|
||||
}
|
||||
|
||||
function updateTypeButtons(): void {
|
||||
const el = $(".page.pageLeaderboards .buttonGroup.typeButtons");
|
||||
el.find("button").removeClass("active");
|
||||
el.find(`button[data-type=${state.type}]`).addClass("active");
|
||||
}
|
||||
|
||||
function updateSecondaryButtons(): void {
|
||||
$(".page.pageLeaderboards .buttonGroup.secondary").addClass("hidden");
|
||||
$(".page.pageLeaderboards .buttons .divider").addClass("hidden");
|
||||
$(".page.pageLeaderboards .buttons .divider2").addClass("hidden");
|
||||
|
||||
if (state.type === "allTime") {
|
||||
$(".page.pageLeaderboards .buttonGroup.modeButtons").removeClass("hidden");
|
||||
$(".page.pageLeaderboards .buttons .divider").removeClass("hidden");
|
||||
$(".page.pageLeaderboards .buttons .divider2").addClass("hidden");
|
||||
|
||||
updateModeButtons();
|
||||
function updateModeButtons(): void {
|
||||
if (state.type !== "allTime" && state.type !== "daily") {
|
||||
$(".page.pageLeaderboards .buttonGroup.modeButtons").addClass("hidden");
|
||||
$(".page.pageLeaderboards .sideButtons .divider").addClass("hidden");
|
||||
return;
|
||||
}
|
||||
if (state.type === "daily") {
|
||||
$(".page.pageLeaderboards .buttonGroup.modeButtons").removeClass("hidden");
|
||||
$(".page.pageLeaderboards .buttonGroup.languageButtons").removeClass(
|
||||
"hidden"
|
||||
);
|
||||
$(".page.pageLeaderboards .buttons .divider").removeClass("hidden");
|
||||
$(".page.pageLeaderboards .buttons .divider2").removeClass("hidden");
|
||||
$(".page.pageLeaderboards .buttonGroup.modeButtons").removeClass("hidden");
|
||||
$(".page.pageLeaderboards .sideButtons .divider").removeClass("hidden");
|
||||
|
||||
updateModeButtons();
|
||||
updateLanguageButtons();
|
||||
const el = $(".page.pageLeaderboards .buttonGroup.modeButtons");
|
||||
el.find("button").removeClass("active");
|
||||
el.find(
|
||||
`button[data-mode=${state.mode}][data-mode2=${state.mode2}]`
|
||||
).addClass("active");
|
||||
|
||||
//hide all mode buttons
|
||||
$(`.page.pageLeaderboards .buttonGroup.modeButtons button`).addClass(
|
||||
"hidden"
|
||||
);
|
||||
|
||||
//show all valid ones
|
||||
for (const mode of Object.keys(validLeaderboards[state.type]) as Mode[]) {
|
||||
for (const mode2 of Object.keys(
|
||||
// oxlint-disable-next-line no-non-null-assertion
|
||||
validLeaderboards[state.type][mode]!
|
||||
)) {
|
||||
$(
|
||||
`.page.pageLeaderboards .buttonGroup.modeButtons button[data-mode="${mode}"][data-mode2="${mode2}"]`
|
||||
).removeClass("hidden");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateLanguageButtons(): void {
|
||||
if (state.type !== "daily") {
|
||||
$(".page.pageLeaderboards .buttonGroup.languageButtons").addClass("hidden");
|
||||
$(".page.pageLeaderboards .sideButtons .divider2").addClass("hidden");
|
||||
return;
|
||||
}
|
||||
$(".page.pageLeaderboards .buttonGroup.languageButtons").removeClass(
|
||||
"hidden"
|
||||
);
|
||||
$(".page.pageLeaderboards .sideButtons .divider2").removeClass("hidden");
|
||||
|
||||
const el = $(".page.pageLeaderboards .buttonGroup.languageButtons");
|
||||
el.find("button").removeClass("active");
|
||||
el.find(`button[data-language=${state.language}]`).addClass("active");
|
||||
|
||||
//hide all languages
|
||||
$(`.page.pageLeaderboards .buttonGroup.languageButtons button`).addClass(
|
||||
"hidden"
|
||||
);
|
||||
|
||||
//show all valid ones
|
||||
for (const lang of validLeaderboards[state.type][state.mode]?.[state.mode2] ??
|
||||
[]) {
|
||||
$(
|
||||
`.page.pageLeaderboards .buttonGroup.languageButtons button[data-language="${lang}"]`
|
||||
).removeClass("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1009,37 +1074,142 @@ function stopTimer(): void {
|
|||
$(".page.pageLeaderboards .titleAndButtons .timer").text("-");
|
||||
}
|
||||
|
||||
// async function appendLanguageButtons(): Promise<void> {
|
||||
// const languages =
|
||||
// (await ServerConfiguration.get()?.dailyLeaderboards.validModeRules.map(
|
||||
// (r) => r.language
|
||||
// )) ?? [];
|
||||
|
||||
// const el = $(".page.pageLeaderboards .buttonGroup.languageButtons");
|
||||
// el.empty();
|
||||
|
||||
// for (const language of languages) {
|
||||
// el.append(`
|
||||
// <button data-language="${language}">
|
||||
// <i class="fas fa-globe"></i>
|
||||
// ${language}
|
||||
// </button>
|
||||
// `);
|
||||
// }
|
||||
// }
|
||||
|
||||
function updateModeButtons(): void {
|
||||
if (state.type !== "allTime" && state.type !== "daily") return;
|
||||
const el = $(".page.pageLeaderboards .buttonGroup.modeButtons");
|
||||
el.find("button").removeClass("active");
|
||||
el.find(`button[data-mode=${state.mode2}]`).addClass("active");
|
||||
function convertRuleOption(rule: string): string[] {
|
||||
if (rule.startsWith("(")) {
|
||||
return rule.slice(1, -1).split("|");
|
||||
}
|
||||
return [rule];
|
||||
}
|
||||
|
||||
function updateLanguageButtons(): void {
|
||||
if (state.type !== "daily") return;
|
||||
const el = $(".page.pageLeaderboards .buttonGroup.languageButtons");
|
||||
el.find("button").removeClass("active");
|
||||
el.find(`button[data-language=${state.language}]`).addClass("active");
|
||||
async function updateValidDailyLeaderboards(): Promise<void> {
|
||||
const dailyRulesConfig = await ServerConfiguration.get()?.dailyLeaderboards
|
||||
.validModeRules;
|
||||
|
||||
if (dailyRulesConfig === undefined) {
|
||||
throw new Error(
|
||||
"cannot load server configuration for dailyLeaderboards.validModeRules"
|
||||
);
|
||||
}
|
||||
|
||||
//a rule can contain multiple values. create a flat list out of them
|
||||
const dailyRules = dailyRulesConfig.flatMap((rule) => {
|
||||
const languages = convertRuleOption(rule.language) as Language[];
|
||||
const mode2List = convertRuleOption(rule.mode2);
|
||||
|
||||
return mode2List.map((mode2) => ({
|
||||
mode: rule.mode as Mode,
|
||||
mode2,
|
||||
languages,
|
||||
}));
|
||||
});
|
||||
|
||||
validLeaderboards.daily = dailyRules.reduce<
|
||||
Partial<Record<Mode, Record<string /*mode2*/, Language[]>>>
|
||||
>((acc, { mode, mode2, languages }) => {
|
||||
let modes = acc[mode];
|
||||
if (modes === undefined) {
|
||||
modes = {};
|
||||
acc[mode] = modes;
|
||||
}
|
||||
|
||||
let modes2 = modes[mode2];
|
||||
if (modes2 === undefined) {
|
||||
modes2 = [];
|
||||
modes[mode2] = modes2;
|
||||
}
|
||||
|
||||
modes2.push(...languages);
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function checkIfLeaderboardIsValid(): void {
|
||||
if (state.type === "weekly") return;
|
||||
|
||||
const validLeaderboard = validLeaderboards[state.type];
|
||||
|
||||
let validModes2 = validLeaderboard[state.mode];
|
||||
if (validModes2 === undefined) {
|
||||
const firstMode = Object.keys(validLeaderboard).sort()[0] as Mode;
|
||||
if (firstMode === undefined) {
|
||||
throw new Error(`no valid leaderboard config for type ${state.type}`);
|
||||
}
|
||||
state.mode = firstMode;
|
||||
// oxlint-disable-next-line no-non-null-assertion
|
||||
validModes2 = validLeaderboard[state.mode]!;
|
||||
}
|
||||
|
||||
let supportedLanguages = validModes2[state.mode2];
|
||||
if (supportedLanguages === undefined) {
|
||||
const firstMode2 = Object.keys(validModes2).sort(
|
||||
(a, b) => parseInt(a) - parseInt(b)
|
||||
)[0];
|
||||
if (firstMode2 === undefined) {
|
||||
throw new Error(
|
||||
`no valid leaderboard config for type ${state.type} and mode ${state.mode}`
|
||||
);
|
||||
}
|
||||
state.mode2 = firstMode2;
|
||||
supportedLanguages = validModes2[state.mode2];
|
||||
}
|
||||
|
||||
if (supportedLanguages === undefined || supportedLanguages.length < 1) {
|
||||
throw new Error(
|
||||
`Daily leaderboard config not valid for mode:${state.mode} mode2:${state.mode2}`
|
||||
);
|
||||
}
|
||||
|
||||
if (!supportedLanguages.includes(state.language)) {
|
||||
state.language = supportedLanguages.sort()[0] as Language;
|
||||
}
|
||||
}
|
||||
|
||||
async function appendModeAndLanguageButtons(): Promise<void> {
|
||||
const modes = Array.from(
|
||||
new Set(
|
||||
Object.values(validLeaderboards).flatMap(
|
||||
(rule) => Object.keys(rule) as Mode[]
|
||||
)
|
||||
)
|
||||
).sort();
|
||||
|
||||
const mode2Buttons = modes.flatMap((mode) => {
|
||||
const modes2 = Array.from(
|
||||
new Set(
|
||||
Object.values(validLeaderboards).flatMap((rule) =>
|
||||
Object.keys(rule[mode] ?? {})
|
||||
)
|
||||
)
|
||||
).sort((a, b) => parseInt(a) - parseInt(b));
|
||||
|
||||
const icon = mode === "time" ? "fas fa-clock" : "fas fa-align-left";
|
||||
|
||||
return modes2.map(
|
||||
(mode2) => `<button data-mode="${mode}" data-mode2="${mode2}">
|
||||
<i class="${icon}"></i>
|
||||
${mode} ${mode2}
|
||||
</button>`
|
||||
);
|
||||
});
|
||||
$(".modeButtons").html(mode2Buttons.join("\n"));
|
||||
|
||||
const availableLanguages = Array.from(
|
||||
new Set(
|
||||
Object.values(validLeaderboards)
|
||||
.flatMap((rule) => Object.values(rule))
|
||||
.flatMap((mode) => Object.values(mode))
|
||||
.flatMap((it) => it)
|
||||
)
|
||||
).sort();
|
||||
|
||||
const languageButtons = availableLanguages.map(
|
||||
(lang) =>
|
||||
`<button data-language="${lang}">
|
||||
<i class="fas fa-globe"></i>
|
||||
${lang}
|
||||
</button>`
|
||||
);
|
||||
$(".languageButtons").html(languageButtons.join("\n"));
|
||||
}
|
||||
|
||||
function disableButtons(): void {
|
||||
|
|
@ -1126,6 +1296,7 @@ function updateGetParameters(): void {
|
|||
if (state.type === "allTime") {
|
||||
params.mode2 = state.mode2;
|
||||
} else if (state.type === "daily") {
|
||||
params.mode = state.mode;
|
||||
params.language = state.language;
|
||||
params.mode2 = state.mode2;
|
||||
if (state.yesterday) {
|
||||
|
|
@ -1155,19 +1326,19 @@ function readGetParameters(params?: UrlParameter): void {
|
|||
}
|
||||
|
||||
if (state.type === "allTime") {
|
||||
if (params.mode2) {
|
||||
state.mode2 = params.mode2;
|
||||
if (params.mode2 !== undefined) {
|
||||
state.mode2 = params.mode2 as AllTimeState["mode2"];
|
||||
}
|
||||
} else if (state.type === "daily") {
|
||||
if (params.language !== undefined) {
|
||||
state.language = params.language;
|
||||
}
|
||||
if (state.language === undefined) {
|
||||
state.language = "english";
|
||||
}
|
||||
if (params.mode2 !== undefined) {
|
||||
state.mode2 = params.mode2;
|
||||
}
|
||||
if (params.mode !== undefined) {
|
||||
state.mode = params.mode;
|
||||
}
|
||||
if (params.yesterday !== undefined) {
|
||||
state.yesterday = params.yesterday;
|
||||
}
|
||||
|
|
@ -1184,7 +1355,7 @@ function readGetParameters(params?: UrlParameter): void {
|
|||
state.page = 0;
|
||||
}
|
||||
}
|
||||
if (params.goToUserPage) {
|
||||
if (params.goToUserPage === true) {
|
||||
state.goToUserPage = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1235,40 +1406,63 @@ $(".page.pageLeaderboards .buttonGroup.typeButtons").on(
|
|||
if (state.type === "weekly") {
|
||||
state.lastWeek = false;
|
||||
}
|
||||
checkIfLeaderboardIsValid();
|
||||
state.data = null;
|
||||
state.page = 0;
|
||||
void requestData();
|
||||
updateTypeButtons();
|
||||
updateTitle();
|
||||
updateSecondaryButtons();
|
||||
updateSideButtons();
|
||||
updateContent();
|
||||
updateGetParameters();
|
||||
}
|
||||
);
|
||||
|
||||
$(".page.pageLeaderboards .buttonGroup.secondary").on(
|
||||
$(".page.pageLeaderboards .buttonGroup.modeButtons").on(
|
||||
"click",
|
||||
"button",
|
||||
function () {
|
||||
const mode = $(this).attr("data-mode") as "15" | "60" | undefined;
|
||||
const language = $(this).data("language") as Language;
|
||||
const mode = $(this).attr("data-mode") as Mode;
|
||||
const mode2 = $(this).attr("data-mode2");
|
||||
|
||||
if (
|
||||
mode !== undefined &&
|
||||
mode2 !== undefined &&
|
||||
(state.type === "allTime" || state.type === "daily")
|
||||
) {
|
||||
if (state.mode2 === mode) return;
|
||||
state.mode2 = mode;
|
||||
if (state.mode === mode && state.mode2 === mode2) return;
|
||||
state.mode = mode;
|
||||
state.mode2 = mode2;
|
||||
state.page = 0;
|
||||
} else if (language !== undefined && state.type === "daily") {
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
checkIfLeaderboardIsValid();
|
||||
state.data = null;
|
||||
void requestData();
|
||||
updateSideButtons();
|
||||
updateTitle();
|
||||
updateContent();
|
||||
updateGetParameters();
|
||||
}
|
||||
);
|
||||
|
||||
$(".page.pageLeaderboards .buttonGroup.languageButtons").on(
|
||||
"click",
|
||||
"button",
|
||||
function () {
|
||||
const language = $(this).attr("data-language") as Language;
|
||||
|
||||
if (language !== undefined && state.type === "daily") {
|
||||
if (state.language === language) return;
|
||||
state.language = language;
|
||||
state.page = 0;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
checkIfLeaderboardIsValid();
|
||||
state.data = null;
|
||||
void requestData();
|
||||
updateSecondaryButtons();
|
||||
updateSideButtons();
|
||||
updateTitle();
|
||||
updateContent();
|
||||
updateGetParameters();
|
||||
|
|
@ -1286,19 +1480,21 @@ export const page = new PageWithUrlParams({
|
|||
stopTimer();
|
||||
},
|
||||
beforeShow: async (options): Promise<void> => {
|
||||
await ServerConfiguration.configPromise;
|
||||
Skeleton.append("pageLeaderboards", "main");
|
||||
// await appendLanguageButtons(); //todo figure out this race condition
|
||||
await updateValidDailyLeaderboards();
|
||||
await appendModeAndLanguageButtons();
|
||||
readGetParameters(options.urlParams);
|
||||
checkIfLeaderboardIsValid();
|
||||
startTimer();
|
||||
updateTypeButtons();
|
||||
updateTitle();
|
||||
updateSecondaryButtons();
|
||||
updateContent();
|
||||
updateSideButtons();
|
||||
updateGetParameters();
|
||||
void requestData(false);
|
||||
},
|
||||
afterShow: async (): Promise<void> => {
|
||||
updateSecondaryButtons();
|
||||
// updateSideButtons();
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { z } from "zod";
|
|||
import {
|
||||
CommonResponses,
|
||||
meta,
|
||||
MonkeyClientError,
|
||||
responseWithData,
|
||||
responseWithNullableData,
|
||||
} from "./schemas/api";
|
||||
|
|
@ -145,6 +146,7 @@ export const leaderboardsContract = c.router(
|
|||
query: GetLeaderboardQuerySchema.strict(),
|
||||
responses: {
|
||||
200: GetLeaderboardResponseSchema,
|
||||
404: MonkeyClientError,
|
||||
},
|
||||
metadata: meta({
|
||||
authenticationOptions: { isPublic: true },
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue