mirror of
https://github.com/Zagrios/bs-manager.git
synced 2026-07-03 14:08:25 +02:00
Merge pull request #566 from silentrald/feat/560
[feat-560] ask user for installation folder/path
This commit is contained in:
@@ -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.path, args.move)));
|
||||
});
|
||||
@@ -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));
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import ElectronStore from "electron-store";
|
||||
import fs from "fs-extra";
|
||||
import { InstallationLocationService } from "./installation-location.service";
|
||||
import { CustomError } from "shared/models/exceptions/custom-error.class";
|
||||
|
||||
export class ConfigurationService {
|
||||
private static instance: ConfigurationService;
|
||||
@@ -13,17 +15,23 @@ export class ConfigurationService {
|
||||
|
||||
private readonly locations: InstallationLocationService;
|
||||
|
||||
private contentPath: string;
|
||||
private store: ElectronStore;
|
||||
|
||||
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.contentPath = contentPath;
|
||||
this.store = new ElectronStore({
|
||||
cwd: contentPath,
|
||||
name: "config",
|
||||
@@ -32,15 +40,25 @@ export class ConfigurationService {
|
||||
});
|
||||
}
|
||||
|
||||
private checkStore(): void {
|
||||
// Can be null if config.cfg does not exist or corrupted
|
||||
if (!this.store) {
|
||||
throw CustomError.fromError(new Error(`Can't read config.cfg on ${this.contentPath}`));
|
||||
}
|
||||
}
|
||||
|
||||
public set(key: string, value: unknown): void {
|
||||
this.checkStore();
|
||||
this.store.set(key, value);
|
||||
}
|
||||
|
||||
public get<T>(key: string): T {
|
||||
this.checkStore();
|
||||
return this.store.get(key) as T;
|
||||
}
|
||||
|
||||
public delete(key: string): void {
|
||||
this.checkStore();
|
||||
this.store.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,18 +40,22 @@ export class InstallationLocationService {
|
||||
this.updateListeners.forEach(listener => listener());
|
||||
}
|
||||
|
||||
public async setInstallationDirectory(newDir: string): Promise<string> {
|
||||
/**
|
||||
* @param move - if true, move the old installation path to the path param
|
||||
*/
|
||||
public async setInstallationDirectory(newDir: string, move: boolean): Promise<string> {
|
||||
newDir = path.basename(newDir) === this.INSTALLATION_FOLDER ? path.join(newDir, "..") : newDir;
|
||||
const oldDir = this.installationDirectory();
|
||||
|
||||
await ensureFolderExist(oldDir);
|
||||
await copyDirectoryWithJunctions(oldDir, path.join(newDir, this.INSTALLATION_FOLDER), { overwrite: true });
|
||||
if (move) {
|
||||
const oldDir = this.installationDirectory();
|
||||
await ensureFolderExist(oldDir);
|
||||
await copyDirectoryWithJunctions(oldDir, path.join(newDir, this.INSTALLATION_FOLDER), { overwrite: true });
|
||||
deleteFolder(oldDir);
|
||||
}
|
||||
|
||||
this._installationDirectory = newDir;
|
||||
this.staticConfig.set(this.STORE_INSTALLATION_PATH_KEY, newDir);
|
||||
|
||||
deleteFolder(oldDir);
|
||||
|
||||
return this.installationDirectory();
|
||||
}
|
||||
|
||||
@@ -59,6 +63,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 = () => {
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
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 Tippy from "@tippyjs/react";
|
||||
|
||||
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 (
|
||||
<form
|
||||
className="max-w-xl w-max"
|
||||
onSubmit={event => {
|
||||
event.preventDefault();
|
||||
onConfirmButtonPressed();
|
||||
}}>
|
||||
|
||||
<h1 className="tracking-wide w-full uppercase text-3xl text-center">
|
||||
{t("modals.ask-install-path.title")}
|
||||
</h1>
|
||||
|
||||
<p className="py-3">
|
||||
{t("modals.ask-install-path.choose-folder-description")}
|
||||
</p>
|
||||
|
||||
<div className="relative rounded-md pl-2 py-1 mb-3 flex items-center justify-between gap-1 w-full h-8 bg-light-main-color- dark:bg-main-color-1">
|
||||
<span className="text-ellipsis overflow-hidden min-w-0 text-nowrap text-left cursor-help" title={installPath} style={{ direction: "rtl" }}>
|
||||
{installPath}
|
||||
</span>
|
||||
<BsmButton
|
||||
onClick={selectInstallPath}
|
||||
className="shrink-0 whitespace-nowrap mr-2 px-2 font-bold italic text-sm rounded-md"
|
||||
text="modals.ask-install-path.choose-folder"
|
||||
withBar={false}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="h-8 grid grid-flow-col grid-cols-2 gap-2">
|
||||
<Tippy
|
||||
content={t("modals.ask-install-path.default-tooltip")}
|
||||
theme="default"
|
||||
delay={[300, 0]}
|
||||
arrow={false}
|
||||
placement="bottom"
|
||||
>
|
||||
<BsmButton
|
||||
typeColor="cancel"
|
||||
className="rounded-md text-center transition-all flex items-center justify-center"
|
||||
onClick={onDefaultButtonPressed}
|
||||
withBar={false}
|
||||
text="modals.ask-install-path.default"
|
||||
/>
|
||||
</Tippy>
|
||||
<BsmButton
|
||||
typeColor="primary"
|
||||
className="rounded-md text-center transition-all"
|
||||
type="submit"
|
||||
withBar={false}
|
||||
text="misc.confirm"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
@@ -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 (
|
||||
<div
|
||||
className="w-2.5 h-2.5 absolute top-2.5 right-1.5 cursor-pointer"
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
modal.resolver({ exitCode: ModalExitCode.CLOSED });
|
||||
}}
|
||||
>
|
||||
<BsmIcon className="size-full" icon="cross" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const renderModal = (modal: ModalObject) => {
|
||||
if (!modal?.modal) { return null; }
|
||||
|
||||
@@ -44,23 +58,23 @@ export function Modal() {
|
||||
return (
|
||||
<div className="relative p-4 text-gray-800 dark:text-gray-200 rounded-md shadow-lg shadow-black bg-gradient-to-br from-light-main-color-3 to-light-main-color-2 dark:from-main-color-3 dark:to-main-color-2">
|
||||
<ThemeColorGradientSpliter className="absolute top-0 w-full left-0 h-1 rounded-t-md overflow-hidden"/>
|
||||
<div
|
||||
className="w-2.5 h-2.5 absolute top-2.5 right-1.5 cursor-pointer"
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
modal.resolver({ exitCode: ModalExitCode.CLOSED });
|
||||
}}
|
||||
>
|
||||
<BsmIcon className="size-full" icon="cross" />
|
||||
</div>
|
||||
{modal.options?.closable === false ? undefined : renderCloseButton(modal)}
|
||||
<modal.modal resolver={modal.resolver} options={modal.options} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const onOverlayClicked = () => {
|
||||
if (currentModal.options.closable === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentModal.resolver({ exitCode: ModalExitCode.NO_CHOICE });
|
||||
}
|
||||
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{currentModal ? <motion.span key={crypto.randomUUID()} onClick={() => currentModal.resolver({ exitCode: ModalExitCode.NO_CHOICE })} className="fixed size-full bg-black z-[90]" initial={{ opacity: 0 }} animate={{ opacity: currentModal && 0.6 }} exit={{ opacity: 0 }} transition={{ duration: 0.2 }} /> : undefined}
|
||||
{currentModal ? <motion.span key={crypto.randomUUID()} onClick={onOverlayClicked} className="fixed size-full bg-black z-[90]" initial={{ opacity: 0 }} animate={{ opacity: currentModal && 0.6 }} exit={{ opacity: 0 }} transition={{ duration: 0.2 }} /> : undefined}
|
||||
{modals?.map(modal => (
|
||||
<motion.div key={crypto.randomUUID()} className="fixed z-[90] top-1/2 left-1/2" initial={{ y: "100vh", x: "-50%" }} animate={{y: "-50%", scale: modal === currentModal ? 1 : 0, opacity: modal === currentModal ? 1 : 0, display: modal === currentModal ? "block" : ["block", "none"]}} exit={{ y: "100vh" }}>
|
||||
{renderModal(modal)}
|
||||
|
||||
@@ -48,6 +48,7 @@ import { SettingToogleSwitchGrid } from "renderer/components/settings/setting-to
|
||||
import { BasicModal } from "renderer/components/modal/basic-modal.component";
|
||||
import { StaticConfigurationService } from "renderer/services/static-configuration.service";
|
||||
import { tryit } from "shared/helpers/error.helpers";
|
||||
import { InstallationLocationService } from "renderer/services/installation-location.service";
|
||||
|
||||
export function SettingsPage() {
|
||||
|
||||
@@ -68,6 +69,7 @@ export function SettingsPage() {
|
||||
const versionLinker = useService(VersionFolderLinkerService);
|
||||
const autoUpdater = useService(AutoUpdaterService);
|
||||
const staticConfig = useService(StaticConfigurationService);
|
||||
const installationLocationService = useService(InstallationLocationService);
|
||||
|
||||
const { firstColor, secondColor } = useThemeColor();
|
||||
|
||||
@@ -124,7 +126,7 @@ export function SettingsPage() {
|
||||
};
|
||||
|
||||
const loadInstallationFolder = () => {
|
||||
steamDownloader.getInstallationFolder().then(res => {
|
||||
installationLocationService.getInstallationFolder().then(res => {
|
||||
setInstallationFolder(res);
|
||||
});
|
||||
};
|
||||
@@ -202,7 +204,7 @@ export function SettingsPage() {
|
||||
|
||||
notificationService.notifySuccess({ title: "notifications.settings.move-folder.success.titles.transfer-started", desc: "notifications.settings.move-folder.success.descs.transfer-started" });
|
||||
|
||||
lastValueFrom(steamDownloader.setInstallationFolder(fileChooserRes.filePaths[0])).then(res => {
|
||||
lastValueFrom(installationLocationService.setInstallationFolder(fileChooserRes.filePaths[0], true)).then(res => {
|
||||
|
||||
progressBarService.complete();
|
||||
progressBarService.hide(true);
|
||||
|
||||
Vendored
+1
@@ -10,6 +10,7 @@ declare global {
|
||||
};
|
||||
path: {
|
||||
sep: "/"|"\\";
|
||||
basename: (path: string) => string;
|
||||
join: (...args: string[]) => string;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -8,7 +8,6 @@ import { NotificationService } from "../notification.service";
|
||||
import { ProgressBarService } from "../progress-bar.service";
|
||||
import { LoginToSteamModal } from "renderer/components/modal/modal-types/bs-downgrade/login-to-steam-modal.component";
|
||||
import { SteamGuardModal } from "renderer/components/modal/modal-types/bs-downgrade/steam-guard-modal.component";
|
||||
import { LinkOpenerService } from "../link-opener.service";
|
||||
import { DepotDownloaderErrorEvent, DepotDownloaderEvent, DepotDownloaderEventType, DepotDownloaderInfoEvent, DepotDownloaderWarningEvent } from "../../../shared/models/bs-version-download/depot-downloader.model";
|
||||
import { SteamMobileApproveModal } from "renderer/components/modal/modal-types/bs-downgrade/steam-mobile-approve-modal.component";
|
||||
import { DownloaderServiceInterface } from "./bs-store-downloader.interface";
|
||||
@@ -30,7 +29,6 @@ export class SteamDownloaderService extends AbstractBsDownloaderService implemen
|
||||
private readonly ipcService: IpcService;
|
||||
private readonly progressBarService: ProgressBarService;
|
||||
private readonly notificationService: NotificationService;
|
||||
private readonly linkOpener: LinkOpenerService;
|
||||
|
||||
private readonly STEAM_SESSION_USERNAME_KEY = "STEAM-USERNAME";
|
||||
|
||||
@@ -42,7 +40,6 @@ export class SteamDownloaderService extends AbstractBsDownloaderService implemen
|
||||
this.modalService = ModalService.getInstance();
|
||||
this.progressBarService = ProgressBarService.getInstance();
|
||||
this.notificationService = NotificationService.getInstance();
|
||||
this.linkOpener = LinkOpenerService.getInstance();
|
||||
}
|
||||
|
||||
private setSteamSession(username: string): void { localStorage.setItem(this.STEAM_SESSION_USERNAME_KEY, username); }
|
||||
@@ -50,14 +47,6 @@ export class SteamDownloaderService extends AbstractBsDownloaderService implemen
|
||||
public deleteSteamSession(): void { localStorage.removeItem(this.STEAM_SESSION_USERNAME_KEY); }
|
||||
public sessionExist(): boolean { return !!localStorage.getItem(this.STEAM_SESSION_USERNAME_KEY); }
|
||||
|
||||
public async getInstallationFolder(): Promise<string> {
|
||||
return lastValueFrom(this.ipcService.sendV2("bs-download.installation-folder"));
|
||||
}
|
||||
|
||||
public setInstallationFolder(path: string): Observable<string> {
|
||||
return this.ipcService.sendV2("bs-download.set-installation-folder", path);
|
||||
}
|
||||
|
||||
// ### Downloading
|
||||
|
||||
private handleInfoEvents(events$: Observable<DepotDownloaderEvent>): Subscription[] {
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Observable, lastValueFrom } from "rxjs";
|
||||
import { IpcService } from "./ipc.service";
|
||||
|
||||
|
||||
export class InstallationLocationService {
|
||||
|
||||
private static instance: InstallationLocationService;
|
||||
|
||||
private readonly ipcService: IpcService;
|
||||
|
||||
public static getInstance(): InstallationLocationService {
|
||||
if (!InstallationLocationService.instance) {
|
||||
InstallationLocationService.instance = new InstallationLocationService();
|
||||
}
|
||||
return InstallationLocationService.instance;
|
||||
}
|
||||
|
||||
private constructor() {
|
||||
this.ipcService = IpcService.getInstance();
|
||||
}
|
||||
|
||||
public async getInstallationFolder(): Promise<string> {
|
||||
return lastValueFrom(this.ipcService.sendV2("bs-installer.install-path"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param move - if true, move the old installation path to the path param
|
||||
*/
|
||||
public setInstallationFolder(path: string, move: boolean): Observable<string> {
|
||||
return this.ipcService.sendV2(
|
||||
"bs-installer.set-install-path",
|
||||
{ path, move }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ export class ModalService {
|
||||
}
|
||||
}
|
||||
|
||||
export type ModalOptions<T = unknown> = { readonly data?: T, readonly noStyle?: boolean }
|
||||
export type ModalOptions<T = unknown> = { readonly data?: T, readonly noStyle?: boolean, readonly closable?: boolean }
|
||||
export type ModalComponent<Return = unknown, Receive = unknown> = ({ resolver, options }: { readonly resolver: (x: ModalResponse<Return>) => void; readonly options?: ModalOptions<Receive> }) => JSX.Element;
|
||||
export type ModalObject = {modal: ModalComponent, resolver: (value: ModalResponse | PromiseLike<ModalResponse>) => void, options: ModalOptions};
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import { lastValueFrom } from "rxjs";
|
||||
import { logRenderError } from "renderer";
|
||||
|
||||
import { BSVersionManagerService } from "./bs-version-manager.service";
|
||||
import { InstallationLocationService } from "./installation-location.service";
|
||||
import { IpcService } from "./ipc.service";
|
||||
import { ModalService } from "./modale.service";
|
||||
|
||||
import { AskInstallPathModal } from "renderer/components/modal/modal-types/ask-install-path.component";
|
||||
|
||||
// Handle setup modals/prompts, ordering of the modals/prompts may be done here
|
||||
export class SetupService {
|
||||
private static instance: SetupService;
|
||||
|
||||
private readonly installationLocationService: InstallationLocationService;
|
||||
private readonly ipcService: IpcService;
|
||||
private readonly modalService: ModalService;
|
||||
private readonly versionManagerService: BSVersionManagerService;
|
||||
|
||||
private constructor() {
|
||||
this.installationLocationService = InstallationLocationService.getInstance();
|
||||
this.ipcService = IpcService.getInstance();
|
||||
this.modalService = ModalService.getInstance();
|
||||
this.versionManagerService = BSVersionManagerService.getInstance();
|
||||
}
|
||||
|
||||
public static getInstance(): SetupService {
|
||||
if (!SetupService.instance) {
|
||||
SetupService.instance = new SetupService();
|
||||
}
|
||||
return SetupService.instance;
|
||||
}
|
||||
|
||||
public async check(): Promise<void> {
|
||||
try {
|
||||
// NOTE: for modal sequencing
|
||||
await this.checkInstallationPath();
|
||||
} catch (error) {
|
||||
logRenderError(error);
|
||||
}
|
||||
}
|
||||
|
||||
private async checkInstallationPath(): Promise<void> {
|
||||
try {
|
||||
const exists = await lastValueFrom(this.ipcService.sendV2("bs-installer.folder-exists"));
|
||||
if (exists) {
|
||||
return;
|
||||
}
|
||||
|
||||
const modalResponse = await this.modalService.openModal(
|
||||
AskInstallPathModal,
|
||||
{ closable: false }
|
||||
);
|
||||
|
||||
await lastValueFrom(this.installationLocationService.setInstallationFolder(modalResponse.data.installPath, false));
|
||||
|
||||
// Refresh the versions tab
|
||||
await this.versionManagerService.askInstalledVersions();
|
||||
} catch (error) {
|
||||
logRenderError(error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import { ConfigurationService } from "renderer/services/configuration.service";
|
||||
import { OsDiagnosticService } from "renderer/services/os-diagnostic.service";
|
||||
import { useService } from "renderer/hooks/use-service.hook";
|
||||
import { AutoUpdaterService } from "renderer/services/auto-updater.service";
|
||||
import { SetupService } from "renderer/services/setup.service";
|
||||
import { gt, parse } from "semver"
|
||||
import { logRenderError } from "renderer";
|
||||
|
||||
@@ -35,13 +36,17 @@ export default function App() {
|
||||
const notification = useService(NotificationService);
|
||||
const config = useService(ConfigurationService);
|
||||
const autoUpdater = useService(AutoUpdaterService);
|
||||
const setup = useService(SetupService);
|
||||
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
checkIsUpdated();
|
||||
checkOneClicks();
|
||||
setup.check()
|
||||
.then(() => {
|
||||
checkOneClicks();
|
||||
})
|
||||
}, []);
|
||||
|
||||
const checkIsUpdated = async () => {
|
||||
|
||||
@@ -27,8 +27,6 @@ export interface IpcChannelMapping {
|
||||
|
||||
/* ** bs-download-ipcs ** */
|
||||
"import-version": { request: ImportVersionOptions, response: Progression<BSVersion>};
|
||||
"bs-download.installation-folder": { request: void, response: string};
|
||||
"bs-download.set-installation-folder": { request: string, response: string};
|
||||
"auto-download-bs-version": { request: DownloadSteamInfo, response: DepotDownloaderEvent};
|
||||
"download-bs-version": { request: DownloadSteamInfo, response: DepotDownloaderEvent};
|
||||
"download-bs-version-qr": { request: DownloadSteamInfo, response: DepotDownloaderEvent};
|
||||
@@ -44,6 +42,12 @@ export interface IpcChannelMapping {
|
||||
"bsv-search-playlist": {request: PlaylistSearchParams, response: BsvPlaylist[]};
|
||||
"bsv-get-playlist-details-by-id": {request: {id: string, page: number}, response: BsvPlaylistPage};
|
||||
|
||||
/* ** bs-installer-ipcs ** */
|
||||
"bs-installer.folder-exists": { request: void, response: boolean };
|
||||
"bs-installer.default-install-path": { request: void, response: string };
|
||||
"bs-installer.install-path": { request: void, response: string};
|
||||
"bs-installer.set-install-path": { request: { path: string, move: boolean }, response: string};
|
||||
|
||||
/* ** bs-launcher-ipcs ** */
|
||||
"create-launch-shortcut": { request: LaunchOption, response: boolean };
|
||||
"bs-launch.need-start-as-admin": { request: void, response: boolean };
|
||||
|
||||
Reference in New Issue
Block a user