Merge branch 'v1.5.0' into feature/playlists/107

This commit is contained in:
MathieuG-P
2024-03-22 15:12:26 +01:00
59 changed files with 695 additions and 1024 deletions
@@ -29,6 +29,10 @@ export const EditVersionModal: ModalComponent<{ name: string; color: string }, {
setColor(configService.get("second-color" as DefaultConfigKey));
};
const resetName = () => {
setName(version.BSVersion);
};
return (
<form
className="static"
@@ -44,7 +48,12 @@ export const EditVersionModal: ModalComponent<{ name: string; color: string }, {
<label className="block font-bold cursor-pointer tracking-wide text-gray-800 dark:text-gray-200" htmlFor="name">
{t("modals.clone-version.inputs.name.label")}
</label>
<input className="w-full bg-light-main-color-1 dark:bg-main-color-1 px-1 py-[2px] rounded-md outline-none" onChange={e => setName(e.target.value)} value={name} type="text" name="name" id="name" minLength={2} maxLength={15} placeholder={t("modals.clone-version.inputs.name.placeholder")} />
<div className="relative">
<input className="w-full bg-light-main-color-1 dark:bg-main-color-1 px-1 py-[2px] rounded-md outline-none" onChange={e => setName(e.target.value)} value={name} type="text" name="name" id="name" minLength={2} maxLength={15} placeholder={t("modals.clone-version.inputs.name.placeholder")} />
<div className="absolute right-2 top-0 h-full flex items-center">
<BsmButton onClick={resetName} className="px-2 font-bold italic text-sm rounded-md" text="pages.settings.appearance.reset" withBar={false} />
</div>
</div>
</div>
<div>
<span className="block font-bold tracking-wide text-gray-800 dark:text-gray-200">{t("modals.clone-version.inputs.color.label")}</span>
@@ -12,6 +12,7 @@ import { ConfigurationService } from "renderer/services/configuration.service";
import { IpcService } from "renderer/services/ipc.service";
import { ModalComponent } from "renderer/services/modale.service";
import { FolderLinkState, VersionFolderLinkerService, VersionLinkerActionType } from "renderer/services/version-folder-linker.service";
import { lastValueFrom } from "rxjs";
import { BSVersion } from "shared/bs-version.interface";
export const ShareFoldersModal: ModalComponent<void, BSVersion> = ({ options: {data} }) => {
@@ -45,14 +46,14 @@ export const ShareFoldersModal: ModalComponent<void, BSVersion> = ({ options: {d
}, [folders]);
const addFolder = async () => {
const versionPath = await versionManager.getVersionPath(data).toPromise();
const folder = await ipc.sendV2<{ canceled: boolean; filePaths: string[] }, string>("choose-folder", { args: versionPath }).toPromise();
const versionPath = await lastValueFrom(versionManager.getVersionPath(data));
const folder = await lastValueFrom(ipc.sendV2("choose-folder", versionPath));
if (!folder || folder.canceled || !folder.filePaths?.length) {
return;
}
const relativeFolder = await ipc.sendV2<string>("full-version-path-to-relative", { args: { version: data, fullPath: folder.filePaths[0] } }).toPromise();
const relativeFolder = await lastValueFrom(ipc.sendV2("full-version-path-to-relative", { version: data, fullPath: folder.filePaths[0] }));
if (folders.includes(relativeFolder)) {
return;
@@ -10,11 +10,13 @@ import { BsmIconType } from "../svgs/bsm-icon.component";
import "./title-bar.component.css";
import { useService } from "renderer/hooks/use-service.hook";
import { lastValueFrom } from "rxjs";
import { useWindowControls } from "renderer/hooks/use-window-controls.hook";
export default function TitleBar({ template = "index.html" }: { template: AppWindow }) {
const ipcService = useService(IpcService);
const audio = useService(AudioPlayerService);
const windowControls = useWindowControls();
const volume = useObservable(() => audio.volume$, audio.volume);
const color = useThemeColor("first-color");
@@ -22,7 +24,7 @@ export default function TitleBar({ template = "index.html" }: { template: AppWin
const [previewVersion, setPreviewVersion] = useState(null);
useEffect(() => {
lastValueFrom(ipcService.sendV2<string>("current-version")).then(version => {
lastValueFrom(ipcService.sendV2("current-version")).then(version => {
if (version.toLocaleLowerCase().includes("alpha")) {
return setPreviewVersion("ALPHA");
}
@@ -35,19 +37,19 @@ export default function TitleBar({ template = "index.html" }: { template: AppWin
const [maximized, setMaximized] = useState(false);
const closeWindow = () => {
return window.electron.window.close();
return windowControls.close();
};
const maximizeWindow = () => {
window.electron.window.maximise();
windowControls.maximise();
};
const minimizeWindow = () => {
window.electron.window.minimise();
windowControls.minimise();
};
const resetWindow = () => {
window.electron.window.unmaximise();
windowControls.unmaximise();
};
const toogleMaximize = () => {
@@ -117,7 +119,7 @@ export default function TitleBar({ template = "index.html" }: { template: AppWin
</header>
);
}
return (
<header id="titlebar" className="min-h-[22px] bg-transparent w-screen h-[22px] flex content-center items-center justify-start z-10">
<div id="drag-region" className="grow h-full">
@@ -0,0 +1,16 @@
import { useConstant } from "./use-constant.hook";
export function useWindowArgs<Key extends string>(...keys: Key[]): Record<Key, string|undefined> {
const getArgs = (...keys: Key[]) => {
const url = new URLSearchParams(window.location.search);
const result = {} as Record<Key, string>;
keys.forEach(key => {
result[key] = url.get(key) || undefined;
});
return result;
}
return useConstant(() => getArgs(...keys));
}
@@ -0,0 +1,17 @@
import { IpcService } from "renderer/services/ipc.service";
import { useService } from "./use-service.hook";
import { useConstant } from "./use-constant.hook";
import { lastValueFrom } from "rxjs";
export function useWindowControls() {
const ipc = useService(IpcService);
const { close, maximise, minimise, unmaximise } = useConstant(() => ({
close: () => lastValueFrom(ipc.sendV2("close-window")),
maximise: () => lastValueFrom(ipc.sendV2("maximise-window")),
minimise: () => lastValueFrom(ipc.sendV2("minimise-window")),
unmaximise: () => lastValueFrom(ipc.sendV2("unmaximise-window"))
}));
return { close, maximise, minimise, unmaximise };
}
+8 -13
View File
@@ -35,7 +35,6 @@ import { useService } from "renderer/hooks/use-service.hook";
import { lastValueFrom } from "rxjs";
import { BsmException } from "shared/models/bsm-exception.model";
import { useObservable } from "renderer/hooks/use-observable.hook";
import { OculusDownloaderService } from "renderer/services/bs-version-download/oculus-downloader.service";
import { BsStore } from "shared/models/bs-store.enum";
import { SteamIcon } from "renderer/components/svgs/icons/steam-icon.component";
import { OculusIcon } from "renderer/components/svgs/icons/oculus-icon.component";
@@ -52,7 +51,6 @@ export function SettingsPage() {
const modalService = useService(ModalService);
const bsDownloader = useService(BsDownloaderService);
const steamDownloader = useService(SteamDownloaderService);
const oculusDownloader = useService(OculusDownloaderService);
const progressBarService = useService(ProgressBarService);
const notificationService = useService(NotificationService);
const i18nService = useService(I18nService);
@@ -91,7 +89,7 @@ export function SettingsPage() {
const [playlistsDeepLinkEnabled, setPlaylistsDeepLinkEnabled] = useState(false);
const [modelsDeepLinkEnabled, setModelsDeepLinkEnabled] = useState(false);
const [hasDownloaderSession, setHasDownloaderSession] = useState(false);
const appVersion = useObservable(() => ipcService.sendV2<string>("current-version"));
const appVersion = useObservable(() => ipcService.sendV2("current-version"));
const [isChangelogAvailable, setIsChangelogAvailable] = useState(true);
const [changlogsLoading, setChanglogsLoading] = useState(false);
@@ -116,16 +114,11 @@ export function SettingsPage() {
};
const loadDownloadersSession = () => {
if(steamDownloader.sessionExist()){ return setHasDownloaderSession(true); }
oculusDownloader.hasAuthToken().then(hasToken => {
setHasDownloaderSession(hasToken);
});
setHasDownloaderSession(steamDownloader.sessionExist());
}
const clearDownloadersSession = () => {
steamDownloader.deleteSteamSession();
oculusDownloader.clearAuthToken();
loadDownloadersSession();
}
@@ -171,7 +164,7 @@ export function SettingsPage() {
return;
}
const fileChooserRes = await ipcService.sendV2<{ canceled: boolean; filePaths: string[] }>("choose-folder").toPromise();
const fileChooserRes = await lastValueFrom(ipcService.sendV2("choose-folder"));
if (!fileChooserRes.canceled && fileChooserRes.filePaths?.length) {
progressBarService.showFake(0.008);
@@ -217,7 +210,7 @@ export function SettingsPage() {
const openDiscord = () => linkOpener.open("https://discord.gg/uSqbHVpKdV");
const openTwitter = () => linkOpener.open("https://twitter.com/BSManager_");
const openLogs = () => ipcService.sendLazy("open-logs");
const openLogs = () => lastValueFrom(ipcService.sendV2("open-logs"));
const showDeepLinkError = (isDeactivation: boolean) => {
const desc = isDeactivation ? "notifications.settings.additional-content.deep-link.deactivation.error.description" : "notifications.settings.additional-content.deep-link.activation.error.description";
@@ -231,8 +224,10 @@ export function SettingsPage() {
};
const switchDeepLink = async (manager: MapsManagerService | PlaylistsManagerService | ModelsManagerService, enable: boolean, showNotification: boolean, setter: Dispatch<SetStateAction<boolean>>) => {
const res = await (enable ? manager.enableDeepLink() : manager.disableDeepLink());
showNotification && (res ? showDeepLinkSuccess(!enable) : showDeepLinkError(!enable));
const res = await (enable ? manager.enableDeepLink() : manager.disableDeepLink()).then(() => true).catch(() => false);
if(showNotification){
res ? showDeepLinkSuccess(enable) : showDeepLinkError(enable);
}
const isEnable = await manager.isDeepLinksEnabled();
setter(() => isEnable);
return res;
@@ -42,7 +42,7 @@ export function VersionViewer() {
}
navigate(`/bs-version/${version.BSVersion}`, { state: version });
};
const openFolder = () => ipcService.sendLazy("bs-version.open-folder", { args: state });
const openFolder = () => lastValueFrom(ipcService.sendV2("bs-version.open-folder", state));
const verifyFiles = () => bsDownloader.verifyBsVersion(state);
const uninstall = async () => {
-6
View File
@@ -11,12 +11,6 @@ declare global {
sep: "/"|"\\";
join: (...args: string[]) => string;
};
window: {
close: () => void;
minimise: () => void;
maximise: () => void;
unmaximise: () => void;
};
};
}
}
@@ -47,20 +47,17 @@ export class AutoUpdaterService {
}
public isUpdateAvailable(): Promise<boolean> {
return lastValueFrom(this.ipcService.sendV2<boolean>("check-update")).catch(() => false);
return lastValueFrom(this.ipcService.sendV2("check-update")).catch(() => false);
}
public downloadUpdate(): Promise<boolean> {
const promise = this.ipcService.send<boolean>("download-update").then(res => {
return res.success;
});
const promise = lastValueFrom(this.ipcService.sendV2("download-update")).then(() => true).catch(() => false);
this.progressService.show(this.downloadProgress$, true);
return promise;
}
public quitAndInstall() {
this.ipcService.sendLazy("install-update");
lastValueFrom(this.ipcService.sendV2("install-update"));
}
public getLastAppVersion(): string {
@@ -103,7 +100,7 @@ export class AutoUpdaterService {
}
public getAppVersion() : Observable<string> {
return this.ipcService.sendV2<string>("current-version");
return this.ipcService.sendV2("current-version");
}
public async showChangelog(version:string): Promise<void>{
+5 -5
View File
@@ -73,7 +73,7 @@ export class BSLauncherService {
}
private async doMustStartAsAdmin(): Promise<boolean> {
const needAdmin = await lastValueFrom(this.ipcService.sendV2<boolean, void>("bs-launch.need-start-as-admin"));
const needAdmin = await lastValueFrom(this.ipcService.sendV2("bs-launch.need-start-as-admin"));
if(!needAdmin){ return false; }
if(this.config.get("dont-remind-admin")){ return true; }
const modalRes = await this.modals.openModal(NeedLaunchAdminModal);
@@ -83,7 +83,7 @@ export class BSLauncherService {
}
public doLaunch(launchOptions: LaunchOption): Observable<BSLaunchEventData>{
return this.ipcService.sendV2<BSLaunchEventData, LaunchOption>("bs-launch.launch", {args: launchOptions});
return this.ipcService.sendV2("bs-launch.launch", launchOptions);
}
public launch(launchOptions: LaunchOption): Observable<BSLaunchEventData> {
@@ -114,13 +114,13 @@ export class BSLauncherService {
}
public createLaunchShortcut(launchOptions: LaunchOption): Observable<void>{
public createLaunchShortcut(launchOptions: LaunchOption): Observable<boolean>{
const options: LaunchOption = {...launchOptions, version: {...launchOptions.version, color: launchOptions.version.color || this.theme.getBsmColors()[1]}};
return this.ipcService.sendV2<void, LaunchOption>("create-launch-shortcut", {args: options});
return this.ipcService.sendV2("create-launch-shortcut", options);
}
public restoreSteamVR(): Promise<void>{
return lastValueFrom(this.ipcService.sendV2<void, void>("bs-launch.restore-steamvr"));
return lastValueFrom(this.ipcService.sendV2("bs-launch.restore-steamvr"));
}
}
@@ -1,9 +1,9 @@
import { UninstallAllModsModal } from "renderer/components/modal/modal-types/uninstall-all-mods-modal.component";
import { UninstallModModal } from "renderer/components/modal/modal-types/uninstall-mod-modal.component";
import { Observable, BehaviorSubject } from "rxjs";
import { Observable, BehaviorSubject, lastValueFrom } from "rxjs";
import { map } from "rxjs/operators";
import { BSVersion } from "shared/bs-version.interface";
import { InstallModsResult, UninstallModsResult, Mod, ModInstallProgression } from "shared/models/mods";
import { Mod, ModInstallProgression } from "shared/models/mods";
import { ProgressionInterface } from "shared/models/progress-bar";
import { IpcService } from "./ipc.service";
import { ModalExitCode, ModalService } from "./modale.service";
@@ -42,11 +42,11 @@ export class BsModsManagerService {
}
public getAvailableMods(version: BSVersion): Observable<Mod[]> {
return this.ipcService.sendV2<Mod[], BSVersion>("get-available-mods", { args: version });
return this.ipcService.sendV2("get-available-mods", version);
}
public getInstalledMods(version: BSVersion): Observable<Mod[]> {
return this.ipcService.sendV2<Mod[], BSVersion>("get-installed-mods", { args: version });
return this.ipcService.sendV2("get-installed-mods", version);
}
public installMods(mods: Mod[], version: BSVersion): Promise<void> {
@@ -71,19 +71,20 @@ export class BsModsManagerService {
this.progressBar.show(progress$, true, { paddingLeft: "190px", paddingRight: "190px", bottom: "20px" });
this.isInstalling$.next(true);
return this.ipcService.send<InstallModsResult, { mods: Mod[]; version: BSVersion }>("install-mods", { args: { mods, version } }).then(res => {
if (res.success && res.data) {
const isFullyInstalled = res.data.nbInstalledMods === res.data.nbModsToInstall;
const title = `notifications.mods.install-mods.titles.${isFullyInstalled ? "success" : "warning"}`;
const desc = `notifications.mods.install-mods.msg.${isFullyInstalled ? "success" : "warning"}`;
this.notifications.notify({ type: isFullyInstalled ? NotificationType.SUCCESS : NotificationType.WARNING, title, desc, duration: this.NOTIFICATION_DURATION });
} else {
this.notifications.notifyError({ title: "notifications.types.error", desc: `notifications.mods.install-mods.msg.errors.${res.error}`, duration: this.NOTIFICATION_DURATION });
}
return lastValueFrom(this.ipcService.sendV2("install-mods", { mods, version })).then(res => {
const isFullyInstalled = res.nbInstalledMods === res.nbModsToInstall;
const title = `notifications.mods.install-mods.titles.${isFullyInstalled ? "success" : "warning"}`;
const desc = `notifications.mods.install-mods.msg.${isFullyInstalled ? "success" : "warning"}`;
this.notifications.notify({ type: isFullyInstalled ? NotificationType.SUCCESS : NotificationType.WARNING, title, desc, duration: this.NOTIFICATION_DURATION });
}).catch(e => {
this.notifications.notifyError({ title: "notifications.types.error", desc: `notifications.mods.install-mods.msg.errors.${e}`, duration: this.NOTIFICATION_DURATION });
}).finally(() => {
this.isInstalling$.next(false);
this.progressBar.hide();
});
})
}
public async uninstallMod(mod: Mod, version: BSVersion): Promise<void> {
if (!this.progressBar.require()) {
@@ -100,16 +101,15 @@ export class BsModsManagerService {
this.progressBar.show(progress$, true, { paddingLeft: "190px", paddingRight: "190px", bottom: "20px" });
this.isUninstalling$.next(true);
return this.ipcService.send("uninstall-mods", { args: { mods: [mod], version } }).then(res => {
if (res.success) {
this.notifications.notifySuccess({ title: "notifications.mods.uninstall-mod.titles.success", duration: this.NOTIFICATION_DURATION });
} else {
this.notifications.notifyError({ title: "notifications.types.error", desc: `notifications.mods.uninstall-mod.msg.errors.${res.error}`, duration: this.NOTIFICATION_DURATION });
}
return lastValueFrom(this.ipcService.sendV2("uninstall-mods", { mods: [mod], version })).then(() => {
this.notifications.notifySuccess({ title: "notifications.mods.uninstall-mod.titles.success", duration: this.NOTIFICATION_DURATION });
}).catch(e => {
this.notifications.notifyError({ title: "notifications.types.error", desc: `notifications.mods.uninstall-mod.msg.errors.${e}`, duration: this.NOTIFICATION_DURATION });
}).finally(() => {
this.isUninstalling$.next(false);
this.progressBar.hide();
});
})
}
public async uninstallAllMods(version: BSVersion) {
@@ -127,15 +127,13 @@ export class BsModsManagerService {
this.progressBar.show(progress$, true, { paddingLeft: "190px", paddingRight: "190px", bottom: "20px" });
this.isUninstalling$.next(true);
return this.ipcService.send<UninstallModsResult>("uninstall-all-mods", { args: version }).then(res => {
if (res.success) {
this.notifications.notifySuccess({ title: "notifications.mods.uninstall-all-mods.titles.success", desc: "notifications.mods.uninstall-all-mods.msg.success", duration: this.NOTIFICATION_DURATION });
} else {
this.notifications.notifyError({ title: "notifications.types.error", desc: `notifications.mods.uninstall-all-mods.msg.errors.${res.error}`, duration: this.NOTIFICATION_DURATION });
}
return lastValueFrom(this.ipcService.sendV2("uninstall-all-mods", version)).then(() => {
this.notifications.notifySuccess({ title: "notifications.mods.uninstall-all-mods.titles.success", desc: "notifications.mods.uninstall-all-mods.msg.success", duration: this.NOTIFICATION_DURATION });
}).catch(e => {
this.notifications.notifyError({ title: "notifications.types.error", desc: `notifications.mods.uninstall-all-mods.msg.errors.${e}`, duration: this.NOTIFICATION_DURATION });
}).finally(() => {
this.isUninstalling$.next(false);
this.progressBar.hide();
});
})
}
}
@@ -71,7 +71,7 @@ export class OculusDownloaderService extends AbstractBsDownloaderService impleme
private startDownloadBsVersion(downloadInfo: DownloadInfo): Observable<Progression<BSVersion>>{
const ignoreCode = [MetaAuthErrorCodes.META_LOGIN_WINDOW_CLOSED_BY_USER, OculusDownloaderErrorCodes.DOWNLOAD_CANCELLED];
return this.handleDownload(
this.ipc.sendV2<Progression<BSVersion>>("bs-oculus-download", { args: downloadInfo }),
this.ipc.sendV2("bs-oculus-download", downloadInfo ),
ignoreCode
);
}
@@ -115,15 +115,7 @@ export class OculusDownloaderService extends AbstractBsDownloaderService impleme
}
public stopDownload(): Promise<void>{
return lastValueFrom(this.ipc.sendV2<void>("bs-oculus-stop-download"));
return lastValueFrom(this.ipc.sendV2("bs-oculus-stop-download"));
}
public hasAuthToken(): Promise<boolean>{
return lastValueFrom(this.ipc.sendV2<boolean>("bs-oculus-has-auth-token"));
}
public clearAuthToken(): Promise<void>{
return lastValueFrom(this.ipc.sendV2<void>("bs-oculus-clear-auth-token"));
}
}
}
@@ -45,7 +45,7 @@ export class SteamDownloaderService extends AbstractBsDownloaderService implemen
}
public isDotNet6Installed(): Promise<boolean> {
return lastValueFrom(this.ipcService.sendV2<boolean>("is-dotnet-6-installed"));
return lastValueFrom(this.ipcService.sendV2("is-dotnet-6-installed"));
}
private setSteamSession(username: string): void { localStorage.setItem(this.STEAM_SESSION_USERNAME_KEY, username); }
@@ -67,11 +67,11 @@ export class SteamDownloaderService extends AbstractBsDownloaderService implemen
}
public async getInstallationFolder(): Promise<string> {
return lastValueFrom(this.ipcService.sendV2<string>("bs-download.installation-folder"));
return lastValueFrom(this.ipcService.sendV2("bs-download.installation-folder"));
}
public setInstallationFolder(path: string): Observable<string> {
return this.ipcService.sendV2<string>("bs-download.set-installation-folder", { args: path });
return this.ipcService.sendV2("bs-download.set-installation-folder", path);
}
// ### Downloading
@@ -177,7 +177,7 @@ export class SteamDownloaderService extends AbstractBsDownloaderService implemen
tap({
error: (e) => {
this.deleteSteamSession();
!silent && this.hanndleErrorEvent(e)
if(!silent){ this.hanndleErrorEvent(e) }
}
}),
share({connector: () => new ReplaySubject(1)})
@@ -193,20 +193,20 @@ export class SteamDownloaderService extends AbstractBsDownloaderService implemen
const infos: DownloadSteamInfo = {...downloadInfo, username: this.getSteamUsername()}
return this.wrapDownload(
this.ipcService.sendV2<DepotDownloaderEvent>("auto-download-bs-version", { args: infos }),
this.ipcService.sendV2("auto-download-bs-version", infos),
true
);
}
private startDownload(downloadInfo: DownloadSteamInfo){
return this.wrapDownload(
this.ipcService.sendV2<DepotDownloaderEvent>("download-bs-version", { args: downloadInfo })
this.ipcService.sendV2("download-bs-version", downloadInfo )
);
}
private startQrCodeDownload(downloadInfo: DownloadSteamInfo){
return this.wrapDownload(
this.ipcService.sendV2<DepotDownloaderEvent>("download-bs-version-qr", { args: downloadInfo })
this.ipcService.sendV2("download-bs-version-qr", downloadInfo)
);
}
@@ -260,7 +260,7 @@ export class SteamDownloaderService extends AbstractBsDownloaderService implemen
}
private sendInput(input: string){
return lastValueFrom(this.ipcService.sendV2<void>("send-input-bs-download", { args: input }));
return lastValueFrom(this.ipcService.sendV2("send-input-bs-download", input));
}
public downloadBsVersion(version: BSVersion): Promise<BSVersion> {
@@ -272,6 +272,6 @@ export class SteamDownloaderService extends AbstractBsDownloaderService implemen
}
public stopDownload(): Promise<void>{
return lastValueFrom(this.ipcService.sendV2<void>("stop-download-bs-version"));
return lastValueFrom(this.ipcService.sendV2("stop-download-bs-version"));
}
}
@@ -8,7 +8,6 @@ import { EditVersionModal } from "renderer/components/modal/modal-types/edit-ver
import { popElement } from "shared/helpers/array.helpers";
import { ImportVersionModal } from "renderer/components/modal/modal-types/import-version-modal.component";
import { Progression } from "main/helpers/fs.helpers";
import { ImportVersionOptions } from "main/services/bs-local-version.service";
export class BSVersionManagerService {
private static instance: BSVersionManagerService;
@@ -48,16 +47,16 @@ export class BSVersionManagerService {
}
public askAvailableVersions(): Promise<BSVersion[]> {
return this.ipcService.send<BSVersion[]>("bs-version.get-version-dict").then(res => {
this.availableVersions$.next(res.data);
return res.data;
return lastValueFrom(this.ipcService.sendV2("bs-version.get-version-dict")).then(res => {
this.availableVersions$.next(res);
return res;
});
}
public askInstalledVersions(): Promise<BSVersion[]> {
return this.ipcService.send<BSVersion[]>("bs-version.installed-versions").then(res => {
this.setInstalledVersions(res.data);
return res.data;
return lastValueFrom(this.ipcService.sendV2("bs-version.installed-versions")).then(res => {
this.setInstalledVersions(res);
return res;
});
}
@@ -73,17 +72,17 @@ export class BSVersionManagerService {
if (modalRes.data.name?.length < 2) {
return null;
}
return this.ipcService.send<BSVersion>("bs-version.edit", { args: { version, name: modalRes.data.name, color: modalRes.data.color } }).then(res => {
if (!res.success) {
this.notification.notifyError({
title: `notifications.custom-version.errors.titles.${res.error.title}`,
...(res.error.message && { desc: `notifications.custom-version.errors.msg.${res.error.message}` }),
});
return null;
}
return lastValueFrom(this.ipcService.sendV2("bs-version.edit", { version, name: modalRes.data.name, color: modalRes.data.color })).then(res => {
this.askInstalledVersions();
return res.data;
});
return res;
}).catch(e => {
this.notification.notifyError({
title: `notifications.custom-version.errors.titles.${e.error.title}`,
...(e.error.message && { desc: `notifications.custom-version.errors.msg.${e.error.message}` }),
});
return null;
})
}
public async cloneVersion(version: BSVersion): Promise<BSVersion> {
@@ -97,19 +96,21 @@ export class BSVersionManagerService {
if (modalRes.data.name?.length < 2) {
return null;
}
this.progressBar.showFake(0.01);
return this.ipcService.send<BSVersion>("bs-version.clone", { args: { version, name: modalRes.data.name, color: modalRes.data.color } }).then(res => {
this.progressBar.hide(true);
if (!res.success) {
this.notification.notifyError({
title: `notifications.custom-version.errors.titles.${res.error.title}`,
...(res.error.message && { desc: `notifications.custom-version.errors.msg.${res.error.message}` }),
});
return null;
}
return lastValueFrom(this.ipcService.sendV2("bs-version.clone", { version, name: modalRes.data.name, color: modalRes.data.color })).then(res => {
this.notification.notifySuccess({ title: "notifications.custom-version.success.titles.CloningFinished" });
this.askInstalledVersions();
return res.data;
return res;
}).catch(e => {
this.notification.notifyError({
title: `notifications.custom-version.errors.titles.${e.error.title}`,
...(e.error.message && { desc: `notifications.custom-version.errors.msg.${e.error.message}` }),
});
return null;
}).finally(() => {
this.progressBar.hide(true)
});
}
@@ -131,12 +132,12 @@ export class BSVersionManagerService {
const store = resModal.data;
const folderRes = await lastValueFrom(this.ipcService.sendV2<{ canceled: boolean; filePaths: string[] }>("choose-folder"));
const folderRes = await lastValueFrom(this.ipcService.sendV2("choose-folder"));
if(!folderRes || folderRes.canceled || !folderRes.filePaths?.length){
return;
}
const import$ = this.ipcService.sendV2<Progression<BSVersion>, ImportVersionOptions>("import-version", { args: {fromPath: folderRes.filePaths.at(0), store} });
const import$ = this.ipcService.sendV2("import-version", {fromPath: folderRes.filePaths.at(0), store});
subs.push(import$.subscribe(obs));
@@ -167,7 +168,7 @@ export class BSVersionManagerService {
}
public getVersionPath(version: BSVersion): Observable<string> {
return this.ipcService.sendV2("get-version-full-path", { args: version });
return this.ipcService.sendV2("get-version-full-path", version);
}
public static sortVersions(versions: BSVersion[]): BSVersion[] {
+9 -10
View File
@@ -3,6 +3,7 @@ import { Observable, ReplaySubject, identity } from "rxjs";
import { IpcRequest, IpcResponse } from "shared/models/ipc";
import { deserializeError } from 'serialize-error';
import { IpcCompleteChannel, IpcErrorChannel, IpcTearDownChannel } from "shared/models/ipc/ipc-response.interface";
import { IpcChannels, IpcRequestType, IpcResponseType } from "shared/models/ipc/ipc-routes";
export class IpcService {
private static instance: IpcService;
@@ -63,25 +64,23 @@ export class IpcService {
// TODO : Convert all IPCs calls to V2
public sendV2<T, U = unknown>(channel: string, request?: IpcRequest<U>, defaultValue?: T): Observable<T> {
if (!request) {
request = { args: null, responceChannel: null };
}
public sendV2<C extends IpcChannels>(channel: C, data?: IpcRequestType<C>, defaultValue?: IpcResponseType<C>): Observable<IpcResponseType<C>> {
if (!request.responceChannel) {
request.responceChannel = `${channel}_responce_${crypto.randomUUID()}`;
}
const request: IpcRequest<IpcRequestType<C>> = {
args: data,
responceChannel: `${channel}_responce_${crypto.randomUUID()}`
};
const completeChannel: IpcCompleteChannel = `${request.responceChannel}_complete`;
const errorChannel: IpcErrorChannel = `${request.responceChannel}_error`;
const teardownChannel: IpcTearDownChannel = `${request.responceChannel}_teardown`;
const obs = new Observable<T>(observer => {
window.electron.ipcRenderer.on(request.responceChannel, (res: T) => observer.next(res));
const obs = new Observable<IpcResponseType<C>>(observer => {
window.electron.ipcRenderer.on(request.responceChannel, (res: IpcResponseType<C>) => observer.next(res));
window.electron.ipcRenderer.on(errorChannel, (err) => observer.error(deserializeError(err)));
window.electron.ipcRenderer.on(completeChannel, () => observer.complete());
window.electron.ipcRenderer.sendMessage(channel, request);
window.electron.ipcRenderer.sendMessage(channel as string, request);
return () => {
window.electron.ipcRenderer.removeAllListeners(request.responceChannel);
+2 -2
View File
@@ -1,4 +1,4 @@
import { Observable, Subject } from "rxjs";
import { Observable, Subject, lastValueFrom } from "rxjs";
import { IpcService } from "./ipc.service";
export class LinkOpenerService {
@@ -23,7 +23,7 @@ export class LinkOpenerService {
if (internal) {
return this._iframeLink$.next(url);
}
this.ipcService.sendLazy("new-window", { args: url });
lastValueFrom(this.ipcService.sendV2("new-window", url));
}
public closeIframe() {
@@ -75,7 +75,7 @@ export class MapsDownloaderService {
if (this.os.isOffline) {
return null;
}
return this.ipc.sendV2<BsmLocalMap, { map: BsvMapDetail; version: BSVersion }>("download-map", { args: { map, version } });
return this.ipc.sendV2("download-map", { map, version });
}
public async openDownloadMapModal(version?: BSVersion, ownedMaps: BsmLocalMap[] = []): Promise<ModalResponse<void>> {
@@ -132,12 +132,9 @@ export class MapsDownloaderService {
this.downloadedListerners.splice(funcIndex, 1);
}
public async oneClickInstallMap(map: BsvMapDetail): Promise<boolean> {
public async oneClickInstallMap(map: BsvMapDetail): Promise<void> {
this.progressBar.showFake(0.04);
const res = await this.ipc.send<void, BsvMapDetail>("one-click-install-map", { args: map });
return res.success;
return lastValueFrom(this.ipc.sendV2("one-click-install-map", map));
}
public get isDownloading(): boolean {
+11 -21
View File
@@ -1,16 +1,14 @@
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 { Subject, Observable, of } from "rxjs";
import { Subject, Observable, of, lastValueFrom } from "rxjs";
import { BSVersion } from "shared/bs-version.interface";
import { BsmLocalMapsProgress, BsmLocalMap, DeleteMapsProgress } from "shared/models/maps/bsm-local-map.interface";
import { BsmLocalMapsProgress, BsmLocalMap } from "shared/models/maps/bsm-local-map.interface";
import { IpcService } from "./ipc.service";
import { ModalExitCode, ModalService } from "./modale.service";
import { DeleteMapsModal } from "renderer/components/modal/modal-types/delete-maps-modal.component";
import { ProgressBarService } from "./progress-bar.service";
import { OpenSaveDialogOption } from "shared/models/ipc";
import { NotificationService } from "./notification.service";
import { ConfigurationService } from "./configuration.service";
import { ArchiveProgress } from "shared/models/archive.interface";
import { map, last, catchError } from "rxjs/operators";
import { ProgressionInterface } from "shared/models/progress-bar";
import { FolderLinkState, VersionFolderLinkerService } from "./version-folder-linker.service";
@@ -48,7 +46,7 @@ export class MapsManagerService {
}
public getMaps(version?: BSVersion): Observable<BsmLocalMapsProgress> {
return this.ipcService.sendV2<BsmLocalMapsProgress>("load-version-maps", { args: version }, { loaded: 0, total: 0, maps: [] });
return this.ipcService.sendV2("load-version-maps", version, { loaded: 0, total: 0, maps: [] });
}
public async versionHaveMapsLinked(version: BSVersion): Promise<boolean> {
@@ -97,7 +95,7 @@ export class MapsManagerService {
const showProgressBar = this.progressBar.require();
const progress$ = this.ipcService.sendV2<DeleteMapsProgress>("delete-maps", { args: maps }).pipe(map(progress => (progress.deleted / progress.total) * 100));
const progress$ = this.ipcService.sendV2("delete-maps", maps ).pipe(map(progress => (progress.deleted / progress.total) * 100));
if (showProgressBar) {
this.progressBar.show(progress$, true);
@@ -120,20 +118,15 @@ export class MapsManagerService {
return;
}
const resFile = await this.ipcService.send<string, OpenSaveDialogOption>("save-file", {
args: {
filename: version ? `${version.BSVersion}Maps` : "Maps",
filters: [{ name: "zip", extensions: ["zip"] }],
},
});
const resFile = await lastValueFrom(this.ipcService.sendV2("save-file", { filename: version ? `${version.BSVersion}Maps` : "Maps", filters: [{ name: "zip", extensions: ["zip"] }]})).catch(() => null as string);
if (!resFile.success) {
if (!resFile) {
return;
}
const exportProgress$: Observable<ProgressionInterface> = this.ipcService.sendV2<ArchiveProgress, { version: BSVersion; maps: BsmLocalMap[]; outPath: string }>("export-maps", { args: { version, maps, outPath: resFile.data } }).pipe(
const exportProgress$: Observable<ProgressionInterface> = this.ipcService.sendV2("export-maps", { version, maps, outPath: resFile }).pipe(
map(p => {
return { progression: (p.prossesedFiles / p.totalFiles) * 100, label: `${p.prossesedFiles} / ${p.totalFiles}` } as ProgressionInterface;
return { progression: (p.current / p.total) * 100, label: `${p.current} / ${p.total}` } as ProgressionInterface;
})
);
@@ -153,18 +146,15 @@ export class MapsManagerService {
}
public async isDeepLinksEnabled(): Promise<boolean> {
const res = await this.ipcService.send<boolean>("is-map-deep-links-enabled");
return res.success ? res.data : false;
return lastValueFrom(this.ipcService.sendV2("is-map-deep-links-enabled"));
}
public async enableDeepLink(): Promise<boolean> {
const res = await this.ipcService.send<boolean>("register-maps-deep-link");
return res.success ? res.data : false;
return lastValueFrom(this.ipcService.sendV2("register-maps-deep-link"));
}
public async disableDeepLink(): Promise<boolean> {
const res = await this.ipcService.send<boolean>("unregister-maps-deep-link");
return res.success ? res.data : false;
return lastValueFrom(this.ipcService.sendV2("unregister-maps-deep-link"));
}
public get versionLinked$(): Observable<BSVersion> {
@@ -6,7 +6,6 @@ import { ModalResponse, ModalService } from "../modale.service";
import { IpcService } from "../ipc.service";
import { DownloadModelsModal } from "renderer/components/modal/modal-types/models/download-models-modal.component";
import { ProgressBarService } from "../progress-bar.service";
import { Progression } from "main/helpers/fs.helpers";
import { ProgressionInterface } from "shared/models/progress-bar";
import equal from "fast-deep-equal";
@@ -42,7 +41,7 @@ export class ModelsDownloaderService {
}
private async downloadModel(download: ModelDownload) {
const download$ = this.ipc.sendV2<Progression<BsmLocalModel>>("download-model", { args: download });
const download$ = this.ipc.sendV2("download-model", download);
if (!this.progress.isVisible) {
const progress$: Observable<ProgressionInterface> = download$.pipe(
@@ -124,13 +123,13 @@ export class ModelsDownloaderService {
public async oneClickInstallModel(model: MSModel): Promise<boolean> {
this.progress.showFake(0.04);
const res = await this.ipc.send("one-click-install-model", { args: model });
const res = await lastValueFrom(this.ipc.sendV2("one-click-install-model", model)).then(() => true).catch(() => false);
this.progress.complete();
await timer(500).toPromise();
await lastValueFrom(timer(500));
this.progress.hide(true);
return res.success;
return res;
}
}
@@ -10,7 +10,6 @@ import { UnlinkModelsModal } from "renderer/components/modal/modal-types/models/
import { Progression } from "main/helpers/fs.helpers";
import { BsmLocalModel } from "shared/models/models/bsm-local-model.interface";
import { ProgressBarService } from "../progress-bar.service";
import { OpenSaveDialogOption } from "shared/models/os/dialog.model";
import { ProgressionInterface } from "shared/models/progress-bar";
import { NotificationService } from "../notification.service";
import { ConfigurationService } from "../configuration.service";
@@ -101,7 +100,7 @@ export class ModelsManagerService {
}
public $getModels(type: MSModelType, version?: BSVersion): Observable<Progression<BsmLocalModel[]>> {
return this.ipc.sendV2<Progression<BsmLocalModel[]>>("get-version-models", { args: { version, type } });
return this.ipc.sendV2("get-version-models", { version, type });
}
public async exportModels(models: BsmLocalModel[], version?: BSVersion) {
@@ -109,18 +108,16 @@ export class ModelsManagerService {
return;
}
const resFile = await this.ipc.send<string, OpenSaveDialogOption>("save-file", {
args: {
filename: version ? `${version.name ?? version.BSVersion} Models` : "Models",
filters: [{ name: "zip", extensions: ["zip"] }],
},
});
const resFile = await lastValueFrom(this.ipc.sendV2("save-file", {
filename: version ? `${version.name ?? version.BSVersion} Models` : "Models",
filters: [{ name: "zip", extensions: ["zip"] }]
})).catch(() => null as string);
if (!resFile.success) {
if (!resFile) {
return;
}
const exportProgress$: Observable<ProgressionInterface> = this.ipc.sendV2<Progression, { version: BSVersion; models: BsmLocalModel[]; outPath: string }>("export-models", { args: { version, models, outPath: resFile.data } }).pipe(
const exportProgress$ = this.ipc.sendV2("export-models", { version, models, outPath: resFile }).pipe(
map(p => {
return { progression: (p.current / p.total) * 100, label: `${p.current} / ${p.total}` } as ProgressionInterface;
})
@@ -171,7 +168,7 @@ export class ModelsManagerService {
const showProgressBar = this.progressBar.require();
const obs$ = this.ipc.sendV2<Progression<BsmLocalModel[]>>("delete-models", { args: models });
const obs$ = this.ipc.sendV2("delete-models", models);
const progress$ = obs$.pipe(map(progress => (progress.current / progress.total) * 100));
@@ -189,16 +186,14 @@ export class ModelsManagerService {
}
public isDeepLinksEnabled(): Promise<boolean> {
return this.ipc.send<boolean>("is-models-deep-links-enabled").then(res => (res.success ? res.data : false));
return lastValueFrom(this.ipc.sendV2("is-models-deep-links-enabled"));
}
public async enableDeepLink(): Promise<boolean> {
const res = await this.ipc.send<boolean>("register-models-deep-link");
return res.success ? res.data : false;
return lastValueFrom(this.ipc.sendV2("register-models-deep-link"));
}
public async disableDeepLink(): Promise<boolean> {
const res = await this.ipc.send<boolean>("unregister-models-deep-link");
return res.success ? res.data : false;
return lastValueFrom(this.ipc.sendV2("unregister-models-deep-link"));
}
}
@@ -1,4 +1,4 @@
import { BehaviorSubject } from "rxjs";
import { BehaviorSubject, Observable } from "rxjs";
import { SystemNotificationOptions } from "shared/models/notification/system-notification.model";
import { IpcService } from "./ipc.service";
import { NotificationResult, NotificationType, Notification } from "../../shared/models/notification/notification.model";
@@ -58,8 +58,8 @@ export class NotificationService {
return this.notify(notification);
}
public notifySystem(options: SystemNotificationOptions) {
this.ipc.sendLazy<SystemNotificationOptions>("notify-system", { args: options });
public notifySystem(options: SystemNotificationOptions): Observable<void> {
return this.ipc.sendV2("notify-system", options);
}
}
@@ -37,7 +37,7 @@ export class PlaylistDownloaderService {
return new Observable<Progression<DownloadPlaylistProgressionData>>(subscriber => {
(async () => {
const playlist = await lastValueFrom(this.playlistQueue$.pipe(map(queue => queue.at(0)), filter(p => equal(bpList, p)), take(1)));
const download$ = this.ipc.sendV2<Progression<DownloadPlaylistProgressionData>, unknown>("install-playlist", { args: { version, playlist } });
const download$ = this.ipc.sendV2("install-playlist", { version, playlist });
await lastValueFrom(download$.pipe(tap(subscriber)));
})()
@@ -50,7 +50,7 @@ export class PlaylistDownloaderService {
public oneClickInstallPlaylist(bpListUrl: string): Observable<Progression<DownloadPlaylistProgressionData>> {
const download$ = this.ipc.sendV2<Progression<DownloadPlaylistProgressionData>, string>("one-click-install-playlist", { args: bpListUrl });
const download$ = this.ipc.sendV2("one-click-install-playlist", bpListUrl);
const progress$ = download$.pipe(map(data => (data.current / data.total) * 100));
this.progress.show(progress$, true);
@@ -31,11 +31,11 @@ export class PlaylistsManagerService {
}
public getVersionPlaylistsDetails(version: BSVersion): Observable<Progression<LocalBPListsDetails[]>> {
return this.ipc.sendV2("get-version-playlists-details", { args: version });
return this.ipc.sendV2("get-version-playlists-details", version);
}
public deletePlaylist(opt: {path: string, deleteMaps?: boolean}): Observable<Progression> {
return this.ipc.sendV2<Progression, {path: string, deleteMaps?: boolean}>("delete-playlist", { args: opt });
return this.ipc.sendV2("delete-playlist", opt);
}
public async linkVersion(version: BSVersion): Promise<boolean> {
@@ -67,15 +67,15 @@ export class PlaylistsManagerService {
}
public isDeepLinksEnabled(): Promise<boolean> {
return lastValueFrom(this.ipc.sendV2<boolean>("is-playlists-deep-links-enabled"));
return lastValueFrom(this.ipc.sendV2("is-playlists-deep-links-enabled"));
}
public enableDeepLink(): Promise<boolean> {
return lastValueFrom(this.ipc.sendV2<boolean>("register-playlists-deep-link"));
return lastValueFrom(this.ipc.sendV2("register-playlists-deep-link"));
}
public disableDeepLink(): Promise<boolean> {
return lastValueFrom(this.ipc.sendV2<boolean>("unregister-playlists-deep-link"));
return lastValueFrom(this.ipc.sendV2("unregister-playlists-deep-link"));
}
public $playlistsFolderLinkState(version: BSVersion): Observable<FolderLinkState> {
@@ -1,5 +1,5 @@
import { distinctUntilChanged, map } from "rxjs/operators";
import { BehaviorSubject, Observable, Subscription, timer, of } from "rxjs";
import { BehaviorSubject, Observable, Subscription, timer, of, lastValueFrom } from "rxjs";
import { IpcService } from "./ipc.service";
import { NotificationService } from "./notification.service";
import { CSSProperties } from "react";
@@ -36,7 +36,7 @@ export class ProgressBarService {
}
private setSystemProgression(progression: number) {
this.ipcService.sendLazy("window.progression", { args: progression });
lastValueFrom(this.ipcService.sendV2("window.progression", progression));
}
public subscribreTo(obs: Observable<ProgressionInterface | number>) {
+2 -6
View File
@@ -1,5 +1,6 @@
import { Supporter } from "shared/models/supporters";
import { IpcService } from "./ipc.service";
import { lastValueFrom } from "rxjs";
export class SupportersService {
private static instance: SupportersService;
@@ -18,11 +19,6 @@ export class SupportersService {
}
public getSupporters(): Promise<Supporter[]> {
return this.ipcService.send<Supporter[]>("get-supporters").then(res => {
if (!res.success) {
return null;
}
return res.data;
});
return lastValueFrom(this.ipcService.sendV2("get-supporters"));
}
}
@@ -1,6 +1,7 @@
import { BsvMapDetail } from "shared/models/maps";
import { BsvPlaylist, SearchParams } from "shared/models/maps/beat-saver.model";
import { SearchParams } from "shared/models/maps/beat-saver.model";
import { IpcService } from "../ipc.service";
import { lastValueFrom } from "rxjs";
export class BeatSaverService {
private static instance: BeatSaverService;
@@ -18,22 +19,14 @@ export class BeatSaverService {
}
public async getMapDetailsFromHashs(hashs: string[]): Promise<BsvMapDetail[]> {
const res = await this.ipc.send<BsvMapDetail[], string[]>("bsv-get-map-details-from-hashs", { args: hashs });
return res.data ?? [];
return lastValueFrom(this.ipc.sendV2("bsv-get-map-details-from-hashs", hashs));
}
public async getMapDetailsById(id: string): Promise<BsvMapDetail> {
const res = await this.ipc.send<BsvMapDetail, string>("bsv-get-map-details-by-id", { args: id });
return res.data ?? null;
return lastValueFrom(this.ipc.sendV2("bsv-get-map-details-by-id", id));
}
public async searchMaps(search: SearchParams): Promise<BsvMapDetail[]> {
const res = await this.ipc.send<BsvMapDetail[], SearchParams>("bsv-search-map", { args: search });
return res.data ?? [];
}
public async getPlaylistDetailsById(id: string): Promise<BsvPlaylist> {
const res = await this.ipc.send<BsvPlaylist>("bsv-get-playlist-details-by-id", { args: id });
return res.data ?? null;
return lastValueFrom(this.ipc.sendV2("bsv-search-map", search));
}
}
@@ -1,6 +1,6 @@
import { MSGetQuery, MSGetQueryFilter, MSGetQueryFilterType, MSModel } from "shared/models/models/model-saber.model";
import { IpcService } from "../ipc.service";
import { Observable } from "rxjs";
import { Observable, lastValueFrom } from "rxjs";
import { MS_QUERY_FILTER_TYPES } from "shared/models/models/constants";
export class ModelSaberService {
@@ -19,16 +19,12 @@ export class ModelSaberService {
this.ipc = IpcService.getInstance();
}
public async getModelById(id: number | string): Promise<MSModel> {
const res = await this.ipc.send<MSModel>("ms-get-model-by-id", { args: id });
if (!res.success) {
return null;
}
return res.data;
public getModelById(id: number | string): Promise<MSModel> {
return lastValueFrom(this.ipc.sendV2("ms-get-model-by-id", id));
}
public searchModels(query: MSGetQuery): Observable<MSModel[]> {
return this.ipc.sendV2("search-models", { args: query });
return this.ipc.sendV2("search-models", query);
}
public parseFilter(stringFilters: string): MSGetQueryFilter[] {
@@ -59,7 +59,7 @@ export class VersionFolderLinkerService {
}
private doAction(action: VersionLinkerAction): Observable<boolean> {
return this.ipcService.sendV2<boolean, VersionLinkerAction>("link-version-folder-action", { args: action });
return this.ipcService.sendV2("link-version-folder-action", action);
}
private get currentAction$(): Observable<VersionLinkerAction> {
@@ -126,7 +126,7 @@ export class VersionFolderLinkerService {
}
public isVersionFolderLinked(version: BSVersion, relativeFolder: string): Observable<boolean> {
return this.ipcService.sendV2("is-version-folder-linked", { args: { version, relativeFolder } });
return this.ipcService.sendV2("is-version-folder-linked", { version, relativeFolder });
}
public $folderLinkedState(version: BSVersion, relativeFolder: string): Observable<FolderLinkState> {
@@ -159,7 +159,7 @@ export class VersionFolderLinkerService {
}
public getLinkedFolders(version: BSVersion, options?: { relative?: boolean }): Observable<string[]> {
return this.ipcService.sendV2("get-linked-folders", { args: { version, options } });
return this.ipcService.sendV2("get-linked-folders", { version, options });
}
public relinkAllVersionsFolders(): Observable<void> {
@@ -19,11 +19,11 @@ export class WindowManagerService {
}
public openThenCloseAll(window: AppWindow): Promise<void> {
return lastValueFrom(this.ipcService.sendV2<void>("open-window-then-close-all", { args: window }));
return lastValueFrom(this.ipcService.sendV2("open-window-then-close-all", window));
}
public openWindowOrFocus(window: AppWindow): Promise<void> {
return lastValueFrom(this.ipcService.sendV2<void, AppWindow>("open-window-or-focus", { args: window }));
return lastValueFrom(this.ipcService.sendV2("open-window-or-focus", window));
}
}
@@ -3,25 +3,27 @@ import { BsmProgressBar } from "renderer/components/progress-bar/bsm-progress-ba
import { BsmImage } from "renderer/components/shared/bsm-image.component";
import TitleBar from "renderer/components/title-bar/title-bar.component";
import { useTranslation } from "renderer/hooks/use-translation.hook";
import { IpcService } from "renderer/services/ipc.service";
import { MapsDownloaderService } from "renderer/services/maps-downloader.service";
import { NotificationService } from "renderer/services/notification.service";
import { ProgressBarService } from "renderer/services/progress-bar.service";
import { BeatSaverService } from "renderer/services/thrird-partys/beat-saver.service";
import { lastValueFrom } from "rxjs";
import { BsvMapDetail } from "shared/models/maps";
import defaultImage from "../../../../assets/images/default-version-img.jpg";
import { useService } from "renderer/hooks/use-service.hook";
import { useWindowArgs } from "renderer/hooks/use-window-args.hook";
import { useWindowControls } from "renderer/hooks/use-window-controls.hook";
export default function OneClickDownloadMap() {
const ipc = useService(IpcService);
const bsv = useService(BeatSaverService);
const mapsDownloader = useService(MapsDownloaderService);
const progressBar = useService(ProgressBarService);
const notification = useService(NotificationService);
const { close: closeWindow } = useWindowControls();
const { mapId, isHash } = useWindowArgs("mapId", "isHash");
const [mapInfo, setMapInfo] = useState<BsvMapDetail>(null);
const t = useTranslation();
const cover = mapInfo ? mapInfo.versions.at(0).coverURL : null;
@@ -31,14 +33,16 @@ export default function OneClickDownloadMap() {
progressBar.open();
const promise = (async () => {
const ipcRes = await lastValueFrom(ipc.sendV2<{ id: string; isHash: boolean }>("one-click-map-info"));
console.log("AAAA", mapId, isHash);
const mapDetails = ipcRes.isHash ? (await bsv.getMapDetailsFromHashs([ipcRes.id])).at(0) : await bsv.getMapDetailsById(ipcRes.id);
const promise = (async () => {
const mapDetails = isHash === "true" ? (await bsv.getMapDetailsFromHashs([mapId])).at(0) : await bsv.getMapDetailsById(mapId);
console.log(mapDetails);
setMapInfo(() => mapDetails);
const res = await mapsDownloader.oneClickInstallMap(mapDetails);
const res = await mapsDownloader.oneClickInstallMap(mapDetails).then(() => true).catch(() => false);
progressBar.complete();
@@ -57,9 +61,9 @@ export default function OneClickDownloadMap() {
});
promise.finally(() => {
window.electron.window.close();
closeWindow();
});
}, []);
return (
@@ -3,7 +3,6 @@ import { BsmProgressBar } from "renderer/components/progress-bar/bsm-progress-ba
import { BsmImage } from "renderer/components/shared/bsm-image.component";
import TitleBar from "renderer/components/title-bar/title-bar.component";
import { useTranslation } from "renderer/hooks/use-translation.hook";
import { IpcService } from "renderer/services/ipc.service";
import { NotificationService } from "renderer/services/notification.service";
import { ProgressBarService } from "renderer/services/progress-bar.service";
import { ModelSaberService } from "renderer/services/thrird-partys/model-saber.service";
@@ -11,16 +10,18 @@ import { MSModel } from "shared/models/models/model-saber.model";
import defaultImage from "../../../../assets/images/default-version-img.jpg";
import { ModelsDownloaderService } from "renderer/services/models-management/models-downloader.service";
import { useService } from "renderer/hooks/use-service.hook";
import { lastValueFrom } from "rxjs";
import { useWindowArgs } from "renderer/hooks/use-window-args.hook";
import { useWindowControls } from "renderer/hooks/use-window-controls.hook";
export default function OneClickDownloadModel() {
const ipc = useService(IpcService);
const modelSaber = useService(ModelSaberService);
const progress = useService(ProgressBarService);
const modelDownloader = useService(ModelsDownloaderService);
const notification = useService(NotificationService);
const { close: closeWindow } = useWindowControls();
const { modelId } = useWindowArgs("modelId");
const [model, setModel] = useState<MSModel>(null);
const t = useTranslation();
@@ -30,9 +31,8 @@ export default function OneClickDownloadModel() {
useEffect(() => {
const promise = (async () => {
const infos = await lastValueFrom(ipc.sendV2<{ id: string; type: string }>("one-click-model-info"));
const model = await modelSaber.getModelById(infos.id);
const model = await modelSaber.getModelById(modelId);
if (!model) {
throw new Error("Failed to get model from ModelSaber");
@@ -58,7 +58,7 @@ export default function OneClickDownloadModel() {
});
promise.finally(() => {
window.electron.window.close();
closeWindow();
});
}, []);
@@ -13,16 +13,19 @@ import { useService } from "renderer/hooks/use-service.hook";
import { useConstant } from "renderer/hooks/use-constant.hook";
import { BPList } from "shared/models/playlists/playlist.interface";
import { useOnUpdate } from "renderer/hooks/use-on-update.hook";
import { useWindowArgs } from "renderer/hooks/use-window-args.hook";
import { useWindowControls } from "renderer/hooks/use-window-controls.hook";
export default function OneClickDownloadPlaylist() {
const t = useTranslation();
const playlistDownloader = useService(PlaylistDownloaderService);
const notification = useService(NotificationService);
const { close: closeWindow } = useWindowControls();
const mapsContainer = useRef<HTMLDivElement>(null);
const playlistUrl = useConstant(() => new URLSearchParams(window.location.search).get("playlistUrl"));
const { playlistUrl } = useWindowArgs("playlistUrl");
const download$ = useConstant(() => playlistDownloader.oneClickInstallPlaylist(playlistUrl));
const playlistInfos = useObservable<BPList>(() => download$.pipe(filter(progress => !!progress.data?.playlistInfos), map(progress => progress.data.playlistInfos), take(1)));
const downloadedMaps = useObservable(() => download$.pipe(filter(progress => !!progress.data?.downloadedMaps), map(progress => progress.data.downloadedMaps)));
@@ -34,7 +37,7 @@ export default function OneClickDownloadPlaylist() {
}).catch(() => {
notification.notifySystem({ title: t("notifications.types.error"), body: t("notifications.playlists.one-click-install.error") });
}).finally(() => {
window.electron.window.close();
closeWindow();
});
}, []);
@@ -61,7 +64,7 @@ export default function OneClickDownloadPlaylist() {
<BsmImage className="mt-2 aspect-square w-1/2 object-cover rounded-md shadow-black shadow-lg" placeholder={defaultImage} image={playlistImage()} errorImage={defaultImage} />
<h1 className="mt-4 overflow-hidden font-bold italic text-xl text-gray-200 tracking-wide w-full text-center whitespace-nowrap text-ellipsis px-2">{playlistInfos?.playlistTitle}</h1>
<div className="w-full py-3 flex items-center justify-center max-w-full overflow-x-scroll overflow-y-hidden scrollbar scrollbar-thin scrollbar-track-transparent scrollbar-thumb-neutral-900" ref={mapsContainer}>
<div className="flex justify-start items-start gap-2.5">{downloadedMaps?.map(map => map?.coverUrl &&
<div className="flex justify-start items-start gap-2.5">{downloadedMaps?.map(map => map?.coverUrl &&
<motion.img layout="position" key={map.hash} className="block aspect-square w-14 object-cover rounded-md shadow-black shadow-md" src={map?.coverUrl} initial={{ scale: 0 }} animate={{ scale: 1 }} whileHover={{ rotate: 5 }} />
)}</div>
</div>
+6 -4
View File
@@ -13,20 +13,22 @@ import { BsNoteFill } from "renderer/components/svgs/icons/bs-note-fill.componen
import { useThemeColor } from "renderer/hooks/use-theme-color.hook";
import { BsmButton } from "renderer/components/shared/bsm-button.component";
import { motion } from "framer-motion"
import { BSLaunchError, BSLaunchEventType, LaunchOption } from "shared/models/bs-launch";
import { BSLaunchError, BSLaunchEventType } from "shared/models/bs-launch";
import { NotificationService } from "renderer/services/notification.service";
import { useTranslation } from "renderer/hooks/use-translation.hook";
import { useWindowControls } from "renderer/hooks/use-window-controls.hook";
export default function ShortcutLaunch() {
const windows = useService(WindowManagerService);
const ipc = useService(IpcService);
const bsLauncher = useService(BSLauncherService);
const notification = useService(NotificationService);
const { close: closeWindow } = useWindowControls();
const t = useTranslation();
const color = useThemeColor("second-color");
const launchOptions = useObservable(() => ipc.sendV2<LaunchOption>("shortcut-launch-options").pipe(take(1)), null)
const launchOptions = useObservable(() => ipc.sendV2("shortcut-launch-options").pipe(take(1)), null)
const [rotation, setRotation] = useState(0);
const [status, setStatus] = useState<BSLaunchEventType>();
@@ -57,7 +59,7 @@ export default function ShortcutLaunch() {
});
sub.add(() => {
window.electron.window.close();
closeWindow();
});
return () => sub.unsubscribe();