diff --git a/assets/jsons/translations/de.json b/assets/jsons/translations/de.json index f3edef0c..0b6ec058 100644 --- a/assets/jsons/translations/de.json +++ b/assets/jsons/translations/de.json @@ -14,7 +14,8 @@ "refuse": "Ablehnen", "apply": "Übernehmen", "copy": "Kopieren", - "copied": "Kopiert!" + "copied": "Kopiert!", + "confirm": "Confirm" }, "nav-bar": { "add-version": "Version hinzufügen", @@ -767,6 +768,11 @@ }, "launch-as-admin": "Als Administrator starten", "not-remind-me": "Nicht mehr erinnern" + }, + "ask-install-path": { + "title": "Installations Ordner", + "choose-folder": "Ordner Wählen", + "default": "Default" } }, "maps": { diff --git a/assets/jsons/translations/en.json b/assets/jsons/translations/en.json index 3d9b1392..e80af42c 100644 --- a/assets/jsons/translations/en.json +++ b/assets/jsons/translations/en.json @@ -14,7 +14,8 @@ "refuse": "Refuse", "apply": "Apply", "copy": "Copy", - "copied": "Copied!" + "copied": "Copied!", + "confirm": "Confirm" }, "nav-bar": { "add-version": "Add a version", @@ -774,6 +775,11 @@ }, "launch-as-admin": "Launch as Administrator", "not-remind-me": "Do not remind me" + }, + "ask-install-path": { + "title": "Installation folder", + "choose-folder": "Choose folder", + "default": "Default" } }, "maps": { diff --git a/assets/jsons/translations/es.json b/assets/jsons/translations/es.json index e3122355..c0d85e29 100644 --- a/assets/jsons/translations/es.json +++ b/assets/jsons/translations/es.json @@ -14,7 +14,8 @@ "refuse": "Rechazar", "apply": "Aplicar", "copy": "Copiar", - "copied": "¡Copiado!" + "copied": "¡Copiado!", + "confirm": "Confirm" }, "nav-bar": { "add-version": "Agregar una versión", @@ -767,6 +768,11 @@ }, "launch-as-admin": "Iniciar como Administrador", "not-remind-me": "No volver a recordármelo" + }, + "ask-install-path": { + "title": "Carpeta de instalación", + "choose-folder": "Elige la carpeta", + "default": "Default" } }, "maps": { diff --git a/assets/jsons/translations/fr.json b/assets/jsons/translations/fr.json index d2748d53..1cfca14c 100644 --- a/assets/jsons/translations/fr.json +++ b/assets/jsons/translations/fr.json @@ -14,7 +14,8 @@ "refuse": "Refuser", "apply": "Appliquer", "copy": "Copier", - "copied": "Copié !" + "copied": "Copié !", + "confirm": "Confirm" }, "nav-bar": { "add-version": "Ajouter une version", @@ -768,6 +769,11 @@ }, "launch-as-admin": "Lancer en administateur", "not-remind-me": "Ne plus me rappeler" + }, + "ask-install-path": { + "title": "Dossier d'installation", + "choose-folder": "Choisir un dossier", + "default": "Default" } }, "maps": { diff --git a/assets/jsons/translations/ja.json b/assets/jsons/translations/ja.json index 948f31e8..981d3bd0 100644 --- a/assets/jsons/translations/ja.json +++ b/assets/jsons/translations/ja.json @@ -14,7 +14,8 @@ "refuse": "拒否する", "apply": "適用", "copy": "コピー", - "copied": "コピー済み!" + "copied": "コピー済み!", + "confirm": "Confirm" }, "nav-bar": { "add-version": "バージョンを追加", @@ -767,6 +768,11 @@ }, "launch-as-admin": "管理者として起動", "not-remind-me": "Больше не напоминать" + }, + "ask-install-path": { + "title": "インストールフォルダー", + "choose-folder": "フォルダーを選択", + "default": "Default" } }, "maps": { diff --git a/assets/jsons/translations/ru.json b/assets/jsons/translations/ru.json index c246de4e..48ae1bd7 100644 --- a/assets/jsons/translations/ru.json +++ b/assets/jsons/translations/ru.json @@ -14,7 +14,8 @@ "refuse": "Отказаться", "apply": "Применить", "copy": "Скопировать", - "copied": "Скопировано!" + "copied": "Скопировано!", + "confirm": "Confirm" }, "nav-bar": { "add-version": "Добавить версию игры", @@ -766,6 +767,11 @@ "info-3": "Обратите внимание, что не рекомендуется предоставлять права администратора Steam, так как это также влияет на установленные игры и моды, представляя угрозу безопасности." }, "launch-as-admin": "Запустить от имени администратора" + }, + "ask-install-path": { + "title": "Папка установок", + "choose-folder": "Изменить папку", + "default": "Default" } }, "maps": { diff --git a/assets/jsons/translations/zh-tw.json b/assets/jsons/translations/zh-tw.json index 95ab1ff8..6d23db13 100644 --- a/assets/jsons/translations/zh-tw.json +++ b/assets/jsons/translations/zh-tw.json @@ -14,7 +14,8 @@ "refuse": "拒絕", "apply": "應用", "copy": "複製", - "copied": "已複製!" + "copied": "已複製!", + "confirm": "Confirm" }, "nav-bar": { "add-version": "新增版本", @@ -767,6 +768,11 @@ }, "launch-as-admin": "以管理員身份啟動", "not-remind-me": "不再提醒我" + }, + "ask-install-path": { + "title": "安裝文件夾", + "choose-folder": "選擇文件夾", + "default": "Default" } }, "maps": { diff --git a/assets/jsons/translations/zh.json b/assets/jsons/translations/zh.json index b052d4c2..86bfac7e 100644 --- a/assets/jsons/translations/zh.json +++ b/assets/jsons/translations/zh.json @@ -14,7 +14,8 @@ "refuse": "拒绝", "apply": "应用", "copy": "复制", - "copied": "已复制!" + "copied": "已复制!", + "confirm": "Confirm" }, "nav-bar": { "add-version": "添加版本", @@ -767,6 +768,11 @@ }, "launch-as-admin": "以管理员身份启动", "not-remind-me": "不再提醒我" + }, + "ask-install-path": { + "title": "安装文件夹", + "choose-folder": "选择文件夹", + "default": "Default" } }, "maps": { diff --git a/src/main/ipcs/bs-installer-ipcs.ts b/src/main/ipcs/bs-installer-ipcs.ts new file mode 100644 index 00000000..25672b13 --- /dev/null +++ b/src/main/ipcs/bs-installer-ipcs.ts @@ -0,0 +1,27 @@ +import { pathExistsSync } from "fs-extra"; +import { from, of } from "rxjs"; + +import { InstallationLocationService } from "main/services/installation-location.service"; +import { IpcService } from "main/services/ipc.service"; + +const ipc = IpcService.getInstance(); + +ipc.on("bs-installer.folder-exists", (_, reply) => { + const service = InstallationLocationService.getInstance(); + reply(of(pathExistsSync(service.installationDirectory()))); +}); + +ipc.on("bs-installer.default-install-path", (_, reply) => { + const service = InstallationLocationService.getInstance(); + reply(of(service.defaultInstallationDirectory())); +}); + +ipc.on("bs-installer.install-path", (_, reply) => { + const service = InstallationLocationService.getInstance(); + reply(of(service.installationDirectory())); +}); + +ipc.on("bs-installer.set-install-path", (args, reply) => { + const service = InstallationLocationService.getInstance(); + reply(from(service.setInstallationDirectory(args))); +}); diff --git a/src/main/ipcs/bs-version-download/bs-download-ipcs.ts b/src/main/ipcs/bs-version-download/bs-download-ipcs.ts index c27c591d..dfc4e682 100644 --- a/src/main/ipcs/bs-version-download/bs-download-ipcs.ts +++ b/src/main/ipcs/bs-version-download/bs-download-ipcs.ts @@ -1,8 +1,7 @@ import { BsOculusDownloaderService } from "../../services/bs-version-download/bs-oculus-downloader.service"; import { BsSteamDownloaderService } from "../../services/bs-version-download/bs-steam-downloader.service"; -import { InstallationLocationService } from "../../services/installation-location.service"; import { IpcService } from "../../services/ipc.service"; -import { from, of } from "rxjs"; +import { of } from "rxjs"; import { BSLocalVersionService } from "../../services/bs-local-version.service"; const ipc = IpcService.getInstance(); @@ -14,16 +13,6 @@ ipc.on("import-version", (args, reply) => { // #region Steam -ipc.on("bs-download.installation-folder", (_, reply) => { - const installLocation = InstallationLocationService.getInstance(); - reply(of(installLocation.installationDirectory())); -}); - -ipc.on("bs-download.set-installation-folder", (args, reply) => { - const installerService = InstallationLocationService.getInstance(); - reply(from(installerService.setInstallationDirectory(args))); -}); - ipc.on("auto-download-bs-version", (args, reply) => { const bsInstaller = BsSteamDownloaderService.getInstance(); reply(bsInstaller.autoDownloadBsVersion(args)); diff --git a/src/main/ipcs/index.ts b/src/main/ipcs/index.ts index 38384c27..f80cf9f8 100644 --- a/src/main/ipcs/index.ts +++ b/src/main/ipcs/index.ts @@ -1,4 +1,5 @@ import "./os-controls-ipcs"; +import "./bs-installer-ipcs.ts"; import "./bs-launcher-ipcs"; import "./bs-version-ipcs"; import "./bs-uninstall-ipcs"; diff --git a/src/main/preload.ts b/src/main/preload.ts index 243f5e76..a4958a7f 100644 --- a/src/main/preload.ts +++ b/src/main/preload.ts @@ -24,6 +24,9 @@ contextBridge.exposeInMainWorld("electron", { }, path: { sep, + basename: (path: string): string => { + return !path ? "" : path.split(sep).at(-1); + }, join: (...args: string[]): string => { return args.join(sep); } diff --git a/src/main/services/configuration.service.ts b/src/main/services/configuration.service.ts index 9049a009..a299d281 100644 --- a/src/main/services/configuration.service.ts +++ b/src/main/services/configuration.service.ts @@ -1,4 +1,5 @@ import ElectronStore from "electron-store"; +import fs from "fs-extra"; import { InstallationLocationService } from "./installation-location.service"; export class ConfigurationService { @@ -17,13 +18,17 @@ export class ConfigurationService { private constructor() { this.locations = InstallationLocationService.getInstance(); - this.initStore(); + this.initStore(false); - this.locations.onInstallLocationUpdate(() => { this.initStore() }); + this.locations.onInstallLocationUpdate(() => this.initStore(true)); } - private async initStore() { + private async initStore(createFolder: boolean) { const contentPath = this.locations.installationDirectory(); + if (!createFolder && !fs.pathExistsSync(contentPath)) { + return; + } + this.store = new ElectronStore({ cwd: contentPath, name: "config", diff --git a/src/main/services/installation-location.service.ts b/src/main/services/installation-location.service.ts index f1c012f1..1696ea0d 100644 --- a/src/main/services/installation-location.service.ts +++ b/src/main/services/installation-location.service.ts @@ -59,6 +59,12 @@ export class InstallationLocationService { this.updateListeners.add(fn); } + public defaultInstallationDirectory(): string { + const { result: oldPath } = tryit(() => path.join(app.getPath("documents"), this.INSTALLATION_FOLDER)); + const installationDirectory = (oldPath && pathExistsSync(oldPath)) ? app.getPath("documents") : app.getPath("home"); + return path.join(installationDirectory, this.INSTALLATION_FOLDER); + } + public installationDirectory(): string { const installParentPath = () => { diff --git a/src/renderer/components/modal/modal-types/ask-install-path.component.tsx b/src/renderer/components/modal/modal-types/ask-install-path.component.tsx new file mode 100644 index 00000000..eb94fcde --- /dev/null +++ b/src/renderer/components/modal/modal-types/ask-install-path.component.tsx @@ -0,0 +1,103 @@ +import { lastValueFrom } from "rxjs"; +import { useEffect, useState } from "react"; +import { useTranslation } from "renderer/hooks/use-translation.hook"; +import { useService } from "renderer/hooks/use-service.hook"; + +import { IpcService } from "renderer/services/ipc.service"; +import { ModalComponent, ModalExitCode } from "renderer/services/modale.service"; + +import { BsmButton } from "renderer/components/shared/bsm-button.component"; + +export const AskInstallPathModal: ModalComponent<{ installPath: string }, {}> = ({ resolver }) => { + + const t = useTranslation(); + const ipcService = useService(IpcService); + + const [installPath, setInstallPath] = useState(""); + const [installFolder, setInstallFolder] = useState(""); + const [defaultInstallPath, setDefaultInstallPath] = useState(""); + + useEffect(() => { + lastValueFrom(ipcService.sendV2("bs-installer.default-install-path")) + .then(defaultPath => { + setInstallPath(defaultPath); + setDefaultInstallPath(defaultPath); + setInstallFolder(window.electron.path.basename(defaultPath)); + }); + }, []); + + const selectInstallPath = async () => { + const response = await lastValueFrom(ipcService.sendV2("choose-folder")); + if (response.canceled || !response.filePaths?.length) { + return; + } + + const path = response.filePaths[0]; + setInstallPath( + window.electron.path.basename(path) === installFolder ? + path : + window.electron.path.join(response.filePaths[0], installFolder) + ); + } + + const onDefaultButtonPressed = () => { + setInstallPath(defaultInstallPath); + } + + const onConfirmButtonPressed = () => { + resolver({ + data: { installPath }, + exitCode: ModalExitCode.COMPLETED + }); + } + + return ( +
+ ) +} diff --git a/src/renderer/components/modal/modal.component.tsx b/src/renderer/components/modal/modal.component.tsx index fe047633..2d06fbe3 100644 --- a/src/renderer/components/modal/modal.component.tsx +++ b/src/renderer/components/modal/modal.component.tsx @@ -17,7 +17,7 @@ export function Modal() { useEffect(() => { const onEscape = (e: KeyboardEvent) => { - if (e.key !== "Escape") { + if (currentModal.options.closable === false || e.key !== "Escape") { return; } currentModal.resolver({ exitCode: ModalExitCode.CLOSED }); @@ -34,6 +34,20 @@ export function Modal() { }; }, [currentModal]); + const renderCloseButton = (modal: ModalObject) => { + return ( +