From 341e8aed9990463a1b4abbbcd1d3fdf780ae9ead Mon Sep 17 00:00:00 2001 From: MathieuG-P <40181755+Zagrios@users.noreply.github.com> Date: Sun, 4 Dec 2022 01:08:18 +0100 Subject: [PATCH] create download maps modal --- .../filter-panel.component.tsx | 28 ++++---- .../maps-playlists-panel.component.tsx | 6 +- .../download-maps-modal.component.tsx | 49 +++++++++++++ .../components/modal/modal.component.tsx | 6 +- .../shared/bsm-select.component.tsx | 28 ++++++++ .../pages/version-viewer.component.tsx | 2 - .../partials/beat-saver/sort-order.ts | 8 +++ .../{ => maps}/map-tags/map-styles.ts | 0 .../partials/{ => maps}/map-tags/map-types.ts | 0 .../beat-saver/beat-saver-api.service.ts | 71 ++++++++++++++++++- .../services/beat-saver/beat-saver.service.ts | 15 +++- .../services/maps-downloader.service.ts | 10 ++- src/shared/models/maps/beat-saver.model.ts | 5 +- 13 files changed, 201 insertions(+), 27 deletions(-) create mode 100644 src/renderer/components/modal/modal-types/download-maps-modal.component.tsx create mode 100644 src/renderer/components/shared/bsm-select.component.tsx create mode 100644 src/renderer/partials/beat-saver/sort-order.ts rename src/renderer/partials/{ => maps}/map-tags/map-styles.ts (100%) rename src/renderer/partials/{ => maps}/map-tags/map-types.ts (100%) diff --git a/src/renderer/components/maps-mangement-components/filter-panel.component.tsx b/src/renderer/components/maps-mangement-components/filter-panel.component.tsx index c9e901c8..e681f658 100644 --- a/src/renderer/components/maps-mangement-components/filter-panel.component.tsx +++ b/src/renderer/components/maps-mangement-components/filter-panel.component.tsx @@ -1,8 +1,8 @@ import { MapFilter, MapTag } from "shared/models/maps/beat-saver.model" import {motion} from "framer-motion" import { MutableRefObject} from "react" -import { MAP_TYPES } from "renderer/partials/map-tags/map-types" -import { MAP_STYLES } from "renderer/partials/map-tags/map-styles" +import { MAP_TYPES } from "renderer/partials/maps/map-tags/map-types" +import { MAP_STYLES } from "renderer/partials/maps/map-tags/map-styles" import { BsmCheckbox } from "../shared/bsm-checkbox.component" import { diffColors } from "./map-item.component" import { min_to_s } from "renderer/helpers/time-utils" @@ -13,7 +13,7 @@ export type Props = { className?: string, ref?: MutableRefObject playlist?: boolean, - filter?: MapFilter + filter: MapFilter onChange?: (filter: MapFilter) => void } @@ -65,7 +65,7 @@ export function FilterPanel({className, ref, playlist = false, filter, onChange} } const onNpssChange = ([min, max]: number[]) => { - const newFilter: MapFilter = {...(filter ?? {}), minNps: min, maxNps: max}; + const newFilter: MapFilter = {...filter, minNps: min, maxNps: max}; if(max === MAX_NPS){ delete newFilter["maxNps"]; } @@ -73,7 +73,7 @@ export function FilterPanel({className, ref, playlist = false, filter, onChange} } const onDurationsChange = ([min, max]: number[]) => { - const newFilter: MapFilter = {...(filter ?? {}), minDuration: min, maxDuration: max}; + const newFilter: MapFilter = {...filter, minDuration: min, maxDuration: max}; if(max === MAX_DURATION){ delete newFilter["maxDuration"]; } @@ -128,40 +128,40 @@ export function FilterPanel({className, ref, playlist = false, filter, onChange}
{/* TODO TRADUIRE */}

general

-
handleCheckbox("automapper")}> +
handleCheckbox("automapper")}> handleCheckbox("automapper")}/> AI
-
handleCheckbox("ranked")}> +
handleCheckbox("ranked")}> handleCheckbox("ranked")}/> Rancked
-
handleCheckbox("curated")}> +
handleCheckbox("curated")}> handleCheckbox("curated")}/> Curated
-
handleCheckbox("verified")}> +
handleCheckbox("verified")}> handleCheckbox("verified")}/> Verified Mapper
-
handleCheckbox("fullSpread")}> +
handleCheckbox("fullSpread")}> handleCheckbox("fullSpread")}/> Full Spread

requirements

-
handleCheckbox("chroma")}> +
handleCheckbox("chroma")}> handleCheckbox("chroma")}/> Chroma
-
handleCheckbox("noodle")}> +
handleCheckbox("noodle")}> handleCheckbox("noodle")}/> Noodle
-
handleCheckbox("me")}> +
handleCheckbox("me")}> handleCheckbox("me")}/> Me
-
handleCheckbox("cinema")}> +
handleCheckbox("cinema")}> handleCheckbox("cinema")}/> Cinema
diff --git a/src/renderer/components/maps-mangement-components/maps-playlists-panel.component.tsx b/src/renderer/components/maps-mangement-components/maps-playlists-panel.component.tsx index 2adf4782..2d79f3ec 100644 --- a/src/renderer/components/maps-mangement-components/maps-playlists-panel.component.tsx +++ b/src/renderer/components/maps-mangement-components/maps-playlists-panel.component.tsx @@ -10,6 +10,7 @@ import { useThemeColor } from "renderer/hooks/use-theme-color.hook" import { MapsManagerService } from "renderer/services/maps-manager.service" import { motion, Variants } from "framer-motion"; import ReactTooltip from 'react-tooltip'; +import { MapsDownloaderService } from "renderer/services/maps-downloader.service" type Props = { oneBlock?: boolean, @@ -19,6 +20,7 @@ type Props = { export function MapsPlaylistsPanel({version, oneBlock = false}: Props) { const mapsService = MapsManagerService.getInstance(); + const mapsDownloader = MapsDownloaderService.getInstance(); const [tabIndex, setTabIndex] = useState(0); const [mapFilter, setMapFilter] = useState({ @@ -58,7 +60,7 @@ export function MapsPlaylistsPanel({version, oneBlock = false}: Props) { } const handleMapsAddClick = () => { - + mapsDownloader.openDownloadMapModal(version); } const handlePlaylistAddClick = () => { @@ -90,7 +92,7 @@ export function MapsPlaylistsPanel({version, oneBlock = false}: Props) { - {e.stopPropagation(); onClickLink(index)}}/> + {e.stopPropagation(); onClickAdd(index)}}/> Ajouté des maps {/* TODO TRANSLATE */} diff --git a/src/renderer/components/modal/modal-types/download-maps-modal.component.tsx b/src/renderer/components/modal/modal-types/download-maps-modal.component.tsx new file mode 100644 index 00000000..d01280b9 --- /dev/null +++ b/src/renderer/components/modal/modal-types/download-maps-modal.component.tsx @@ -0,0 +1,49 @@ +import { useEffect, useState } from "react"; +import { FilterPanel } from "renderer/components/maps-mangement-components/filter-panel.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"; +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"; + +export const DownloadMapsModal: ModalComponent = ({data}) => { + + const beatSaver = BeatSaverService.getInstance(); + + const [filter, setFilter] = useState(null); + const [maps, setMaps] = useState(null); + const [page, setPage] = useState(0); + const [sortOrder, setSortOrder] = useState(BSV_SORT_ORDER.at(0)); + + const sortOptions: BsmSelectOption[] = (() => { + return BSV_SORT_ORDER.map(sort => ({text: sort, value: sort})); + })(); + + console.log(maps); + + useEffect(() => { + beatSaver.searchMaps({ + page, sortOrder + }).then(setMaps); + }, []) + + + return ( +
+
+ + + + + + +
+
    + +
+
+ ) +} diff --git a/src/renderer/components/modal/modal.component.tsx b/src/renderer/components/modal/modal.component.tsx index 1b610182..01518a96 100644 --- a/src/renderer/components/modal/modal.component.tsx +++ b/src/renderer/components/modal/modal.component.tsx @@ -20,8 +20,10 @@ export function Modal() {
modalSevice.resolve({exitCode: ModalExitCode.NO_CHOICE})} className="absolute top-0 bottom-0 right-0 left-0 bg-black" initial={{opacity: 0}} animate={{opacity: ModalComponent && .60}} exit={{opacity: 0}} transition={{duration: .2}}/> -
- +
+
+ +
diff --git a/src/renderer/components/shared/bsm-select.component.tsx b/src/renderer/components/shared/bsm-select.component.tsx new file mode 100644 index 00000000..5be4d8cd --- /dev/null +++ b/src/renderer/components/shared/bsm-select.component.tsx @@ -0,0 +1,28 @@ +import { ChangeEvent, CSSProperties } from "react" + +type Props = { + className?: string, + style?: CSSProperties, + options?: BsmSelectOption[], + onChange?: (value: any) => void +} + +export function BsmSelect({className, style, options, onChange}: Props) { + + const handleChange = (event: ChangeEvent) => { + onChange?.(event.target.value); + } + + return ( + + ) +} + +export interface BsmSelectOption{ + text: string, + value: string +} diff --git a/src/renderer/pages/version-viewer.component.tsx b/src/renderer/pages/version-viewer.component.tsx index 520878ee..d4a36ea7 100644 --- a/src/renderer/pages/version-viewer.component.tsx +++ b/src/renderer/pages/version-viewer.component.tsx @@ -3,7 +3,6 @@ import { useState } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; import BSLogo from '../../../assets/images/apngs/bs-logo.png'; import { TabNavBar } from 'renderer/components/shared/tab-nav-bar.component'; -import wipGif from "../../../assets/images/gifs/wip.gif" import { BsmDropdownButton } from 'renderer/components/shared/bsm-dropdown-button.component'; import { BsmImage } from 'renderer/components/shared/bsm-image.component'; import { BSUninstallerService } from '../services/bs-uninstaller.service'; @@ -17,7 +16,6 @@ import { LaunchSlide } from 'renderer/components/version-viewer/slides/launch/la import { ModsSlide } from 'renderer/components/version-viewer/slides/mods/mods-slide.component'; import { UninstallModal } from 'renderer/components/modal/modal-types/uninstall-modal.component'; import { MapsPlaylistsPanel } from 'renderer/components/maps-mangement-components/maps-playlists-panel.component'; -import { LocalMapsListPanel } from 'renderer/components/maps-mangement-components/local-maps-list-panel.component'; export function VersionViewer() { diff --git a/src/renderer/partials/beat-saver/sort-order.ts b/src/renderer/partials/beat-saver/sort-order.ts new file mode 100644 index 00000000..de4f1a75 --- /dev/null +++ b/src/renderer/partials/beat-saver/sort-order.ts @@ -0,0 +1,8 @@ +import { SearchOrder } from "shared/models/maps/beat-saver.model"; + +export const BSV_SORT_ORDER: SearchOrder[] = [ + "Latest", + "Relevance", + "Rating", + "Curated" +]; \ No newline at end of file diff --git a/src/renderer/partials/map-tags/map-styles.ts b/src/renderer/partials/maps/map-tags/map-styles.ts similarity index 100% rename from src/renderer/partials/map-tags/map-styles.ts rename to src/renderer/partials/maps/map-tags/map-styles.ts diff --git a/src/renderer/partials/map-tags/map-types.ts b/src/renderer/partials/maps/map-tags/map-types.ts similarity index 100% rename from src/renderer/partials/map-tags/map-types.ts rename to src/renderer/partials/maps/map-tags/map-types.ts diff --git a/src/renderer/services/beat-saver/beat-saver-api.service.ts b/src/renderer/services/beat-saver/beat-saver-api.service.ts index 3446425b..862df710 100644 --- a/src/renderer/services/beat-saver/beat-saver-api.service.ts +++ b/src/renderer/services/beat-saver/beat-saver-api.service.ts @@ -15,6 +15,57 @@ export class BeatSaverApiService { private constructor(){} + private mapFilterToUrlParams(filter: MapFilter): URLSearchParams{ + + if(!filter){ return new URLSearchParams(); } + + const enbledTagsString = Array.from(filter.enabledTags); + const excludedTagsString = Array.from(filter.excludedTags).map(tag => `!${tag}`); + + const tags = [...enbledTagsString, excludedTagsString].join("|"); + + 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 ?? ""), + }; + + return new URLSearchParams(params); + + } + + private searchParamsToUrlParams(search: SearchParams): URLSearchParams{ + + if(!search){ return new URLSearchParams(); } + + const searchParams = { + includeEmpty: String(search.includeEmpty ?? ""), + sortOrder: String(search.sortOrder ?? ""), + q: String(search.q ?? "") + }; + + const filterUrlParms = this.mapFilterToUrlParams(search.filter); + + return new URLSearchParams({ + ...searchParams, + ...Object.fromEntries(filterUrlParms) + }); + + } + public async getMapsDetailsByHashs(hashs: T[]): Promise, BsvMapDetail>>>{ if(hashs.length > 50){ throw "too musch map hashs"; } @@ -37,8 +88,24 @@ export class BeatSaverApiService { } - public async searchMaps(params: SearchParams): Promise>{ - + public async searchMaps(search: SearchParams): Promise>{ + + const url = new URL(`${this.bsaverApiUrl}/search/text/${search?.page ?? 0}`); + + url.search = this.searchParamsToUrlParams(search).toString(); + + const res = await fetch(url); + + console.log(res); + + if(!res.ok){ + return {status: res.status, data: null}; + } + + const data = await res.json(); + + return {status: res.status, data}; + } } \ No newline at end of file diff --git a/src/renderer/services/beat-saver/beat-saver.service.ts b/src/renderer/services/beat-saver/beat-saver.service.ts index b1ee2a37..1c2d0071 100644 --- a/src/renderer/services/beat-saver/beat-saver.service.ts +++ b/src/renderer/services/beat-saver/beat-saver.service.ts @@ -2,6 +2,7 @@ import { splitIntoChunk } from "renderer/helpers/array-tools"; import { of } from "rxjs"; import { Observable } from "rxjs"; import { BsvMapDetail } from "shared/models/maps"; +import { SearchParams } from "shared/models/maps/beat-saver.model"; import { OsDiagnosticService } from "../os-diagnostic.service"; import { BeatSaverApiService } from "./beat-saver-api.service"; @@ -61,8 +62,20 @@ export class BeatSaverService { observer.complete(); })() - }) + }); + } + + public searchMaps(search: SearchParams): Promise{ + + return this.bsaverApi.searchMaps(search).then(res => { + return res.status === 200 ? res.data.docs : []; + }).catch(err => { + console.log(err); + return []; + }); } + + } \ No newline at end of file diff --git a/src/renderer/services/maps-downloader.service.ts b/src/renderer/services/maps-downloader.service.ts index 9369a364..4634a32b 100644 --- a/src/renderer/services/maps-downloader.service.ts +++ b/src/renderer/services/maps-downloader.service.ts @@ -1,4 +1,6 @@ +import { DownloadMapsModal } from "renderer/components/modal/modal-types/download-maps-modal.component"; import { BSVersion } from "shared/bs-version.interface"; +import { ModalResponse, ModalService } from "./modale.service"; export class MapsDownloaderService { @@ -9,12 +11,16 @@ export class MapsDownloaderService { return MapsDownloaderService.instance; } - private constructor(){ + private readonly modals: ModalService; + private constructor(){ + this.modals = ModalService.getInsance(); } - public openDownloadMapModal(version?: BSVersion){ + public openDownloadMapModal(version?: BSVersion): Promise>{ + return this.modals.openModal(DownloadMapsModal, version); + } } \ No newline at end of file diff --git a/src/shared/models/maps/beat-saver.model.ts b/src/shared/models/maps/beat-saver.model.ts index 8b99bb59..6f2ed8bc 100644 --- a/src/shared/models/maps/beat-saver.model.ts +++ b/src/shared/models/maps/beat-saver.model.ts @@ -173,10 +173,11 @@ export interface SearchResponse { } export interface SearchParams { - order: SearchOrder, + sortOrder: SearchOrder, filter?: MapFilter, page?: number, - q?: string + q?: string, + includeEmpty?: boolean } export type SearchOrder = "Latest"|"Relevance"|"Rating"|"Curated"; \ No newline at end of file