add hover transition to the footer
All checks were successful
Deploy SSR / deploy (push) Successful in 1m43s

This commit is contained in:
Lee
2024-09-14 18:40:39 +01:00
parent 21e39be003
commit f705218bfd
48 changed files with 527 additions and 288 deletions

View File

@ -1,4 +1,4 @@
import { BeatSaverMap as BSMap } from "@/common/service/types/beatsaver/beatsaver-map";
import { BeatSaverMapToken as BSMap } from "@/common/model/token/beatsaver/beat-saver-map-token";
import { Entity } from "dexie";
import Database from "../database";

View File

@ -1,4 +1,4 @@
import ScoreSaberPlayer from "@/common/service/types/scoresaber/scoresaber-player";
import ScoreSaberPlayerToken from "@/common/model/token/scoresaber/score-saber-player-token";
import { scoresaberService } from "@/common/service/impl/scoresaber";
import { ScoreSort } from "@/common/service/score-sort";
@ -8,7 +8,11 @@ export const leaderboards = {
search: true,
},
queries: {
lookupScores: (player: ScoreSaberPlayer, sort: ScoreSort, page: number) =>
lookupScores: (
player: ScoreSaberPlayerToken,
sort: ScoreSort,
page: number,
) =>
scoresaberService.lookupPlayerScores({
playerId: player.id,
sort: sort,

View File

@ -0,0 +1,57 @@
import Score from "@/common/model/score/score";
import { Modifier } from "@/common/model/score/modifier";
import ScoreSaberScoreToken from "@/common/model/token/scoresaber/score-saber-score-token";
export default class ScoreSaberScore extends Score {
constructor(
score: number,
weight: number | undefined,
rank: number,
worth: number,
modifiers: Modifier[],
misses: number,
badCuts: number,
fullCombo: boolean,
timestamp: Date,
) {
super(
score,
weight,
rank,
worth,
modifiers,
misses,
badCuts,
fullCombo,
timestamp,
);
}
/**
* Gets a {@link ScoreSaberScore} from a {@link ScoreSaberScoreToken}.
*
* @param token the token to convert
*/
public static fromToken(token: ScoreSaberScoreToken): ScoreSaberScore {
const modifiers: Modifier[] = token.modifiers.split(",").map((mod) => {
mod = mod.toUpperCase();
const modifier = Modifier[mod as keyof typeof Modifier];
if (modifier === undefined) {
throw new Error(`Unknown modifier: ${mod}`);
}
return modifier;
});
return new ScoreSaberScore(
token.baseScore,
token.weight,
token.rank,
token.pp,
modifiers,
token.missedNotes,
token.badCuts,
token.fullCombo,
new Date(token.timeSet),
);
}
}

View File

@ -0,0 +1,18 @@
/**
* The score modifiers.
*/
export enum Modifier {
DA = "Disappearing Arrows",
FS = "Faster Song",
SF = "Super Fast Song",
SS = "Slower Song",
GN = "Ghost Notes",
NA = "No Arrows",
NO = "No Obstacles",
SA = "Strict Angles",
SC = "Small Notes",
PM = "Pro Mode",
CS = "Fail on Saber Clash",
IF = "One Life",
BE = "Battery Energy",
}

View File

@ -0,0 +1,116 @@
import { Modifier } from "@/common/model/score/modifier";
export default class Score {
/**
* The base score for the score.
* @private
*/
private readonly _score: number;
/**
* The weight of the score, or undefined if not ranked.s
* @private
*/
private readonly _weight: number | undefined;
/**
* The rank for the score.
* @private
*/
private readonly _rank: number;
/**
* The worth of the score (this could be pp, ap, cr, etc.),
* or undefined if not ranked.
* @private
*/
private readonly _worth: number;
/**
* The modifiers used on the score.
* @private
*/
private readonly _modifiers: Modifier[];
/**
* The amount missed notes.
* @private
*/
private readonly _misses: number;
/**
* The amount of bad cuts.
* @private
*/
private readonly _badCuts: number;
/**
* Whether every note was hit.
* @private
*/
private readonly _fullCombo: boolean;
/**
* The time the score was set.
* @private
*/
private readonly _timestamp: Date;
constructor(
score: number,
weight: number | undefined,
rank: number,
worth: number,
modifiers: Modifier[],
misses: number,
badCuts: number,
fullCombo: boolean,
timestamp: Date,
) {
this._score = score;
this._weight = weight;
this._rank = rank;
this._worth = worth;
this._modifiers = modifiers;
this._misses = misses;
this._badCuts = badCuts;
this._fullCombo = fullCombo;
this._timestamp = timestamp;
}
get score(): number {
return this._score;
}
get weight(): number | undefined {
return this._weight;
}
get rank(): number {
return this._rank;
}
get worth(): number {
return this._worth;
}
get modifiers(): Modifier[] {
return this._modifiers;
}
get misses(): number {
return this._misses;
}
get badCuts(): number {
return this._badCuts;
}
get fullCombo(): boolean {
return this._fullCombo;
}
get timestamp(): Date {
return this._timestamp;
}
}

View File

@ -1,4 +1,4 @@
export default interface BeatSaverAccount {
export default interface BeatSaverAccountToken {
/**
* The id of the mapper
*/

View File

@ -1,4 +1,4 @@
export default interface BeatSaverMapMetadata {
export default interface BeatSaverMapMetadataToken {
/**
* The bpm of the song.
*/

View File

@ -1,4 +1,4 @@
export default interface BeatSaverMapStats {
export default interface BeatSaverMapStatsToken {
/**
* The amount of time the map has been played.
*/

View File

@ -0,0 +1,24 @@
import BeatSaverAccountToken from "./beat-saver-account-token";
import BeatSaverMapMetadataToken from "./beat-saver-map-metadata-token";
import BeatSaverMapStatsToken from "./beat-saver-map-stats-token";
export interface BeatSaverMapToken {
id: string;
name: string;
description: string;
uploader: BeatSaverAccountToken;
metadata: BeatSaverMapMetadataToken;
stats: BeatSaverMapStatsToken;
uploaded: string;
automapper: boolean;
ranked: boolean;
qualified: boolean;
// todo: versions
createdAt: string;
updatedAt: string;
lastPublishedAt: string;
tags: string[];
declaredAi: string;
blRanked: boolean;
blQualified: boolean;
}

View File

@ -1,4 +1,4 @@
export interface ScoreSaberBadge {
export interface ScoreSaberBadgeToken {
/**
* The description of the badge.
*/

View File

@ -1,4 +1,4 @@
export default interface ScoreSaberDifficulty {
export default interface ScoreSaberDifficultyToken {
leaderboardId: number;
difficulty: number;
gameMode: string;

View File

@ -1,4 +1,4 @@
export default interface ScoreSaberLeaderboardPlayerInfo {
export default interface ScoreSaberLeaderboardPlayerInfoToken {
id: string;
name: string;
profilePicture: string;

View File

@ -0,0 +1,14 @@
import ScoreSaberMetadataToken from "./score-saber-metadata-token";
import ScoreSaberScoreToken from "./score-saber-score-token";
export default interface ScoreSaberLeaderboardScoresPageToken {
/**
* The scores on this page.
*/
scores: ScoreSaberScoreToken[];
/**
* The metadata for the page.
*/
metadata: ScoreSaberMetadataToken;
}

View File

@ -1,13 +1,13 @@
import ScoreSaberDifficulty from "./scoresaber-difficulty";
import ScoreSaberDifficultyToken from "./score-saber-difficulty-token";
export default interface ScoreSaberLeaderboard {
export default interface ScoreSaberLeaderboardToken {
id: number;
songHash: string;
songName: string;
songSubName: string;
songAuthorName: string;
levelAuthorName: string;
difficulty: ScoreSaberDifficulty;
difficulty: ScoreSaberDifficultyToken;
maxScore: number;
createdDate: string;
rankedDate: string;
@ -22,5 +22,5 @@ export default interface ScoreSaberLeaderboard {
plays: boolean;
dailyPlays: boolean;
coverImage: string;
difficulties: ScoreSaberDifficulty[];
difficulties: ScoreSaberDifficultyToken[];
}

View File

@ -1,4 +1,4 @@
export default interface ScoreSaberMetadata {
export default interface ScoreSaberMetadataToken {
/**
* The total amount of returned results.
*/

View File

@ -0,0 +1,14 @@
import ScoreSaberLeaderboardToken from "./score-saber-leaderboard-token";
import ScoreSaberScoreToken from "./score-saber-score-token";
export default interface ScoreSaberPlayerScoreToken {
/**
* The score of the player score.
*/
score: ScoreSaberScoreToken;
/**
* The leaderboard the score was set on.
*/
leaderboard: ScoreSaberLeaderboardToken;
}

View File

@ -0,0 +1,14 @@
import ScoreSaberMetadataToken from "./score-saber-metadata-token";
import ScoreSaberPlayerScoreToken from "./score-saber-player-score-token";
export default interface ScoreSaberPlayerScoresPageToken {
/**
* The scores on this page.
*/
playerScores: ScoreSaberPlayerScoreToken[];
/**
* The metadata for the page.
*/
metadata: ScoreSaberMetadataToken;
}

View File

@ -0,0 +1,8 @@
import ScoreSaberPlayerToken from "./score-saber-player-token";
export interface ScoreSaberPlayerSearchToken {
/**
* The players that were found
*/
players: ScoreSaberPlayerToken[];
}

View File

@ -1,7 +1,7 @@
import { ScoreSaberBadge } from "./scoresaber-badge";
import ScoreSaberScoreStats from "./scoresaber-score-stats";
import { ScoreSaberBadgeToken } from "./score-saber-badge-token";
import ScoreSaberScoreStatsToken from "./score-saber-score-stats-token";
export default interface ScoreSaberPlayer {
export default interface ScoreSaberPlayerToken {
/**
* The ID of the player.
*/
@ -50,7 +50,7 @@ export default interface ScoreSaberPlayer {
/**
* The badges the player has.
*/
badges: ScoreSaberBadge[] | null;
badges: ScoreSaberBadgeToken[] | null;
/**
* The previous 50 days of rank history.
@ -60,7 +60,7 @@ export default interface ScoreSaberPlayer {
/**
* The score stats of the player.
*/
scoreStats: ScoreSaberScoreStats;
scoreStats: ScoreSaberScoreStatsToken;
/**
* The permissions of the player. (bitwise)

View File

@ -1,4 +1,4 @@
export default interface ScoreSaberScoreStats {
export default interface ScoreSaberScoreStatsToken {
/**
* The total amount of score accumulated over all scores.
*/

View File

@ -1,9 +1,9 @@
import ScoreSaberLeaderboard from "./scoresaber-leaderboard";
import ScoreSaberLeaderboardPlayerInfo from "./scoresaber-leaderboard-player-info";
import ScoreSaberLeaderboardToken from "./score-saber-leaderboard-token";
import ScoreSaberLeaderboardPlayerInfoToken from "./score-saber-leaderboard-player-info-token";
export default interface ScoreSaberScore {
export default interface ScoreSaberScoreToken {
id: string;
leaderboardPlayerInfo: ScoreSaberLeaderboardPlayerInfo;
leaderboardPlayerInfo: ScoreSaberLeaderboardPlayerInfoToken;
rank: number;
baseScore: number;
modifiedScore: number;
@ -21,5 +21,5 @@ export default interface ScoreSaberScore {
deviceHmd: string;
deviceControllerLeft: string;
deviceControllerRight: string;
leaderboard: ScoreSaberLeaderboard;
leaderboard: ScoreSaberLeaderboardToken;
}

View File

@ -1,7 +1,7 @@
import BeatSaverMap from "@/common/database/types/beatsaver-map";
import { db } from "../../database/database";
import Service from "../service";
import { BeatSaverMap as BSMap } from "../types/beatsaver/beatsaver-map";
import { BeatSaverMapToken as BSMap } from "@/common/model/token/beatsaver/beat-saver-map-token";
const API_BASE = "https://api.beatsaver.com";
const LOOKUP_MAP_BY_HASH_ENDPOINT = `${API_BASE}/maps/hash/:query`;

View File

@ -1,9 +1,9 @@
import Service from "../service";
import { ScoreSort } from "../score-sort";
import ScoreSaberLeaderboardScoresPage from "../types/scoresaber/scoresaber-leaderboard-scores-page";
import ScoreSaberPlayer from "../types/scoresaber/scoresaber-player";
import ScoreSaberPlayerScoresPage from "../types/scoresaber/scoresaber-player-scores-page";
import { ScoreSaberPlayerSearch } from "../types/scoresaber/scoresaber-player-search";
import ScoreSaberLeaderboardScoresPageToken from "@/common/model/token/scoresaber/score-saber-leaderboard-scores-page-token";
import ScoreSaberPlayerToken from "@/common/model/token/scoresaber/score-saber-player-token";
import ScoreSaberPlayerScoresPageToken from "@/common/model/token/scoresaber/score-saber-player-scores-page-token";
import { ScoreSaberPlayerSearchToken } from "@/common/model/token/scoresaber/score-saber-player-search-token";
const API_BASE = "https://scoresaber.com/api";
const SEARCH_PLAYERS_ENDPOINT = `${API_BASE}/players?search=:query`;
@ -26,10 +26,10 @@ class ScoreSaberService extends Service {
async searchPlayers(
query: string,
useProxy = true,
): Promise<ScoreSaberPlayerSearch | undefined> {
): Promise<ScoreSaberPlayerSearchToken | undefined> {
const before = performance.now();
this.log(`Searching for players matching "${query}"...`);
const results = await this.fetch<ScoreSaberPlayerSearch>(
const results = await this.fetch<ScoreSaberPlayerSearchToken>(
useProxy,
SEARCH_PLAYERS_ENDPOINT.replace(":query", query),
);
@ -56,10 +56,10 @@ class ScoreSaberService extends Service {
async lookupPlayer(
playerId: string,
useProxy = true,
): Promise<ScoreSaberPlayer | undefined> {
): Promise<ScoreSaberPlayerToken | undefined> {
const before = performance.now();
this.log(`Looking up player "${playerId}"...`);
const response = await this.fetch<ScoreSaberPlayer>(
const response = await this.fetch<ScoreSaberPlayerToken>(
useProxy,
LOOKUP_PLAYER_ENDPOINT.replace(":id", playerId),
);
@ -94,12 +94,12 @@ class ScoreSaberService extends Service {
page: number;
search?: string;
useProxy?: boolean;
}): Promise<ScoreSaberPlayerScoresPage | undefined> {
}): Promise<ScoreSaberPlayerScoresPageToken | undefined> {
const before = performance.now();
this.log(
`Looking up scores for player "${playerId}", sort "${sort}", page "${page}"${search ? `, search "${search}"` : ""}...`,
);
const response = await this.fetch<ScoreSaberPlayerScoresPage>(
const response = await this.fetch<ScoreSaberPlayerScoresPageToken>(
useProxy,
LOOKUP_PLAYER_SCORES_ENDPOINT.replace(":id", playerId)
.replace(":limit", 8 + "")
@ -128,12 +128,12 @@ class ScoreSaberService extends Service {
leaderboardId: string,
page: number,
useProxy = true,
): Promise<ScoreSaberLeaderboardScoresPage | undefined> {
): Promise<ScoreSaberLeaderboardScoresPageToken | undefined> {
const before = performance.now();
this.log(
`Looking up scores for leaderboard "${leaderboardId}", page "${page}"...`,
);
const response = await this.fetch<ScoreSaberLeaderboardScoresPage>(
const response = await this.fetch<ScoreSaberLeaderboardScoresPageToken>(
useProxy,
LOOKUP_LEADERBOARD_SCORES_ENDPOINT.replace(":id", leaderboardId).replace(
":page",

View File

@ -1,24 +0,0 @@
import BeatSaverAccount from "./beatsaver-account";
import BeatSaverMapMetadata from "./beatsaver-map-metadata";
import BeatSaverMapStats from "./beatsaver-map-stats";
export interface BeatSaverMap {
id: string;
name: string;
description: string;
uploader: BeatSaverAccount;
metadata: BeatSaverMapMetadata;
stats: BeatSaverMapStats;
uploaded: string;
automapper: boolean;
ranked: boolean;
qualified: boolean;
// todo: versions
createdAt: string;
updatedAt: string;
lastPublishedAt: string;
tags: string[];
declaredAi: string;
blRanked: boolean;
blQualified: boolean;
}

View File

@ -1,14 +0,0 @@
import ScoreSaberMetadata from "./scoresaber-metadata";
import ScoreSaberScore from "./scoresaber-score";
export default interface ScoreSaberLeaderboardScoresPage {
/**
* The scores on this page.
*/
scores: ScoreSaberScore[];
/**
* The metadata for the page.
*/
metadata: ScoreSaberMetadata;
}

View File

@ -1,14 +0,0 @@
import ScoreSaberLeaderboard from "./scoresaber-leaderboard";
import ScoreSaberScore from "./scoresaber-score";
export default interface ScoreSaberPlayerScore {
/**
* The score of the player score.
*/
score: ScoreSaberScore;
/**
* The leaderboard the score was set on.
*/
leaderboard: ScoreSaberLeaderboard;
}

View File

@ -1,14 +0,0 @@
import ScoreSaberMetadata from "./scoresaber-metadata";
import ScoreSaberPlayerScore from "./scoresaber-player-score";
export default interface ScoreSaberPlayerScoresPage {
/**
* The scores on this page.
*/
playerScores: ScoreSaberPlayerScore[];
/**
* The metadata for the page.
*/
metadata: ScoreSaberMetadata;
}

View File

@ -1,8 +0,0 @@
import ScoreSaberPlayer from "./scoresaber-player";
export interface ScoreSaberPlayerSearch {
/**
* The players that were found
*/
players: ScoreSaberPlayer[];
}

View File

@ -1,5 +1,5 @@
import * as Comlink from "comlink";
import { WorkerApi } from "@/common/workers/worker";
import { WorkerApi } from "@/common/worker/worker";
export const scoresaberReloadedWorker = () =>
Comlink.wrap<WorkerApi>(new Worker(new URL("./worker.ts", import.meta.url)));

View File

@ -32,7 +32,7 @@ export default function Footer() {
return (
<Link
key={index}
className="px-2 text-pp hover:brightness-75"
className="px-2 text-pp hover:brightness-75 transition-all transform-gpu"
href={item.link}
>
{item.name}

View File

@ -1,7 +1,7 @@
"use client";
import { scoresaberService } from "@/common/service/impl/scoresaber";
import ScoreSaberPlayer from "@/common/service/types/scoresaber/scoresaber-player";
import ScoreSaberPlayerToken from "@/common/model/token/scoresaber/score-saber-player-token";
import { formatNumberWithCommas } from "@/common/number-utils";
import { zodResolver } from "@hookform/resolvers/zod";
import Link from "next/link";
@ -25,7 +25,7 @@ export default function SearchPlayer() {
username: "",
},
});
const [results, setResults] = useState<ScoreSaberPlayer[] | undefined>();
const [results, setResults] = useState<ScoreSaberPlayerToken[] | undefined>();
const [loading, setLoading] = useState(false);
async function onSubmit({ username }: z.infer<typeof formSchema>) {

View File

@ -1,9 +1,9 @@
import ScoreSaberScore from "@/common/service/types/scoresaber/scoresaber-score";
import ScoreSaberScoreToken from "@/common/model/token/scoresaber/score-saber-score-token";
import Image from "next/image";
import Link from "next/link";
type Props = {
score: ScoreSaberScore;
score: ScoreSaberScoreToken;
};
export default function LeaderboardPlayer({ score }: Props) {

View File

@ -1,5 +1,5 @@
import ScoreSaberLeaderboard from "@/common/service/types/scoresaber/scoresaber-leaderboard";
import ScoreSaberScore from "@/common/service/types/scoresaber/scoresaber-score";
import ScoreSaberLeaderboardToken from "@/common/model/token/scoresaber/score-saber-leaderboard-token";
import ScoreSaberScoreToken from "@/common/model/token/scoresaber/score-saber-score-token";
import { formatNumberWithCommas } from "@/common/number-utils";
import StatValue from "@/components/stat-value";
import { XMarkIcon } from "@heroicons/react/24/solid";
@ -8,15 +8,15 @@ import clsx from "clsx";
type Badge = {
name: string;
create: (
score: ScoreSaberScore,
leaderboard: ScoreSaberLeaderboard,
score: ScoreSaberScoreToken,
leaderboard: ScoreSaberLeaderboardToken,
) => string | React.ReactNode | undefined;
};
const badges: Badge[] = [
{
name: "PP",
create: (score: ScoreSaberScore) => {
create: (score: ScoreSaberScoreToken) => {
const pp = score.pp;
if (pp === 0) {
return undefined;
@ -26,14 +26,17 @@ const badges: Badge[] = [
},
{
name: "Accuracy",
create: (score: ScoreSaberScore, leaderboard: ScoreSaberLeaderboard) => {
create: (
score: ScoreSaberScoreToken,
leaderboard: ScoreSaberLeaderboardToken,
) => {
const acc = (score.baseScore / leaderboard.maxScore) * 100;
return `${acc.toFixed(2)}%`;
},
},
{
name: "Full Combo",
create: (score: ScoreSaberScore) => {
create: (score: ScoreSaberScoreToken) => {
const fullCombo = score.missedNotes === 0;
return (
@ -55,8 +58,8 @@ const badges: Badge[] = [
];
type Props = {
score: ScoreSaberScore;
leaderboard: ScoreSaberLeaderboard;
score: ScoreSaberScoreToken;
leaderboard: ScoreSaberLeaderboardToken;
};
export default function LeaderboardScoreStats({ score, leaderboard }: Props) {

View File

@ -1,7 +1,7 @@
"use client";
import ScoreSaberLeaderboard from "@/common/service/types/scoresaber/scoresaber-leaderboard";
import ScoreSaberScore from "@/common/service/types/scoresaber/scoresaber-score";
import ScoreSaberLeaderboardToken from "@/common/model/token/scoresaber/score-saber-leaderboard-token";
import ScoreSaberScoreToken from "@/common/model/token/scoresaber/score-saber-score-token";
import LeaderboardPlayer from "./leaderboard-player";
import LeaderboardScoreStats from "./leaderboard-score-stats";
import ScoreRankInfo from "@/components/score/score-rank-info";
@ -10,12 +10,12 @@ type Props = {
/**
* The score to display.
*/
score: ScoreSaberScore;
score: ScoreSaberScoreToken;
/**
* The leaderboard to display.
*/
leaderboard: ScoreSaberLeaderboard;
leaderboard: ScoreSaberLeaderboardToken;
};
export default function LeaderboardScore({ score, leaderboard }: Props) {

View File

@ -1,8 +1,8 @@
"use client";
import { scoresaberService } from "@/common/service/impl/scoresaber";
import ScoreSaberLeaderboard from "@/common/service/types/scoresaber/scoresaber-leaderboard";
import ScoreSaberLeaderboardScoresPage from "@/common/service/types/scoresaber/scoresaber-leaderboard-scores-page";
import ScoreSaberLeaderboardToken from "@/common/model/token/scoresaber/score-saber-leaderboard-token";
import ScoreSaberLeaderboardScoresPageToken from "@/common/model/token/scoresaber/score-saber-leaderboard-scores-page-token";
import useWindowDimensions from "@/hooks/use-window-dimensions";
import { useQuery } from "@tanstack/react-query";
import { motion } from "framer-motion";
@ -12,7 +12,7 @@ import Pagination from "../input/pagination";
import LeaderboardScore from "./leaderboard-score";
type Props = {
leaderboard: ScoreSaberLeaderboard;
leaderboard: ScoreSaberLeaderboardToken;
};
export default function LeaderboardScores({ leaderboard }: Props) {
@ -20,7 +20,7 @@ export default function LeaderboardScores({ leaderboard }: Props) {
const [currentPage, setCurrentPage] = useState(1);
const [currentScores, setCurrentScores] = useState<
ScoreSaberLeaderboardScoresPage | undefined
ScoreSaberLeaderboardScoresPageToken | undefined
>();
const {

View File

@ -2,8 +2,8 @@
import { scoresaberService } from "@/common/service/impl/scoresaber";
import { ScoreSort } from "@/common/service/score-sort";
import ScoreSaberPlayer from "@/common/service/types/scoresaber/scoresaber-player";
import ScoreSaberPlayerScoresPage from "@/common/service/types/scoresaber/scoresaber-player-scores-page";
import ScoreSaberPlayerToken from "@/common/model/token/scoresaber/score-saber-player-token";
import ScoreSaberPlayerScoresPageToken from "@/common/model/token/scoresaber/score-saber-player-scores-page-token";
import { useQuery } from "@tanstack/react-query";
import PlayerHeader from "./player-header";
import PlayerRankChart from "./player-rank-chart";
@ -12,8 +12,8 @@ import PlayerScores from "./player-scores";
const REFRESH_INTERVAL = 5 * 60 * 1000; // 5 minutes
type Props = {
initialPlayerData: ScoreSaberPlayer;
initialScoreData?: ScoreSaberPlayerScoresPage;
initialPlayerData: ScoreSaberPlayerToken;
initialScoreData?: ScoreSaberPlayerScoresPageToken;
sort: ScoreSort;
page: number;
};

View File

@ -1,4 +1,4 @@
import ScoreSaberPlayer from "@/common/service/types/scoresaber/scoresaber-player";
import ScoreSaberPlayerToken from "@/common/model/token/scoresaber/score-saber-player-token";
import { formatNumberWithCommas } from "@/common/number-utils";
import { GlobeAmericasIcon } from "@heroicons/react/24/solid";
import Card from "../card";
@ -13,29 +13,29 @@ const playerData = [
icon: () => {
return <GlobeAmericasIcon className="h-5 w-5" />;
},
render: (player: ScoreSaberPlayer) => {
render: (player: ScoreSaberPlayerToken) => {
return <p>#{formatNumberWithCommas(player.rank)}</p>;
},
},
{
showWhenInactiveOrBanned: false,
icon: (player: ScoreSaberPlayer) => {
icon: (player: ScoreSaberPlayerToken) => {
return <CountryFlag country={player.country.toLowerCase()} size={15} />;
},
render: (player: ScoreSaberPlayer) => {
render: (player: ScoreSaberPlayerToken) => {
return <p>#{formatNumberWithCommas(player.countryRank)}</p>;
},
},
{
showWhenInactiveOrBanned: true,
render: (player: ScoreSaberPlayer) => {
render: (player: ScoreSaberPlayerToken) => {
return <p className="text-pp">{formatNumberWithCommas(player.pp)}pp</p>;
},
},
];
type Props = {
player: ScoreSaberPlayer;
player: ScoreSaberPlayerToken;
};
export default function PlayerHeader({ player }: Props) {

View File

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
"use client";
import ScoreSaberPlayer from "@/common/service/types/scoresaber/scoresaber-player";
import ScoreSaberPlayerToken from "@/common/model/token/scoresaber/score-saber-player-token";
import { formatNumberWithCommas } from "@/common/number-utils";
import {
CategoryScale,
@ -86,7 +86,7 @@ export const options: any = {
};
type Props = {
player: ScoreSaberPlayer;
player: ScoreSaberPlayerToken;
};
export default function PlayerRankChart({ player }: Props) {

View File

@ -11,13 +11,13 @@ import Pagination from "../input/pagination";
import { Button } from "../ui/button";
import { leaderboards } from "@/common/leaderboards";
import { ScoreSort } from "@/common/service/score-sort";
import ScoreSaberPlayer from "@/common/service/types/scoresaber/scoresaber-player";
import ScoreSaberPlayerScoresPage from "@/common/service/types/scoresaber/scoresaber-player-scores-page";
import ScoreSaberPlayerToken from "@/common/model/token/scoresaber/score-saber-player-token";
import ScoreSaberPlayerScoresPageToken from "@/common/model/token/scoresaber/score-saber-player-scores-page-token";
import Score from "@/components/score/score";
type Props = {
initialScoreData?: ScoreSaberPlayerScoresPage;
player: ScoreSaberPlayer;
initialScoreData?: ScoreSaberPlayerScoresPageToken;
player: ScoreSaberPlayerToken;
sort: ScoreSort;
page: number;
};
@ -88,7 +88,7 @@ export default function PlayerScores({
});
const [previousPage, setPreviousPage] = useState(page);
const [currentScores, setCurrentScores] = useState<
ScoreSaberPlayerScoresPage | undefined
ScoreSaberPlayerScoresPageToken | undefined
>(initialScoreData);
const {

View File

@ -1,57 +1,59 @@
import { formatNumberWithCommas } from "@/common/number-utils";
import StatValue from "@/components/stat-value";
import ScoreSaberPlayer from "@/common/service/types/scoresaber/scoresaber-player";
import ScoreSaberPlayerToken from "@/common/model/token/scoresaber/score-saber-player-token";
type Badge = {
name: string;
color?: string;
create: (player: ScoreSaberPlayer) => string | React.ReactNode | undefined;
create: (
player: ScoreSaberPlayerToken,
) => string | React.ReactNode | undefined;
};
const badges: Badge[] = [
{
name: "Ranked Play Count",
color: "bg-pp",
create: (player: ScoreSaberPlayer) => {
create: (player: ScoreSaberPlayerToken) => {
return formatNumberWithCommas(player.scoreStats.rankedPlayCount);
},
},
{
name: "Total Ranked Score",
color: "bg-pp",
create: (player: ScoreSaberPlayer) => {
create: (player: ScoreSaberPlayerToken) => {
return formatNumberWithCommas(player.scoreStats.totalRankedScore);
},
},
{
name: "Average Ranked Accuracy",
color: "bg-pp",
create: (player: ScoreSaberPlayer) => {
create: (player: ScoreSaberPlayerToken) => {
return player.scoreStats.averageRankedAccuracy.toFixed(2) + "%";
},
},
{
name: "Total Play Count",
create: (player: ScoreSaberPlayer) => {
create: (player: ScoreSaberPlayerToken) => {
return formatNumberWithCommas(player.scoreStats.totalPlayCount);
},
},
{
name: "Total Score",
create: (player: ScoreSaberPlayer) => {
create: (player: ScoreSaberPlayerToken) => {
return formatNumberWithCommas(player.scoreStats.totalScore);
},
},
{
name: "Total Replays Watched",
create: (player: ScoreSaberPlayer) => {
create: (player: ScoreSaberPlayerToken) => {
return formatNumberWithCommas(player.scoreStats.replaysWatched);
},
},
];
type Props = {
player: ScoreSaberPlayer;
player: ScoreSaberPlayerToken;
};
export default function PlayerStats({ player }: Props) {

View File

@ -1,7 +1,7 @@
"use client";
import { copyToClipboard } from "@/common/browser-utils";
import ScoreSaberPlayerScore from "@/common/service/types/scoresaber/scoresaber-player-score";
import ScoreSaberPlayerScoreToken from "@/common/model/token/scoresaber/score-saber-player-score-token";
import BeatSaverMap from "@/common/database/types/beatsaver-map";
import { songNameToYouTubeLink } from "@/common/youtube-utils";
import BeatSaverLogo from "@/components/logos/beatsaver-logo";
@ -12,7 +12,7 @@ import LeaderboardButton from "./leaderboard-button";
import ScoreButton from "./score-button";
type Props = {
playerScore: ScoreSaberPlayerScore;
playerScore: ScoreSaberPlayerScoreToken;
beatSaverMap?: BeatSaverMap;
isLeaderboardExpanded: boolean;
setIsLeaderboardExpanded: Dispatch<SetStateAction<boolean>>;

View File

@ -1,4 +1,4 @@
import ScoreSaberLeaderboard from "@/common/service/types/scoresaber/scoresaber-leaderboard";
import ScoreSaberLeaderboardToken from "@/common/model/token/scoresaber/score-saber-leaderboard-token";
import BeatSaverMap from "@/common/database/types/beatsaver-map";
import { getDifficultyFromScoreSaberDifficulty } from "@/common/scoresaber-utils";
import { songDifficultyToColor } from "@/common/song-utils";
@ -9,7 +9,7 @@ import clsx from "clsx";
import Image from "next/image";
type Props = {
leaderboard: ScoreSaberLeaderboard;
leaderboard: ScoreSaberLeaderboardToken;
beatSaverMap?: BeatSaverMap;
};

View File

@ -1,10 +1,10 @@
import ScoreSaberScore from "@/common/service/types/scoresaber/scoresaber-score";
import ScoreSaberScoreToken from "@/common/model/token/scoresaber/score-saber-score-token";
import { formatNumberWithCommas } from "@/common/number-utils";
import { timeAgo } from "@/common/time-utils";
import { GlobeAmericasIcon } from "@heroicons/react/24/solid";
type Props = {
score: ScoreSaberScore;
score: ScoreSaberScoreToken;
};
export default function ScoreRankInfo({ score }: Props) {

View File

@ -1,5 +1,5 @@
import ScoreSaberLeaderboard from "@/common/service/types/scoresaber/scoresaber-leaderboard";
import ScoreSaberScore from "@/common/service/types/scoresaber/scoresaber-score";
import ScoreSaberLeaderboardToken from "@/common/model/token/scoresaber/score-saber-leaderboard-token";
import ScoreSaberScoreToken from "@/common/model/token/scoresaber/score-saber-score-token";
import { formatNumberWithCommas } from "@/common/number-utils";
import StatValue from "@/components/stat-value";
import { XMarkIcon } from "@heroicons/react/24/solid";
@ -9,12 +9,12 @@ import { accuracyToColor } from "@/common/song-utils";
type Badge = {
name: string;
color?: (
score: ScoreSaberScore,
leaderboard: ScoreSaberLeaderboard,
score: ScoreSaberScoreToken,
leaderboard: ScoreSaberLeaderboardToken,
) => string | undefined;
create: (
score: ScoreSaberScore,
leaderboard: ScoreSaberLeaderboard,
score: ScoreSaberScoreToken,
leaderboard: ScoreSaberLeaderboardToken,
) => string | React.ReactNode | undefined;
};
@ -24,7 +24,7 @@ const badges: Badge[] = [
color: () => {
return "bg-pp";
},
create: (score: ScoreSaberScore) => {
create: (score: ScoreSaberScoreToken) => {
const pp = score.pp;
if (pp === 0) {
return undefined;
@ -34,18 +34,24 @@ const badges: Badge[] = [
},
{
name: "Accuracy",
color: (score: ScoreSaberScore, leaderboard: ScoreSaberLeaderboard) => {
color: (
score: ScoreSaberScoreToken,
leaderboard: ScoreSaberLeaderboardToken,
) => {
const acc = (score.baseScore / leaderboard.maxScore) * 100;
return accuracyToColor(acc);
},
create: (score: ScoreSaberScore, leaderboard: ScoreSaberLeaderboard) => {
create: (
score: ScoreSaberScoreToken,
leaderboard: ScoreSaberLeaderboardToken,
) => {
const acc = (score.baseScore / leaderboard.maxScore) * 100;
return `${acc.toFixed(2)}%`;
},
},
{
name: "Score",
create: (score: ScoreSaberScore) => {
create: (score: ScoreSaberScoreToken) => {
return `${formatNumberWithCommas(score.baseScore)}`;
},
},
@ -59,7 +65,7 @@ const badges: Badge[] = [
},
{
name: "Full Combo",
create: (score: ScoreSaberScore) => {
create: (score: ScoreSaberScoreToken) => {
const fullCombo = score.missedNotes === 0;
return (
@ -81,8 +87,8 @@ const badges: Badge[] = [
];
type Props = {
score: ScoreSaberScore;
leaderboard: ScoreSaberLeaderboard;
score: ScoreSaberScoreToken;
leaderboard: ScoreSaberLeaderboardToken;
};
export default function ScoreStats({ score, leaderboard }: Props) {

View File

@ -1,7 +1,7 @@
"use client";
import { beatsaverService } from "@/common/service/impl/beatsaver";
import ScoreSaberPlayerScore from "@/common/service/types/scoresaber/scoresaber-player-score";
import ScoreSaberPlayerScoreToken from "@/common/model/token/scoresaber/score-saber-player-score-token";
import BeatSaverMap from "@/common/database/types/beatsaver-map";
import LeaderboardScores from "@/components/leaderboard/leaderboard-scores";
import { useCallback, useEffect, useState } from "react";
@ -14,7 +14,7 @@ type Props = {
/**
* The score to display.
*/
playerScore: ScoreSaberPlayerScore;
playerScore: ScoreSaberPlayerScoreToken;
};
export default function Score({ playerScore }: Props) {