[bugfix] proper filter for leaderboard/ranking

* use leaderboard query param with beatsaver
* fixed issue where blRanked is not showing on mapItem components
This commit is contained in:
silentrald
2024-10-19 00:27:27 +08:00
parent 2e3febf66a
commit c8003a8ae8
11 changed files with 101 additions and 17 deletions
@@ -1,5 +1,5 @@
import { BsvMapDetail } from "shared/models/maps";
import { BsvPlaylistPage, MapFilter, PlaylistSearchParams, PlaylistSearchResponse, SearchParams, SearchResponse } from "shared/models/maps/beat-saver.model";
import { BsvPlaylistPage, MapFilter, MapLeaderboard, PlaylistSearchParams, PlaylistSearchResponse, SearchParams, SearchResponse } from "shared/models/maps/beat-saver.model";
import { RequestService } from "../../request.service";
import { CustomError } from "shared/models/exceptions/custom-error.class";
@@ -32,6 +32,10 @@ export class BeatSaverApiService {
return new URLSearchParams();
}
if (!filter.leaderboard) {
filter.leaderboard = MapLeaderboard.All;
}
const enbledTagsString = filter.enabledTags ? Array.from(filter.enabledTags) : null;
const excludedTagsString = filter.excludedTags ? Array.from(filter.excludedTags).map(tag => `!${tag}`) : null;
@@ -1,4 +1,4 @@
import { BsvMapDetail, MapFilter, MapRequirement, MapSpecificity, MapStyle, MapTag, MapType } from "shared/models/maps/beat-saver.model";
import { BsvMapDetail, MapFilter, MapLeaderboard, MapRequirement, MapSpecificity, MapStyle, MapTag, MapType } from "shared/models/maps/beat-saver.model";
import { motion } from "framer-motion";
import { MutableRefObject, useEffect, useRef, useState } from "react";
import { BsmCheckbox } from "../../shared/bsm-checkbox.component";
@@ -14,6 +14,8 @@ import { BsmLocalMap } from "shared/models/maps/bsm-local-map.interface";
import { SongDetails } from "shared/models/maps";
import formatDuration from "format-duration";
import { MapInfo } from "shared/models/maps/info/map-info.model";
import { BsmSelect, BsmSelectOption } from "renderer/components/shared/bsm-select.component";
import { useConstant } from "renderer/hooks/use-constant.hook";
export type Props = {
className?: string;
@@ -45,6 +47,13 @@ export function FilterPanel({ className, ref, playlist = false, filter, localDat
const isTagActivated = (tag: MapTag): boolean => filter?.enabledTags?.has(tag) || filter?.excludedTags?.has(tag);
const isTagExcluded = (tag: MapTag): boolean => filter?.excludedTags?.has(tag);
const leaderboardOptions: BsmSelectOption<MapLeaderboard>[] = useConstant(
() => Object.values(MapLeaderboard).map(key => ({
text: `maps.map-leaderboard.${key}`,
value: key,
}))
);
useEffect(() => {
if (firstRun.current) {
firstRun.current = false;
@@ -137,10 +146,6 @@ export function FilterPanel({ className, ref, playlist = false, filter, localDat
return t(`maps.map-styles.${style}`);
};
const translateMapSpecificity = (specificity: MapSpecificity): string => {
return t(`maps.map-specificities.${specificity}`);
};
type BooleanKeys<T> = { [k in keyof T]: T[k] extends boolean ? k : never }[keyof T];
const handleCheckbox = (key: BooleanKeys<MapFilter>) => {
@@ -153,6 +158,12 @@ export function FilterPanel({ className, ref, playlist = false, filter, localDat
onChange(newFilter);
};
const handleLeaderboardChange = (value: MapLeaderboard) => {
const newFilter = { ...(filter ?? {}) };
newFilter.leaderboard = value;
onChange(newFilter);
}
const handleApply = () => {
onApply(filter);
setHaveChanged(() => false);
@@ -173,9 +184,17 @@ export function FilterPanel({ className, ref, playlist = false, filter, localDat
{Object.values(MapSpecificity).map(specificity => (
<div key={specificity} className="flex justify-start items-center h-[22px] z-20 relative py-0.5 cursor-pointer" onClick={() => handleCheckbox(specificity)}>
<BsmCheckbox className="h-full aspect-square relative bg-inherit mr-1" checked={filter?.[specificity]} onChange={() => handleCheckbox(specificity)} />
<span className="grow capitalize">{translateMapSpecificity(specificity)}</span>
<span className="grow capitalize">{t(`maps.map-specificities.${specificity}`)}</span>
</div>
))}
<h2 className="mb-1 uppercase text-sm">{t("maps.map-filter-panel.leaderboard")}</h2>
<BsmSelect
options={leaderboardOptions}
selected={MapLeaderboard.All}
onChange={handleLeaderboardChange}
/>
<h2 className="my-1 uppercase text-sm">{t("maps.map-filter-panel.requirements")}</h2>
{Object.values(MapRequirement).map(requirement => (
<div key={requirement} className="flex justify-start items-center h-[22px] z-20 relative py-0.5 cursor-pointer" onClick={() => handleCheckbox(requirement)}>
+7 -2
View File
@@ -209,11 +209,16 @@ export enum MapRequirement {
export enum MapSpecificity {
Automapper = "automapper",
Ranked = "ranked",
Curated = "curated",
Verified = "verified",
FullSpread = "fullSpread"
}
export enum MapLeaderboard {
All = "All",
Ranked = "Ranked",
BeatLeader = "BeatLeader",
ScoreSaber = "ScoreSaber",
}
// [ Admin, Uploader, SageScore, None ]
@@ -235,7 +240,7 @@ export interface MapFilter {
from?: number;
to?: number;
fullSpread?: boolean;
ranked?: boolean;
leaderboard?: string;
installed?: boolean;
minDuration?: number;
maxDuration?: number;