we can now see maps from bsaver

This commit is contained in:
MathieuG-P
2022-12-05 03:19:05 +01:00
parent 36c1eb20fc
commit 0ea41da41c
4 changed files with 120 additions and 43 deletions
+1
View File
@@ -231,6 +231,7 @@
"electron-log": "^4.4.8",
"electron-store": "^8.1.0",
"electron-updater": "^5.2.4",
"fast-deep-equal": "^3.1.3",
"framer-motion": "^7.2.1",
"fs-extra": "^10.1.0",
"history": "^5.3.0",
@@ -171,12 +171,12 @@ export function FilterPanel({className, ref, playlist = false, filter, onChange}
<h2 className="uppercase text-sm mb-1">TAGS</h2>
<div className="w-full flex flex-row flex-wrap items-start justify-start content-start gap-1 mb-2">
{MAP_TYPES.map(tag => (
<span key={tag} onClick={e => handleTagClick(tag)} className={`text-sm text-black rounded-md px-1 font-bold cursor-pointer ${(!isTagActivated(tag)) && "opacity-40 hover:opacity-100"}`} style={{backgroundColor: isTagExcluded(tag) ? diffColors.Expert : diffColors.Normal}}>{tag}</span>
<span key={tag} onClick={e => handleTagClick(tag)} className={`text-[12.5px] text-black rounded-md px-1 font-bold cursor-pointer ${(!isTagActivated(tag)) && "opacity-40 hover:opacity-100"}`} style={{backgroundColor: isTagExcluded(tag) ? diffColors.Expert : diffColors.Normal}}>{tag}</span>
))}
</div>
<div className="w-full flex flex-row flex-wrap items-start justify-start content-start gap-1">
{MAP_STYLES.map(tag => (
<span key={tag} onClick={e => handleTagClick(tag)} className={`text-sm text-black rounded-md px-1 font-bold cursor-pointer ${(!isTagActivated(tag)) && "opacity-40 hover:opacity-100"}`} style={{backgroundColor: isTagExcluded(tag) ? diffColors.Expert : diffColors.Easy}}>{tag}</span>
<span key={tag} onClick={e => handleTagClick(tag)} className={`text-[12.5px] text-black rounded-md px-1 font-bold cursor-pointer ${(!isTagActivated(tag)) && "opacity-40 hover:opacity-100"}`} style={{backgroundColor: isTagExcluded(tag) ? diffColors.Expert : diffColors.Easy}}>{tag}</span>
))}
</div>
@@ -1,5 +1,7 @@
import { useEffect, useState } from "react";
import { motion, useInView } from "framer-motion";
import { useCallback, useEffect, useRef, useState } from "react";
import { FilterPanel } from "renderer/components/maps-mangement-components/filter-panel.component";
import { MapItem, ParsedMapDiff } from "renderer/components/maps-mangement-components/map-item.component";
import { BsmButton } from "renderer/components/shared/bsm-button.component";
import { BsmDropdownButton } from "renderer/components/shared/bsm-dropdown-button.component";
import { BsmSelect, BsmSelectOption } from "renderer/components/shared/bsm-select.component";
@@ -7,42 +9,116 @@ import { BSV_SORT_ORDER } from "renderer/partials/beat-saver/sort-order";
import { BeatSaverService } from "renderer/services/beat-saver/beat-saver.service";
import { ModalComponent } from "renderer/services/modale.service";
import { BSVersion } from "shared/bs-version.interface";
import { BsvMapDetail, MapFilter } from "shared/models/maps/beat-saver.model";
import { BsvMapCharacteristic, BsvMapDetail, MapFilter, SearchParams } from "shared/models/maps/beat-saver.model";
import BeatWaitingImg from "../../../../../assets/images/apngs/beat-waiting.png"
export const DownloadMapsModal: ModalComponent<void, BSVersion> = ({data}) => {
const beatSaver = BeatSaverService.getInstance();
const [filter, setFilter] = useState<MapFilter>(null);
const [maps, setMaps] = useState<BsvMapDetail[]>(null);
const [page, setPage] = useState(0);
const [filter, setFilter] = useState<MapFilter>({});
const [query, setQuery] = useState("");
const [maps, setMaps] = useState<BsvMapDetail[]>([]);
const [sortOrder, setSortOrder] = useState(BSV_SORT_ORDER.at(0));
const [searchParams, setSearchParams] = useState<SearchParams>({
sortOrder: sortOrder,
filter: filter,
page: 0,
q: query
});
const loaderRef = useRef(null);
const sortOptions: BsmSelectOption[] = (() => {
return BSV_SORT_ORDER.map(sort => ({text: sort, value: sort}));
})();
console.log(maps);
const loadMaps = (params: SearchParams) => {
beatSaver.searchMaps(params).then((maps => setMaps(prev => [...prev, ...maps])));
}
useEffect(() => {
beatSaver.searchMaps({
page, sortOrder
}).then(setMaps);
}, [])
loadMaps(searchParams);
}, [searchParams]);
const extractMapDiffs = (map: BsvMapDetail): Map<BsvMapCharacteristic, ParsedMapDiff[]> => {
const res = new Map<BsvMapCharacteristic, ParsedMapDiff[]>();
if(map.versions.at(0).diffs){
map.versions.at(0).diffs.forEach(diff => {
const arr = res.get(diff.characteristic) || [];
arr.push({name: diff.difficulty, type: diff.difficulty, stars: diff.stars});
res.set(diff.characteristic, arr);
});
}
return res;
}
const renderMap = (map: BsvMapDetail) => {
return (
<MapItem
autor={map.metadata.levelAuthorName}
autorId={map.uploader.id}
bpm={map.metadata.bpm}
coverUrl={map.versions.at(0).coverURL}
createdAt={map.createdAt}
duration={map.metadata.duration}
hash={map.versions.at(0).hash}
likes={map.stats.upvotes}
mapId={map.id}
ranked={map.ranked}
qualified={map.qualified}
title={map.name}
songAutor={map.metadata.songAuthorName}
diffs={extractMapDiffs(map)}
songUrl={map.versions.at(0).previewURL}
key={map.id}
/>
)
}
const handleSearch = () => {
const searchParams: SearchParams = {
sortOrder: sortOrder,
filter: filter,
q: query,
page: 0,
};
setMaps(() => []);
setSearchParams(() => searchParams);
}
const handleLoadMore = () => {
setSearchParams((prev) => {
return {...prev, page: prev.page + 1}
});
}
return (
<form className="text-gray-800 dark:text-gray-200 flex flex-col">
<div className="flex h-7 gap-2">
<BsmDropdownButton className="shrink-0 h-full relative z-[1] flex justify-center" buttonClassName="flex items-center justify-center h-full rounded-full px-2 py-1 !bg-main-color-1" icon="search" text="Filtres" withBar={false}>
<FilterPanel className="absolute top-[calc(100%+3px)] bg-main-color-3 origin-top w-[500px] h-fit p-2 rounded-md shadow-md shadow-black" filter={filter} onChange={setFilter}/>
<form className="text-gray-800 dark:text-gray-200 flex flex-col max-w-[95vw] w-[970px] h-[85vh] gap-3" onSubmit={e => {e.preventDefault(); handleSearch()}}>
<div className="flex h-8 gap-2 shrink-0">
<BsmDropdownButton className="shrink-0 h-full relative z-[1] flex justify-start" buttonClassName="flex items-center justify-center h-full rounded-full px-2 py-1 !bg-main-color-1" icon="search" text="Filtres" withBar={false}>
<FilterPanel className="absolute top-[calc(100%+3px)] bg-main-color-3 origin-top w-[450px] h-fit p-2 rounded-md shadow-md shadow-black" filter={filter} onChange={setFilter}/>
</BsmDropdownButton>
<input className="h-full bg-main-color-1 rounded-full px-2 grow pb-0.5" type="text" name="" id="" placeholder="Rechercher"/>
<BsmButton className="shrink-0 aspect-square rounded-full p-1 !bg-main-color-1" icon="search" withBar={false}/>
<input className="h-full bg-main-color-1 rounded-full px-2 grow pb-0.5" type="text" name="" id="" placeholder="Rechercher" value={query} onChange={e => setQuery(e.target.value.trim())}/>
<BsmButton className="shrink-0 aspect-square rounded-full p-1 !bg-main-color-1" icon="search" withBar={false} onClick={e => {e.preventDefault(); handleSearch()}}/>
<BsmSelect className="bg-main-color-1 rounded-full px-2 pb-0.5" options={sortOptions} onChange={setSortOrder}/>
</div>
<ul>
<ul className="w-full grow flex content-start flex-wrap gap-2 px-2 overflow-y-scroll overflow-x-hidden scrollbar-thin scrollbar-thumb-rounded-full scrollbar-thumb-neutral-900" >
{maps.length === 0 ? (
<div className="w-full h-full flex flex-col items-center justify-center">
<img className="w-32 h-32 spin-loading" src={BeatWaitingImg}></img>
<span className="text-lg">Chargement des maps...</span> {/* TODO TRANSLATE */}
</div>
) : (
<>
{maps.map(renderMap)}
<motion.span onViewportEnter={handleLoadMore} ref={loaderRef} className="block w-full h-8"/>
</>
)}
</ul>
</form>
)
@@ -19,28 +19,28 @@ export class BeatSaverApiService {
if(!filter){ return new URLSearchParams(); }
const enbledTagsString = Array.from(filter.enabledTags);
const excludedTagsString = Array.from(filter.excludedTags).map(tag => `!${tag}`);
const enbledTagsString = filter.enabledTags ? Array.from(filter.enabledTags) : null;
const excludedTagsString = filter.excludedTags ? Array.from(filter.excludedTags).map(tag => `!${tag}`) : null;
const tags = [...enbledTagsString, excludedTagsString].join("|");
const tags = (enbledTagsString || excludedTagsString) ? [...enbledTagsString, excludedTagsString].join("|") : null;
const params = {
automapper: String(filter.automapper ?? ""),
chroma: String(filter.chroma ?? ""),
cinema: String(filter.cinema ?? ""),
me: String(filter.me ?? ""),
noodle: String(filter.noodle ?? ""),
ranked: String(filter.ranked ?? ""),
verified: String(filter.ranked ?? ""),
curated: String(filter.curated ?? ""),
fullSpread : String(filter.fullSpread ?? ""),
from: String(filter.from ?? ""),
to: String(filter.to ?? ""),
tags: tags,
minDuration: String(filter.minDuration ?? ""),
maxDuration: String(filter.maxDuration ?? ""),
minNps: String(filter.minNps ?? ""),
maxNps: String(filter.maxNps ?? ""),
...(filter.automapper && {automapper: String(filter.automapper)}),
...(filter.chroma && {chroma: String(filter.chroma)}),
...(filter.cinema && {cinema: String(filter.cinema)}),
...(filter.me && {me: String(filter.me)}),
...(filter.noodle && {noodle: String(filter.noodle)}),
...(filter.ranked && {ranked: String(filter.ranked)}),
...(filter.verified && {verified: String(filter.verified)}),
...(filter.curated && {curated: String(filter.curated)}),
...(filter.fullSpread && {fullSpread: String(filter.fullSpread)}),
...(filter.from && {from: String(filter.from)}),
...(filter.to && {to: String(filter.to)}),
...(tags && {tags: String(tags)}),
...(filter.minDuration && {minDuration: String(filter.minDuration)}),
...(filter.maxDuration && {maxDuration: String(filter.maxDuration)}),
...(filter.minNps && {minNps: String(filter.minNps)}),
...(filter.maxNps && {maxNps: String(filter.maxNps)})
};
return new URLSearchParams(params);
@@ -52,9 +52,9 @@ export class BeatSaverApiService {
if(!search){ return new URLSearchParams(); }
const searchParams = {
includeEmpty: String(search.includeEmpty ?? ""),
sortOrder: String(search.sortOrder ?? ""),
q: String(search.q ?? "")
...(search.includeEmpty && {includeEmpty: String(search.includeEmpty)}),
...(search.sortOrder && {sortOrder: search.sortOrder}),
...(search.q && {q: search.q})
};
const filterUrlParms = this.mapFilterToUrlParams(search.filter);