diff --git a/src/main/helpers/array-tools.ts b/src/main/helpers/array-tools.ts new file mode 100644 index 00000000..e7967792 --- /dev/null +++ b/src/main/helpers/array-tools.ts @@ -0,0 +1,7 @@ +export function splitIntoChunk(arr: T[], chunkSize: number): T[][] { + const resArr = []; + for(let i = 0; i < arr.length; i += chunkSize){ + resArr.push(arr.slice(i, i + chunkSize)); + } + return resArr; +} \ No newline at end of file diff --git a/src/main/ipcs/beat-saver-ipcs.ts b/src/main/ipcs/beat-saver-ipcs.ts new file mode 100644 index 00000000..1eaa4e15 --- /dev/null +++ b/src/main/ipcs/beat-saver-ipcs.ts @@ -0,0 +1,27 @@ +import { ipcMain } from "electron"; +import { UtilsService } from "../services/utils.service"; +import { IpcRequest } from "shared/models/ipc"; +import { SearchParams } from "shared/models/maps/beat-saver.model"; +import { BeatSaverService } from "../services/thrid-party/beat-saver/beat-saver.service"; + +ipcMain.on("bsv-search-map", async (event, request: IpcRequest) => { + const utlis = UtilsService.getInstance(); + const bsvService = BeatSaverService.getInstance(); + + bsvService.searchMaps(request.args).then(maps => { + utlis.ipcSend(request.responceChannel, {success: true, data: maps}); + }).catch(e => { + utlis.ipcSend(request.responceChannel, {success: false, error: e}); + }) +}); + +ipcMain.on("bsv-bet-map-details-from-hashs", async (event, request: IpcRequest) => { + const utlis = UtilsService.getInstance(); + const bsvService = BeatSaverService.getInstance(); + + bsvService.getMapDetailsFromHashs(request.args).then(maps => { + utlis.ipcSend(request.responceChannel, {success: true, data: maps}); + }).catch(e => { + utlis.ipcSend(request.responceChannel, {success: false, error: e}); + }) +}); \ No newline at end of file diff --git a/src/main/ipcs/index.ts b/src/main/ipcs/index.ts index 7a2d2395..ebb87582 100644 --- a/src/main/ipcs/index.ts +++ b/src/main/ipcs/index.ts @@ -8,3 +8,4 @@ import './launcher-ipcs'; import './window-manager-ipcs'; import './bs-mods-ipcs'; import './bs-maps-ipcs'; +import './beat-saver-ipcs' \ No newline at end of file diff --git a/src/renderer/services/beat-saver/beat-saver-api.service.ts b/src/main/services/thrid-party/beat-saver/beat-saver-api.service.ts similarity index 94% rename from src/renderer/services/beat-saver/beat-saver-api.service.ts rename to src/main/services/thrid-party/beat-saver/beat-saver-api.service.ts index f2455e0c..9d57f801 100644 --- a/src/renderer/services/beat-saver/beat-saver-api.service.ts +++ b/src/main/services/thrid-party/beat-saver/beat-saver-api.service.ts @@ -1,6 +1,7 @@ import { ApiResult } from "renderer/models/api/api.model"; import { BsvMapDetail } from "shared/models/maps"; -import { MapFilter, SearchOrder, SearchParams, SearchResponse } from "shared/models/maps/beat-saver.model"; +import { MapFilter, SearchParams, SearchResponse } from "shared/models/maps/beat-saver.model"; +import fetch from "node-fetch" export class BeatSaverApiService { @@ -90,11 +91,13 @@ export class BeatSaverApiService { public async searchMaps(search: SearchParams): Promise>{ + console.log(search); + const url = new URL(`${this.bsaverApiUrl}/search/text/${search?.page ?? 0}`); url.search = this.searchParamsToUrlParams(search).toString(); - const res = await fetch(url); + const res = await fetch(url.toString()); console.log(res); @@ -102,7 +105,7 @@ export class BeatSaverApiService { return {status: res.status, data: null}; } - const data = await res.json(); + const data: any = await res.json(); return {status: res.status, data}; diff --git a/src/renderer/services/beat-saver/beat-saver.service.ts b/src/main/services/thrid-party/beat-saver/beat-saver.service.ts similarity index 51% rename from src/renderer/services/beat-saver/beat-saver.service.ts rename to src/main/services/thrid-party/beat-saver/beat-saver.service.ts index d1ca9e4b..c05897de 100644 --- a/src/renderer/services/beat-saver/beat-saver.service.ts +++ b/src/main/services/thrid-party/beat-saver/beat-saver.service.ts @@ -1,9 +1,6 @@ -import { splitIntoChunk } from "renderer/helpers/array-tools"; -import { of } from "rxjs"; -import { Observable } from "rxjs"; +import { splitIntoChunk } from "../../../helpers/array-tools"; 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"; export class BeatSaverService { @@ -16,17 +13,16 @@ export class BeatSaverService { } private readonly bsaverApi: BeatSaverApiService; - private readonly os: OsDiagnosticService; - - private readonly cachedMapsDetails = new Map() + private readonly cachedMapsDetails = new Map(); private constructor(){ this.bsaverApi = BeatSaverApiService.getInstance(); - this.os = OsDiagnosticService.getInstance(); } - public getMapDetailsFromHashs(hashs: string[]): Observable{ + public async getMapDetailsFromHashs(hashs: string[]): Promise{ + + console.log(hashs); const filtredHashs = hashs.map(h => h.toLowerCase()).filter(hash => !Array.from(this.cachedMapsDetails.keys()).includes(hash)); const chunkHash = splitIntoChunk(filtredHashs, 50); @@ -38,37 +34,19 @@ export class BeatSaverService { return res; }, [] as BsvMapDetail[]); - if(this.os.isOffline){ - return of(mapDetails); + for(const hashs of chunkHash){ + + const res = await this.bsaverApi.getMapsDetailsByHashs(hashs); + + if(res.status === 200){ + mapDetails.push(...Object.values(res.data).filter(detail => !!detail)); + mapDetails.forEach(detail => { + this.cachedMapsDetails.set(detail.versions.at(0).hash.toLowerCase(), detail); + }); + } } - return new Observable(observer => { - (async () => { - - if(mapDetails.length > 0){ - observer.next(mapDetails); - } - - for(const hashs of chunkHash){ - - const res = await this.bsaverApi.getMapsDetailsByHashs(hashs); - - if(res.status === 200){ - mapDetails.push(...Object.values(res.data).filter(detail => !!detail)); - mapDetails.forEach(detail => { - this.cachedMapsDetails.set(detail.versions.at(0).hash.toLowerCase(), detail); - }); - } - - if(mapDetails.length > 0){ - observer.next(mapDetails); - } - - } - - observer.complete(); - })() - }); + return mapDetails; } public searchMaps(search: SearchParams): Promise{ diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 54a91641..c93fdb04 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -13,6 +13,7 @@ import { PageStateService } from "./services/page-state.service"; import { MapsPage } from "./pages/maps-page.component"; import "tailwindcss/tailwind.css"; import { BsmIframeView } from "./components/shared/bsm-map-preview.component"; +import 'tippy.js/dist/tippy.css'; export default function App() { 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 index e4809011..5ba1352f 100644 --- a/src/renderer/components/modal/modal-types/download-maps-modal.component.tsx +++ b/src/renderer/components/modal/modal-types/download-maps-modal.component.tsx @@ -7,7 +7,7 @@ import { BsmDropdownButton } from "renderer/components/shared/bsm-dropdown-butto import { BsmSelect, BsmSelectOption } from "renderer/components/shared/bsm-select.component"; import { useObservable } from "renderer/hooks/use-observable.hook"; import { BSV_SORT_ORDER } from "renderer/partials/beat-saver/sort-order"; -import { BeatSaverService } from "renderer/services/beat-saver/beat-saver.service"; +import { BeatSaverService } from "renderer/services/thrird-partys/beat-saver.service"; import { MapsDownloaderService } from "renderer/services/maps-downloader.service"; import { MapsManagerService } from "renderer/services/maps-manager.service"; import { ModalComponent } from "renderer/services/modale.service"; diff --git a/src/renderer/components/shared/bsm-image.component.tsx b/src/renderer/components/shared/bsm-image.component.tsx index 5f65af73..719c212a 100644 --- a/src/renderer/components/shared/bsm-image.component.tsx +++ b/src/renderer/components/shared/bsm-image.component.tsx @@ -1,6 +1,17 @@ -import { CSSProperties, SyntheticEvent, useState } from "react"; +import { CSSProperties, forwardRef, SyntheticEvent, useState } from "react"; -export function BsmImage({className, image, errorImage, placeholder, loading, style}: {className?: string, image: string, errorImage?: string, placeholder?: string, loading?: "lazy"|"eager", style?: CSSProperties}) { +type Props = { + className?: string, + image: string, + errorImage?: string, + placeholder?: string, + loading?: "lazy"|"eager", + style?: CSSProperties + title?: string, + onClick?: (e: MouseEvent) => void +} + +export const BsmImage = forwardRef(({className, image, errorImage, placeholder, loading, style, title, onClick}: Props, ref) => { const [isLoaded, setIsLoaded] = useState(false); @@ -22,6 +33,7 @@ export function BsmImage({className, image, errorImage, placeholder, loading, st } return ( - + // @ts-ignore + onClick?.(e)}/> ) -} +}) diff --git a/src/renderer/services/maps-manager.service.ts b/src/renderer/services/maps-manager.service.ts index 8026c816..3ccf1f27 100644 --- a/src/renderer/services/maps-manager.service.ts +++ b/src/renderer/services/maps-manager.service.ts @@ -4,7 +4,7 @@ import { finalize } from "rxjs/operators"; import { Subject, Observable } from "rxjs"; import { BSVersion } from "shared/bs-version.interface"; import { BsmLocalMap } from "shared/models/maps/bsm-local-map.interface"; -import { BeatSaverService } from "./beat-saver/beat-saver.service"; +import { BeatSaverService } from "./thrird-partys/beat-saver.service"; import { IpcService } from "./ipc.service"; import { ModalExitCode, ModalService } from "./modale.service"; import { DeleteMapsModal } from "renderer/components/modal/modal-types/delete-maps-modal.component"; @@ -47,7 +47,7 @@ export class MapsManagerService { if(!withDetails){ return obs.complete(); } - this.bsaver.getMapDetailsFromHashs(res.data.map(localMap => localMap.hash)).pipe(finalize(() => obs.complete())).subscribe(mapsDetails => { + this.bsaver.getMapDetailsFromHashs(res.data.map(localMap => localMap.hash)).then(mapsDetails => { mapsDetails.forEach(details => { const corespondingMap = res.data.find(localMap => localMap.hash === details.versions.find(details => details?.hash === localMap.hash)?.hash); if(!corespondingMap){ return; } @@ -159,6 +159,10 @@ export class MapsManagerService { this.progressBar.hide(true); } + public async enableDeepLink(): Promise{ + return true; + } + public get versionLinked$(): Observable{ return this.lastLinkedVersion$.asObservable(); } diff --git a/src/renderer/services/thrird-partys/beat-saver.service.ts b/src/renderer/services/thrird-partys/beat-saver.service.ts new file mode 100644 index 00000000..9613d9b7 --- /dev/null +++ b/src/renderer/services/thrird-partys/beat-saver.service.ts @@ -0,0 +1,31 @@ +import { BsvMapDetail } from "shared/models/maps"; +import { SearchParams } from "shared/models/maps/beat-saver.model"; +import { IpcService } from "../ipc.service"; + +export class BeatSaverService { + + private static instance: BeatSaverService; + + public static getInstance(): BeatSaverService{ + if(!BeatSaverService.instance){ BeatSaverService.instance = new BeatSaverService(); } + return BeatSaverService.instance; + } + private readonly ipc: IpcService; + + private constructor(){ + this.ipc = IpcService.getInstance(); + } + + public async getMapDetailsFromHashs(hashs: string[]): Promise{ + const res = await this.ipc.send("bsv-bet-map-details-from-hashs", {args: hashs}); + return res.data ?? []; + } + + public async searchMaps(search: SearchParams): Promise{ + const res = await this.ipc.send("bsv-search-map", {args: search}); + return res.data ?? [] + } + + + +} \ No newline at end of file