mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-30 15:17:43 -05:00
prerender
This commit is contained in:
@@ -1,6 +1,14 @@
|
||||
import * as UserRepository from "#lib/features/user-page/UserRepository.server.ts";
|
||||
import { query } from "$app/server";
|
||||
import { IS_E2E_TEST_RUN } from "#lib/utils/e2e.ts";
|
||||
import { prerender, query } from "$app/server";
|
||||
|
||||
export const getPatrons = query(async () => {
|
||||
// xxx: is this really something needed? (other places too)
|
||||
// prerendered data would be baked from the build machine's database, so the
|
||||
// e2e build keeps this as a regular query against the per-worker test dbs
|
||||
export const getPatrons = IS_E2E_TEST_RUN
|
||||
? query(findPatrons)
|
||||
: prerender(findPatrons);
|
||||
|
||||
function findPatrons() {
|
||||
return UserRepository.findAllPatrons();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -27,7 +27,11 @@ import {
|
||||
userSeasonsPage,
|
||||
weaponCategoryUrl,
|
||||
} from "#lib/utils/urls.ts";
|
||||
import { getLeaderboards } from "../leaderboards.remote.ts";
|
||||
import { getLeaderboards, getXPLeaderboard } from "../leaderboards.remote.ts";
|
||||
import {
|
||||
isXPLeaderboardType,
|
||||
type LeaderboardType,
|
||||
} from "../leaderboards-constants.ts";
|
||||
import { leaderboardsSearchParams } from "../leaderboards-search-params.ts";
|
||||
import { seasonHasTopTen } from "../leaderboards-utils.ts";
|
||||
import TopTenPlayer from "../TopTenPlayer.svelte";
|
||||
@@ -38,7 +42,7 @@ import XPTable from "./XPTable.svelte";
|
||||
|
||||
const params = searchParamsState(leaderboardsSearchParams);
|
||||
|
||||
const data = $derived(await getLeaderboards(params.current));
|
||||
const data = $derived(await loadLeaderboards(params.current));
|
||||
|
||||
const isAllUserLeaderboard = $derived(params.current.type === "USER");
|
||||
|
||||
@@ -75,6 +79,26 @@ function onTabChange(key: string) {
|
||||
if (key === "TEAMS") return params.set({ type: "TEAM" });
|
||||
params.set({ type: "XP-ALL", season: null });
|
||||
}
|
||||
|
||||
async function loadLeaderboards(query: {
|
||||
type: LeaderboardType;
|
||||
season: number | null;
|
||||
}) {
|
||||
if (isXPLeaderboardType(query.type)) {
|
||||
return {
|
||||
xpLeaderboard: await getXPLeaderboard(query.type),
|
||||
userLeaderboard: null,
|
||||
ownEntryPeek: null,
|
||||
teamLeaderboard: null,
|
||||
season: Seasons.currentOrPrevious()!.nth,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
xpLeaderboard: null,
|
||||
...(await getLeaderboards({ type: query.type, season: query.season })),
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<Main halfWidth>
|
||||
@@ -234,7 +258,7 @@ function onTabChange(key: string) {
|
||||
<TeamTable
|
||||
entries={data.teamLeaderboard}
|
||||
season={data.season}
|
||||
queryArgs={params.current}
|
||||
leaderboardType={params.current.type}
|
||||
showQualificationDividers={params.current.type !== "TEAM-ALL"}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -9,27 +9,29 @@ import { hasRole } from "#lib/features/auth/user-state.ts";
|
||||
import * as Seasons from "#lib/features/mmr/Seasons.ts";
|
||||
import { m } from "#lib/paraglide/messages.js";
|
||||
import { teamPage, userPage } from "#lib/utils/urls.ts";
|
||||
import { TEAM_LEADERBOARD_QUALIFYING_COUNT } from "../leaderboards-constants.ts";
|
||||
import type { LeaderboardsQueryArgs } from "../leaderboards-schemas.ts";
|
||||
import {
|
||||
type LeaderboardType,
|
||||
TEAM_LEADERBOARD_QUALIFYING_COUNT,
|
||||
} from "../leaderboards-constants.ts";
|
||||
import type { TeamLeaderboardEntry } from "../leaderboards-types.ts";
|
||||
import TeamStaffMenu from "./TeamStaffMenu.svelte";
|
||||
|
||||
interface Props {
|
||||
entries: TeamLeaderboardEntry[];
|
||||
season: number;
|
||||
queryArgs: LeaderboardsQueryArgs;
|
||||
leaderboardType: LeaderboardType;
|
||||
showQualificationDividers?: boolean;
|
||||
}
|
||||
|
||||
let {
|
||||
entries,
|
||||
season,
|
||||
queryArgs,
|
||||
leaderboardType,
|
||||
showQualificationDividers: showQualificationDividersProp,
|
||||
}: Props = $props();
|
||||
|
||||
const isStaff = $derived(hasRole("STAFF"));
|
||||
const showStaffActions = $derived(isStaff && queryArgs.type !== "TEAM-ALL");
|
||||
const showStaffActions = $derived(isStaff && leaderboardType !== "TEAM-ALL");
|
||||
const isCurrentSeason = $derived(season === Seasons.current()?.nth);
|
||||
const showQualificationDividers = $derived(
|
||||
showQualificationDividersProp && isCurrentSeason && entries.length > 20,
|
||||
|
||||
@@ -10,13 +10,16 @@ export const WEAPON_LEADERBOARD_MAX_SIZE = 100;
|
||||
/** How many teams of the team leaderboard qualify, the divider being shown below the last of them. */
|
||||
export const TEAM_LEADERBOARD_QUALIFYING_COUNT = 12;
|
||||
|
||||
export const LEADERBOARD_TYPES = [
|
||||
export const SEASONAL_LEADERBOARD_TYPES = [
|
||||
"USER",
|
||||
"TEAM",
|
||||
"TEAM-ALL",
|
||||
...(weaponCategories.map(
|
||||
(category) => `USER-${category.name}`,
|
||||
) as `USER-${(typeof weaponCategories)[number]["name"]}`[]),
|
||||
] as const;
|
||||
|
||||
export const XP_LEADERBOARD_TYPES = [
|
||||
"XP-ALL",
|
||||
...(rankedModesShort.map(
|
||||
(mode) => `XP-MODE-${mode}`,
|
||||
@@ -26,4 +29,20 @@ export const LEADERBOARD_TYPES = [
|
||||
) as `XP-WEAPON-${(typeof mainWeaponIds)[number]}`[]),
|
||||
] as const;
|
||||
|
||||
export const LEADERBOARD_TYPES = [
|
||||
...SEASONAL_LEADERBOARD_TYPES,
|
||||
...XP_LEADERBOARD_TYPES,
|
||||
] as const;
|
||||
|
||||
export type LeaderboardType = (typeof LEADERBOARD_TYPES)[number];
|
||||
|
||||
export type SeasonalLeaderboardType =
|
||||
(typeof SEASONAL_LEADERBOARD_TYPES)[number];
|
||||
|
||||
export type XPLeaderboardType = (typeof XP_LEADERBOARD_TYPES)[number];
|
||||
|
||||
export function isXPLeaderboardType(
|
||||
type: LeaderboardType,
|
||||
): type is XPLeaderboardType {
|
||||
return type.startsWith("XP");
|
||||
}
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import * as v from "valibot";
|
||||
import type { SkillTeamIdentifier } from "#lib/features/mmr/mmr-utils.ts";
|
||||
import { LEADERBOARD_TYPES } from "./leaderboards-constants.ts";
|
||||
import {
|
||||
SEASONAL_LEADERBOARD_TYPES,
|
||||
XP_LEADERBOARD_TYPES,
|
||||
} from "./leaderboards-constants.ts";
|
||||
|
||||
export const leaderboardsQuerySchema = v.object({
|
||||
type: v.picklist(LEADERBOARD_TYPES),
|
||||
type: v.picklist(SEASONAL_LEADERBOARD_TYPES),
|
||||
season: v.nullable(v.pipe(v.number(), v.integer())),
|
||||
});
|
||||
|
||||
export const xpLeaderboardTypeSchema = v.picklist(XP_LEADERBOARD_TYPES);
|
||||
|
||||
export type LeaderboardsQueryArgs = v.InferOutput<
|
||||
typeof leaderboardsQuerySchema
|
||||
>;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import type { getLeaderboards } from "./leaderboards.remote.ts";
|
||||
import type {
|
||||
getLeaderboards,
|
||||
getXPLeaderboard,
|
||||
} from "./leaderboards.remote.ts";
|
||||
|
||||
export type LeaderboardsData = Awaited<ReturnType<typeof getLeaderboards>>;
|
||||
|
||||
@@ -10,8 +13,8 @@ export type TeamLeaderboardEntry = NonNullable<
|
||||
LeaderboardsData["teamLeaderboard"]
|
||||
>[number];
|
||||
|
||||
export type XPLeaderboardEntry = NonNullable<
|
||||
LeaderboardsData["xpLeaderboard"]
|
||||
export type XPLeaderboardEntry = Awaited<
|
||||
ReturnType<typeof getXPLeaderboard>
|
||||
>[number];
|
||||
|
||||
export type OwnEntryPeekData = NonNullable<LeaderboardsData["ownEntryPeek"]>;
|
||||
|
||||
@@ -6,10 +6,15 @@ import type { weaponCategories } from "@sendou/in-game-lists/weapon-ids";
|
||||
import { logger } from "@sendou/utils/logger";
|
||||
import { getUser, requireUser } from "#lib/features/auth/user.server.ts";
|
||||
import { requireRole } from "#lib/modules/permissions/guards.server.ts";
|
||||
import { command, query, requested } from "$app/server";
|
||||
import { IS_E2E_TEST_RUN } from "#lib/utils/e2e.ts";
|
||||
import { command, prerender, query, requested } from "$app/server";
|
||||
import * as Seasons from "../mmr/Seasons.ts";
|
||||
import * as LeaderboardRepository from "./LeaderboardRepository.server.ts";
|
||||
import { WEAPON_LEADERBOARD_MAX_SIZE } from "./leaderboards-constants.ts";
|
||||
import {
|
||||
WEAPON_LEADERBOARD_MAX_SIZE,
|
||||
XP_LEADERBOARD_TYPES,
|
||||
type XPLeaderboardType,
|
||||
} from "./leaderboards-constants.ts";
|
||||
import {
|
||||
cachedFullUserLeaderboard,
|
||||
cachedTeamLeaderboard,
|
||||
@@ -21,6 +26,7 @@ import {
|
||||
import {
|
||||
leaderboardsQuerySchema,
|
||||
teamLeaderboardEntrySchema,
|
||||
xpLeaderboardTypeSchema,
|
||||
} from "./leaderboards-schemas.ts";
|
||||
|
||||
export const getLeaderboards = query(
|
||||
@@ -60,19 +66,6 @@ export const getLeaderboards = query(
|
||||
const showOwnEntryPeek =
|
||||
fullUserLeaderboard && !isWeaponLeaderboard && user;
|
||||
|
||||
const xpLeaderboard =
|
||||
type === "XP-ALL"
|
||||
? await LeaderboardRepository.findAllXPLeaderboard()
|
||||
: type.startsWith("XP-MODE")
|
||||
? await LeaderboardRepository.findModeXPLeaderboard(
|
||||
type.split("-")[2] as RankedModeShort,
|
||||
)
|
||||
: type.startsWith("XP-WEAPON")
|
||||
? await LeaderboardRepository.findWeaponXPLeaderboard(
|
||||
Number(type.split("-")[2]) as MainWeaponId,
|
||||
)
|
||||
: null;
|
||||
|
||||
return {
|
||||
userLeaderboard: filteredLeaderboard ?? userLeaderboard,
|
||||
ownEntryPeek: showOwnEntryPeek
|
||||
@@ -83,12 +76,35 @@ export const getLeaderboards = query(
|
||||
})
|
||||
: null,
|
||||
teamLeaderboard,
|
||||
xpLeaderboard,
|
||||
season,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
// prerendered data would be baked from the build machine's database, so the
|
||||
// e2e build keeps this as a regular query against the per-worker test dbs
|
||||
export const getXPLeaderboard = IS_E2E_TEST_RUN
|
||||
? query(xpLeaderboardTypeSchema, findXPLeaderboard)
|
||||
: prerender(xpLeaderboardTypeSchema, findXPLeaderboard, {
|
||||
inputs: () => [...XP_LEADERBOARD_TYPES],
|
||||
});
|
||||
|
||||
function findXPLeaderboard(type: XPLeaderboardType) {
|
||||
if (type === "XP-ALL") {
|
||||
return LeaderboardRepository.findAllXPLeaderboard();
|
||||
}
|
||||
|
||||
if (type.startsWith("XP-MODE")) {
|
||||
return LeaderboardRepository.findModeXPLeaderboard(
|
||||
type.split("-")[2] as RankedModeShort,
|
||||
);
|
||||
}
|
||||
|
||||
return LeaderboardRepository.findWeaponXPLeaderboard(
|
||||
Number(type.split("-")[2]) as MainWeaponId,
|
||||
);
|
||||
}
|
||||
|
||||
export const skipTeam = command(
|
||||
teamLeaderboardEntrySchema,
|
||||
async ({ season, identifier }) => {
|
||||
|
||||
Reference in New Issue
Block a user