Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[bugfix] proper filter for leaderboard/ranking #623

Merged
merged 7 commits into from
Dec 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions assets/jsons/translations/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -915,7 +915,8 @@
"tags": "Stichwörter",
"specificities": "Allgemein",
"requirements": "Anforderungen",
"exclude": "Ausschließen"
"exclude": "Ausschließen",
"leaderboard": "Rangliste"
},
"map-types": {
"accuracy": "Präzision",
Expand Down Expand Up @@ -966,11 +967,16 @@
},
"map-specificities": {
"automapper": "KI",
"ranked": "Ranked",
"curated": "Curated",
"verified": "Verifiziert",
"fullSpread": "Full Spread"
},
"map-leaderboard": {
"All": "Alle",
"Ranked": "Ranked",
"BeatLeader": "BeatLeader",
"ScoreSaber": "ScoreSaber"
},
"map-excludes": {
"installed": "Installiert"
},
Expand Down
10 changes: 8 additions & 2 deletions assets/jsons/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -910,7 +910,8 @@
"tags": "tags",
"specificities": "general",
"requirements": "requirements",
"exclude": "exclude"
"exclude": "exclude",
"leaderboard": "leaderboard"
},
"map-types": {
"accuracy": "accuracy",
Expand Down Expand Up @@ -961,11 +962,16 @@
},
"map-specificities": {
"automapper": "AI",
"ranked": "ranked",
"curated": "curated",
"verified": "verified",
"fullSpread": "full spread"
},
"map-leaderboard": {
"All": "All",
"Ranked": "Ranked",
"BeatLeader": "BeatLeader",
"ScoreSaber": "ScoreSaber"
},
"map-excludes": {
"installed": "installed"
},
Expand Down
10 changes: 8 additions & 2 deletions assets/jsons/translations/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -915,7 +915,8 @@
"tags": "tags",
"specificities": "general",
"requirements": "requisitos",
"exclude": "excluir"
"exclude": "excluir",
"leaderboard": "tabla de clasificación"
},
"map-types": {
"accuracy": "precisión",
Expand Down Expand Up @@ -966,11 +967,16 @@
},
"map-specificities": {
"automapper": "IA",
"ranked": "rankado",
"curated": "recomendado",
"verified": "verificado",
"fullSpread": "panel completo"
},
"map-leaderboard": {
"All": "Todos",
"Ranked": "Rankado",
"BeatLeader": "BeatLeader",
"ScoreSaber": "ScoreSaber"
},
"map-excludes": {
"installed": "instalado"
},
Expand Down
10 changes: 8 additions & 2 deletions assets/jsons/translations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -916,7 +916,8 @@
"tags": "tags",
"specificities": "général",
"requirements": "requis",
"exclude": "exclure"
"exclude": "exclure",
"leaderboard": "tableau des leaders"
},
"map-types": {
"accuracy": "précision",
Expand Down Expand Up @@ -967,11 +968,16 @@
},
"map-specificities": {
"automapper": "IA",
"ranked": "classée",
"curated": "recommandée",
"verified": "vérifiée",
"fullSpread": "panel complet"
},
"map-leaderboard": {
"All": "Tout",
"Ranked": "Classée",
"BeatLeader": "BeatLeader",
"ScoreSaber": "ScoreSaber"
},
"map-excludes": {
"installed": "installé"
},
Expand Down
10 changes: 8 additions & 2 deletions assets/jsons/translations/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -915,7 +915,8 @@
"tags": "タグ",
"specificities": "一般",
"requirements": "要Mod",
"exclude": "除外する"
"exclude": "除外する",
"leaderboard": "ランキング"
},
"map-types": {
"accuracy": "正確",
Expand Down Expand Up @@ -966,11 +967,16 @@
},
"map-specificities": {
"automapper": "AI",
"ranked": "ランク入賞",
"curated": "厳選済み",
"verified": "認証済み",
"fullSpread": "フルスプレッド"
},
"map-leaderboard": {
"All": "すべて",
"Ranked": "ランク入賞",
"BeatLeader": "BeatLeader",
"ScoreSaber": "ScoreSaber"
},
"map-excludes": {
"installed": "インストール済み"
},
Expand Down
10 changes: 8 additions & 2 deletions assets/jsons/translations/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -914,7 +914,8 @@
"tags": "тэги",
"specificities": "основное",
"requirements": "требуемые моды",
"exclude": "исключить"
"exclude": "исключить",
"leaderboard": "Таблица лидеров"
},
"map-types": {
"accuracy": "точность",
Expand Down Expand Up @@ -965,11 +966,16 @@
},
"map-specificities": {
"automapper": "ИИ",
"ranked": "с рейтингом",
"curated": "оценённые куратором",
"verified": "проверенные авторы",
"fullSpread": "полный набор"
},
"map-leaderboard": {
"All": "все",
"Ranked": "с рейтингом",
"BeatLeader": "BeatLeader",
"ScoreSaber": "ScoreSaber"
},
"map-excludes": {
"installed": "установленный"
},
Expand Down
10 changes: 8 additions & 2 deletions assets/jsons/translations/zh-tw.json
Original file line number Diff line number Diff line change
Expand Up @@ -915,7 +915,8 @@
"tags": "標籤",
"specificities": "general",
"requirements": "要求",
"exclude": "排除"
"exclude": "排除",
"leaderboard": "排行榜"
},
"map-types": {
"accuracy": "精確度",
Expand Down Expand Up @@ -966,11 +967,16 @@
},
"map-specificities": {
"automapper": "AI",
"ranked": "Ranked",
"curated": "Curated",
"verified": "Verified",
"fullSpread": "Full spread"
},
"map-leaderboard": {
"All": "所有",
"Ranked": "排名的",
"BeatLeader": "BeatLeader",
"ScoreSaber": "ScoreSaber"
},
"map-excludes": {
"installed": "已安裝"
},
Expand Down
10 changes: 8 additions & 2 deletions assets/jsons/translations/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -915,7 +915,8 @@
"tags": "标签",
"specificities": "general",
"requirements": "要求",
"exclude": "排除"
"exclude": "排除",
"leaderboard": "排行榜"
},
"map-types": {
"accuracy": "精确度",
Expand Down Expand Up @@ -966,11 +967,16 @@
},
"map-specificities": {
"automapper": "AI",
"ranked": "ranked",
"curated": "curated",
"verified": "verified",
"fullSpread": "full spread"
},
"map-leaderboard": {
"All": "所有",
"Ranked": "排名的",
"BeatLeader": "BeatLeader",
"ScoreSaber": "ScoreSaber"
},
"map-excludes": {
"installed": "已安装"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -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";

Expand Down Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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>) => {
Expand All @@ -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);
Expand All @@ -173,9 +184,18 @@ 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
className="rounded-md bg-theme-1"
options={leaderboardOptions}
selected={filter.leaderboard || 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)}>
Expand Down Expand Up @@ -287,10 +307,23 @@ function isFitAutomapper(filter: MapFilter, automapper: boolean): boolean {
return automapper;
}

function isFitLeaderboard(filter: MapFilter, ranked: boolean, blRanked: boolean): boolean {
switch (filter?.leaderboard) {
case MapLeaderboard.All:
return true;

case MapLeaderboard.Ranked:
return ranked || blRanked;

case MapLeaderboard.ScoreSaber:
return ranked;

case MapLeaderboard.BeatLeader:
return blRanked;

function isFitRanked(filter: MapFilter, ranked: boolean): boolean {
if (!filter?.ranked) { return true; }
return ranked;
default: // filter is undefined
return true;
}
}

function isFitCurated(filter: MapFilter, curated: boolean): boolean {
Expand Down Expand Up @@ -322,7 +355,7 @@ export const isLocalMapFitMapFilter = ({filter, map, search}: { filter: MapFilte
if (!isFitChroma(filter, map.songDetails?.difficulties.some(diff => !!diff.chroma))) { return false; }
if (!isFitFullSpread(filter, map.songDetails?.difficulties.length)) { return false; }
if (!isFitAutomapper(filter, map.songDetails?.automapper)){ return false; }
if (!isFitRanked(filter, map.songDetails?.ranked || map.songDetails?.blRanked)) { return false; }
if (!isFitLeaderboard(filter, map.songDetails?.ranked, map.songDetails?.blRanked)) { return false; }
if (!isFitCurated(filter, map.songDetails?.curated)) { return false; }
if (!isFitVerified(filter, map.songDetails?.uploader.verified)) { return false; }
if (!isFitSearch(search, {songName: map.mapInfo?.songName, songAuthorName: map.mapInfo?.songAuthorName, levelMappers: map.mapInfo?.levelMappers})) { return false; }
Expand All @@ -343,7 +376,7 @@ export const isBsvMapFitMapFilter = ({filter, map, search}: { filter: MapFilter,
if (!isFitChroma(filter, map.versions?.at(0)?.diffs.some(diff => !!diff.chroma))) { return false; }
if (!isFitFullSpread(filter, map.versions?.at(0)?.diffs.length)) { return false; }
if (!isFitAutomapper(filter, map.automapper)){ return false; }
if (!isFitRanked(filter, map.ranked || map.blRanked)) { return false; }
if (!isFitLeaderboard(filter, map.ranked, map.blRanked)) { return false; }
if (!isFitCurated(filter, !!map.curator)) { return false; }
if (!isFitVerified(filter, !!map.curatedAt)) { return false; }
if (!isFitSearch(search, {songName: map.name, songAuthorName: map.metadata.songAuthorName, levelMappers: [map.metadata.levelAuthorName]})) { return false; }
Expand All @@ -363,7 +396,7 @@ export const isSongDetailsFitMapFilter = ({filter, map, search}: { filter: MapFi
if (!isFitChroma(filter, map.difficulties.some(diff => !!diff.chroma))) { return false; }
if (!isFitFullSpread(filter, map.difficulties.length)) { return false; }
if (!isFitAutomapper(filter, map.automapper)){ return false; }
if (!isFitRanked(filter, map.ranked || map.blRanked)) { return false; }
if (!isFitLeaderboard(filter, map.ranked, map.blRanked)) { return false; }
if (!isFitCurated(filter, map.curated)) { return false; }
if (!isFitVerified(filter, map.uploader.verified)) { return false; }
if (!isFitSearch(search, {songName: map.name, songAuthorName: map.uploader.name, levelMappers: [map.uploader.name]})) { return false; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,12 +324,12 @@ export function MapItemComponent <T = unknown>({ hash, title, autor, songAutor,
<motion.div className="w-full h-5 pb-1 pr-7 flex items-center gap-1" onHoverStart={bottomBarHoverStart} onHoverEnd={bottomBarHoverEnd}>
{ranked && (
<div className="text-yellow-300 bg-current rounded-full px-1 h-full flex items-center justify-center">
<span className="uppercase text-xs font-bold tracking-wide brightness-[.25]">{t("maps.map-specificities.ranked")}</span>
<span className="uppercase text-xs font-bold tracking-wide brightness-[.25]">{t("maps.map-leaderboard.Ranked")}</span>
</div>
)}
{blRanked && (
<div className="bg-pink-400 bg-current rounded-full px-1 h-full flex items-center justify-center">
<span className="uppercase text-xs font-bold tracking-wide brightness-[.25]">{t("maps.map-specificities.ranked")}</span>
<span className="uppercase text-xs font-bold tracking-wide brightness-[.25]">{t("maps.map-leaderboard.Ranked")}</span>
</div>
)}
<div className="h-full grow flex items-start content-start">{renderDiffPreview()}</div>
Expand Down
Loading
Loading