diff --git a/src/main/services/additional-content/maps/local-maps-manager.service.ts b/src/main/services/additional-content/maps/local-maps-manager.service.ts index 8d81d5ef..a5c1ac98 100644 --- a/src/main/services/additional-content/maps/local-maps-manager.service.ts +++ b/src/main/services/additional-content/maps/local-maps-manager.service.ts @@ -328,32 +328,34 @@ export class LocalMapsManagerService { return null; } - public importMaps(zipPaths: string[], version?: BSVersion): Observable { - const progress: Progression = { + + + public importMaps(zipPaths: string[], version?: BSVersion): Observable> { + const progress: Progression = { total: zipPaths.length, current: 0, - data: "", }; - return new Observable>(observer => { + return new Observable>(observer => { (async () => { - for (const zipPath of zipPaths) { - ++progress.current; - progress.data = zipPath; - observer.next(progress); + try { + observer.next(progress); // 0% - try { - await this.importMap(zipPath, version); - } catch (error: any) { - log.error(`Could not import "${zipPath}"`, error); - this.ipc.send<{map: string, error: string}>( - "map-not-imported", - this.windows.getWindows("index.html").at(0), - { map: zipPath, error: error.code } - ); + for (const zipPath of zipPaths) { + ++progress.current; + try { + progress.data = await this.importMap(zipPath, version); + } catch (error: any) { + log.error(`Could not import "${zipPath}"`, error); + progress.data = undefined; + } + observer.next(progress); } + } catch(error: any) { + observer.error(error); + } finally { + observer.complete(); } - observer.complete(); })(); }); } @@ -371,8 +373,8 @@ export class LocalMapsManagerService { log.info(`Importing map "${zipPath}" to "${mapPath}"`); const zip = await JSZip.loadAsync(await readFile(zipPath)); - const infoFile = zip.file("Info.dat"); - if (!infoFile) { // Simple check for importing maps + const infoFiles = zip.file(/(I|i)nfo.dat/); + if (infoFiles.length === 0) { // Simple check for importing maps throw new CustomError(`Invalid zip file "${zipPath}"`, "invalid-zip"); } @@ -394,12 +396,6 @@ export class LocalMapsManagerService { const localMap = await this.loadMapInfoFromPath(mapPath); localMap.songDetails = this.songDetailsCache.getSongDetails(localMap.hash); - this.ipc.send<{map: BsmLocalMap, version?: BSVersion}>( - "map-imported", - this.windows.getWindows("index.html").at(0), - { map: localMap, version } - ); - return localMap; } catch (error: any) { throw error instanceof CustomError diff --git a/src/renderer/components/maps-playlists-panel/maps/local-maps-list-panel.component.tsx b/src/renderer/components/maps-playlists-panel/maps/local-maps-list-panel.component.tsx index a43835ca..c35ce18b 100644 --- a/src/renderer/components/maps-playlists-panel/maps/local-maps-list-panel.component.tsx +++ b/src/renderer/components/maps-playlists-panel/maps/local-maps-list-panel.component.tsx @@ -131,7 +131,7 @@ export const LocalMapsListPanel = forwardRef(({ ve const mapsCopy = maps ? [ ...maps ] : []; // importMap can collide with existing map - const index = maps.findIndex(map => map.songDetails.name === importMap.songDetails.name); + const index = maps.findIndex(map => map.songDetails?.name === importMap.songDetails.name); if (index > -1) { mapsCopy.splice(index, 1); } diff --git a/src/renderer/services/maps-manager.service.ts b/src/renderer/services/maps-manager.service.ts index 6c321cf5..4c6055b1 100644 --- a/src/renderer/services/maps-manager.service.ts +++ b/src/renderer/services/maps-manager.service.ts @@ -39,7 +39,7 @@ export class MapsManagerService { private readonly lastLinkedVersion$: Subject = new Subject(); private readonly lastUnlinkedVersion$: Subject = new Subject(); - private importListeners: ((map: BsmLocalMap, version?: BSVersion) => void)[] = []; + private importListeners = new Set<((map: BsmLocalMap, version?: BSVersion) => void)>(); private importCount = 0; private constructor() { @@ -49,23 +49,6 @@ export class MapsManagerService { this.notifications = NotificationService.getInstance(); this.config = ConfigurationService.getInstance(); this.linker = VersionFolderLinkerService.getInstance(); - - this.ipcService.watch<{map: BsmLocalMap, version?: BSVersion}>("map-imported") - .subscribe(data => { - ++this.importCount; - this.importListeners.forEach(listener => listener(data.map, data.version)); - }); - - // NOTE: Generic notify call from server side - this.ipcService.watch<{map: string, error: string}>("map-not-imported") - .subscribe(data => { - this.notifications.notifyError({ - title: "notifications.maps.import-map.titles.error", - desc: ["not-found-zip", "invalid-zip"].includes(data.error) - ? `notifications.maps.import-map.msgs.${data.error}` - : data.error - }); - }); } public getMaps(version?: BSVersion): Observable { @@ -222,20 +205,29 @@ export class MapsManagerService { return 0; } - // Incremented by receiving an ipc call on "map-imported" this.importCount = 0; - const importProgress$: Observable = this.ipcService.sendV2( - "bs-maps.import-maps", - { paths, version } - ) - .pipe(map(progress => ({ - progression: (progress.current / progress.total) * 100, - label: progress.data - } as ProgressionInterface))); + const importObserver$ = this.ipcService.sendV2( + "bs-maps.import-maps", + { paths, version } + ); + importObserver$.subscribe(progress => { + if (!progress.data) { + return; + } - this.progressBar.show(importProgress$); - await lastValueFrom(importProgress$); + ++this.importCount; + this.importListeners.forEach(listeners => listeners(progress.data, version)); + }); + + this.progressBar.show(importObserver$.pipe( + map(progress => ({ + progression: (progress.current / progress.total) * 100, + label: progress.data?.songDetails?.name + } as ProgressionInterface)) + )); + + await lastValueFrom(importObserver$); return this.importCount; } catch (error: any) { this.notifications.notifyError({ @@ -264,14 +256,11 @@ export class MapsManagerService { } public addImportListener(listener: (map: BsmLocalMap, version?: BSVersion) => void): void { - this.importListeners.push(listener); + this.importListeners.add(listener); } public removeImportListener(listener: (map: BsmLocalMap, version?: BSVersion) => void): void { - const index = this.importListeners.indexOf(listener); - if (index > -1) { - this.importListeners.splice(index, 1); - } + this.importListeners.delete(listener); } public get versionLinked$(): Observable { diff --git a/src/shared/models/ipc/ipc-routes.ts b/src/shared/models/ipc/ipc-routes.ts index bcdefe20..53538214 100644 --- a/src/shared/models/ipc/ipc-routes.ts +++ b/src/shared/models/ipc/ipc-routes.ts @@ -58,7 +58,7 @@ export interface IpcChannelMapping { "load-version-maps": { request: BSVersion, response: BsmLocalMapsProgress}; "delete-maps": { request: BsmLocalMap[], response: DeleteMapsProgress }; "export-maps": { request: { version: BSVersion; maps: BsmLocalMap[]; outPath: string }, response: Progression }; - "bs-maps.import-maps": { request: { paths: string[]; version: BSVersion | undefined }, response: Progression }; + "bs-maps.import-maps": { request: { paths: string[]; version: BSVersion | undefined }, response: Progression }; "bs-maps.download-map": { request: { map: BsvMapDetail; version: BSVersion | undefined }, response: BsmLocalMap }; "last-downloaded-map": { request: void, response: { version?: BSVersion, map: BsmLocalMap } }; "one-click-install-map": { request: BsvMapDetail, response: void };