Skip to content

Commit

Permalink
Merge pull request #623 from silentrald/bugfix/leaderboard-filtering
Browse files Browse the repository at this point in the history
[bugfix] proper filter for leaderboard/ranking
  • Loading branch information
Zagrios authored Dec 27, 2024
2 parents 2e71be3 + b48b79e commit bda39e7
Show file tree
Hide file tree
Showing 12 changed files with 123 additions and 33 deletions.
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

0 comments on commit bda39e7

Please sign in to comment.