[chore] rework link unlink modals

This commit is contained in:
MathieuG-P
2024-12-11 20:30:35 +01:00
parent 0f5ea88a27
commit be5cc9c645
25 changed files with 238 additions and 1186 deletions
+7 -1
View File
@@ -2,10 +2,11 @@ import { shell } from "electron";
import { BSVersionLibService } from "../services/bs-version-lib.service";
import { BSLocalVersionService } from "../services/bs-local-version.service";
import { IpcService } from "../services/ipc.service";
import { from } from "rxjs";
import { from, of } from "rxjs";
import path from "path";
import { pathExists } from "fs-extra";
import { VersionFolderLinkerService } from "../services/version-folder-linker.service";
import { FolderLinkerService } from "main/services/folder-linker.service";
const ipc = IpcService.getInstance();
@@ -67,3 +68,8 @@ ipc.on("relink-all-versions-folders", (_, reply) => {
const versionLinker = VersionFolderLinkerService.getInstance();
reply(from(versionLinker.relinkAllVersionsFolders()));
});
ipc.on("get-shared-folder", (_, reply) => {
const versionLinker = FolderLinkerService.getInstance();
reply(of(versionLinker.sharedFolder()));
});
@@ -104,6 +104,7 @@ export class BSLocalVersionService {
const versionFilePath = path.join(bsPath, 'Beat Saber_Data', 'globalgamemanagers');
const folderVersion = await this.getVersionFromGlobalGameManagerFile(versionFilePath);
folderVersion.path = bsPath;
if(!folderVersion){ return null; }
+2 -2
View File
@@ -36,12 +36,12 @@ export class FolderLinkerService {
});
}
private async sharedFolder(): Promise<string> {
public sharedFolder(): string {
return this.installLocationService.sharedContentPath();
}
private async getSharedFolder(folderPath: string, intermediateFolder?: string): Promise<string> {
return path.join(await this.sharedFolder(), intermediateFolder ?? "", path.basename(folderPath));
return path.join(this.sharedFolder(), intermediateFolder ?? "", path.basename(folderPath));
}
private getBackupFolder(folderPath: string): string {
@@ -1,31 +0,0 @@
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">{t("modals.maps-actions.link-maps.title")}</h1>
<BsmImage className="mx-auto h-24" image={BeatRunning} />
<p className="max-w-sm w-full">{t("modals.maps-actions.link-maps.desc")}</p>
<p className="max-w-sm w-full text-sm italic mt-2">{t("modals.maps-actions.link-maps.info")}</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={t("modals.maps-actions.link-maps.keep-maps.title")}>
{t("modals.maps-actions.link-maps.keep-maps.label")}
</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="modals.maps-actions.link-maps.valid-btn" />
</div>
</form>
);
};
@@ -1,34 +0,0 @@
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";
import Tippy from "@tippyjs/react";
export const LinkPlaylistModal: 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">{t("playlist.link-playlists")}</h1>
<BsmImage className="mx-auto h-24" image={BeatRunning} />
<p className="max-w-sm w-full">{t("playlist.link-playlist-desc")}</p>
<p className="max-w-sm w-full text-sm italic mt-2">{t("playlist.link-playlist-info")}</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} />
<Tippy content={t("playlist.keep-playlists-tip")} placement="top" theme="default">
<span className="text-sm mb-0.5 cursor-help">
{t("playlist.keep-playlists")}
</span>
</Tippy>
</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="playlist.link-playlists" />
</div>
</form>
);
};
@@ -1,32 +0,0 @@
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 { MSModelType } from "shared/models/models/model-saber.model";
import BeatRunning from "../../../../../../assets/images/apngs/beat-running.png";
export const LinkModelsModal: ModalComponent<boolean, MSModelType> = ({ resolver, options: {data} }) => {
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">{t(`models.modals.link-models.${data}.title`)}</h1>
<BsmImage className="mx-auto h-24" image={BeatRunning} />
<p className="max-w-sm w-full">{t(`models.modals.link-models.${data}.desc`)}</p>
<p className="max-w-sm w-full text-sm italic mt-2">{t(`models.modals.link-models.${data}.info`)}</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={t(`models.modals.link-models.${data}.keep-models.title`)}>
{t(`models.modals.link-models.${data}.keep-models.label`)}
</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={`models.modals.link-models.${data}.valid-btn`} />
</div>
</form>
);
};
@@ -1,31 +0,0 @@
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 { MSModelType } from "shared/models/models/model-saber.model";
export const UnlinkModelsModal: ModalComponent<boolean, MSModelType> = ({ resolver, options: {data} }) => {
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">{t(`models.modals.unlink-models.${data}.title`)}</h1>
<BsmImage className="mx-auto h-24" image={BeatConflict} />
<p className="max-w-sm w-full">{t(`models.modals.unlink-models.${data}.desc`)}</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={t(`models.modals.unlink-models.${data}.keep-models.title`)}>
{t(`models.modals.unlink-models.${data}.keep-models.label`)}
</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={`models.modals.unlink-models.${data}.valid-btn`} />
</div>
</form>
);
};
@@ -1,30 +0,0 @@
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">{t("modals.maps-actions.unlink-maps.title")}</h1>
<BsmImage className="mx-auto h-24" image={BeatConflict} />
<p className="max-w-sm w-full">{t("modals.maps-actions.unlink-maps.desc")}</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={t("modals.maps-actions.unlink-maps.keep-maps.title")}>
{t("modals.maps-actions.unlink-maps.keep-maps.label")}
</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="modals.maps-actions.unlink-maps.valid-btn" />
</div>
</form>
);
};
@@ -1,33 +0,0 @@
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 Tippy from "@tippyjs/react";
export const UnlinkPlaylistModal: 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">{t("playlist.unlink-playlists")}</h1>
<BsmImage className="mx-auto h-24" image={BeatConflict} />
<p className="max-w-sm w-full">{t("playlist.unlink-playlist-desc")}</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} />
<Tippy content={t("playlist.unlink-keep-playlists-tip")} placement="top" theme="default">
<span className="text-sm mb-0.5 cursor-help">
{t("playlist.keep-playlists")}
</span>
</Tippy>
</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="playlist.unlink-playlists" />
</div>
</form>
);
};
@@ -1,5 +1,5 @@
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 { LinkContentModal } from "renderer/components/modal/modal-types/link-contents-modal.component";
import { UnlinkContentsModal } from "renderer/components/modal/modal-types/unlink-contents-modal.component";
import { Subject, Observable, of, lastValueFrom, Subscription, throwError } from "rxjs";
import { BSVersion } from "shared/bs-version.interface";
import { BsmLocalMapsProgress, BsmLocalMap } from "shared/models/maps/bsm-local-map.interface";
@@ -60,7 +60,7 @@ export class MapsManagerService {
}
public async linkVersion(version: BSVersion): Promise<boolean> {
const modalRes = await this.modal.openModal(LinkMapsModal);
const modalRes = await this.modal.openModal(LinkContentModal, { data: { version, contentType: "maps" } });
if (modalRes.exitCode !== ModalExitCode.COMPLETED) {
return Promise.resolve(false);
@@ -69,12 +69,12 @@ export class MapsManagerService {
return this.linker.linkVersionFolder({
version,
relativeFolder: MapsManagerService.RELATIVE_MAPS_FOLDER,
options: { keepContents: !!modalRes.data },
options: { keepContents: true },
});
}
public async unlinkVersion(version: BSVersion): Promise<boolean> {
const modalRes = await this.modal.openModal(UnlinkMapsModal);
const modalRes = await this.modal.openModal(UnlinkContentsModal, { data: { version, contentType: "maps" } });
if (modalRes.exitCode !== ModalExitCode.COMPLETED) {
return Promise.resolve(false);
@@ -5,8 +5,6 @@ import { MODEL_TYPE_FOLDERS } from "shared/models/models/constants";
import { Observable, lastValueFrom, map } from "rxjs";
import { BSVersion } from "shared/bs-version.interface";
import { ModalExitCode, ModalService } from "../modale.service";
import { LinkModelsModal } from "renderer/components/modal/modal-types/models/link-models-modal.component";
import { UnlinkModelsModal } from "renderer/components/modal/modal-types/models/unlink-models-modal.component";
import { Progression } from "main/helpers/fs.helpers";
import { BsmLocalModel } from "shared/models/models/bsm-local-model.interface";
import { ProgressBarService } from "../progress-bar.service";
@@ -14,6 +12,8 @@ import { ProgressionInterface } from "shared/models/progress-bar";
import { NotificationService } from "../notification.service";
import { ConfigurationService } from "../configuration.service";
import { DeleteModelsModal } from "renderer/components/modal/modal-types/models/delete-models-modal.component";
import { LinkContentModal } from "renderer/components/modal/modal-types/link-contents-modal.component";
import { UnlinkContentsModal } from "renderer/components/modal/modal-types/unlink-contents-modal.component";
export class ModelsManagerService {
private static instance: ModelsManagerService;
@@ -72,7 +72,7 @@ export class ModelsManagerService {
}
public async linkModels(type: MSModelType, version?: BSVersion): Promise<boolean> {
const res = await this.modalService.openModal(LinkModelsModal, {data: type});
const res = await this.modalService.openModal(LinkContentModal, {data: { version, contentType: `${type}s` as any }});
if (res.exitCode !== ModalExitCode.COMPLETED) {
return null;
@@ -86,7 +86,7 @@ export class ModelsManagerService {
}
public async unlinkModels(type: MSModelType, version?: BSVersion): Promise<boolean> {
const res = await this.modalService.openModal(UnlinkModelsModal, {data: type});
const res = await this.modalService.openModal(UnlinkContentsModal, {data: { version, contentType: `${type}s` as any }});
if (res.exitCode !== ModalExitCode.COMPLETED) {
return null;
@@ -5,14 +5,14 @@ import { FolderLinkState, VersionFolderLinkerService } from "./version-folder-li
import { Progression } from "main/helpers/fs.helpers";
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";
import { BsmLocalMap } from "shared/models/maps/bsm-local-map.interface";
import { ProgressBarService } from "./progress-bar.service";
import equal from "fast-deep-equal";
import { noop } from "shared/helpers/function.helpers";
import { NotificationService } from "./notification.service";
import { CustomError } from "shared/models/exceptions/custom-error.class";
import { LinkContentModal } from "renderer/components/modal/modal-types/link-contents-modal.component";
import { UnlinkContentsModal } from "renderer/components/modal/modal-types/unlink-contents-modal.component";
export class PlaylistsManagerService {
private static instance: PlaylistsManagerService;
@@ -123,7 +123,7 @@ export class PlaylistsManagerService {
}
public async linkVersion(version: BSVersion): Promise<boolean> {
const modalRes = await this.modal.openModal(LinkPlaylistModal);
const modalRes = await this.modal.openModal(LinkContentModal, { data: { version, contentType: "playlists" } });
if (modalRes.exitCode !== ModalExitCode.COMPLETED) {
return Promise.resolve(false);
@@ -137,7 +137,7 @@ export class PlaylistsManagerService {
}
public async unlinkVersion(version: BSVersion): Promise<boolean> {
const modalRes = await this.modal.openModal(UnlinkPlaylistModal);
const modalRes = await this.modal.openModal(UnlinkContentsModal, { data: { version, contentType: "playlists" } });
if (modalRes.exitCode !== ModalExitCode.COMPLETED) {
return Promise.resolve(false);
@@ -182,6 +182,10 @@ export class VersionFolderLinkerService {
public relinkAllVersionsFolders(): Observable<void> {
return this.ipcService.sendV2("relink-all-versions-folders");
}
public getSharedFolder(): Observable<string> {
return this.ipcService.sendV2("get-shared-folder");
}
}
export const enum VersionLinkerActionType {
+1
View File
@@ -17,6 +17,7 @@ export interface BSVersion extends PartialBSVersion {
color?: string; // TODO : Should be in metadata
OculusBinaryId?: string;
metadata?: BSVersionMetadata;
path?: string;
recommended?: boolean;
}
+1
View File
@@ -113,6 +113,7 @@ export interface IpcChannelMapping {
"link-version-folder-action": { request: VersionLinkerAction, response: void };
"is-version-folder-linked": { request: { version: BSVersion; relativeFolder: string }, response: boolean };
"relink-all-versions-folders": { request: void, response: void };
"get-shared-folder": { request: void, response: string };
/* ** launcher-ipcs ** */
"download-update": { request: void, response: Progression };