diff --git a/app/components/Image.tsx b/app/components/Image.tsx index 2b8c49f9c..b4e30c855 100644 --- a/app/components/Image.tsx +++ b/app/components/Image.tsx @@ -21,6 +21,7 @@ interface ImageProps { height?: number; size?: number; style?: React.CSSProperties; + containerStyle?: React.CSSProperties; testId?: string; onClick?: () => void; } @@ -36,10 +37,16 @@ export function Image({ style, testId, containerClassName, + containerStyle, onClick, }: ImageProps) { return ( - + { @@ -32,6 +33,7 @@ export function Placement({ textClassName, size = 20, textOnly = false, + showAsSuperscript = true, }: PlacementProps) { const { t } = useTranslation(undefined, {}); @@ -44,7 +46,7 @@ export function Placement({ fallbackLng: [], }); - const isSuperscript = ordinalSuffix.startsWith("^"); + const isSuperscript = showAsSuperscript && ordinalSuffix.startsWith("^"); const ordinalSuffixText = ordinalSuffix.replace(/^\^/, ""); const iconPath = textOnly ? null : getSpecialPlacementIconPath(placement); diff --git a/app/features/leaderboards/components/TopTenPlayer.tsx b/app/features/leaderboards/components/TopTenPlayer.tsx new file mode 100644 index 000000000..6f1dc756a --- /dev/null +++ b/app/features/leaderboards/components/TopTenPlayer.tsx @@ -0,0 +1,77 @@ +import { Flag } from "~/components/Flag"; +import { Image } from "~/components/Image"; +import { Placement } from "~/components/Placement"; +import { winnersImageUrl } from "~/utils/urls"; +import playerData from "../top-ten.json"; +import invariant from "tiny-invariant"; +import clsx from "clsx"; + +export function TopTenPlayer({ + power, + placement, + season, + small = false, +}: { + power?: number; + placement: number; + season: number; + small?: boolean; +}) { + const data = playerData[season]?.[placement - 1]; + invariant(data, `No data for season ${season} and placement ${placement}`); + const { name, countryCode, transforms } = data; + + const transformMultiplier = small ? 1 / 3 : 1; + + return ( +
+
+ +
+
+
3 ? { marginBlockEnd: "-4px" } : undefined} + > + {placement <= 3 ? ( + + ) : null}{" "} + {" "} + place +
+ {!small ? ( + <> +
+ {name} +
+
+ {power} +
+ + ) : null} +
+
+ ); +} diff --git a/app/features/leaderboards/core/leaderboards.server.ts b/app/features/leaderboards/core/leaderboards.server.ts index aa0e26419..3f76f00bf 100644 --- a/app/features/leaderboards/core/leaderboards.server.ts +++ b/app/features/leaderboards/core/leaderboards.server.ts @@ -4,12 +4,17 @@ import type { SeasonPopularUsersWeapon } from "../queries/seasonPopularUsersWeap import type { MainWeaponId } from "~/modules/in-game-lists"; import { weaponCategories } from "~/modules/in-game-lists"; import type { TeamSPLeaderboardItem } from "../queries/teamSPLeaderboard.server"; +import { seasonHasTopTen } from "../leaderboards-utils"; export function addTiers(entries: UserSPLeaderboardItem[], season: number) { const tiers = freshUserSkills(season); const encounteredTiers = new Set(); - return entries.map((entry) => { + return entries.map((entry, i) => { + if (i < 10 && seasonHasTopTen(season)) { + return { ...entry, tier: undefined }; + } + const tier = tiers.userSkills[entry.id].tier; const tierKey = `${tier.name}${tier.isPlus ? "+" : ""}`; const tierAlreadyEncountered = encounteredTiers.has(tierKey); diff --git a/app/features/leaderboards/leaderboards-utils.ts b/app/features/leaderboards/leaderboards-utils.ts new file mode 100644 index 000000000..981bc0676 --- /dev/null +++ b/app/features/leaderboards/leaderboards-utils.ts @@ -0,0 +1,37 @@ +import playerData from "./top-ten.json"; + +export function seasonHasTopTen(season: number) { + return !!playerData[season]; +} + +export function playerTopTenData({ + season, + userId, +}: { + season: number; + userId: number; +}) { + for (const player of playerData[season] ?? []) { + if (player.id === userId) { + return player; + } + } + + return null; +} + +export function playerTopTenPlacement({ + season, + userId, +}: { + season: number; + userId: number; +}) { + for (const [i, player] of (playerData[season] ?? []).entries()) { + if (player.id === userId) { + return i + 1; + } + } + + return null; +} diff --git a/app/features/leaderboards/routes/leaderboards.tsx b/app/features/leaderboards/routes/leaderboards.tsx index 375b99a9c..d69a26803 100644 --- a/app/features/leaderboards/routes/leaderboards.tsx +++ b/app/features/leaderboards/routes/leaderboards.tsx @@ -53,6 +53,8 @@ import { seasonPopularUsersWeapon } from "../queries/seasonPopularUsersWeapon.se import { cachified } from "cachified"; import { cache, ttl } from "~/utils/cache.server"; import { HALF_HOUR_IN_MS } from "~/constants"; +import { TopTenPlayer } from "../components/TopTenPlayer"; +import { seasonHasTopTen } from "../leaderboards-utils"; export const handle: SendouRouteHandle = { i18n: ["vods"], @@ -157,6 +159,10 @@ export default function LeaderboardsPage() { const [searchParams, setSearchParams] = useSearchParams(); const data = useLoaderData(); + const isAllUserLeaderboard = + !searchParams.get(TYPE_SEARCH_PARAM_KEY) || + searchParams.get(TYPE_SEARCH_PARAM_KEY) === "USER"; + return (