add link unlink maps modals AND start maps deletion

This commit is contained in:
MathieuG-P
2022-11-28 02:30:02 +01:00
parent 69918d0f3f
commit c172b1ed81
9 changed files with 168 additions and 34 deletions
+12
View File
@@ -47,4 +47,16 @@ ipcMain.on("unlink-version-maps", async (event, request: IpcRequest<{version: BS
utils.ipcSend<void>(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<void>(request.responceChannel, {success: true});
}).catch(err => {
utils.ipcSend<void>(request.responceChannel, {success: true, error: err});
});
});
@@ -127,6 +127,10 @@ export class LocalMapsManagerService {
}
public async deleteMaps(maps: BsmLocalMap[], verion?: BSVersion){
// TODO
}
}
@@ -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<Subscription[]>([]);
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<BsvMapCharacteristic, ParsedMapDiff[]> => {
const res = new Map<BsvMapCharacteristic, ParsedMapDiff[]>();
@@ -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}
/>;
}
@@ -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
<span className="absolute w-[10px] h-[10px] bottom-0 right-full bg-inherit" style={{clipPath: 'path("M11 11 L11 0 L10 0 A10 10 0 0 1 0 10 L 0 11 Z")'}}/>
<div className="flex flex-col justify-center items-center gap-1 w-full h-full overflow-hidden opacity-0 group-hover:opacity-100">
{onDelete && <BsmButton className="w-6 h-6 p-[2px] rounded-md !bg-inherit hover:!bg-main-color-2" iconClassName="w-full h-full brightness-150" iconColor={color} icon="trash" withBar={false} onClick={e => {e.stopPropagation(); openPreview()}}/>}
{onDelete && <BsmButton className="w-6 h-6 p-[2px] rounded-md !bg-inherit hover:!bg-main-color-2" iconClassName="w-full h-full brightness-150" iconColor={color} icon="trash" withBar={false} onClick={e => {e.stopPropagation(); onDelete(hash)}}/>}
{previewUrl && <BsmButton className="w-6 h-6 p-[2px] rounded-md !bg-inherit hover:!bg-main-color-2" iconClassName="w-full h-full brightness-150" iconColor={color} icon="eye" withBar={false} onClick={e => {e.stopPropagation(); openPreview()}}/>}
{mapId && <BsmButton className="w-6 h-6 p-1 rounded-md !bg-inherit hover:!bg-main-color-2" iconClassName="w-full h-full brightness-150" iconColor={color} icon="twitch" withBar={false} onClick={e => {e.stopPropagation(); copyBsr()}}/>}
</div>
@@ -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<React.HTMLAttributes<HTMLLIElement>, 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 (
<li className="relative text-center text-lg font-bold hover:backdrop-brightness-75 flex justify-center items-center content-center" onClick={props.onClick}>
<span>{text}</span>
@@ -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<void, {linked: boolean, maps: BsmLocalMap[]}> = ({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 (
<form className="text-gray-800 dark:text-gray-200">
<h1 className="text-3xl uppercase tracking-wide w-full text-center">{titleText}</h1>
<BsmImage className="mx-auto h-24" image={BeatConflict} />
<p className="max-w-sm w-full">{descText}</p>
{linked && <p className="max-w-sm w-full text-sm italic mt-2">{linkDescText}</p>}
<div className="grid grid-flow-col grid-cols-2 gap-4 mt-4">
<BsmButton typeColor="cancel" className="rounded-md text-center transition-all" onClick={() => resolver({exitCode: ModalExitCode.CANCELED})} withBar={false} text="misc.cancel"/>
<BsmButton typeColor="primary" className="rounded-md text-center transition-all" onClick={() => resolver({exitCode: ModalExitCode.COMPLETED})} withBar={false} text="Supprimer"/>
</div>
</form>
)
}
@@ -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<boolean> = ({resolver}) => {
const t = useTranslation();
const [keepMaps, setKeepMaps] = useState(true);
return (
<form className="text-gray-800 dark:text-gray-200">
<h1 className="text-3xl uppercase tracking-wide w-full text-center">Lier les maps</h1>
<BsmImage className="mx-auto h-24" image={BeatRunning} />
<p className="max-w-sm w-full">La liaison des maps permet de partager les maps entre toute les version. Lier cette version permettra l'utilisation des maps partager</p>
<p className="max-w-sm w-full text-sm italic">L'ajout et la suppression de maps sera également partagés</p>
<div className="relative h-5 flex my-3 items-center">
<BsmCheckbox className="h-full aspect-square relative bg-inherit mr-1.5 z-[1]" checked={keepMaps} onChange={setKeepMaps}/>
<span className="text-sm mb-0.5 cursor-help" title="Conserver les maps déplacera les maps de la version actuelle dans le dossier des maps partagées. Dans le cas contraire elles seront perdues">Conserver les maps</span>
</div>
<div className="grid grid-flow-col grid-cols-2 gap-4 mt-4">
<BsmButton typeColor="cancel" className="rounded-md text-center transition-all" onClick={() => resolver({exitCode: ModalExitCode.CANCELED})} withBar={false} text="misc.cancel"/>
<BsmButton typeColor="primary" className="rounded-md text-center transition-all" onClick={() => resolver({exitCode: ModalExitCode.COMPLETED, data: keepMaps})} withBar={false} text="Lier les maps"/>
</div>
</form>
)
}
@@ -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<boolean> = ({resolver}) => {
const t = useTranslation();
const [keepMaps, setKeepMaps] = useState(true);
return (
<form className="text-gray-800 dark:text-gray-200">
<h1 className="text-3xl uppercase tracking-wide w-full text-center">Délier les maps</h1>
<BsmImage className="mx-auto h-24" image={BeatConflict} />
<p className="max-w-sm w-full">Attention, délier les maps ne permettra plus l'utilisation des maps paratagés pour cette version.</p>
<div className="relative h-5 flex my-3 items-center">
<BsmCheckbox className="h-full aspect-square relative bg-inherit mr-1.5 z-[1]" checked={keepMaps} onChange={setKeepMaps}/>
<span className="text-sm mb-0.5 cursor-help" title="Conserver les maps créera une copie des maps partagées pour la version actuelle. Dans le cas contraire, aucune map ne sera conservée pour cette version.">Conserver les maps</span>
</div>
<div className="grid grid-flow-col grid-cols-2 gap-4 mt-4">
<BsmButton typeColor="cancel" className="rounded-md text-center transition-all" onClick={() => resolver({exitCode: ModalExitCode.CANCELED})} withBar={false} text="misc.cancel"/>
<BsmButton typeColor="error" className="rounded-md text-center transition-all" onClick={() => resolver({exitCode: ModalExitCode.COMPLETED, data: keepMaps})} withBar={false} text="délier les maps"/>
</div>
</form>
)
}
+44 -15
View File
@@ -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<BSVersion> = new Subject();
private readonly lastUnlinkedVersion$: Subject<BSVersion> = 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<BsmLocalMap[]>{
@@ -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<void>{
return this.ipcService.send<void, {version: BSVersion, keepMaps: boolean}>("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<void>{
const modalRes = await this.modal.openModal(LinkMapsModal);
if(modalRes.exitCode !== ModalExitCode.COMPLETED){ return; }
const res = await this.ipcService.send<void, {version: BSVersion, keepMaps: boolean}>("link-version-maps", {args: {version, keepMaps: !!modalRes.data}});
if(res.success){
this.lastLinkedVersion$.next(version);
return;
}
}
public unlinkVersion(version: BSVersion): Promise<void>{
return this.ipcService.send<void, {version: BSVersion, keepMaps: boolean}>("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<void>{
const modalRes = await this.modal.openModal(UnlinkMapsModal);
if(modalRes.exitCode !== ModalExitCode.COMPLETED){ return; }
const res = await this.ipcService.send<void, {version: BSVersion, keepMaps: boolean}>("unlink-version-maps", {args: {version, keepMaps: !!modalRes.data}});
if(res.success){
return this.lastUnlinkedVersion$.next(version);
}
}
public async deleteMaps(maps: BsmLocalMap[], version?: BSVersion): Promise<boolean>{
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<void, {version: BSVersion, maps: BsmLocalMap[]}>("delete-maps", {args: {version, maps}});
return res.success;
}
public get versionLinked$(): Observable<BSVersion>{