implement scoresaber score tracking (for previous scores)
This commit is contained in:
parent
3c4406c4b7
commit
d42c888e82
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
@ -14,6 +14,7 @@
|
|||||||
"@elysiajs/swagger": "^1.1.3",
|
"@elysiajs/swagger": "^1.1.3",
|
||||||
"@ssr/common": "workspace:common",
|
"@ssr/common": "workspace:common",
|
||||||
"@tqman/nice-logger": "^1.0.1",
|
"@tqman/nice-logger": "^1.0.1",
|
||||||
|
"@typegoose/auto-increment": "^4.7.0",
|
||||||
"@typegoose/typegoose": "^12.8.0",
|
"@typegoose/typegoose": "^12.8.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.9.0",
|
"@typescript-eslint/eslint-plugin": "^8.9.0",
|
||||||
"@typescript-eslint/parser": "^8.9.0",
|
"@typescript-eslint/parser": "^8.9.0",
|
||||||
|
@ -8,7 +8,6 @@ import { etag } from "@bogeychan/elysia-etag";
|
|||||||
import AppController from "./controller/app.controller";
|
import AppController from "./controller/app.controller";
|
||||||
import * as dotenv from "@dotenvx/dotenvx";
|
import * as dotenv from "@dotenvx/dotenvx";
|
||||||
import mongoose from "mongoose";
|
import mongoose from "mongoose";
|
||||||
import { setLogLevel } from "@typegoose/typegoose";
|
|
||||||
import PlayerController from "./controller/player.controller";
|
import PlayerController from "./controller/player.controller";
|
||||||
import { PlayerService } from "./service/player.service";
|
import { PlayerService } from "./service/player.service";
|
||||||
import { cron } from "@elysiajs/cron";
|
import { cron } from "@elysiajs/cron";
|
||||||
@ -33,9 +32,10 @@ dotenv.config({
|
|||||||
override: true,
|
override: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Connect to Mongo
|
||||||
await mongoose.connect(Config.mongoUri!); // Connect to MongoDB
|
await mongoose.connect(Config.mongoUri!); // Connect to MongoDB
|
||||||
setLogLevel("DEBUG");
|
|
||||||
|
|
||||||
|
// Connect to websockets
|
||||||
connectScoresaberWebsocket({
|
connectScoresaberWebsocket({
|
||||||
onScore: async score => {
|
onScore: async score => {
|
||||||
await ScoreService.trackScoreSaberScore(score);
|
await ScoreService.trackScoreSaberScore(score);
|
||||||
|
@ -7,7 +7,6 @@ import BeatSaverService from "./beatsaver.service";
|
|||||||
import ScoreSaberLeaderboard, {
|
import ScoreSaberLeaderboard, {
|
||||||
getScoreSaberLeaderboardFromToken,
|
getScoreSaberLeaderboardFromToken,
|
||||||
} from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
} from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
||||||
import { getScoreSaberScoreFromToken } from "@ssr/common/score/impl/scoresaber-score";
|
|
||||||
import { scoresaberService } from "@ssr/common/service/impl/scoresaber";
|
import { scoresaberService } from "@ssr/common/service/impl/scoresaber";
|
||||||
import { ScoreSort } from "@ssr/common/score/score-sort";
|
import { ScoreSort } from "@ssr/common/score/score-sort";
|
||||||
import { Leaderboards } from "@ssr/common/leaderboard";
|
import { Leaderboards } from "@ssr/common/leaderboard";
|
||||||
@ -16,7 +15,6 @@ import LeaderboardService from "./leaderboard.service";
|
|||||||
import { BeatSaverMap } from "@ssr/common/model/beatsaver/map";
|
import { BeatSaverMap } from "@ssr/common/model/beatsaver/map";
|
||||||
import { PlayerScore } from "@ssr/common/score/player-score";
|
import { PlayerScore } from "@ssr/common/score/player-score";
|
||||||
import LeaderboardScoresResponse from "@ssr/common/response/leaderboard-scores-response";
|
import LeaderboardScoresResponse from "@ssr/common/response/leaderboard-scores-response";
|
||||||
import Score from "@ssr/common/score/score";
|
|
||||||
import PlayerScoresResponse from "@ssr/common/response/player-scores-response";
|
import PlayerScoresResponse from "@ssr/common/response/player-scores-response";
|
||||||
import { DiscordChannels, logToChannel } from "../bot/bot";
|
import { DiscordChannels, logToChannel } from "../bot/bot";
|
||||||
import { EmbedBuilder } from "discord.js";
|
import { EmbedBuilder } from "discord.js";
|
||||||
@ -30,6 +28,8 @@ import {
|
|||||||
AdditionalScoreDataModel,
|
AdditionalScoreDataModel,
|
||||||
} from "@ssr/common/model/additional-score-data/additional-score-data";
|
} from "@ssr/common/model/additional-score-data/additional-score-data";
|
||||||
import { BeatLeaderScoreImprovementToken } from "@ssr/common/types/token/beatleader/score/score-improvement";
|
import { BeatLeaderScoreImprovementToken } from "@ssr/common/types/token/beatleader/score/score-improvement";
|
||||||
|
import { getScoreSaberScoreFromToken, ScoreSaberScoreModel } from "@ssr/common/model/score/impl/scoresaber-score";
|
||||||
|
import { ScoreType } from "@ssr/common/model/score/score";
|
||||||
|
|
||||||
const playerScoresCache = new SSRCache({
|
const playerScoresCache = new SSRCache({
|
||||||
ttl: 1000 * 60, // 1 minute
|
ttl: 1000 * 60, // 1 minute
|
||||||
@ -52,7 +52,7 @@ export class ScoreService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { score: scoreToken, leaderboard: leaderboardToken } = playerScore;
|
const { score: scoreToken, leaderboard: leaderboardToken } = playerScore;
|
||||||
const score = getScoreSaberScoreFromToken(scoreToken, leaderboardToken);
|
const score = getScoreSaberScoreFromToken(scoreToken, leaderboardToken, scoreToken.leaderboardPlayerInfo.id);
|
||||||
const leaderboard = getScoreSaberLeaderboardFromToken(leaderboardToken);
|
const leaderboard = getScoreSaberLeaderboardFromToken(leaderboardToken);
|
||||||
const playerInfo = score.playerInfo;
|
const playerInfo = score.playerInfo;
|
||||||
|
|
||||||
@ -153,8 +153,13 @@ export class ScoreService {
|
|||||||
player.markModified("statisticHistory");
|
player.markModified("statisticHistory");
|
||||||
await player.save();
|
await player.save();
|
||||||
|
|
||||||
|
const scoreToken = getScoreSaberScoreFromToken(score, leaderboard, playerId);
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-expect-error
|
||||||
|
delete scoreToken.playerInfo;
|
||||||
|
await ScoreSaberScoreModel.create(scoreToken);
|
||||||
console.log(
|
console.log(
|
||||||
`Updated scores set statistic for "${playerName}"(${playerId}), scores today: ${scores.rankedScores} ranked, ${scores.unrankedScores} unranked`
|
`Tracked score and updated scores set statistic for "${playerName}"(${playerId}), scores today: ${scores.rankedScores} ranked, ${scores.unrankedScores} unranked`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,7 +273,7 @@ export class ScoreService {
|
|||||||
* Gets scores for a player.
|
* Gets scores for a player.
|
||||||
*
|
*
|
||||||
* @param leaderboardName the leaderboard to get the scores from
|
* @param leaderboardName the leaderboard to get the scores from
|
||||||
* @param id the players id
|
* @param playerId the players id
|
||||||
* @param page the page to get
|
* @param page the page to get
|
||||||
* @param sort the sort to use
|
* @param sort the sort to use
|
||||||
* @param search the search to use
|
* @param search the search to use
|
||||||
@ -276,14 +281,14 @@ export class ScoreService {
|
|||||||
*/
|
*/
|
||||||
public static async getPlayerScores(
|
public static async getPlayerScores(
|
||||||
leaderboardName: Leaderboards,
|
leaderboardName: Leaderboards,
|
||||||
id: string,
|
playerId: string,
|
||||||
page: number,
|
page: number,
|
||||||
sort: string,
|
sort: string,
|
||||||
search?: string
|
search?: string
|
||||||
): Promise<PlayerScoresResponse<unknown, unknown> | undefined> {
|
): Promise<PlayerScoresResponse<unknown, unknown> | undefined> {
|
||||||
return fetchWithCache(
|
return fetchWithCache(
|
||||||
playerScoresCache,
|
playerScoresCache,
|
||||||
`player-scores-${leaderboardName}-${id}-${page}-${sort}-${search}`,
|
`player-scores-${leaderboardName}-${playerId}-${page}-${sort}-${search}`,
|
||||||
async () => {
|
async () => {
|
||||||
const scores: PlayerScore<unknown, unknown>[] | undefined = [];
|
const scores: PlayerScore<unknown, unknown>[] | undefined = [];
|
||||||
let metadata: Metadata = new Metadata(0, 0, 0, 0); // Default values
|
let metadata: Metadata = new Metadata(0, 0, 0, 0); // Default values
|
||||||
@ -291,7 +296,7 @@ export class ScoreService {
|
|||||||
switch (leaderboardName) {
|
switch (leaderboardName) {
|
||||||
case "scoresaber": {
|
case "scoresaber": {
|
||||||
const leaderboardScores = await scoresaberService.lookupPlayerScores({
|
const leaderboardScores = await scoresaberService.lookupPlayerScores({
|
||||||
playerId: id,
|
playerId: playerId,
|
||||||
page: page,
|
page: page,
|
||||||
sort: sort as ScoreSort,
|
sort: sort as ScoreSort,
|
||||||
search: search,
|
search: search,
|
||||||
@ -308,17 +313,18 @@ export class ScoreService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
for (const token of leaderboardScores.playerScores) {
|
for (const token of leaderboardScores.playerScores) {
|
||||||
const score = getScoreSaberScoreFromToken(token.score, token.leaderboard);
|
const score = getScoreSaberScoreFromToken(token.score, token.leaderboard, playerId);
|
||||||
if (score == undefined) {
|
if (score == undefined) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const leaderboard = getScoreSaberLeaderboardFromToken(token.leaderboard);
|
const leaderboard = getScoreSaberLeaderboardFromToken(token.leaderboard);
|
||||||
if (leaderboard == undefined) {
|
if (leaderboard == undefined) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const additionalData = await this.getAdditionalScoreData(
|
const additionalData = await this.getAdditionalScoreData(
|
||||||
id,
|
playerId,
|
||||||
leaderboard.songHash,
|
leaderboard.songHash,
|
||||||
`${leaderboard.difficulty.difficulty}-${leaderboard.difficulty.characteristic}`,
|
`${leaderboard.difficulty.difficulty}-${leaderboard.difficulty.characteristic}`,
|
||||||
score.score
|
score.score
|
||||||
@ -352,17 +358,20 @@ export class ScoreService {
|
|||||||
* Gets scores for a leaderboard.
|
* Gets scores for a leaderboard.
|
||||||
*
|
*
|
||||||
* @param leaderboardName the leaderboard to get the scores from
|
* @param leaderboardName the leaderboard to get the scores from
|
||||||
* @param id the leaderboard id
|
* @param leaderboardId the leaderboard id
|
||||||
* @param page the page to get
|
* @param page the page to get
|
||||||
* @returns the scores
|
* @returns the scores
|
||||||
*/
|
*/
|
||||||
public static async getLeaderboardScores(
|
public static async getLeaderboardScores(
|
||||||
leaderboardName: Leaderboards,
|
leaderboardName: Leaderboards,
|
||||||
id: string,
|
leaderboardId: string,
|
||||||
page: number
|
page: number
|
||||||
): Promise<LeaderboardScoresResponse<unknown, unknown> | undefined> {
|
): Promise<LeaderboardScoresResponse<unknown, unknown> | undefined> {
|
||||||
return fetchWithCache(leaderboardScoresCache, `leaderboard-scores-${leaderboardName}-${id}-${page}`, async () => {
|
return fetchWithCache(
|
||||||
const scores: Score[] = [];
|
leaderboardScoresCache,
|
||||||
|
`leaderboard-scores-${leaderboardName}-${leaderboardId}-${page}`,
|
||||||
|
async () => {
|
||||||
|
const scores: ScoreType[] = [];
|
||||||
let leaderboard: Leaderboard | undefined;
|
let leaderboard: Leaderboard | undefined;
|
||||||
let beatSaverMap: BeatSaverMap | undefined;
|
let beatSaverMap: BeatSaverMap | undefined;
|
||||||
let metadata: Metadata = new Metadata(0, 0, 0, 0); // Default values
|
let metadata: Metadata = new Metadata(0, 0, 0, 0); // Default values
|
||||||
@ -371,7 +380,7 @@ export class ScoreService {
|
|||||||
case "scoresaber": {
|
case "scoresaber": {
|
||||||
const leaderboardResponse = await LeaderboardService.getLeaderboard<ScoreSaberLeaderboard>(
|
const leaderboardResponse = await LeaderboardService.getLeaderboard<ScoreSaberLeaderboard>(
|
||||||
leaderboardName,
|
leaderboardName,
|
||||||
id
|
leaderboardId
|
||||||
);
|
);
|
||||||
if (leaderboardResponse == undefined) {
|
if (leaderboardResponse == undefined) {
|
||||||
throw new NotFoundError(`Leaderboard "${leaderboardName}" not found`);
|
throw new NotFoundError(`Leaderboard "${leaderboardName}" not found`);
|
||||||
@ -379,13 +388,17 @@ export class ScoreService {
|
|||||||
leaderboard = leaderboardResponse.leaderboard;
|
leaderboard = leaderboardResponse.leaderboard;
|
||||||
beatSaverMap = leaderboardResponse.beatsaver;
|
beatSaverMap = leaderboardResponse.beatsaver;
|
||||||
|
|
||||||
const leaderboardScores = await scoresaberService.lookupLeaderboardScores(id, page);
|
const leaderboardScores = await scoresaberService.lookupLeaderboardScores(leaderboardId, page);
|
||||||
if (leaderboardScores == undefined) {
|
if (leaderboardScores == undefined) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const token of leaderboardScores.scores) {
|
for (const token of leaderboardScores.scores) {
|
||||||
const score = getScoreSaberScoreFromToken(token, leaderboardResponse.leaderboard);
|
const score = getScoreSaberScoreFromToken(
|
||||||
|
token,
|
||||||
|
leaderboardResponse.leaderboard,
|
||||||
|
token.leaderboardPlayerInfo.id
|
||||||
|
);
|
||||||
if (score == undefined) {
|
if (score == undefined) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -411,6 +424,7 @@ export class ScoreService {
|
|||||||
beatSaver: beatSaverMap,
|
beatSaver: beatSaverMap,
|
||||||
metadata: metadata,
|
metadata: metadata,
|
||||||
};
|
};
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@typegoose/auto-increment": "^4.7.0",
|
||||||
"@typegoose/typegoose": "^12.8.0",
|
"@typegoose/typegoose": "^12.8.0",
|
||||||
"ky": "^1.7.2",
|
"ky": "^1.7.2",
|
||||||
"ws": "^8.18.0"
|
"ws": "^8.18.0"
|
||||||
|
@ -4,7 +4,7 @@ import { HandAccuracy } from "./hand-accuracy";
|
|||||||
import { Misses } from "./misses";
|
import { Misses } from "./misses";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The model for a BeatSaver map.
|
* The model for additional score data.
|
||||||
*/
|
*/
|
||||||
@modelOptions({
|
@modelOptions({
|
||||||
options: { allowMixed: Severity.ALLOW },
|
options: { allowMixed: Severity.ALLOW },
|
||||||
|
123
projects/common/src/model/score/impl/scoresaber-score.ts
Normal file
123
projects/common/src/model/score/impl/scoresaber-score.ts
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
import { getModelForClass, modelOptions, plugin, Prop, ReturnModelType, Severity } from "@typegoose/typegoose";
|
||||||
|
import Score from "../score";
|
||||||
|
import { Modifier } from "../../../score/modifier";
|
||||||
|
import ScoreSaberScoreToken from "../../../types/token/scoresaber/score-saber-score-token";
|
||||||
|
import ScoreSaberLeaderboardToken from "../../../types/token/scoresaber/score-saber-leaderboard-token";
|
||||||
|
import ScoreSaberLeaderboard from "../../../leaderboard/impl/scoresaber-leaderboard";
|
||||||
|
import { type ScoreSaberLeaderboardPlayerInfoToken } from "../../../types/token/scoresaber/score-saber-leaderboard-player-info-token";
|
||||||
|
import { Document } from "mongoose";
|
||||||
|
import { AutoIncrementID } from "@typegoose/auto-increment";
|
||||||
|
|
||||||
|
@modelOptions({
|
||||||
|
options: { allowMixed: Severity.ALLOW },
|
||||||
|
schemaOptions: {
|
||||||
|
collection: "scoresaber-scores",
|
||||||
|
toObject: {
|
||||||
|
virtuals: true,
|
||||||
|
transform: function (_, ret) {
|
||||||
|
ret.id = ret._id;
|
||||||
|
delete ret._id;
|
||||||
|
delete ret.__v;
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
@plugin(AutoIncrementID, {
|
||||||
|
field: "_id",
|
||||||
|
startAt: 1,
|
||||||
|
trackerModelName: "scores",
|
||||||
|
trackerCollection: "increments",
|
||||||
|
overwriteModelName: "scoresaber-scores",
|
||||||
|
})
|
||||||
|
export class ScoreSaberScoreInternal extends Score {
|
||||||
|
/**
|
||||||
|
* The score's id.
|
||||||
|
*/
|
||||||
|
@Prop({ required: true, index: true })
|
||||||
|
public readonly scoreId!: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The leaderboard the score was set on.
|
||||||
|
*/
|
||||||
|
@Prop({ required: true, index: true })
|
||||||
|
public readonly leaderboardId!: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The amount of pp for the score.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
@Prop({ required: true })
|
||||||
|
public readonly pp!: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The weight of the score, or undefined if not ranked.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
@Prop()
|
||||||
|
public readonly weight?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The max combo of the score.
|
||||||
|
*/
|
||||||
|
@Prop({ required: true })
|
||||||
|
public readonly maxCombo!: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScoreSaberScorePublic extends ScoreSaberScoreInternal {
|
||||||
|
/**
|
||||||
|
* The player who set the score.
|
||||||
|
*/
|
||||||
|
public playerInfo!: ScoreSaberLeaderboardPlayerInfoToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ScoreSaberScore = InstanceType<typeof ScoreSaberScorePublic>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a {@link ScoreSaberScore} from a {@link ScoreSaberScoreToken}.
|
||||||
|
*
|
||||||
|
* @param token the token to convert
|
||||||
|
* @param playerId the id of the player who set the score
|
||||||
|
* @param leaderboard the leaderboard the score was set on
|
||||||
|
*/
|
||||||
|
export function getScoreSaberScoreFromToken(
|
||||||
|
token: ScoreSaberScoreToken,
|
||||||
|
leaderboard: ScoreSaberLeaderboardToken | ScoreSaberLeaderboard,
|
||||||
|
playerId?: string
|
||||||
|
): ScoreSaberScore {
|
||||||
|
const modifiers: Modifier[] =
|
||||||
|
token.modifiers == undefined || token.modifiers === ""
|
||||||
|
? []
|
||||||
|
: 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 {
|
||||||
|
leaderboard: "scoresaber",
|
||||||
|
playerId: playerId || token.leaderboardPlayerInfo.id,
|
||||||
|
leaderboardId: leaderboard.id,
|
||||||
|
score: token.baseScore,
|
||||||
|
accuracy: (token.baseScore / leaderboard.maxScore) * 100,
|
||||||
|
rank: token.rank,
|
||||||
|
modifiers: modifiers,
|
||||||
|
misses: token.missedNotes + token.badCuts,
|
||||||
|
missedNotes: token.missedNotes,
|
||||||
|
badCuts: token.badCuts,
|
||||||
|
fullCombo: token.fullCombo,
|
||||||
|
timestamp: new Date(token.timeSet),
|
||||||
|
scoreId: token.id,
|
||||||
|
pp: token.pp,
|
||||||
|
weight: token.weight,
|
||||||
|
maxCombo: token.maxCombo,
|
||||||
|
playerInfo: token.leaderboardPlayerInfo,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ScoreSaberScoreDocument = ScoreSaberScore & Document;
|
||||||
|
export const ScoreSaberScoreModel: ReturnModelType<typeof ScoreSaberScoreInternal> =
|
||||||
|
getModelForClass(ScoreSaberScoreInternal);
|
96
projects/common/src/model/score/score.ts
Normal file
96
projects/common/src/model/score/score.ts
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
import { Modifier } from "../../score/modifier";
|
||||||
|
import { AdditionalScoreData } from "../additional-score-data/additional-score-data";
|
||||||
|
import { type Leaderboards } from "../../leaderboard";
|
||||||
|
import { prop } from "@typegoose/typegoose";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The model for a score.
|
||||||
|
*/
|
||||||
|
export default class Score {
|
||||||
|
/**
|
||||||
|
* The internal score id.
|
||||||
|
*/
|
||||||
|
@prop()
|
||||||
|
private _id?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The leaderboard the score is from.
|
||||||
|
*/
|
||||||
|
@prop({ required: true })
|
||||||
|
public readonly leaderboard!: Leaderboards;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The id of the player who set the score.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
@prop({ required: true, index: true })
|
||||||
|
public readonly playerId!: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base score for the score.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
@prop({ required: true })
|
||||||
|
public readonly score!: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The accuracy of the score.
|
||||||
|
*/
|
||||||
|
@prop({ required: true })
|
||||||
|
public readonly accuracy!: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The rank for the score.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
@prop({ required: true })
|
||||||
|
public readonly rank!: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The modifiers used on the score.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
@prop({ enum: () => Modifier, type: String, required: true })
|
||||||
|
public readonly modifiers!: Modifier[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The total amount of misses.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
@prop({ required: true })
|
||||||
|
public readonly misses!: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The amount of missed notes.
|
||||||
|
*/
|
||||||
|
@prop({ required: true })
|
||||||
|
public readonly missedNotes!: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The amount of bad cuts.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
@prop({ required: true })
|
||||||
|
public readonly badCuts!: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether every note was hit.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
@prop({ required: true })
|
||||||
|
public readonly fullCombo!: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The additional data for the score.
|
||||||
|
*/
|
||||||
|
public additionalData?: AdditionalScoreData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The time the score was set.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
@prop({ required: true })
|
||||||
|
public readonly timestamp!: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ScoreType = InstanceType<typeof Score>;
|
@ -1,76 +0,0 @@
|
|||||||
import Score from "../score";
|
|
||||||
import { Modifier } from "../modifier";
|
|
||||||
import ScoreSaberScoreToken from "../../types/token/scoresaber/score-saber-score-token";
|
|
||||||
import ScoreSaberLeaderboardPlayerInfoToken from "../../types/token/scoresaber/score-saber-leaderboard-player-info-token";
|
|
||||||
import ScoreSaberLeaderboardToken from "../../types/token/scoresaber/score-saber-leaderboard-token";
|
|
||||||
import ScoreSaberLeaderboard from "../../leaderboard/impl/scoresaber-leaderboard";
|
|
||||||
|
|
||||||
export default interface ScoreSaberScore extends Score {
|
|
||||||
/**
|
|
||||||
* The score's id.
|
|
||||||
*/
|
|
||||||
readonly id: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The amount of pp for the score.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
readonly pp: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The weight of the score, or undefined if not ranked.s
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
readonly weight?: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The max combo of the score.
|
|
||||||
*/
|
|
||||||
readonly maxCombo: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The player who set the score
|
|
||||||
*/
|
|
||||||
readonly playerInfo: ScoreSaberLeaderboardPlayerInfoToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a {@link ScoreSaberScore} from a {@link ScoreSaberScoreToken}.
|
|
||||||
*
|
|
||||||
* @param token the token to convert
|
|
||||||
* @param leaderboard the leaderboard the score was set on
|
|
||||||
*/
|
|
||||||
export function getScoreSaberScoreFromToken(
|
|
||||||
token: ScoreSaberScoreToken,
|
|
||||||
leaderboard?: ScoreSaberLeaderboardToken | ScoreSaberLeaderboard
|
|
||||||
): ScoreSaberScore {
|
|
||||||
const modifiers: Modifier[] =
|
|
||||||
token.modifiers == undefined || token.modifiers === ""
|
|
||||||
? []
|
|
||||||
: 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 {
|
|
||||||
leaderboard: "scoresaber",
|
|
||||||
score: token.baseScore,
|
|
||||||
accuracy: leaderboard ? (token.baseScore / leaderboard.maxScore) * 100 : Infinity,
|
|
||||||
rank: token.rank,
|
|
||||||
modifiers: modifiers,
|
|
||||||
misses: token.missedNotes + token.badCuts,
|
|
||||||
missedNotes: token.missedNotes,
|
|
||||||
badCuts: token.badCuts,
|
|
||||||
fullCombo: token.fullCombo,
|
|
||||||
timestamp: new Date(token.timeSet),
|
|
||||||
id: token.id,
|
|
||||||
pp: token.pp,
|
|
||||||
weight: token.weight,
|
|
||||||
maxCombo: token.maxCombo,
|
|
||||||
playerInfo: token.leaderboardPlayerInfo,
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,67 +0,0 @@
|
|||||||
import { Modifier } from "./modifier";
|
|
||||||
import { Leaderboards } from "../leaderboard";
|
|
||||||
import { AdditionalScoreData } from "../model/additional-score-data/additional-score-data";
|
|
||||||
|
|
||||||
export default interface Score {
|
|
||||||
/**
|
|
||||||
* The leaderboard the score is from.
|
|
||||||
*/
|
|
||||||
readonly leaderboard: Leaderboards;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The base score for the score.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
readonly score: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The accuracy of the score.
|
|
||||||
*/
|
|
||||||
readonly accuracy: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The rank for the score.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
readonly rank: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The modifiers used on the score.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
readonly modifiers: Modifier[];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The amount total amount of misses.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
readonly misses: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The amount of missed notes.
|
|
||||||
*/
|
|
||||||
readonly missedNotes: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The amount of bad cuts.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
readonly badCuts: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether every note was hit.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
readonly fullCombo: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The additional data for the score.
|
|
||||||
*/
|
|
||||||
additionalData?: AdditionalScoreData;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The time the score was set.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
readonly timestamp: Date;
|
|
||||||
}
|
|
@ -1,8 +1,8 @@
|
|||||||
export default interface ScoreSaberLeaderboardPlayerInfoToken {
|
export type ScoreSaberLeaderboardPlayerInfoToken = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
profilePicture: string;
|
profilePicture: string;
|
||||||
country: string;
|
country: string;
|
||||||
permissions: number;
|
permissions: number;
|
||||||
role: string;
|
role: string;
|
||||||
}
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import ScoreSaberLeaderboardToken from "./score-saber-leaderboard-token";
|
import ScoreSaberLeaderboardToken from "./score-saber-leaderboard-token";
|
||||||
import ScoreSaberLeaderboardPlayerInfoToken from "./score-saber-leaderboard-player-info-token";
|
import { ScoreSaberLeaderboardPlayerInfoToken } from "./score-saber-leaderboard-player-info-token";
|
||||||
|
|
||||||
export default interface ScoreSaberScoreToken {
|
export default interface ScoreSaberScoreToken {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import ScoreSaberPlayerToken from "../types/token/scoresaber/score-saber-player-token";
|
import ScoreSaberPlayerToken from "../types/token/scoresaber/score-saber-player-token";
|
||||||
import ScoreSaberLeaderboardPlayerInfoToken from "../types/token/scoresaber/score-saber-leaderboard-player-info-token";
|
|
||||||
import ScoreSaberPlayer from "../player/impl/scoresaber-player";
|
import ScoreSaberPlayer from "../player/impl/scoresaber-player";
|
||||||
|
import { ScoreSaberLeaderboardPlayerInfoToken } from "../types/token/scoresaber/score-saber-leaderboard-player-info-token";
|
||||||
|
|
||||||
export type ScoreSaberRole = {
|
export type ScoreSaberRole = {
|
||||||
/**
|
/**
|
||||||
|
@ -4,7 +4,7 @@ import { Colors } from "@/common/colors";
|
|||||||
import { getAverageColor } from "@/common/image-utils";
|
import { getAverageColor } from "@/common/image-utils";
|
||||||
import { LeaderboardData } from "@/components/leaderboard/leaderboard-data";
|
import { LeaderboardData } from "@/components/leaderboard/leaderboard-data";
|
||||||
import { Config } from "@ssr/common/config";
|
import { Config } from "@ssr/common/config";
|
||||||
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
|
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
|
||||||
import { LeaderboardResponse } from "@ssr/common/response/leaderboard-response";
|
import { LeaderboardResponse } from "@ssr/common/response/leaderboard-response";
|
||||||
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
||||||
import { fetchLeaderboard } from "@ssr/common/utils/leaderboard.util";
|
import { fetchLeaderboard } from "@ssr/common/utils/leaderboard.util";
|
||||||
|
@ -8,7 +8,7 @@ import { getCookieValue } from "@ssr/common/utils/cookie-utils";
|
|||||||
import { Config } from "@ssr/common/config";
|
import { Config } from "@ssr/common/config";
|
||||||
import ScoreSaberPlayer, { getScoreSaberPlayerFromToken } from "@ssr/common/player/impl/scoresaber-player";
|
import ScoreSaberPlayer, { getScoreSaberPlayerFromToken } from "@ssr/common/player/impl/scoresaber-player";
|
||||||
import { ScoreSort } from "@ssr/common/score/score-sort";
|
import { ScoreSort } from "@ssr/common/score/score-sort";
|
||||||
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
|
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
|
||||||
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
||||||
import { fetchPlayerScores } from "@ssr/common/utils/score-utils";
|
import { fetchPlayerScores } from "@ssr/common/utils/score-utils";
|
||||||
import PlayerScoresResponse from "@ssr/common/response/player-scores-response";
|
import PlayerScoresResponse from "@ssr/common/response/player-scores-response";
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
|
|
||||||
import { ScoreStatsToken } from "@ssr/common/types/token/beatleader/score-stats/score-stats";
|
import { ScoreStatsToken } from "@ssr/common/types/token/beatleader/score-stats/score-stats";
|
||||||
import { formatTime } from "@ssr/common/utils/time-utils";
|
import { formatTime } from "@ssr/common/utils/time-utils";
|
||||||
import GenericChart, { DatasetConfig } from "@/components/chart/generic-chart";
|
import GenericChart, { DatasetConfig } from "@/components/chart/generic-chart";
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import LeaderboardScores from "@/components/leaderboard/leaderboard-scores";
|
import LeaderboardScores from "@/components/leaderboard/leaderboard-scores";
|
||||||
import { LeaderboardInfo } from "@/components/leaderboard/leaderboard-info";
|
import { LeaderboardInfo } from "@/components/leaderboard/leaderboard-info";
|
||||||
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
|
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
|
||||||
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
||||||
import { LeaderboardResponse } from "@ssr/common/response/leaderboard-response";
|
import { LeaderboardResponse } from "@ssr/common/response/leaderboard-response";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
|
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
|
||||||
import { formatNumberWithCommas, formatPp } from "@ssr/common/utils/number-utils";
|
import { formatNumberWithCommas, formatPp } from "@ssr/common/utils/number-utils";
|
||||||
import ScoreSaberPlayerToken from "@ssr/common/types/token/scoresaber/score-saber-player-token";
|
import ScoreSaberPlayerToken from "@ssr/common/types/token/scoresaber/score-saber-player-token";
|
||||||
import { PlayerInfo } from "@/components/player/player-info";
|
import { PlayerInfo } from "@/components/player/player-info";
|
||||||
|
@ -10,7 +10,7 @@ import { scoreAnimation } from "@/components/score/score-animation";
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { getDifficulty } from "@/common/song-utils";
|
import { getDifficulty } from "@/common/song-utils";
|
||||||
import { fetchLeaderboardScores } from "@ssr/common/utils/score-utils";
|
import { fetchLeaderboardScores } from "@ssr/common/utils/score-utils";
|
||||||
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
|
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
|
||||||
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
||||||
import LeaderboardScoresResponse from "@ssr/common/response/leaderboard-scores-response";
|
import LeaderboardScoresResponse from "@ssr/common/response/leaderboard-scores-response";
|
||||||
import useDatabase from "@/hooks/use-database";
|
import useDatabase from "@/hooks/use-database";
|
||||||
|
@ -15,7 +15,7 @@ import useDatabase from "@/hooks/use-database";
|
|||||||
import { useLiveQuery } from "dexie-react-hooks";
|
import { useLiveQuery } from "dexie-react-hooks";
|
||||||
import ScoreSaberPlayer, { getScoreSaberPlayerFromToken } from "@ssr/common/player/impl/scoresaber-player";
|
import ScoreSaberPlayer, { getScoreSaberPlayerFromToken } from "@ssr/common/player/impl/scoresaber-player";
|
||||||
import { ScoreSort } from "@ssr/common/score/score-sort";
|
import { ScoreSort } from "@ssr/common/score/score-sort";
|
||||||
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
|
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
|
||||||
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
||||||
import PlayerScoresResponse from "@ssr/common/response/player-scores-response";
|
import PlayerScoresResponse from "@ssr/common/response/player-scores-response";
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ import { scoreAnimation } from "@/components/score/score-animation";
|
|||||||
import ScoreSaberPlayer from "@ssr/common/player/impl/scoresaber-player";
|
import ScoreSaberPlayer from "@ssr/common/player/impl/scoresaber-player";
|
||||||
import { ScoreSort } from "@ssr/common/score/score-sort";
|
import { ScoreSort } from "@ssr/common/score/score-sort";
|
||||||
import { setCookieValue } from "@ssr/common/utils/cookie-utils";
|
import { setCookieValue } from "@ssr/common/utils/cookie-utils";
|
||||||
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
|
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
|
||||||
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
||||||
import { fetchPlayerScores } from "@ssr/common/utils/score-utils";
|
import { fetchPlayerScores } from "@ssr/common/utils/score-utils";
|
||||||
import PlayerScoresResponse from "@ssr/common/response/player-scores-response";
|
import PlayerScoresResponse from "@ssr/common/response/player-scores-response";
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
|
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
|
||||||
|
|
||||||
export type ScoreBadgeProps = {
|
export type ScoreBadgeProps = {
|
||||||
/**
|
/**
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import StatValue from "@/components/stat-value";
|
import StatValue from "@/components/stat-value";
|
||||||
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
|
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
|
||||||
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -10,7 +10,7 @@ import { copyToClipboard } from "@/common/browser-utils";
|
|||||||
import { ArrowDownIcon } from "@heroicons/react/24/solid";
|
import { ArrowDownIcon } from "@heroicons/react/24/solid";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import ScoreEditorButton from "@/components/score/score-editor-button";
|
import ScoreEditorButton from "@/components/score/score-editor-button";
|
||||||
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
|
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
|
||||||
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
||||||
import { BeatSaverMap } from "@ssr/common/model/beatsaver/map";
|
import { BeatSaverMap } from "@ssr/common/model/beatsaver/map";
|
||||||
import BeatSaberPepeLogo from "@/components/logos/beatsaber-pepe-logo";
|
import BeatSaberPepeLogo from "@/components/logos/beatsaber-pepe-logo";
|
||||||
|
@ -6,7 +6,7 @@ import { Slider } from "@/components/ui/slider";
|
|||||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||||
import { ResetIcon } from "@radix-ui/react-icons";
|
import { ResetIcon } from "@radix-ui/react-icons";
|
||||||
import Tooltip from "@/components/tooltip";
|
import Tooltip from "@/components/tooltip";
|
||||||
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
|
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
|
||||||
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
||||||
|
|
||||||
type ScoreEditorButtonProps = {
|
type ScoreEditorButtonProps = {
|
||||||
|
@ -7,7 +7,7 @@ import { parseDate } from "@ssr/common/utils/time-utils";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import useWebSocket, { ReadyState } from "react-use-websocket";
|
import useWebSocket, { ReadyState } from "react-use-websocket";
|
||||||
import { ScoreSaberWebsocketMessageToken } from "@ssr/common/types/token/scoresaber/websocket/scoresaber-websocket-message";
|
import { ScoreSaberWebsocketMessageToken } from "@ssr/common/types/token/scoresaber/websocket/scoresaber-websocket-message";
|
||||||
import { getScoreSaberScoreFromToken } from "@ssr/common/score/impl/scoresaber-score";
|
import { getScoreSaberScoreFromToken } from "@ssr/common/model/score/impl/scoresaber-score";
|
||||||
import { getScoreSaberLeaderboardFromToken } from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
import { getScoreSaberLeaderboardFromToken } from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
||||||
|
|
||||||
export default function ScoreFeed() {
|
export default function ScoreFeed() {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
|
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
|
||||||
import { Modifier } from "@ssr/common/score/modifier";
|
import { Modifier } from "@ssr/common/score/modifier";
|
||||||
|
|
||||||
type ScoreModifiersProps = {
|
type ScoreModifiersProps = {
|
||||||
|
@ -2,7 +2,7 @@ import { formatNumberWithCommas } from "@ssr/common/utils/number-utils";
|
|||||||
import { GlobeAmericasIcon } from "@heroicons/react/24/solid";
|
import { GlobeAmericasIcon } from "@heroicons/react/24/solid";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { getPageFromRank } from "@ssr/common/utils/utils";
|
import { getPageFromRank } from "@ssr/common/utils/utils";
|
||||||
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
|
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
|
||||||
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
||||||
import { ScoreTimeSet } from "@/components/score/score-time-set";
|
import { ScoreTimeSet } from "@/components/score/score-time-set";
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { getScoreBadgeFromAccuracy } from "@/common/song-utils";
|
import { getScoreBadgeFromAccuracy } from "@/common/song-utils";
|
||||||
import { ScoreBadge, ScoreBadges } from "@/components/score/score-badge";
|
import { ScoreBadge, ScoreBadges } from "@/components/score/score-badge";
|
||||||
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
|
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
|
||||||
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
||||||
import ScoreMissesBadge from "@/components/score/badges/score-misses";
|
import ScoreMissesBadge from "@/components/score/badges/score-misses";
|
||||||
import { HandAccuracyBadge } from "@/components/score/badges/hand-accuracy";
|
import { HandAccuracyBadge } from "@/components/score/badges/hand-accuracy";
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { format } from "@formkit/tempo";
|
import { format } from "@formkit/tempo";
|
||||||
import { timeAgo } from "@ssr/common/utils/time-utils";
|
import { timeAgo } from "@ssr/common/utils/time-utils";
|
||||||
import Tooltip from "@/components/tooltip";
|
import Tooltip from "@/components/tooltip";
|
||||||
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
|
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
|
||||||
|
|
||||||
type ScoreTimeSetProps = {
|
type ScoreTimeSetProps = {
|
||||||
/**
|
/**
|
||||||
|
@ -9,7 +9,7 @@ import ScoreStats from "./score-stats";
|
|||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { getPageFromRank } from "@ssr/common/utils/utils";
|
import { getPageFromRank } from "@ssr/common/utils/utils";
|
||||||
import { scoresaberService } from "@ssr/common/service/impl/scoresaber";
|
import { scoresaberService } from "@ssr/common/service/impl/scoresaber";
|
||||||
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
|
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
|
||||||
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
||||||
import { BeatSaverMap } from "@ssr/common/model/beatsaver/map";
|
import { BeatSaverMap } from "@ssr/common/model/beatsaver/map";
|
||||||
import { useIsMobile } from "@/hooks/use-is-mobile";
|
import { useIsMobile } from "@/hooks/use-is-mobile";
|
||||||
|
Reference in New Issue
Block a user