From b98e1ccdfadc6159183d68aeb1c2c52c6b2959c4 Mon Sep 17 00:00:00 2001 From: MathieuG-P <40181755+Zagrios@users.noreply.github.com> Date: Wed, 27 Mar 2024 21:33:19 +0100 Subject: [PATCH] [feature-107] Advancement on playlists feature --- src/main/ipcs/bs-playlist-ipcs.ts | 14 ++++- .../local-playlists-manager.service.ts | 31 +-------- .../maps/local-maps-manager.service.ts | 60 +++++++++++++++--- .../maps/song-cache.service.ts | 4 +- .../local-playlists-list-panel.component.tsx | 20 ++++-- .../playlists/playlist-item.component.tsx | 63 +++++++++++++------ .../delete-playlist-modal.component.tsx | 34 ++++++++++ .../components/modal/modal.component.tsx | 2 +- .../notification-item.component.tsx | 2 +- .../svgs/icons/close-icon.component.tsx | 10 +-- .../services/playlist-downloader.service.ts | 50 +++++++++------ .../services/playlists-manager.service.ts | 4 +- src/shared/helpers/array.helpers.ts | 5 ++ src/shared/helpers/type.helpers.ts | 1 + src/shared/models/ipc/ipc-routes.ts | 4 +- 15 files changed, 210 insertions(+), 94 deletions(-) create mode 100644 src/renderer/components/modal/modal-types/playlist/delete-playlist-modal.component.tsx create mode 100644 src/shared/helpers/type.helpers.ts diff --git a/src/main/ipcs/bs-playlist-ipcs.ts b/src/main/ipcs/bs-playlist-ipcs.ts index 3e1e7336..5af61e25 100644 --- a/src/main/ipcs/bs-playlist-ipcs.ts +++ b/src/main/ipcs/bs-playlist-ipcs.ts @@ -1,10 +1,11 @@ import { LocalBPList } from "shared/models/playlists/local-playlist.models"; import { LocalPlaylistsManagerService } from "../services/additional-content/local-playlists-manager.service"; import { IpcService } from "../services/ipc.service"; -import { of } from "rxjs"; +import { mergeMap, of } from "rxjs"; import { BPList } from "shared/models/playlists/playlist.interface"; import path from "path"; -import { pathToFileURL } from "url"; +import { LocalMapsManagerService } from "../services/additional-content/maps/local-maps-manager.service"; +import { Progression } from "main/helpers/fs.helpers"; const ipc = IpcService.getInstance(); @@ -61,5 +62,12 @@ ipc.on("get-version-playlists-details", (args, reply) => { ipc.on("delete-playlist", (args, reply) => { const playlists = LocalPlaylistsManagerService.getInstance(); - reply(playlists.deletePlaylist(args)); + const maps = LocalMapsManagerService.getInstance(); + reply(playlists.deletePlaylistFile(args.bpList).pipe(mergeMap(() => { + if(args.deleteMaps){ + console.log("ALALALZELALZELAZELA"); + return maps.deleteMapsFromHashs(args.version, args.bpList.songs.map(s => s.hash)); + } + return of({ current: 0, total: 0 } as Progression); + }))); }); diff --git a/src/main/services/additional-content/local-playlists-manager.service.ts b/src/main/services/additional-content/local-playlists-manager.service.ts index b23249cf..2388b7c2 100644 --- a/src/main/services/additional-content/local-playlists-manager.service.ts +++ b/src/main/services/additional-content/local-playlists-manager.service.ts @@ -20,6 +20,7 @@ import { SongCacheService } from "./maps/song-cache.service"; import { InstallationLocationService } from "../installation-location.service"; import sanitize from "sanitize-filename"; import { isValidUrl } from "shared/helpers/url.helpers"; +import { allSettled } from "shared/helpers/promise.helpers"; export class LocalPlaylistsManagerService { private static instance: LocalPlaylistsManagerService; @@ -284,34 +285,8 @@ export class LocalPlaylistsManagerService { } - public deletePlaylist(opt: {path: string, deleteMaps?: boolean}): Observable{ - - return new Observable(obs => { - (async () => { - - const bpList = await this.readPlaylistFromSource(opt.path); - - const progress: Progression = { current: 0, total: opt.deleteMaps ? bpList.songs.length + 1 : 1}; - - if(opt.deleteMaps){ - const mapsHashs = bpList.songs.map(s => ({ hash: s.hash })); - await lastValueFrom(this.maps.deleteMaps(mapsHashs).pipe( - tap({ - next: () => { - progress.current += 1 - obs.next(progress); - }, - }), - )); - } - - await unlinkPath(opt.path); - progress.current += 1; - obs.next(progress); - })() - .catch(err => obs.error(err)) - .finally(() => obs.complete()); - }); + public deletePlaylistFile(bpList: LocalBPList): Observable{ + return from(unlinkPath(bpList.path)); } public oneClickInstallPlaylist(bpListUrl: string): Observable> { 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 47c494b8..374e64c6 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 @@ -26,6 +26,7 @@ import { SongCacheService } from "./song-cache.service"; import { IpcService } from "../../ipc.service"; import { pathToFileURL } from "url"; import { sToMs } from "../../../../shared/helpers/time.helpers"; +import { FieldRequired } from "shared/helpers/type.helpers"; export class LocalMapsManagerService { private static instance: LocalMapsManagerService; @@ -236,7 +237,7 @@ export class LocalMapsManagerService { return this.linker.unlinkFolder(versionMapsPath, { keepContents: keepMaps, intermediateFolder: LocalMapsManagerService.SHARED_MAPS_FOLDER }); } - public deleteMaps(maps: Partial[]): Observable { + public deleteMaps(maps: FieldRequired[]): Observable { return new Observable(observer => { const progress: DeleteMapsProgress = { total: maps.length, deleted: 0 }; @@ -244,17 +245,13 @@ export class LocalMapsManagerService { for (const map of maps) { let mapPath = map.path; - if (!mapPath) { - const mapInfo = map.hash ? this.songCache.getMapInfoFromHash(map.hash) : null; - mapPath = mapInfo?.path; - } - - if (mapPath && pathExistsSync(mapPath)) { + if (pathExistsSync(mapPath)) { await deleteFolder(mapPath); this.songCache.deleteMapInfoFromDirname(path.basename(mapPath)); progress.deleted++; - observer.next(progress); } + + observer.next(progress); } })() .catch(e => observer.error(e)) @@ -262,6 +259,53 @@ export class LocalMapsManagerService { }); } + public deleteMapsFromHashs(version: BSVersion, hashs: string[]): Observable { + return new Observable(observer => { + const progress: Progression = { total: hashs.length, current: 0 }; + + (async () => { + const versionMapsPath = await this.getMapsFolderPath(version); + const mapsPaths = await getFoldersInFolder(versionMapsPath); + + for (const mapPath of mapsPaths) { + const mapInfo = await this.loadMapInfoFromPath(mapPath); + if (hashs.includes(mapInfo.hash)) { + await deleteFolder(mapPath); + this.songCache.deleteMapInfoFromDirname(path.basename(mapPath)); + progress.current++; + } + + observer.next(progress); + } + })() + .catch(e => observer.error(e)) + .finally(() => observer.complete()); + }); + } + + public async getMapInfoFromHash(hash: string, version: BSVersion): Promise { + const versionMapsPath = await this.getMapsFolderPath(version); + const mapInfo = this.songCache.getMapInfoFromHash(hash); + + const cachedMapPath = path.join(versionMapsPath, mapInfo.dirname); + + if(pathExistsSync(cachedMapPath)){ + return this.loadMapInfoFromPath(cachedMapPath); + } + + // if not in cache, search in the folder + const mapsPaths = await getFoldersInFolder(versionMapsPath); + + for (const mapPath of mapsPaths) { + const mapInfo = await this.loadMapInfoFromPath(mapPath); + if (mapInfo.hash === hash) { + return mapInfo; + } + } + + return null; + } + public async downloadMap(map: BsvMapDetail, version?: BSVersion): Promise { if (!map.versions.at(0).hash) { diff --git a/src/main/services/additional-content/maps/song-cache.service.ts b/src/main/services/additional-content/maps/song-cache.service.ts index fffac6fc..998881d5 100644 --- a/src/main/services/additional-content/maps/song-cache.service.ts +++ b/src/main/services/additional-content/maps/song-cache.service.ts @@ -26,9 +26,9 @@ export class SongCacheService { return this.rawInfosCache.get(dirname); } - public getMapInfoFromHash(hash: string): { path: string, info: CachedRawInfoWithHash } | undefined { + public getMapInfoFromHash(hash: string): { dirname: string, info: CachedRawInfoWithHash } | undefined { const res = Object.entries(this.rawInfosCache.cache).find(([, info]) => info.hash === hash); - return res ? { path: res[0], info: res[1] } : undefined; + return res ? { dirname: res[0], info: res[1] } : undefined; } public setMapInfoFromDirname(dirname: string, info: CachedRawInfoWithHash): void { diff --git a/src/renderer/components/maps-playlists-panel/playlists/local-playlists-list-panel.component.tsx b/src/renderer/components/maps-playlists-panel/playlists/local-playlists-list-panel.component.tsx index 0d7c45dd..e284e4f5 100644 --- a/src/renderer/components/maps-playlists-panel/playlists/local-playlists-list-panel.component.tsx +++ b/src/renderer/components/maps-playlists-panel/playlists/local-playlists-list-panel.component.tsx @@ -12,7 +12,7 @@ import { noop } from "shared/helpers/function.helpers"; import { LocalBPList, LocalBPListsDetails } from "shared/models/playlists/local-playlist.models"; import { PlaylistItem } from "./playlist-item.component"; import { useStateMap } from "renderer/hooks/use-state-map.hook"; -import { ModalService } from "renderer/services/modale.service"; +import { ModalExitCode, ModalService } from "renderer/services/modale.service"; import { LocalPlaylistDetailsModal } from "renderer/components/modal/modal-types/playlist/local-playlist-details-modal.component"; import { InstalledMapsContext } from "../maps-playlists-panel.component"; import { IpcService } from "renderer/services/ipc.service"; @@ -21,6 +21,7 @@ import { useObservable } from "renderer/hooks/use-observable.hook"; import { PlaylistDownloaderService } from "renderer/services/playlist-downloader.service"; import { ProgressBarService } from "renderer/services/progress-bar.service"; import { NotificationService } from "renderer/services/notification.service"; +import { DeletePlaylistModal } from "renderer/components/modal/modal-types/playlist/delete-playlist-modal.component"; type Props = { version: BSVersion; @@ -29,6 +30,8 @@ type Props = { isActive?: boolean; }; +// TODO : Translate + export const LocalPlaylistsListPanel = forwardRef(({ version, className, isActive, linkedState }, forwardedRef) => { const playlistService = useService(PlaylistsManagerService); @@ -111,10 +114,14 @@ export const LocalPlaylistsListPanel = forwardRef(({ version, cl return lastValueFrom(ipc.sendV2("view-path-in-explorer", path)); }; - const deletePlaylist = (path: string) => { + const deletePlaylist = async (bpList: LocalBPList) => { // !! Need to call the modal to confirm the deletion and to ask if the maps should be deleted too - lastValueFrom(playlistService.deletePlaylist({ path, deleteMaps: false })).then(() => { - setPlaylists(playlists.filter(p => p.path !== path)); + const { exitCode, data: deleteMaps } = await modals.openModal(DeletePlaylistModal, { data: bpList }); + + if(exitCode !== ModalExitCode.COMPLETED){ return; } + + lastValueFrom(playlistService.deletePlaylist({ version, bpList, deleteMaps })).then(() => { + setPlaylists(playlists.filter(p => p.path !== bpList.path)); }) }; @@ -153,10 +160,13 @@ export const LocalPlaylistsListPanel = forwardRef(({ version, cl duration={p.duration} maxNps={p.maxNps} minNps={p.minNps} + isDownloading$={playlistDownloader.$isPlaylistDownloading(p, version)} + isInQueue$={playlistDownloader.$isPlaylistInQueue(p, version)} onClickOpen={() => openPlaylistDetails(p.path)} - onClickDelete={() => deletePlaylist(p.path)} + onClickDelete={() => deletePlaylist(p)} onClickSync={() => installPlaylist(p)} onClickOpenFile={() => viewPlaylistFile(p.path)} + onClickCancelDownload={() => playlistDownloader.cancelDownload(p, version)} /> )} diff --git a/src/renderer/components/maps-playlists-panel/playlists/playlist-item.component.tsx b/src/renderer/components/maps-playlists-panel/playlists/playlist-item.component.tsx index 2c35127d..2ea24c4d 100644 --- a/src/renderer/components/maps-playlists-panel/playlists/playlist-item.component.tsx +++ b/src/renderer/components/maps-playlists-panel/playlists/playlist-item.component.tsx @@ -11,6 +11,9 @@ import { useState } from 'react'; import { SearchIcon } from 'renderer/components/svgs/icons/search-icon.component'; import { BsmButton } from 'renderer/components/shared/bsm-button.component'; import Tippy from '@tippyjs/react'; +import { Observable, of } from 'rxjs'; +import { useObservable } from 'renderer/hooks/use-observable.hook'; +import { BsmBasicSpinner } from 'renderer/components/shared/bsm-basic-spinner/bsm-basic-spinner.component'; type Props = { title?: string; @@ -23,11 +26,13 @@ type Props = { minNps?: number; maxNps?: number; selected?: boolean; - path?: string; + isDownloading$?: Observable; + isInQueue$?: Observable; onClickOpen?: () => void; onClickOpenFile?: () => void; onClickDelete?: () => void; onClickSync?: () => void; + onClickCancelDownload?: () => void; } export function PlaylistItem({ title, @@ -40,15 +45,20 @@ export function PlaylistItem({ title, minNps, maxNps, selected, + isDownloading$, + isInQueue$, onClickOpen, onClickOpenFile, onClickSync, - onClickDelete + onClickDelete, + onClickCancelDownload }: Props) { const color = useThemeColor("first-color"); const [hovered, setHovered] = useState(false); + const isDownloading = useObservable(() => isDownloading$ ?? of(), false, [isDownloading$]); + const isInQueue = useObservable(() => isInQueue$ ?? of(), false, [isInQueue$]); const nbMapsText = nbMaps ? Intl.NumberFormat(undefined, { notation: "compact" }).format(nbMaps).trim() : null; const nbMappersText = nbMappers ? Intl.NumberFormat(undefined, { notation: "compact" }).format(nbMappers).trim() : null; @@ -94,21 +104,39 @@ export function PlaylistItem({ title, -
+ -
- {onClickSync && - - } + + {(isDownloading || isInQueue) && onClickCancelDownload && ( + + + + )} + {onClickSync && ( + isDownloading ? ( + + ) : !isInQueue ? ( + + + + ) : (<>) + + )} {onClickOpenFile && } - {onClickDelete && + {(onClickDelete && !isDownloading && !isInQueue) && } - -
-
+ + diff --git a/src/renderer/components/modal/modal-types/playlist/delete-playlist-modal.component.tsx b/src/renderer/components/modal/modal-types/playlist/delete-playlist-modal.component.tsx new file mode 100644 index 00000000..6dc6ee26 --- /dev/null +++ b/src/renderer/components/modal/modal-types/playlist/delete-playlist-modal.component.tsx @@ -0,0 +1,34 @@ +import { useState } from "react"; +import { BsmButton } from "renderer/components/shared/bsm-button.component"; +import { BsmCheckbox } from "renderer/components/shared/bsm-checkbox.component"; +import { BsmImage } from "renderer/components/shared/bsm-image.component"; +import { useTranslation } from "renderer/hooks/use-translation.hook"; +import { ModalComponent, ModalExitCode } from "renderer/services/modale.service"; +import BeatConflict from "../../../../../../assets/images/apngs/beat-conflict.png"; +import { BPList } from "shared/models/playlists/playlist.interface"; +import Tippy from "@tippyjs/react"; + +export const DeletePlaylistModal: ModalComponent = ({ resolver, options: { data }}) => { + + const t = useTranslation(); + + const [deleteMaps, setDeleteMaps] = useState(false); + + return ( +
+

Supprimer la playlist ?

+ +

{`Est-tu sûr de vouloir supprimer la playlist "${data.playlistTitle}" ?`}

+
+ setDeleteMaps(() => val)} /> + + Supprimer les maps + +
+
+ resolver({ exitCode: ModalExitCode.CANCELED })} withBar={false} text="misc.cancel" /> + resolver({ exitCode: ModalExitCode.COMPLETED, data: deleteMaps })} withBar={false} text="misc.delete" /> +
+ + ); +}; diff --git a/src/renderer/components/modal/modal.component.tsx b/src/renderer/components/modal/modal.component.tsx index 8c232824..37e918e5 100644 --- a/src/renderer/components/modal/modal.component.tsx +++ b/src/renderer/components/modal/modal.component.tsx @@ -50,7 +50,7 @@ export function Modal() { modal.resolver({ exitCode: ModalExitCode.CLOSED }); }} > - + diff --git a/src/renderer/components/notification/notification-item.component.tsx b/src/renderer/components/notification/notification-item.component.tsx index c51f2301..a8342fd8 100644 --- a/src/renderer/components/notification/notification-item.component.tsx +++ b/src/renderer/components/notification/notification-item.component.tsx @@ -74,7 +74,7 @@ export const NotificationItem = forwardRef(({ resolver, notification }: { resolv )} - resolver(NotificationResult.CLOSE)} /> + resolver(NotificationResult.CLOSE)} /> ); }); diff --git a/src/renderer/components/svgs/icons/close-icon.component.tsx b/src/renderer/components/svgs/icons/close-icon.component.tsx index 94430145..11581fe8 100644 --- a/src/renderer/components/svgs/icons/close-icon.component.tsx +++ b/src/renderer/components/svgs/icons/close-icon.component.tsx @@ -1,9 +1,9 @@ -import { CSSProperties } from "react"; +import { createSvgIcon } from "../svg-icon.type"; -export function CloseIcon(props: { className?: string; style?: CSSProperties }) { +export const CloseIcon = createSvgIcon((props, ref) => { return ( - - + + ); -} +}); diff --git a/src/renderer/services/playlist-downloader.service.ts b/src/renderer/services/playlist-downloader.service.ts index a23746af..4a051071 100644 --- a/src/renderer/services/playlist-downloader.service.ts +++ b/src/renderer/services/playlist-downloader.service.ts @@ -1,4 +1,4 @@ -import { BehaviorSubject, Observable, Subscription, distinctUntilChanged, filter, lastValueFrom, map, shareReplay, take, takeUntil, takeWhile, tap } from "rxjs"; +import { BehaviorSubject, Observable, Subject, Subscription, distinctUntilChanged, filter, lastValueFrom, map, shareReplay, take, takeUntil, takeWhile, tap } from "rxjs"; import { BPList, DownloadPlaylistProgressionData } from "shared/models/playlists/playlist.interface"; import { IpcService } from "./ipc.service"; import { ProgressBarService } from "./progress-bar.service"; @@ -6,6 +6,7 @@ import { Progression } from "main/helpers/fs.helpers"; import equal from "fast-deep-equal"; import { BSVersion } from "shared/bs-version.interface"; import { LocalBPListsDetails } from "shared/models/playlists/local-playlist.models"; +import { removeIndex } from "shared/helpers/array.helpers"; export class PlaylistDownloaderService { private static instance: PlaylistDownloaderService; @@ -21,7 +22,7 @@ export class PlaylistDownloaderService { private readonly progress: ProgressBarService; private readonly ipc: IpcService; - private readonly canceled$ = new BehaviorSubject(false); + private readonly cancel$ = new Subject(); private readonly playlistQueue$ = new BehaviorSubject([]); private readonly onPlaylistDownloadedListeners = new Map void)[]>(); @@ -34,40 +35,34 @@ export class PlaylistDownloaderService { this.playlistQueue$.next([...this.playlistQueue$.value, { version, source: bpList }]); - console.log("AAAAA"); - return new Observable>(subscriber => { let subs: Subscription[] = []; + let canShowProgress = false; (async () => { const queueInfo = await lastValueFrom(this.playlistQueue$.pipe(map(queue => queue.at(0)), filter(p => equal(bpList, p.source)), take(1))); - - console.log(queueInfo); - - const download$ = this.ipc.sendV2("install-playlist", { version: queueInfo.version, playlist: queueInfo.source, ignoreSongsHashs }).pipe(takeWhile(() => !this.canceled$.value)); + const download$ = this.ipc.sendV2("install-playlist", { version: queueInfo.version, playlist: queueInfo.source, ignoreSongsHashs }).pipe(takeUntil(this.cancel$)); subs.push(download$.pipe(map(progress => progress?.data?.playlist), filter(Boolean), take(1)).subscribe(playlist => { queueInfo.downloaded = playlist; this.onPlaylistDownloadedListeners.get(version)?.forEach(cb => cb(playlist)); })); - const canShowProgress = !this.progress.isVisible; + canShowProgress = !this.progress.isVisible; if (canShowProgress) { - this.progress.show(download$.pipe(map(data => (data.current / data.total) * 100)), true); + this.progress.show(download$, true); } - await lastValueFrom(download$.pipe(tap(subscriber))).finally(() => { - if (canShowProgress) { - this.progress.hide(true); - } - }) + await lastValueFrom(download$.pipe(tap(subscriber))); })() .then(() => subscriber.complete()) .catch(err => subscriber.error(err)) .finally(() => { this.playlistQueue$.next(this.playlistQueue$.value.filter(p => !equal(p.version, version) || !equal(p.source, bpList))); - this.canceled$.next(false); + if (canShowProgress) { + this.progress.hide(true); + } }); return () => subs.forEach(s => s.unsubscribe()); @@ -75,8 +70,20 @@ export class PlaylistDownloaderService { }).pipe(shareReplay(1)); } - public cancelCurrentDownload() { - this.canceled$.next(true); + public cancelDownload(bplist: BPList, version?: BSVersion) { + const currentDownload = this.playlistQueue$.value.at(0); + if(!currentDownload) { return; } + + if((equal(currentDownload.source, bplist) || equal(currentDownload.downloaded, bplist)) && equal(currentDownload.version, version)){ + return this.cancel$.next(); + } + + const indexToRemove = this.playlistQueue$.value.findIndex(p => (equal(p.source, bplist) || equal(p.downloaded, bplist)) && equal(p.version, version)); + if(indexToRemove === -1){ return; } + + + const newArr = removeIndex(indexToRemove, [...this.playlistQueue$.value]); + this.playlistQueue$.next(newArr); } public get currentDownloading$(): Observable { @@ -88,7 +95,12 @@ export class PlaylistDownloaderService { } public $isPlaylistDownloading(bpList: BPList, version?: BSVersion): Observable { - return this.currentDownloading$.pipe(map(p => p && (equal(p.source, bpList) || equal(p.downloaded, bpList)) && equal(p.version, version)), distinctUntilChanged()); + return this.currentDownloading$.pipe(map(p => { + if(!p){ return false; } + if(!equal(p.source, bpList) && !equal(p.downloaded, bpList)){ return false; } + if(version && !equal(p.version, version)){ return false; } + return true; + }), distinctUntilChanged()); } public addOnPlaylistDownloadedListener(version: BSVersion, cb: (playlist: LocalBPListsDetails) => void){ diff --git a/src/renderer/services/playlists-manager.service.ts b/src/renderer/services/playlists-manager.service.ts index 2516044c..f6acf712 100644 --- a/src/renderer/services/playlists-manager.service.ts +++ b/src/renderer/services/playlists-manager.service.ts @@ -3,7 +3,7 @@ import { IpcService } from "./ipc.service"; import { Observable, lastValueFrom } from "rxjs"; import { FolderLinkState, VersionFolderLinkerService } from "./version-folder-linker.service"; import { Progression } from "main/helpers/fs.helpers"; -import { LocalBPListsDetails } from "shared/models/playlists/local-playlist.models"; +import { LocalBPList, LocalBPListsDetails } from "shared/models/playlists/local-playlist.models"; import { ModalExitCode, ModalService } from "./modale.service"; import { UnlinkPlaylistModal } from "renderer/components/modal/modal-types/unlink-playlist-modal.component"; import { LinkPlaylistModal } from "renderer/components/modal/modal-types/link-playlist-modal.component"; @@ -34,7 +34,7 @@ export class PlaylistsManagerService { return this.ipc.sendV2("get-version-playlists-details", version); } - public deletePlaylist(opt: {path: string, deleteMaps?: boolean}): Observable { + public deletePlaylist(opt: {version: BSVersion, bpList: LocalBPList, deleteMaps?: boolean}): Observable { return this.ipc.sendV2("delete-playlist", opt); } diff --git a/src/shared/helpers/array.helpers.ts b/src/shared/helpers/array.helpers.ts index 4c52e2b7..58927f0b 100644 --- a/src/shared/helpers/array.helpers.ts +++ b/src/shared/helpers/array.helpers.ts @@ -18,3 +18,8 @@ export function popElement(func: (element: T) => boolean, arr: T[]) } return arr.splice(index, 1)[0]; } + +export function removeIndex(index: number, arr: T[]): T[] { + arr.splice(index, 1); + return arr; +} diff --git a/src/shared/helpers/type.helpers.ts b/src/shared/helpers/type.helpers.ts new file mode 100644 index 00000000..3df721b6 --- /dev/null +++ b/src/shared/helpers/type.helpers.ts @@ -0,0 +1 @@ +export type FieldRequired = Partial & Required>; // All fields of T are optional except for K diff --git a/src/shared/models/ipc/ipc-routes.ts b/src/shared/models/ipc/ipc-routes.ts index 12dd89d0..edce1c80 100644 --- a/src/shared/models/ipc/ipc-routes.ts +++ b/src/shared/models/ipc/ipc-routes.ts @@ -18,7 +18,7 @@ import { FileFilter, OpenDialogReturnValue } from "electron"; import { SystemNotificationOptions } from "../notification/system-notification.model"; import { Supporter } from "../supporters"; import { AppWindow } from "../window-manager/app-window.model"; -import { LocalBPListsDetails } from "../playlists/local-playlist.models"; +import { LocalBPList, LocalBPListsDetails } from "../playlists/local-playlist.models"; export type IpcReplier = (data: Observable) => void; @@ -82,7 +82,7 @@ export interface IpcChannelMapping extends Record}; "get-version-playlists-details": {request: BSVersion, response: Progression}; - "delete-playlist": {request: {path: string, deleteMaps?: boolean}, response: Progression}; + "delete-playlist": {request: {version: BSVersion, bpList: LocalBPList, deleteMaps?: boolean}, response: Progression}; /* ** bs-uninstall-ipcs ** */ "bs.uninstall": { request: BSVersion, response: boolean };