diff --git a/src/common/song-utils.ts b/src/common/song-utils.ts index e8741ba..0603387 100644 --- a/src/common/song-utils.ts +++ b/src/common/song-utils.ts @@ -1,11 +1,19 @@ -const diffColors: Record = { - easy: "#3CB371", - normal: "#59b0f4", - hard: "#FF6347", - expert: "#bf2a42", - expertplus: "#8f48db", +type Difficulty = { + name: DifficultyName; + gamemode?: string; + color: string; }; +type DifficultyName = "Easy" | "Normal" | "Hard" | "Expert" | "Expert+"; + +const difficulties: Difficulty[] = [ + { name: "Easy", color: "#59b0f4" }, + { name: "Normal", color: "#59b0f4" }, + { name: "Hard", color: "#FF6347" }, + { name: "Expert", color: "#bf2a42" }, + { name: "Expert+", color: "#8f48db" }, +]; + export type ScoreBadge = { name: string; min: number | null; @@ -14,11 +22,11 @@ export type ScoreBadge = { }; const scoreBadges: ScoreBadge[] = [ - { name: "SS+", min: 95, max: null, color: diffColors.expertplus }, - { name: "SS", min: 90, max: 95, color: diffColors.expert }, - { name: "S+", min: 85, max: 90, color: diffColors.hard }, - { name: "S", min: 80, max: 85, color: diffColors.normal }, - { name: "A", min: 70, max: 80, color: diffColors.easy }, + { name: "SS+", min: 95, max: null, color: getDifficulty("Expert+")!.color }, + { name: "SS", min: 90, max: 95, color: getDifficulty("Expert")!.color }, + { name: "S+", min: 85, max: 90, color: getDifficulty("Hard")!.color }, + { name: "S", min: 80, max: 85, color: getDifficulty("Normal")!.color }, + { name: "A", min: 70, max: 80, color: getDifficulty("Easy")!.color }, { name: "-", min: null, max: 70, color: "hsl(var(--accent))" }, ]; @@ -49,6 +57,39 @@ export function getScoreBadgeFromAccuracy(acc: number): ScoreBadge { return scoreBadges[scoreBadges.length - 1]; } +/** + * Parses a raw difficulty into a {@link Difficulty} + * Example: _Easy_SoloStandard -> { name: "Easy", type: "Standard", color: "#59b0f4" } + * + * @param rawDifficulty the raw difficulty to parse + * @return the parsed difficulty + */ +export function getDifficultyFromRawDifficulty(rawDifficulty: string): Difficulty { + const [name, ...type] = rawDifficulty + .replace("Plus", "+") // Replaces Plus with + so we can match it to our difficulty names + .replace("Solo", "") // Removes "Solo" + .replace(/^_+|_+$/g, "") // Removes leading and trailing underscores + .split("_"); + const difficulty = difficulties.find(d => d.name === name); + if (!difficulty) { + throw new Error(`Unknown difficulty: ${rawDifficulty}`); + } + return { + ...difficulty, + gamemode: type.join("_"), + }; +} + +/** + * Gets a {@link Difficulty} from its name + * + * @param diff the name of the difficulty + * @returns the difficulty + */ +export function getDifficulty(diff: DifficultyName) { + return difficulties.find(d => d.name === diff); +} + /** * Turns the difficulty of a song into a color * @@ -56,6 +97,5 @@ export function getScoreBadgeFromAccuracy(acc: number): ScoreBadge { * @returns the color for the difficulty */ export function songDifficultyToColor(diff: string) { - diff = diff.replace("+", "Plus"); - return diffColors[diff.toLowerCase() as keyof typeof diffColors]; + return getDifficultyFromRawDifficulty(diff).color; } diff --git a/src/components/leaderboard/leaderboard-info.tsx b/src/components/leaderboard/leaderboard-info.tsx index 3c27f37..70c188a 100644 --- a/src/components/leaderboard/leaderboard-info.tsx +++ b/src/components/leaderboard/leaderboard-info.tsx @@ -48,7 +48,7 @@ export function LeaderboardInfo({ leaderboard, beatSaverMap }: LeaderboardInfoPr {`${leaderboard.songName} diff --git a/src/components/leaderboard/leaderboard-scores.tsx b/src/components/leaderboard/leaderboard-scores.tsx index 444d74d..7dfdab7 100644 --- a/src/components/leaderboard/leaderboard-scores.tsx +++ b/src/components/leaderboard/leaderboard-scores.tsx @@ -13,8 +13,8 @@ import LeaderboardScore from "./leaderboard-score"; import { scoreAnimation } from "@/components/score/score-animation"; import ScoreSaberPlayer from "@/common/model/player/impl/scoresaber-player"; import { Button } from "@/components/ui/button"; -import { getDifficultyFromScoreSaberDifficulty } from "@/common/scoresaber-utils"; import { clsx } from "clsx"; +import { getDifficultyFromRawDifficulty } from "@/common/song-utils"; type LeaderboardScoresProps = { /** @@ -130,14 +130,14 @@ export default function LeaderboardScores({ * scores when new scores are loaded. */ useEffect(() => { - if (topOfScoresRef.current) { + if (topOfScoresRef.current && shouldFetch) { const topOfScoresPosition = topOfScoresRef.current.getBoundingClientRect().top + window.scrollY; window.scrollTo({ top: topOfScoresPosition - 75, // Navbar height (plus some padding) behavior: "smooth", }); } - }, [currentPage, topOfScoresRef]); + }, [currentPage, topOfScoresRef, shouldFetch]); if (currentScores === undefined) { return undefined; @@ -153,18 +153,24 @@ export default function LeaderboardScores({ {currentScores.scores.length === 0 &&

No scores found. Invalid Page?

} -
+
{showDifficulties && - leaderboard.difficulties.map(({ difficulty, leaderboardId }) => { + leaderboard.difficulties.map(({ difficultyRaw, leaderboardId }) => { + const difficulty = getDifficultyFromRawDifficulty(difficultyRaw); + // todo: add support for other gamemodes? + if (difficulty.gamemode !== "Standard") { + return null; + } + return ( ); })}