diff --git a/src/main/ipcs/bs-maps-ipcs.ts b/src/main/ipcs/bs-maps-ipcs.ts index 22d95292..80217aca 100644 --- a/src/main/ipcs/bs-maps-ipcs.ts +++ b/src/main/ipcs/bs-maps-ipcs.ts @@ -47,4 +47,16 @@ ipcMain.on("unlink-version-maps", async (event, request: IpcRequest<{version: BS utils.ipcSend(request.responceChannel, {success: true, error: err}); }); +}); + +ipcMain.on("delete-maps", async (event, request: IpcRequest<{version: BSVersion, maps: BsmLocalMap[]}>) => { + const utils = UtilsService.getInstance(); + const maps = LocalMapsManagerService.getInstance(); + + maps.deleteMaps(request.args.maps, request.args.version).then(() => { + utils.ipcSend(request.responceChannel, {success: true}); + }).catch(err => { + utils.ipcSend(request.responceChannel, {success: true, error: err}); + }); + }); \ No newline at end of file diff --git a/src/main/services/maps/local-maps-manager.service.ts b/src/main/services/maps/local-maps-manager.service.ts index d7ddf9b1..98eea524 100644 --- a/src/main/services/maps/local-maps-manager.service.ts +++ b/src/main/services/maps/local-maps-manager.service.ts @@ -127,6 +127,10 @@ export class LocalMapsManagerService { } + public async deleteMaps(maps: BsmLocalMap[], verion?: BSVersion){ + // TODO + } + } \ No newline at end of file diff --git a/src/renderer/components/maps-mangement-components/local-maps-list-panel.component.tsx b/src/renderer/components/maps-mangement-components/local-maps-list-panel.component.tsx index 6dc25489..f6cabc9b 100644 --- a/src/renderer/components/maps-mangement-components/local-maps-list-panel.component.tsx +++ b/src/renderer/components/maps-mangement-components/local-maps-list-panel.component.tsx @@ -22,16 +22,10 @@ export function LocalMapsListPanel({version, className, filter, search} : Props) const isVisible = useInView(ref, {once: true}); const [maps, setMaps] = useState([] as BsmLocalMap[]); const [selectedMaps, setSelectedMaps] = useState([] as string[]); + const [subs] = useState([]); useEffect(() => { - const subs: Subscription[] = []; - - const loadMaps = () => { - console.log("LOAD MAPS"); - subs.push(mapsManager.getMaps(version).subscribe(localMaps => setMaps(() => [...localMaps]))); - } - if(isVisible){ loadMaps(); subs.push(mapsManager.versionLinked$.subscribe(loadMaps)); @@ -42,7 +36,15 @@ export function LocalMapsListPanel({version, className, filter, search} : Props) setMaps(() => []); subs.forEach(s => s.unsubscribe()); } - }, [isVisible, version]) + }, [isVisible, version]); + + const loadMaps = () => { + subs.push(mapsManager.getMaps(version).subscribe(localMaps => setMaps(() => [...localMaps]))); + } + + const handleDelete = (map: BsmLocalMap) => { + mapsManager.deleteMaps([map], version).then(res => res && loadMaps()) + } const extractMapDiffs = (map: BsmLocalMap): Map => { const res = new Map(); @@ -193,7 +195,7 @@ export function LocalMapsListPanel({version, className, filter, search} : Props) duration={map.bsaverInfo?.metadata?.duration} selected={selectedMaps.some(hash => hash === map.hash)} diffs={extractMapDiffs(map)} mapId={map.bsaverInfo?.id} qualified={null} ranked={map.bsaverInfo?.ranked} autorId={map.bsaverInfo?.uploader?.id} likes={map.bsaverInfo?.stats?.upvotes} createdAt={map.bsaverInfo?.createdAt} - onDelete={() => {}} + onDelete={() => handleDelete(map)} onSelected={onMapSelected} />; } diff --git a/src/renderer/components/maps-mangement-components/map-item.component.tsx b/src/renderer/components/maps-mangement-components/map-item.component.tsx index 196d059c..dc2f655b 100644 --- a/src/renderer/components/maps-mangement-components/map-item.component.tsx +++ b/src/renderer/components/maps-mangement-components/map-item.component.tsx @@ -54,7 +54,7 @@ export function MapItem({hash, title, autor, songAutor, coverUrl, songUrl, autor const songPlaying = useObservable(audioPlayer.playing$.pipe(map(playing => playing && audioPlayer.src === songUrl))); const zipUrl = `https://r2cdn.beatsaver.com/${hash}.zip`; - const previewUrl = mapId ? `https://skystudioapps.com/bs-viewer/?id=${mapId}` : null; + const previewUrl = mapId ? `https://skystudioapps.com/bs-viewer/?url=${zipUrl}` : null; const mapUrl = mapId ? `https://beatsaver.com/maps/${mapId}` : null; const authorUrl = autorId ? `https://beatsaver.com/profile/${autorId}` : null; const createdDate = createdAt ? dateFormat(createdAt, "d mmm yyyy") : null; @@ -204,7 +204,7 @@ export function MapItem({hash, title, autor, songAutor, coverUrl, songUrl, autor
- {onDelete && {e.stopPropagation(); openPreview()}}/>} + {onDelete && {e.stopPropagation(); onDelete(hash)}}/>} {previewUrl && {e.stopPropagation(); openPreview()}}/>} {mapId && {e.stopPropagation(); copyBsr()}}/>}
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 767aee75..7a4f7a59 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 @@ -49,27 +49,24 @@ export function MapsPlaylistsPanel({version, oneBlock = false}: Props) { if(!mapsLinked){ return mapsService.linkVersion(version).then(loadMapIsLinked); } - mapsService.unlinkVersion(version).then(loadMapIsLinked); + return mapsService.unlinkVersion(version).then(loadMapIsLinked); } const handlePlaylistLinkClick = () => { - + //TODO } const renderTab = (props: DetailedHTMLProps, HTMLLIElement>, text: string, index: number): JSX.Element => { const mainColor = mapsLinked ? color : "red"; - const variants: Variants = { - hover: {rotate: 22.5}, - tap: {rotate: 45} - } - const onClickLink = (index: number) => { if(index === 0){ return handleMapsLinkClick() }; - handlePlaylistLinkClick(); + return handlePlaylistLinkClick(); } + const variants: Variants = { hover: {rotate: 22.5}, tap: {rotate: 45} }; + return (
  • {text} diff --git a/src/renderer/components/modal/modal-types/delete-maps-modal.component.tsx b/src/renderer/components/modal/modal-types/delete-maps-modal.component.tsx new file mode 100644 index 00000000..909bd79b --- /dev/null +++ b/src/renderer/components/modal/modal-types/delete-maps-modal.component.tsx @@ -0,0 +1,30 @@ +import { BsmButton } from "renderer/components/shared/bsm-button.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 { BsmLocalMap } from "shared/models/maps/bsm-local-map.interface"; +import BeatConflict from '../../../../../assets/images/apngs/beat-conflict.png' + +export const DeleteMapsModal: ModalComponent = ({resolver, data:{linked, maps}}) => { + + const t = useTranslation(); + + const multiple = maps.length > 1; + + const titleText = multiple ? "Supprimer les maps" : "Supprimer la map"; + const descText = multiple ? `Est-tu sur de vouloir supprimer les ${maps.length} maps ?` : `Est-tu sur de vouloir supprimer la map ${maps.at(0).rawInfo._songName} ?`; + const linkDescText = multiple ? "Ces maps font parties des maps partagées" : "Cette map fait partie des maps paratagées"; + + return ( +
    +

    {titleText}

    + +

    {descText}

    + {linked &&

    {linkDescText}

    } +
    + resolver({exitCode: ModalExitCode.CANCELED})} withBar={false} text="misc.cancel"/> + resolver({exitCode: ModalExitCode.COMPLETED})} withBar={false} text="Supprimer"/> +
    + + ) +} \ No newline at end of file diff --git a/src/renderer/components/modal/modal-types/link-maps-modal.component.tsx b/src/renderer/components/modal/modal-types/link-maps-modal.component.tsx new file mode 100644 index 00000000..7bac460a --- /dev/null +++ b/src/renderer/components/modal/modal-types/link-maps-modal.component.tsx @@ -0,0 +1,31 @@ +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 BeatRunning from '../../../../../assets/images/apngs/beat-running.png' + +export const LinkMapsModal: ModalComponent = ({resolver}) => { + + const t = useTranslation(); + const [keepMaps, setKeepMaps] = useState(true); + + + return ( +
    +

    Lier les maps

    + +

    La liaison des maps permet de partager les maps entre toute les version. Lier cette version permettra l'utilisation des maps partager

    +

    L'ajout et la suppression de maps sera également partagés

    +
    + + Conserver les maps +
    +
    + resolver({exitCode: ModalExitCode.CANCELED})} withBar={false} text="misc.cancel"/> + resolver({exitCode: ModalExitCode.COMPLETED, data: keepMaps})} withBar={false} text="Lier les maps"/> +
    + + ) +} \ No newline at end of file diff --git a/src/renderer/components/modal/modal-types/unlink-maps-modal.component.tsx b/src/renderer/components/modal/modal-types/unlink-maps-modal.component.tsx new file mode 100644 index 00000000..d99aaf10 --- /dev/null +++ b/src/renderer/components/modal/modal-types/unlink-maps-modal.component.tsx @@ -0,0 +1,29 @@ +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' + +export const UnlinkMapsModal: ModalComponent = ({resolver}) => { + + const t = useTranslation(); + const [keepMaps, setKeepMaps] = useState(true); + + return ( +
    +

    Délier les maps

    + +

    Attention, délier les maps ne permettra plus l'utilisation des maps paratagés pour cette version.

    +
    + + Conserver les maps +
    +
    + resolver({exitCode: ModalExitCode.CANCELED})} withBar={false} text="misc.cancel"/> + resolver({exitCode: ModalExitCode.COMPLETED, data: keepMaps})} withBar={false} text="délier les maps"/> +
    + + ) +} \ No newline at end of file diff --git a/src/renderer/services/maps-manager.service.ts b/src/renderer/services/maps-manager.service.ts index a7c7f672..d261b859 100644 --- a/src/renderer/services/maps-manager.service.ts +++ b/src/renderer/services/maps-manager.service.ts @@ -1,8 +1,13 @@ +import { LinkMapsModal } from "renderer/components/modal/modal-types/link-maps-modal.component"; +import { UnlinkMapsModal } from "renderer/components/modal/modal-types/unlink-maps-modal.component"; +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 { IpcService } from "./ipc.service"; +import { ModalExitCode, ModalService } from "./modale.service"; +import { DeleteMapsModal } from "renderer/components/modal/modal-types/delete-maps-modal.component"; export class MapsManagerService { @@ -15,6 +20,7 @@ export class MapsManagerService { private readonly ipcService: IpcService; private readonly bsaver: BeatSaverService; + private readonly modal: ModalService private readonly lastLinkedVersion$: Subject = new Subject(); private readonly lastUnlinkedVersion$: Subject = new Subject(); @@ -22,6 +28,7 @@ export class MapsManagerService { private constructor(){ this.ipcService = IpcService.getInstance(); this.bsaver = BeatSaverService.getInstance(); + this.modal = ModalService.getInsance(); } public getMaps(version?: BSVersion): Observable{ @@ -30,7 +37,7 @@ export class MapsManagerService { if(!res.success){ return obs.next(null);} obs.next(res.data); - this.bsaver.getMapDetailsFromHashs(res.data.map(localMap => localMap.hash)).subscribe(mapsDetails => { + this.bsaver.getMapDetailsFromHashs(res.data.map(localMap => localMap.hash)).pipe(finalize(() => obs.complete())).subscribe(mapsDetails => { mapsDetails.forEach(details => { res.data.find(localMap => localMap.hash === details.versions.find(details => details?.hash === localMap.hash)?.hash).bsaverInfo = details }); @@ -47,22 +54,44 @@ export class MapsManagerService { }) } - public linkVersion(version: BSVersion): Promise{ - return this.ipcService.send("link-version-maps", {args: {version, keepMaps: true}}).then(res => { - if(res.success){ - return this.lastLinkedVersion$.next(version); - } - throw res.error; - }); + public async linkVersion(version: BSVersion): Promise{ + + const modalRes = await this.modal.openModal(LinkMapsModal); + + if(modalRes.exitCode !== ModalExitCode.COMPLETED){ return; } + + const res = await this.ipcService.send("link-version-maps", {args: {version, keepMaps: !!modalRes.data}}); + + if(res.success){ + this.lastLinkedVersion$.next(version); + return; + } } - public unlinkVersion(version: BSVersion): Promise{ - return this.ipcService.send("unlink-version-maps", {args: {version, keepMaps: true}}).then(res => { - if(res.success){ - return this.lastUnlinkedVersion$.next(version); - } - throw res.error; - }); + public async unlinkVersion(version: BSVersion): Promise{ + + const modalRes = await this.modal.openModal(UnlinkMapsModal); + + if(modalRes.exitCode !== ModalExitCode.COMPLETED){ return; } + + const res = await this.ipcService.send("unlink-version-maps", {args: {version, keepMaps: !!modalRes.data}}); + + if(res.success){ + return this.lastUnlinkedVersion$.next(version); + } + } + + public async deleteMaps(maps: BsmLocalMap[], version?: BSVersion): Promise{ + + const versionLinked = await this.versionHaveMapsLinked(version); + + const modalRes = await this.modal.openModal(DeleteMapsModal, {linked: versionLinked, maps}); + + if(modalRes.exitCode !== ModalExitCode.COMPLETED){ return false; } + + const res = await this.ipcService.send("delete-maps", {args: {version, maps}}); + + return res.success; } public get versionLinked$(): Observable{