testing
This commit is contained in:
73
projects/common/src/pagination.ts
Normal file
73
projects/common/src/pagination.ts
Normal file
@ -0,0 +1,73 @@
|
||||
import { NotFoundError } from "backend/src/error/not-found-error";
|
||||
import { Metadata } from "./types/metadata";
|
||||
|
||||
export class Pagination<T> {
|
||||
/**
|
||||
* The amount of items per page.
|
||||
* @private
|
||||
*/
|
||||
private itemsPerPage: number = 0;
|
||||
|
||||
/**
|
||||
* The amount of items in total.
|
||||
* @private
|
||||
*/
|
||||
private totalItems: number = 0;
|
||||
|
||||
/**
|
||||
* The items to paginate.
|
||||
* @private
|
||||
*/
|
||||
private items: T[] = [];
|
||||
|
||||
/**
|
||||
* Sets the number of items per page.
|
||||
*
|
||||
* @param itemsPerPage - The number of items per page.
|
||||
* @returns the pagination
|
||||
*/
|
||||
setItemsPerPage(itemsPerPage: number): Pagination<T> {
|
||||
this.itemsPerPage = itemsPerPage;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the items to paginate.
|
||||
*
|
||||
* @param items the items to paginate
|
||||
* @returns the pagination
|
||||
*/
|
||||
setItems(items: T[]): Pagination<T> {
|
||||
this.items = items;
|
||||
this.totalItems = items.length;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a page of items.
|
||||
*
|
||||
* @param page the page number to retrieve.
|
||||
* @returns the page of items.
|
||||
* @throws throws an error if the page number is invalid.
|
||||
*/
|
||||
getPage(page: number): Page<T> {
|
||||
const totalPages = Math.ceil(this.totalItems / this.itemsPerPage);
|
||||
|
||||
if (page < 1 || page > totalPages) {
|
||||
throw new NotFoundError("Invalid page number");
|
||||
}
|
||||
|
||||
const items = this.items.slice((page - 1) * this.itemsPerPage, page * this.itemsPerPage);
|
||||
return new Page<T>(items, new Metadata(totalPages, this.totalItems, page, this.itemsPerPage));
|
||||
}
|
||||
}
|
||||
|
||||
class Page<T> {
|
||||
readonly items: T[];
|
||||
readonly metadata: Metadata;
|
||||
|
||||
constructor(items: T[], metadata: Metadata) {
|
||||
this.items = items;
|
||||
this.metadata = metadata;
|
||||
}
|
||||
}
|
@ -168,6 +168,7 @@ class ScoreSaberService extends Service {
|
||||
* @param playerId the ID of the player to look up
|
||||
* @param sort the sort to use
|
||||
* @param page the page to get scores for
|
||||
* @param limit the amount of scores to fetch
|
||||
* @param search
|
||||
* @returns the scores of the player, or undefined
|
||||
*/
|
||||
@ -175,11 +176,13 @@ class ScoreSaberService extends Service {
|
||||
playerId,
|
||||
sort,
|
||||
page,
|
||||
limit = 8,
|
||||
search,
|
||||
}: {
|
||||
playerId: string;
|
||||
sort: ScoreSort;
|
||||
page: number;
|
||||
limit?: number;
|
||||
search?: string;
|
||||
useProxy?: boolean;
|
||||
}): Promise<ScoreSaberPlayerScoresPageToken | undefined> {
|
||||
@ -189,7 +192,7 @@ class ScoreSaberService extends Service {
|
||||
);
|
||||
const response = await this.fetch<ScoreSaberPlayerScoresPageToken>(
|
||||
LOOKUP_PLAYER_SCORES_ENDPOINT.replace(":id", playerId)
|
||||
.replace(":limit", 8 + "")
|
||||
.replace(":limit", limit + "")
|
||||
.replace(":sort", sort)
|
||||
.replace(":page", page + "") + (search ? `&search=${search}` : "")
|
||||
);
|
||||
|
69
projects/common/src/sorter/impl/scoresaber-sorter.ts
Normal file
69
projects/common/src/sorter/impl/scoresaber-sorter.ts
Normal file
@ -0,0 +1,69 @@
|
||||
import { ScoreSorter } from "../score-sorter";
|
||||
import { ScoreSaberScore } from "../../model/score/impl/scoresaber-score";
|
||||
import { ScoreSortType } from "../sort-type";
|
||||
import { SortDirection } from "../sort-direction";
|
||||
|
||||
export class ScoreSaberScoreSorter extends ScoreSorter<ScoreSaberScore> {
|
||||
sort(type: ScoreSortType, direction: SortDirection, items: ScoreSaberScore[]): ScoreSaberScore[] {
|
||||
switch (type) {
|
||||
case ScoreSortType.date:
|
||||
return this.sortRecent(direction, items);
|
||||
case ScoreSortType.pp:
|
||||
return this.sortPp(direction, items);
|
||||
case ScoreSortType.accuracy:
|
||||
return this.sortAccuracy(direction, items);
|
||||
case ScoreSortType.misses:
|
||||
return this.sortMisses(direction, items);
|
||||
default:
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the scores by the time they were set.
|
||||
*
|
||||
* @param direction the direction to sort the scores
|
||||
* @param items the scores to sort
|
||||
* @returns the sorted scores
|
||||
*/
|
||||
sortRecent(direction: SortDirection, items: ScoreSaberScore[]): ScoreSaberScore[] {
|
||||
return items.sort((a, b) =>
|
||||
direction === SortDirection.ASC
|
||||
? a.timestamp.getTime() - b.timestamp.getTime()
|
||||
: b.timestamp.getTime() - a.timestamp.getTime()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the scores by their pp value
|
||||
*
|
||||
* @param direction the direction to sort the scores
|
||||
* @param items the scores to sort
|
||||
* @returns the sorted scores
|
||||
*/
|
||||
sortPp(direction: SortDirection, items: ScoreSaberScore[]): ScoreSaberScore[] {
|
||||
return items.sort((a, b) => (direction === SortDirection.ASC ? a.pp - b.pp : b.pp - a.pp));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the scores by their accuracy value
|
||||
*
|
||||
* @param direction the direction to sort the scores
|
||||
* @param items the scores to sort
|
||||
* @returns the sorted scores
|
||||
*/
|
||||
sortAccuracy(direction: SortDirection, items: ScoreSaberScore[]): ScoreSaberScore[] {
|
||||
return items.sort((a, b) => (direction === SortDirection.ASC ? a.accuracy - b.accuracy : b.accuracy - a.accuracy));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the scores by their misses
|
||||
*
|
||||
* @param direction the direction to sort the scores
|
||||
* @param items the scores to sort
|
||||
* @returns the sorted scores
|
||||
*/
|
||||
sortMisses(direction: SortDirection, items: ScoreSaberScore[]): ScoreSaberScore[] {
|
||||
return items.sort((a, b) => (direction === SortDirection.ASC ? a.misses - b.misses : b.misses - a.misses));
|
||||
}
|
||||
}
|
14
projects/common/src/sorter/score-sorter.ts
Normal file
14
projects/common/src/sorter/score-sorter.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { ScoreSortType } from "./sort-type";
|
||||
import { SortDirection } from "./sort-direction";
|
||||
|
||||
export abstract class ScoreSorter<T> {
|
||||
/**
|
||||
* Sorts the items
|
||||
*
|
||||
* @param type the type of sort
|
||||
* @param direction the direction of the sort
|
||||
* @param items the items to sort
|
||||
* @returns the sorted items
|
||||
*/
|
||||
public abstract sort(type: ScoreSortType, direction: SortDirection, items: T[]): T[];
|
||||
}
|
4
projects/common/src/sorter/sort-direction.ts
Normal file
4
projects/common/src/sorter/sort-direction.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export enum SortDirection {
|
||||
ASC = "asc",
|
||||
DESC = "desc",
|
||||
}
|
6
projects/common/src/sorter/sort-type.ts
Normal file
6
projects/common/src/sorter/sort-type.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export enum ScoreSortType {
|
||||
date = "date",
|
||||
pp = "pp",
|
||||
accuracy = "accuracy",
|
||||
misses = "misses",
|
||||
}
|
5
projects/common/src/sorter/sorters.ts
Normal file
5
projects/common/src/sorter/sorters.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { ScoreSaberScoreSorter } from "./impl/scoresaber-sorter";
|
||||
|
||||
export const ScoreSorters = {
|
||||
scoreSaber: new ScoreSaberScoreSorter(),
|
||||
};
|
@ -1,6 +1,6 @@
|
||||
import { isServer } from "./utils";
|
||||
|
||||
export type CookieName = "playerId" | "lastScoreSort";
|
||||
export type CookieName = "playerId" | "lastScoreSort" | "lastScoreSortDirection";
|
||||
|
||||
/**
|
||||
* Gets the value of a cookie
|
||||
|
@ -4,6 +4,8 @@ import PlayerScoresResponse from "../response/player-scores-response";
|
||||
import { Config } from "../config";
|
||||
import { ScoreSort } from "../score/score-sort";
|
||||
import LeaderboardScoresResponse from "../response/leaderboard-scores-response";
|
||||
import { ScoreSortType } from "../sorter/sort-type";
|
||||
import { SortDirection } from "../sorter/sort-direction";
|
||||
|
||||
/**
|
||||
* Fetches the player's scores
|
||||
@ -12,17 +14,19 @@ import LeaderboardScoresResponse from "../response/leaderboard-scores-response";
|
||||
* @param id the player id
|
||||
* @param page the page
|
||||
* @param sort the sort
|
||||
* @param direction the direction to sort
|
||||
* @param search the search
|
||||
*/
|
||||
export async function fetchPlayerScores<S, L>(
|
||||
leaderboard: Leaderboards,
|
||||
id: string,
|
||||
page: number,
|
||||
sort: ScoreSort,
|
||||
sort: ScoreSortType,
|
||||
direction: SortDirection,
|
||||
search?: string
|
||||
) {
|
||||
return kyFetch<PlayerScoresResponse<S, L>>(
|
||||
`${Config.apiUrl}/scores/player/${leaderboard}/${id}/${page}/${sort}${search ? `?search=${search}` : ""}`
|
||||
`${Config.apiUrl}/scores/player/${leaderboard}/${id}/${page}/${sort}/${direction}${search ? `?search=${search}` : ""}`
|
||||
);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user