From 0b50aa35f921cfa00614eb565bb116ac08e02322 Mon Sep 17 00:00:00 2001 From: MathieuG-P <40181755+Zagrios@users.noreply.github.com> Date: Sat, 25 Jun 2022 03:46:10 +0200 Subject: [PATCH] add possibility to change installations folder --- src/main/ipcs/bs-download-ipcs.ts | 22 +++++++- src/main/ipcs/bs-launcher-ipcs.ts | 2 +- src/main/ipcs/bs-version-ipcs.ts | 2 +- src/main/ipcs/custom-ipcs.ts | 2 +- src/main/ipcs/os-controls-ipcs.ts | 30 ++++++++++ src/main/ipcs/window-controls-ipcs.ts | 18 ------ src/main/services/bs-installer.service.ts | 23 ++++---- src/main/services/bs-uninstaller.service.ts | 2 +- src/main/services/configuration.service.ts | 32 +++++++++++ .../services/installation-location.service.ts | 56 +++++++++++++++++++ .../installation-folder-modal.component.tsx | 22 ++++++++ .../services/bs-downloader.service.ts | 14 +++++ src/renderer/services/modale.service.ts | 1 + 13 files changed, 190 insertions(+), 36 deletions(-) create mode 100644 src/main/ipcs/os-controls-ipcs.ts delete mode 100644 src/main/ipcs/window-controls-ipcs.ts create mode 100644 src/main/services/configuration.service.ts create mode 100644 src/main/services/installation-location.service.ts create mode 100644 src/renderer/components/modal/modal-types/installation-folder-modal.component.tsx diff --git a/src/main/ipcs/bs-download-ipcs.ts b/src/main/ipcs/bs-download-ipcs.ts index e1a2cb42..047a0321 100644 --- a/src/main/ipcs/bs-download-ipcs.ts +++ b/src/main/ipcs/bs-download-ipcs.ts @@ -1,9 +1,9 @@ import { ipcMain } from 'electron'; -import path from 'path'; -import { ChildProcessWithoutNullStreams, spawn } from 'child_process'; -import { UtilsService } from '../services/utils.service'; import { BSVersion } from '../services/bs-version-manager.service'; import { BSInstallerService, DownloadEventType } from '../services/bs-installer.service'; +import { IpcRequest } from '../../shared/models/ipc-models.model'; +import { InstallationLocationService } from '../services/installation-location.service'; +import { UtilsService } from '../services/utils.service'; export interface InitDownloadInfoInterface { @@ -29,6 +29,22 @@ ipcMain.on('bs-download.start', async (event, args: DownloadInfo) => { ipcMain.on(`bs-download.${DownloadEventType.GUARD_CODE}`, async (event, args) => { BSInstallerService.getInstance().sendInputProcess(args); +}); + +ipcMain.on('bs-download.installation-folder', async (event, request: IpcRequest) => { + const installationFolder = InstallationLocationService.getInstance().installationDirectory; + UtilsService.getInstance().newIpcSenc(request.responceChannel, {success: true, data: installationFolder}); +}); + +ipcMain.on('bs-download.set-installation-folder', (event, request: IpcRequest) => { + const installerService = InstallationLocationService.getInstance(); + installerService.setInstallationDirectory(request.args).then(res => { + console.log("déplacement terminer"); + UtilsService.getInstance().newIpcSenc(request.responceChannel, {success: true, data: res}); + }).catch(err => { + console.log(err); + UtilsService.getInstance().newIpcSenc(request.responceChannel, {success: false, error: err}); + }); }) diff --git a/src/main/ipcs/bs-launcher-ipcs.ts b/src/main/ipcs/bs-launcher-ipcs.ts index a2c4a248..c453ac49 100644 --- a/src/main/ipcs/bs-launcher-ipcs.ts +++ b/src/main/ipcs/bs-launcher-ipcs.ts @@ -23,7 +23,7 @@ ipcMain.on('bs-launch.launch', async (event, args: LauchOption) => { //Create a cwd = await steamService.getGameFolder(BS_APP_ID, "Beat Saber"); } else{ - cwd = path.join(installerService.getBSInstallationFolder(), args.version.BSVersion); + cwd = path.join(installerService.installationFolder, args.version.BSVersion); } const exePath = path.join(cwd, BS_EXECUTABLE); if(!UtilsService.getInstance().pathExist(exePath)){ UtilsService.getInstance().ipcSend("bs-launch.launch", 2); return; } diff --git a/src/main/ipcs/bs-version-ipcs.ts b/src/main/ipcs/bs-version-ipcs.ts index 63e1aaa4..f76ddd8b 100644 --- a/src/main/ipcs/bs-version-ipcs.ts +++ b/src/main/ipcs/bs-version-ipcs.ts @@ -25,7 +25,7 @@ ipcMain.on("bs-version.open-folder", async (event, args: BSVersion) => { console.log(versionFolder) } else{ - versionFolder = path.join(bsInstallerService.getBSInstallationFolder(), args.BSVersion); + versionFolder = path.join(bsInstallerService.installationFolder, args.BSVersion); } if(UtilsService.getInstance().folderExist(versionFolder)){ exec(`start "" "${versionFolder}"`); diff --git a/src/main/ipcs/custom-ipcs.ts b/src/main/ipcs/custom-ipcs.ts index 0b0d625b..eb6952f5 100644 --- a/src/main/ipcs/custom-ipcs.ts +++ b/src/main/ipcs/custom-ipcs.ts @@ -1,5 +1,5 @@ import './bs-download-ipcs'; -import './window-controls-ipcs'; +import './os-controls-ipcs'; import './bs-launcher-ipcs'; import './bs-version-ipcs'; import './bs-uninstall-ipcs'; diff --git a/src/main/ipcs/os-controls-ipcs.ts b/src/main/ipcs/os-controls-ipcs.ts new file mode 100644 index 00000000..3ec28d76 --- /dev/null +++ b/src/main/ipcs/os-controls-ipcs.ts @@ -0,0 +1,30 @@ +import { ipcMain, shell, dialog } from 'electron'; +import { UtilsService } from '../services/utils.service'; +import { IpcRequest } from '../../shared/models/ipc-models.model'; +import { getMainWindow } from '../main'; + +ipcMain.on('window.close', async () => { + getMainWindow()?.close(); +}); + +ipcMain.on('window.maximize', async () => { + getMainWindow()?.maximize(); +}); + +ipcMain.on('window.minimize', async () => { + getMainWindow()?.minimize(); +}); + +ipcMain.on('window.reset', async () => { + getMainWindow()?.restore(); +}); + +ipcMain.on('new-window', async (event, url: string) => { + shell.openExternal(url); +}); + +ipcMain.on('choose-folder', async (event, request: IpcRequest) => { + dialog.showOpenDialog({properties: ['openDirectory']}).then(res => { + UtilsService.getInstance().newIpcSenc(request.responceChannel, {success: true, data: res}); + }) +}) diff --git a/src/main/ipcs/window-controls-ipcs.ts b/src/main/ipcs/window-controls-ipcs.ts deleted file mode 100644 index 9e473856..00000000 --- a/src/main/ipcs/window-controls-ipcs.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { ipcMain } from 'electron'; -import { getMainWindow } from '../main'; - -ipcMain.on('window.close', async () => { - getMainWindow()?.close(); -}); - -ipcMain.on('window.maximize', async () => { - getMainWindow()?.maximize(); -}); - -ipcMain.on('window.minimize', async () => { - getMainWindow()?.minimize(); -}); - -ipcMain.on('window.reset', async () => { - getMainWindow()?.restore(); -}); diff --git a/src/main/services/bs-installer.service.ts b/src/main/services/bs-installer.service.ts index 9e49bd98..756fdfc9 100644 --- a/src/main/services/bs-installer.service.ts +++ b/src/main/services/bs-installer.service.ts @@ -5,26 +5,30 @@ import { SteamService } from "./steam.service"; import { UtilsService } from "./utils.service"; import { ChildProcessWithoutNullStreams, spawn } from "child_process"; import log from "electron-log"; +import { InstallationLocationService } from "./installation-location.service"; export class BSInstallerService{ - private static readonly INSTALLATION_FOLDER = path.join("BSManager", "BSInstances"); + private static readonly INSTALLATION_FOLDER = "BSInstances"; private static instance: BSInstallerService; private readonly utils: UtilsService; private readonly steamService: SteamService; private readonly bsVersionService: BSVersionManagerService; + private readonly installLocationService: InstallationLocationService; private downloadProcess: ChildProcessWithoutNullStreams; - private installationFolder: string; - private constructor(){ this.bsVersionService = BSVersionManagerService.getInstance(); this.utils = UtilsService.getInstance(); this.steamService = SteamService.getInstance(); - this.installationFolder = path.join(this.utils.getUserDocumentsFolder(), BSInstallerService.INSTALLATION_FOLDER); + this.installLocationService = InstallationLocationService.getInstance(); + } + + public get installationFolder(): string{ + return path.join(this.installLocationService.installationDirectory, BSInstallerService.INSTALLATION_FOLDER); } public static getInstance(){ @@ -36,9 +40,6 @@ export class BSInstallerService{ return path.join(this.utils.getAssetsPath(), 'depot-downloader', 'DepotDownloader.exe'); } - public getBSInstallationFolder(): string{ return this.installationFolder; } - public setBSInstallationFolder(path: string){ this.installationFolder = path; } - public async getInstalledBsVersion(): Promise{ const versions: BSVersion[] = []; const steamBsFolder = await this.steamService.getGameFolder(BS_APP_ID, "Beat Saber") @@ -50,9 +51,9 @@ export class BSInstallerService{ } } - if(!this.utils.folderExist(this.getBSInstallationFolder())){ return versions } + if(!this.utils.folderExist(this.installationFolder)){ return versions } - const folderInInstallation = this.utils.listDirsInDir(this.getBSInstallationFolder()); + const folderInInstallation = this.utils.listDirsInDir(this.installationFolder); folderInInstallation.forEach(f => { const version = this.bsVersionService.getVersionDetailFromVersionNumber(f); @@ -72,7 +73,7 @@ export class BSInstallerService{ const bsVersion = this.bsVersionService.getVersionDetailFromVersionNumber(downloadInfos.bsVersion.BSVersion); if(!bsVersion){ this.utils.ipcSend(`bs-download.${DownloadEventType.ERROR}`); return; } - this.utils.createFolderIfNotExist(this.getBSInstallationFolder()); + this.utils.createFolderIfNotExist(this.installationFolder); this.downloadProcess = spawn( this.getDepotDownloaderExePath(), [ @@ -83,7 +84,7 @@ export class BSInstallerService{ `-dir ${bsVersion.BSVersion}`, (downloadInfos.stay || !downloadInfos.password) && "-remember-password" ], - {shell: true, cwd: this.getBSInstallationFolder()} + {shell: true, cwd: this.installationFolder} ) this.downloadProcess.stdout.on('data', async (data) => { diff --git a/src/main/services/bs-uninstaller.service.ts b/src/main/services/bs-uninstaller.service.ts index 6a4e65af..ea55c51b 100644 --- a/src/main/services/bs-uninstaller.service.ts +++ b/src/main/services/bs-uninstaller.service.ts @@ -22,7 +22,7 @@ export class BSUninstallerService { public async uninstall(version :BSVersion){ if(version.steam){ throw "Cannot uninstall steam version"; } - const versionFolder = path.join(this.bsInstallerService.getBSInstallationFolder(), version.BSVersion); + const versionFolder = path.join(this.bsInstallerService.installationFolder, version.BSVersion); if(!this.utilsService.folderExist(versionFolder)){ throw "Version folder not exist"; } return await this.utilsService.deleteFolder(versionFolder); diff --git a/src/main/services/configuration.service.ts b/src/main/services/configuration.service.ts new file mode 100644 index 00000000..20055090 --- /dev/null +++ b/src/main/services/configuration.service.ts @@ -0,0 +1,32 @@ +import ElectronStore from "electron-store"; + +export class ConfigurationService { + + private static instance: ConfigurationService; + + private readonly _store: ElectronStore; + + public static getInstance(): ConfigurationService{ + if(!ConfigurationService.instance){ ConfigurationService.instance = new ConfigurationService(); } + return ConfigurationService.instance; + } + + private constructor(){ + this._store = new ElectronStore(); + } + + public get store(): ElectronStore{ return this._store; } + + public set(key: string, value: any): void{ + this.store.set(key, value); + } + + public get(key: string): any{ + return this.store.get(key); + } + + public delete(key: string): void{ + this.store.delete(key); + } + +} \ No newline at end of file diff --git a/src/main/services/installation-location.service.ts b/src/main/services/installation-location.service.ts new file mode 100644 index 00000000..9d1dbe78 --- /dev/null +++ b/src/main/services/installation-location.service.ts @@ -0,0 +1,56 @@ +import path from "path"; +import { ConfigurationService } from "./configuration.service"; +import { UtilsService } from "./utils.service"; +import fs from 'fs-extra'; +import log from "electron-log"; + +export class InstallationLocationService { + + private static instance: InstallationLocationService; + + private readonly INSTALLATION_FOLDER = "BSManager"; + private readonly STORE_INSTALLATION_PATH_KEY = "installation-folder"; + + + private readonly configService: ConfigurationService; + private readonly utilsService: UtilsService; + + + private _installationDirectory: string; + + public static getInstance(): InstallationLocationService{ + if(!InstallationLocationService.instance){ InstallationLocationService.instance = new InstallationLocationService(); } + return InstallationLocationService.instance; + } + + private constructor(){ + this.configService = ConfigurationService.getInstance(); + this.utilsService = UtilsService.getInstance(); + this.initInstallationLocation(); + } + + private initInstallationLocation(): void{ + this._installationDirectory = (this.configService.get(this.STORE_INSTALLATION_PATH_KEY) || this.utilsService.getUserDocumentsFolder()) as string; + } + + public get installationDirectory(): string{ + return path.join(this._installationDirectory, this.INSTALLATION_FOLDER); + } + + public setInstallationDirectory(newDir: string): Promise{ + const oldDir = this.installationDirectory; + const newDest = path.join(newDir, this.INSTALLATION_FOLDER); + return new Promise((resolve, reject) => { + fs.move(oldDir, newDest, { overwrite: true }).then(() => { + this._installationDirectory = newDir; + this.configService.store.set(this.STORE_INSTALLATION_PATH_KEY, newDir); + resolve(this.installationDirectory); + }).catch(err => { + reject(err); + log.error(err); + }) + }) + + } + +} \ No newline at end of file diff --git a/src/renderer/components/modal/modal-types/installation-folder-modal.component.tsx b/src/renderer/components/modal/modal-types/installation-folder-modal.component.tsx new file mode 100644 index 00000000..5cf817bb --- /dev/null +++ b/src/renderer/components/modal/modal-types/installation-folder-modal.component.tsx @@ -0,0 +1,22 @@ +import { BsmButton } from "renderer/components/shared/bsm-button.component"; +import { BsmImage } from "renderer/components/shared/bsm-image.component"; +import { ModalExitCode, ModalResponse } from "renderer/services/modale.service"; +import BeatConflict from "../../../../../assets/beat-conflict.png" + +export function InstallationFolderModal({resolver}: {resolver: (x: ModalResponse) => void}) { + + + + return ( + <> + +

installation folder

+ +

Changing the default installation folder for Beat Saber instances will cause all installed data to be moved to the new folder.

+
+ resolver({exitCode: ModalExitCode.CANCELED})} withBar={false} text="Cancel"/> + resolver({exitCode: ModalExitCode.COMPLETED})} withBar={false} text="Choose Folder"/> +
+ + ) +} diff --git a/src/renderer/services/bs-downloader.service.ts b/src/renderer/services/bs-downloader.service.ts index 669040a4..2462a294 100644 --- a/src/renderer/services/bs-downloader.service.ts +++ b/src/renderer/services/bs-downloader.service.ts @@ -1,7 +1,9 @@ import { BehaviorSubject } from 'rxjs'; +import { IpcResponse } from 'shared/models/ipc-models.model'; import { DownloadInfo } from '../../main/ipcs/bs-download-ipcs'; import { BSVersion } from '../../main/services/bs-version-manager.service' import { BSVersionManagerService } from './bs-version-manager.service'; +import { IpcService } from './ipc.service'; import { ModalExitCode, ModalService, ModalType } from './modale.service'; export class BsDownloaderService{ @@ -9,6 +11,7 @@ export class BsDownloaderService{ private static instance: BsDownloaderService; private readonly modalService: ModalService = ModalService.getInsance(); + private readonly ipcService: IpcService; private readonly bsVersionManager: BSVersionManagerService = BSVersionManagerService.getInstance(); public readonly currentBsVersionDownload$: BehaviorSubject = new BehaviorSubject(null); @@ -16,6 +19,7 @@ export class BsDownloaderService{ public readonly selectedBsVersion$: BehaviorSubject = new BehaviorSubject(null); + public static getInstance(): BsDownloaderService{ if(!BsDownloaderService.instance){ BsDownloaderService.instance = new BsDownloaderService(); } return BsDownloaderService.instance; @@ -23,6 +27,7 @@ export class BsDownloaderService{ private constructor(){ this.asignListerners(); + this.ipcService = IpcService.getInstance(); } private asignListerners(): void{ @@ -81,4 +86,13 @@ export class BsDownloaderService{ return !!this.currentBsVersionDownload$.value || !!this.downloadProgress$.value; } + public async getInstallationFolder(): Promise{ + const res = await this.ipcService.send("bs-download.installation-folder"); + return res.success ? res.data : ""; + } + + public setInstallationFolder(path: string): Promise>{ + return this.ipcService.send("bs-download.set-installation-folder", {args: path}); + } + } diff --git a/src/renderer/services/modale.service.ts b/src/renderer/services/modale.service.ts index bfea29d4..d3ab2bc9 100644 --- a/src/renderer/services/modale.service.ts +++ b/src/renderer/services/modale.service.ts @@ -52,6 +52,7 @@ export enum ModalType { STEAM_LOGIN = "STEAM_LOGIN", GUARD_CODE = "GUARD_CODE", UNINSTALL = "UNINSTALL", + INSTALLATION_FOLDER = "INSTALLATION_FOLDER", } export enum ModalExitCode {