mirror of
https://github.com/Zagrios/bs-manager.git
synced 2026-07-03 14:08:25 +02:00
[feature] can now create launch shortcut
This commit is contained in:
@@ -97,6 +97,7 @@
|
||||
"clone": "Klonen",
|
||||
"edit": "Bearbeiten",
|
||||
"uninstall": "Entfernen",
|
||||
"create-shortcut": "Verknüpfung erstellen",
|
||||
"shared-folders": "Geteilte Ordner"
|
||||
}
|
||||
},
|
||||
@@ -451,6 +452,15 @@
|
||||
"msg": "Das Freigeben des Ordners „UserData“ kann zu Fehlern führen. Bei Problemen trenne den Ordner, um die Sicherung wiederherzustellen"
|
||||
}
|
||||
}
|
||||
},
|
||||
"create-launch-shortcut": {
|
||||
"success": {
|
||||
"title": "Verknüpfung erstellt",
|
||||
"msg": "Die Verknüpfung wurde auf dem Desktop erstellt."
|
||||
},
|
||||
"error": {
|
||||
"msg": "Beim Erstellen der Verknüpfung ist ein Fehler aufgetreten."
|
||||
}
|
||||
}
|
||||
},
|
||||
"modals": {
|
||||
@@ -611,6 +621,13 @@
|
||||
"unlink-folder": "Ordner Verknüpfung Aufheben",
|
||||
"link-all": "Alle verlinken"
|
||||
}
|
||||
},
|
||||
"create-launch-shortcut": {
|
||||
"title": "Verknüpfung erstellen",
|
||||
"desc": "Das Erstellen einer Verknüpfung ermöglicht es Ihnen, Beat Saber mit den ausgewählten Optionen zu starten, ohne durch BSManager zu gehen.",
|
||||
"launch-options": "Startoptionen",
|
||||
"advanced-launch": "Erweiterte Start",
|
||||
"valid-btn": "Verknüpfung erstellen"
|
||||
}
|
||||
},
|
||||
"maps": {
|
||||
|
||||
@@ -97,6 +97,7 @@
|
||||
"clone": "Clone",
|
||||
"edit": "Edit",
|
||||
"uninstall": "Uninstall",
|
||||
"create-shortcut": "Create a shortcut",
|
||||
"shared-folders": "Shared Folders"
|
||||
}
|
||||
},
|
||||
@@ -451,6 +452,15 @@
|
||||
"msg": "Sharing the 'UserData' folder can generate errors, in case of problems unlink the folder to restore the backup"
|
||||
}
|
||||
}
|
||||
},
|
||||
"create-launch-shortcut": {
|
||||
"success": {
|
||||
"title": "Shortcut created",
|
||||
"msg": "The shortcut has been created on the desktop."
|
||||
},
|
||||
"error": {
|
||||
"msg": "An error occurred while creating the shortcut."
|
||||
}
|
||||
}
|
||||
},
|
||||
"modals": {
|
||||
@@ -611,6 +621,13 @@
|
||||
"unlink-folder": "Unlink Folder",
|
||||
"link-all": "Link all"
|
||||
}
|
||||
},
|
||||
"create-launch-shortcut": {
|
||||
"title": "Create a shortcut",
|
||||
"desc": "Creating a shortcut will allow you to start Beat Saber with the chosen options without going through BSManager.",
|
||||
"launch-options": "Launch options",
|
||||
"advanced-launch": "Advanced launch",
|
||||
"valid-btn": "Create the shortcut"
|
||||
}
|
||||
},
|
||||
"maps": {
|
||||
|
||||
@@ -96,6 +96,7 @@
|
||||
"clone": "Clonar",
|
||||
"edit": "Editar",
|
||||
"uninstall": "Desinstalar",
|
||||
"create-shortcut": "Crear un atajo",
|
||||
"shared-folders": "Carpetas Compartidas"
|
||||
}
|
||||
},
|
||||
@@ -450,6 +451,15 @@
|
||||
"msg": "Compartir la carpeta 'UserData' puede generar errores, en caso de problemas desvincular la carpeta para restaurar la copia de seguridad"
|
||||
}
|
||||
}
|
||||
},
|
||||
"create-launch-shortcut": {
|
||||
"success": {
|
||||
"title": "Acceso directo creado",
|
||||
"msg": "El acceso directo se ha creado en el escritorio."
|
||||
},
|
||||
"error": {
|
||||
"msg": "Se produjo un error al crear el acceso directo."
|
||||
}
|
||||
}
|
||||
},
|
||||
"modals": {
|
||||
@@ -610,6 +620,13 @@
|
||||
"unlink-folder": "Desenlazar Carpeta",
|
||||
"link-all": "Enlazar todo"
|
||||
}
|
||||
},
|
||||
"create-launch-shortcut": {
|
||||
"title": "Crear un atajo",
|
||||
"desc": "Crear un atajo te permitirá iniciar Beat Saber con las opciones seleccionadas sin pasar por BSManager.",
|
||||
"launch-options": "Opciones de lanzamiento",
|
||||
"advanced-launch": "Lanzamiento avanzado",
|
||||
"valid-btn": "Crear el atajo"
|
||||
}
|
||||
},
|
||||
"maps": {
|
||||
|
||||
@@ -96,6 +96,7 @@
|
||||
"clone": "Cloner",
|
||||
"edit": "Editer",
|
||||
"uninstall": "Désinstaller",
|
||||
"create-shortcut": "Créer un raccourci",
|
||||
"shared-folders": "Dossiers Partagés"
|
||||
}
|
||||
},
|
||||
@@ -450,6 +451,15 @@
|
||||
"msg": "Le partage du dossier 'UserData' peut générer des erreurs, en cas de soucis déliez le dossier pour restaurer la sauvegarde"
|
||||
}
|
||||
}
|
||||
},
|
||||
"create-launch-shortcut": {
|
||||
"success": {
|
||||
"title": "Raccourci créé",
|
||||
"msg": "Le raccourci a été créé sur le bureau."
|
||||
},
|
||||
"error": {
|
||||
"msg": "Une erreur s'est produite lors de la création du raccourci."
|
||||
}
|
||||
}
|
||||
},
|
||||
"modals": {
|
||||
@@ -610,6 +620,13 @@
|
||||
"unlink-folder": "Délier le dossier",
|
||||
"link-all": "Tout lier"
|
||||
}
|
||||
},
|
||||
"create-launch-shortcut": {
|
||||
"title": "Créer un raccourci",
|
||||
"desc": "Créer un raccourci te permettra de démarrer Beat Saber avec les options choisi sans passé par BSManager.",
|
||||
"launch-options": "Options de lancement",
|
||||
"advanced-launch": "Lancement avancé",
|
||||
"valid-btn": "Créer le raccourci"
|
||||
}
|
||||
},
|
||||
"maps": {
|
||||
|
||||
@@ -10,4 +10,4 @@ export const APP_NAME = "BSManager";
|
||||
export const STEAMVR_APP_ID = "250820";
|
||||
|
||||
export const IMAGE_CACHE_FOLDER = "imagescache";
|
||||
export const IMAGE_CACHE_PATH = path.join(path.dirname(app.getPath("exe")), IMAGE_CACHE_FOLDER);
|
||||
export const IMAGE_CACHE_PATH = path.join(app.getPath("userData"), IMAGE_CACHE_FOLDER);
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
|
||||
import { LaunchOption } from "shared/models/bs-launch";
|
||||
import { BSLauncherService } from "../services/bs-launcher.service"
|
||||
import { IpcRequest } from 'shared/models/ipc';
|
||||
import { IpcService } from '../services/ipc.service';
|
||||
import { from } from "rxjs";
|
||||
|
||||
const ipc = IpcService.getInstance();
|
||||
|
||||
ipc.on('bs-launch.launch', async (req: IpcRequest<LaunchOption>, reply) => {
|
||||
ipc.on<LaunchOption>('bs-launch.launch', (req, reply) => {
|
||||
const bsLauncher = BSLauncherService.getInstance();
|
||||
reply(bsLauncher.launch(req.args));
|
||||
});
|
||||
|
||||
ipc.on<LaunchOption>("create-launch-shortcut", (req, reply) => {
|
||||
const bsLauncher = BSLauncherService.getInstance();
|
||||
reply(from(bsLauncher.createLaunchShortcut(req.args)));
|
||||
});
|
||||
|
||||
@@ -87,12 +87,6 @@ if (!gotTheLock) {
|
||||
app.setAppUserModelId(APP_NAME);
|
||||
|
||||
initServicesMustBeInitialized();
|
||||
|
||||
// TODO : to remove
|
||||
setTimeout(() => {
|
||||
// DeepLinkService.getInstance().dispatchLinkOpened("bsmanager://launch?version=1.29.1&versionName=Hi+Twitter&versionIno=2533274792493431&oculusMode=true&desktopMode=true&additionalArgs=--nowait&additionalArgs=-omgargs");
|
||||
}, 3000);
|
||||
|
||||
|
||||
const deepLink = process.argv.find(arg => DeepLinkService.getInstance().isDeepLink(arg));
|
||||
|
||||
@@ -113,25 +107,6 @@ if (!gotTheLock) {
|
||||
ipcMain.on("log-error", (event, args: IpcRequest<any>) => {
|
||||
log.error(args?.args);
|
||||
});
|
||||
|
||||
// TODO : remove this
|
||||
BSLauncherService.getInstance().createLaunchShortcut({
|
||||
debug: false,
|
||||
oculus: true,
|
||||
desktop: true,
|
||||
version: {
|
||||
BSVersion: '1.29.1',
|
||||
BSManifest: '886973241045584398',
|
||||
ReleaseURL: 'https://steamcommunity.com/games/620980/announcements/detail/6169409105101272202',
|
||||
ReleaseImg: 'https://cdn.akamai.steamstatic.com/steamcommunity/public/images/clans/32055887/c328e407367e9914abaf92f609501877ee5abb63.png',
|
||||
ReleaseDate: '1680623885',
|
||||
year: '2023',
|
||||
name: 'Hi Twitter',
|
||||
ino: 2533274792493431,
|
||||
color: '#6545ff'
|
||||
},
|
||||
additionalArgs: [ '--nowait', '-omgargs' ]
|
||||
});
|
||||
|
||||
}).catch(log.error);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import { rename } from "fs/promises";
|
||||
import log from "electron-log";
|
||||
import { Observable, lastValueFrom, of, timer } from "rxjs";
|
||||
import { BsmProtocolService } from "./bsm-protocol.service";
|
||||
import { app} from "electron";
|
||||
import { app, shell} from "electron";
|
||||
import { Resvg } from "@resvg/resvg-js";
|
||||
import Color from "color";
|
||||
import { ensureDir, writeFile } from "fs-extra";
|
||||
@@ -20,13 +20,13 @@ import { objectFromEntries } from "../../shared/helpers/object.helpers";
|
||||
import { WindowManagerService } from "./window-manager.service";
|
||||
import { IpcService } from "./ipc.service";
|
||||
import { BSVersionLibService } from "./bs-version-lib.service";
|
||||
import { execOnOs } from "../helpers/env.helpers";
|
||||
|
||||
export class BSLauncherService {
|
||||
private static instance: BSLauncherService;
|
||||
|
||||
private readonly utilsService: UtilsService;
|
||||
private readonly steamService: SteamService;
|
||||
private readonly oculusService: OculusService;
|
||||
private readonly localVersionService: BSLocalVersionService;
|
||||
private readonly bsmProtocolService: BsmProtocolService;
|
||||
private readonly windows: WindowManagerService;
|
||||
@@ -43,7 +43,6 @@ export class BSLauncherService {
|
||||
private constructor() {
|
||||
this.utilsService = UtilsService.getInstance();
|
||||
this.steamService = SteamService.getInstance();
|
||||
this.oculusService = OculusService.getInstance();
|
||||
this.localVersionService = BSLocalVersionService.getInstance();
|
||||
this.bsmProtocolService = BsmProtocolService.getInstance();
|
||||
this.windows = WindowManagerService.getInstance();
|
||||
@@ -82,14 +81,12 @@ export class BSLauncherService {
|
||||
return this.utilsService.taskRunning(BS_EXECUTABLE);
|
||||
}
|
||||
|
||||
private buildBsLaunchArgs(launchOptions: LaunchOption){
|
||||
private buildBsLaunchArgs(launchOptions: LaunchOption): string[]{
|
||||
const launchArgs = [];
|
||||
|
||||
if (!launchOptions.version.steam && !launchOptions.version.oculus) {
|
||||
launchArgs.push("--no-yeet");
|
||||
}
|
||||
if (launchOptions.oculus) {
|
||||
launchArgs.push("-vrmode oculus");
|
||||
launchArgs.push("-vrmode");
|
||||
launchArgs.push("oculus");
|
||||
}
|
||||
if (launchOptions.desktop) {
|
||||
launchArgs.push("fpfc");
|
||||
@@ -106,14 +103,13 @@ export class BSLauncherService {
|
||||
|
||||
private launchBSProcess(bsExePath: string, args: string[], debug = false): ChildProcessWithoutNullStreams{
|
||||
|
||||
const spawnOptions: SpawnOptionsWithoutStdio = { shell: true, cwd: path.dirname(bsExePath), env: {...process.env, "SteamAppId": BS_APP_ID} };
|
||||
const spawnOptions: SpawnOptionsWithoutStdio = { detached: true, cwd: path.dirname(bsExePath), env: {...process.env, "SteamAppId": BS_APP_ID} };
|
||||
|
||||
if(debug){
|
||||
spawnOptions.detached = true;
|
||||
spawnOptions.windowsVerbatimArguments = true;
|
||||
}
|
||||
|
||||
return spawn(`\"${bsExePath}\"`, args, spawnOptions);
|
||||
return spawn(bsExePath, args, spawnOptions);
|
||||
|
||||
}
|
||||
|
||||
@@ -125,10 +121,6 @@ export class BSLauncherService {
|
||||
return obs.error({type: BSLaunchError.BS_ALREADY_RUNNING} as BSLaunchErrorData);
|
||||
}
|
||||
|
||||
if(launchOptions.version.oculus && (await this.oculusService.oculusRunning())){
|
||||
return obs.error({type: BSLaunchError.OCULUS_NOT_RUNNING} as BSLaunchErrorData);
|
||||
}
|
||||
|
||||
const bsFolderPath = await this.localVersionService.getInstalledVersionPath(launchOptions.version);
|
||||
const exePath = path.join(bsFolderPath, BS_EXECUTABLE);
|
||||
|
||||
@@ -222,13 +214,8 @@ export class BSLauncherService {
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create .ico file for the shortcut with the given color
|
||||
* @param {Color} color
|
||||
* @returns {Promise<string>} Path of the icon
|
||||
*/
|
||||
private async createShortcutIco(color: Color): Promise<string>{
|
||||
|
||||
private createShortcutPngBuffer(color: Color): Buffer{
|
||||
|
||||
const svgIcon = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 406.4 406.4" height="406.4" width="406.4">
|
||||
<rect rx="69.453" height="406.4" width="406.4" fill="${color.hex()}"/>
|
||||
@@ -236,10 +223,36 @@ export class BSLauncherService {
|
||||
</svg>
|
||||
`;
|
||||
|
||||
const pngBuffer = new Resvg(svgIcon, {
|
||||
return new Resvg(svgIcon, {
|
||||
fitTo: { mode: "width", value: 256 }
|
||||
}).render().asPng();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create .png file for the shortcut with the given color
|
||||
* @param {Color} color
|
||||
* @returns {Promise<string>} Path of the icon
|
||||
*/
|
||||
private async createShortcutPng(color: Color): Promise<string>{
|
||||
const pngBuffer = this.createShortcutPngBuffer(color);
|
||||
|
||||
await ensureDir(IMAGE_CACHE_PATH);
|
||||
|
||||
const iconPath = path.join(IMAGE_CACHE_PATH, `launch_shortcut_${color.hex()}.png`);
|
||||
|
||||
await writeFile(iconPath, pngBuffer);
|
||||
|
||||
return iconPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create .ico file for the shortcut with the given color
|
||||
* @param {Color} color
|
||||
* @returns {Promise<string>} Path of the icon
|
||||
*/
|
||||
private async createShortcutIco(color: Color): Promise<string>{
|
||||
const pngBuffer = this.createShortcutPngBuffer(color);
|
||||
const icoBuffer = await toIco([pngBuffer]);
|
||||
|
||||
await ensureDir(IMAGE_CACHE_PATH);
|
||||
@@ -251,36 +264,42 @@ export class BSLauncherService {
|
||||
return iconPath;
|
||||
}
|
||||
|
||||
private createInternetShortcut(options: { url: string, output: string, iconFile?: string, iconIndex?: number }): Promise<void>{
|
||||
|
||||
const data = [
|
||||
"[InternetShortcut]",
|
||||
`URL=${options.url}`,
|
||||
options.iconFile ? `IconFile=${options.iconFile}` : null,
|
||||
`IconIndex=${options.iconIndex ?? 0}`
|
||||
].join("\r\n");
|
||||
|
||||
return writeFile(options.output, data);
|
||||
}
|
||||
|
||||
public async createLaunchShortcut(launchOptions: LaunchOption): Promise<void>{
|
||||
public async createLaunchShortcut(launchOptions: LaunchOption): Promise<boolean>{
|
||||
const shortcutParams = this.launchOptionToShortcutParams(launchOptions);
|
||||
const shortcutUrl = this.bsmProtocolService.buildLink("launch", shortcutParams);
|
||||
const shortcutUrl = this.bsmProtocolService.buildLink("launch", shortcutParams).toString();
|
||||
|
||||
const shortcutName = ["Beat Saber", launchOptions.version.BSVersion, launchOptions.version.name].join(" ")
|
||||
const shortcutName = ["Beat Saber", launchOptions.version.BSVersion, launchOptions.version.name].join(" ");
|
||||
const shortcutIconColor = new Color(launchOptions.version.color, "hex");
|
||||
|
||||
return this.createInternetShortcut({
|
||||
output: path.join(app.getPath("desktop"), `${shortcutName}.url`),
|
||||
url: shortcutUrl.toString(),
|
||||
iconFile: await this.createShortcutIco(new Color(launchOptions.version.color, "hex")),
|
||||
});
|
||||
return execOnOs({
|
||||
win32: async () => (
|
||||
shell.writeShortcutLink(path.join(app.getPath("desktop"), `${shortcutName}.lnk`), {
|
||||
target: shortcutUrl,
|
||||
icon: await this.createShortcutIco(shortcutIconColor),
|
||||
iconIndex: 0,
|
||||
description: [shortcutName, launchOptions.version.color].join(" "), // <= Need color in description to help windows know that the shortcut is different
|
||||
})
|
||||
),
|
||||
linux: async () => (
|
||||
createDesktopUrlShortcut(path.join(app.getPath("desktop"), `${shortcutName}.desktop`), {
|
||||
name: shortcutName,
|
||||
url: shortcutUrl,
|
||||
icon: await this.createShortcutPng(shortcutIconColor),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
private async openShortcutLaunchWindow(launchOptions: ShortcutParams): Promise<void>{
|
||||
const launchOption = this.shortcutParamsToLaunchOption(launchOptions);
|
||||
|
||||
launchOption.version = await this.localVersionService.getVersionOfBSFolder(await this.localVersionService.getInstalledVersionPath(launchOption.version));
|
||||
const bsPath = await this.localVersionService.getInstalledVersionPath(launchOption.version);
|
||||
|
||||
launchOption.version = await this.localVersionService.getVersionOfBSFolder(bsPath, {
|
||||
steam: launchOption.version.steam,
|
||||
oculus: launchOption.version.oculus,
|
||||
});
|
||||
|
||||
launchOption.version = {...(await this.remoteVersion.getVersionDetails(launchOption.version.BSVersion)), ...launchOption.version};
|
||||
|
||||
@@ -304,3 +323,27 @@ type ShortcutParams = {
|
||||
versionSteam?: string;
|
||||
versionOculus?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create .desktop file for url shortcut (only for linux)
|
||||
* @param {string} shortcutPath
|
||||
* @param options
|
||||
* @returns
|
||||
*/
|
||||
function createDesktopUrlShortcut(shortcutPath: string, options?: {
|
||||
url: string
|
||||
name: string,
|
||||
icon: string
|
||||
}): Promise<boolean> {
|
||||
const { url, name, icon } = options || {};
|
||||
|
||||
const data = [
|
||||
"[Desktop Entry]",
|
||||
"Type=Link",
|
||||
`Name=${name}`,
|
||||
`Icon=${icon}`,
|
||||
`URL=${url}`
|
||||
].join("\n");
|
||||
|
||||
return writeFile(shortcutPath, data).then(() => true);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import { InstallationLocationService } from "./installation-location.service";
|
||||
import { SteamService } from "./steam.service";
|
||||
import { BS_APP_ID, OCULUS_BS_DIR } from "../constants";
|
||||
import path from "path";
|
||||
import { createReadStream } from "fs";
|
||||
import { ConfigurationService } from "./configuration.service";
|
||||
import { lstat, rename } from "fs/promises";
|
||||
import { BsmException } from "shared/models/bsm-exception.model";
|
||||
@@ -14,6 +13,8 @@ import { DownloadLinkType } from "shared/models/mods";
|
||||
import sanitize from "sanitize-filename";
|
||||
import { copyDirectoryWithJunctions, deleteFolder, getFoldersInFolder, pathExist } from "../helpers/fs.helpers";
|
||||
import { FolderLinkerService } from "./folder-linker.service";
|
||||
import { ReadStream, createReadStream } from "fs-extra";
|
||||
import readline from "readline";
|
||||
|
||||
export class BSLocalVersionService {
|
||||
private static instance: BSLocalVersionService;
|
||||
@@ -43,41 +44,55 @@ export class BSLocalVersionService {
|
||||
this.linker = FolderLinkerService.getInstance();
|
||||
}
|
||||
|
||||
|
||||
private async getVersionFromGlobalGameManagerFile(versionFilePath: string): Promise<BSVersion> {
|
||||
|
||||
public async getVersionOfBSFolder(bsPath: string): Promise<BSVersion>{
|
||||
const versionFilePath = path.join(bsPath, 'Beat Saber_Data', 'globalgamemanagers');
|
||||
|
||||
if(!(await pathExist(versionFilePath))){ return null; }
|
||||
|
||||
|
||||
const versionsDict = await this.remoteVersionService.getAvailableVersions();
|
||||
|
||||
const folderVersion = await new Promise<BSVersion>(async (resolve, reject) => {
|
||||
const stream = createReadStream(versionFilePath);
|
||||
|
||||
stream.on('data', line => {
|
||||
|
||||
line = line.toString();
|
||||
let stream: ReadStream;
|
||||
|
||||
for(const bsVersion of versionsDict){
|
||||
|
||||
if(!line.includes(bsVersion.BSVersion)){ continue; }
|
||||
|
||||
resolve({...bsVersion});
|
||||
|
||||
stream.close();
|
||||
stream.destroy();
|
||||
|
||||
return;
|
||||
}
|
||||
try{
|
||||
stream = createReadStream(versionFilePath);
|
||||
const rl = readline.createInterface({
|
||||
input: stream,
|
||||
crlfDelay: Infinity
|
||||
});
|
||||
|
||||
stream.on("close", () => { resolve(null); });
|
||||
stream.on("error", e => { log.error(e); reject(e); });
|
||||
});
|
||||
for await (const line of rl) {
|
||||
for (const bsVersion of versionsDict) {
|
||||
if (line.includes(bsVersion.BSVersion)) {
|
||||
stream.close();
|
||||
return {...bsVersion};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch(e) {
|
||||
log.error(e);
|
||||
} finally {
|
||||
stream?.close();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async getVersionOfBSFolder(
|
||||
bsPath: string,
|
||||
options?: {
|
||||
steam?: boolean;
|
||||
oculus?: boolean;
|
||||
}
|
||||
): Promise<BSVersion>{
|
||||
const versionFilePath = path.join(bsPath, 'Beat Saber_Data', 'globalgamemanagers');
|
||||
const folderVersion = await this.getVersionFromGlobalGameManagerFile(versionFilePath);
|
||||
|
||||
if(!folderVersion){ return null; }
|
||||
|
||||
if(options?.steam || options?.oculus){
|
||||
return {...folderVersion, ...options};
|
||||
}
|
||||
|
||||
if(folderVersion.BSVersion !== path.basename(bsPath)){
|
||||
folderVersion.name = path.basename(bsPath);
|
||||
}
|
||||
@@ -150,10 +165,6 @@ export class BSLocalVersionService {
|
||||
return null;
|
||||
}
|
||||
|
||||
private removeSpecialChar(seq: string): string{
|
||||
return sanitize(seq);
|
||||
}
|
||||
|
||||
public getVersionFolder(version: BSVersion): string{
|
||||
return version.name ?? version.BSVersion;
|
||||
}
|
||||
@@ -175,16 +186,7 @@ export class BSLocalVersionService {
|
||||
return null;
|
||||
}
|
||||
|
||||
const steamBsVersion = await this.getVersionOfBSFolder(steamBsFolder);
|
||||
|
||||
if(!steamBsVersion){
|
||||
return null;
|
||||
}
|
||||
|
||||
steamBsVersion.name = undefined;
|
||||
steamBsVersion.steam = true;
|
||||
|
||||
return steamBsVersion;
|
||||
return this.getVersionOfBSFolder(steamBsFolder, { steam: true });
|
||||
}
|
||||
|
||||
private async getOculusVersion(): Promise<BSVersion> {
|
||||
@@ -194,14 +196,7 @@ export class BSLocalVersionService {
|
||||
return null;
|
||||
}
|
||||
|
||||
const oculusBsVersion = await this.getVersionOfBSFolder(oculusBsFolder);
|
||||
|
||||
if(!oculusBsVersion){ return null; }
|
||||
|
||||
oculusBsVersion.name = undefined;
|
||||
oculusBsVersion.oculus = true;
|
||||
|
||||
return oculusBsVersion;
|
||||
return this.getVersionOfBSFolder(oculusBsFolder, { oculus: true });
|
||||
}
|
||||
|
||||
public async getInstalledVersions(): Promise<BSVersion[]> {
|
||||
@@ -253,7 +248,7 @@ export class BSLocalVersionService {
|
||||
const oldPath = await this.getVersionPath(version);
|
||||
const editedVersion: BSVersion = version.BSVersion === name
|
||||
? {...version, name: undefined, color}
|
||||
: {...version, name: this.removeSpecialChar(name), color};
|
||||
: {...version, name: sanitize(name), color};
|
||||
const newPath = await this.getVersionPath(editedVersion);
|
||||
|
||||
if(oldPath === newPath){
|
||||
@@ -278,7 +273,7 @@ export class BSLocalVersionService {
|
||||
const originPath = await this.getVersionPath(version);
|
||||
const cloneVersion: BSVersion = version.BSVersion === name
|
||||
? {...version, name: undefined, color, steam: false, oculus: false}
|
||||
: {...version, name: this.removeSpecialChar(name), color, steam: false, oculus: false};
|
||||
: {...version, name: sanitize(name), color, steam: false, oculus: false};
|
||||
const newPath = await this.getVersionPath(cloneVersion);
|
||||
|
||||
if(originPath === newPath){
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { SystemNotificationOptions } from "shared/models/notification/system-notification.model";
|
||||
import { UtilsService } from "./utils.service";
|
||||
import { Notification } from "electron";
|
||||
import { Notification as NotificationRenderer } from "../../shared/models/notification/notification.model";
|
||||
import { IpcService } from "./ipc.service";
|
||||
|
||||
export class NotificationService {
|
||||
private static instance: NotificationService;
|
||||
@@ -17,24 +15,13 @@ export class NotificationService {
|
||||
private readonly APP_ICON: string;
|
||||
|
||||
private readonly utils: UtilsService;
|
||||
private readonly ipc: IpcService;
|
||||
|
||||
private constructor() {
|
||||
this.utils = UtilsService.getInstance();
|
||||
this.ipc = IpcService.getInstance();
|
||||
this.APP_ICON = this.utils.getAssetsPath("favicon.ico");
|
||||
}
|
||||
|
||||
public notify(options: SystemNotificationOptions) {
|
||||
new Notification({ ...options, icon: this.APP_ICON }).show();
|
||||
}
|
||||
|
||||
// TODO : Make actions work
|
||||
public notifyRenderer(notification: Omit<NotificationRenderer, "actions">) {
|
||||
try {
|
||||
this.ipc.send("show-notification", "index.html", notification);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
import { ModalComponent, ModalExitCode } from "renderer/services/modale.service"
|
||||
import { BSVersion } from "shared/bs-version.interface"
|
||||
import { BsmCheckbox } from "renderer/components/shared/bsm-checkbox.component";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
import { BsmButton } from "renderer/components/shared/bsm-button.component";
|
||||
import { LaunchOption } from "shared/models/bs-launch";
|
||||
import { useService } from "renderer/hooks/use-service.hook";
|
||||
import { BSLauncherService } from "renderer/services/bs-launcher.service";
|
||||
import { useState } from "react";
|
||||
import { BsNoteFill } from "renderer/components/svgs/icons/bs-note-fill.component";
|
||||
import { useThemeColor } from "renderer/hooks/use-theme-color.hook";
|
||||
import { ChevronTopIcon } from "renderer/components/svgs/icons/chevron-top-icon.component";
|
||||
import Tippy from "@tippyjs/react";
|
||||
|
||||
export const CreateLaunchShortcutModal: ModalComponent<LaunchOption, BSVersion> = ({resolver, data}) => {
|
||||
|
||||
const bsLauncher = useService(BSLauncherService);
|
||||
|
||||
const t = useTranslation();
|
||||
const color = useThemeColor("second-color");
|
||||
|
||||
const [launchOptions, setLaunchOptions] = useState(bsLauncher.getLaunchOptions(data));
|
||||
const [advanced, setAdvanced] = useState(!!launchOptions.additionalArgs?.length);
|
||||
const [additionalArgsString, setAdditionalArgsString] = useState(launchOptions.additionalArgs?.join("; ") ?? "");
|
||||
|
||||
const completeModal = () => {
|
||||
|
||||
if(advanced) {
|
||||
launchOptions.additionalArgs = additionalArgsString.split(";").map(arg => arg.trim()).filter(arg => arg.length);
|
||||
} else {
|
||||
launchOptions.additionalArgs = undefined;
|
||||
}
|
||||
|
||||
resolver({exitCode: ModalExitCode.COMPLETED, data: launchOptions});
|
||||
}
|
||||
|
||||
return (
|
||||
<form className="text-gray-800 dark:text-gray-200 max-w-lg" onSubmit={e => e.preventDefault()}>
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">{t("modals.create-launch-shortcut.title")}</h1>
|
||||
<p className="my-5">{t("modals.create-launch-shortcut.desc")}</p>
|
||||
<div className="flex justify-center my-5 gap-3 items-center">
|
||||
<BsNoteFill className="aspect-square h-16" style={{color: data.color ?? color}}/>
|
||||
<div className="flex flex-col">
|
||||
<span className="dark:text-neutral-300 tracking-wide italic">Beat Saber</span>
|
||||
<span className="font-bold text-3xl">{[data.BSVersion, data.name].join(" ")}</span>
|
||||
</div>
|
||||
</div>
|
||||
<h2 className="font-bold">{t("modals.create-launch-shortcut.launch-options")}</h2>
|
||||
<div className="mb-1 grid grid-flow-col gap-3 w-full rounded-md py-2 bg-light-main-color-1 dark:bg-main-color-1">
|
||||
{data.oculus !== true && (
|
||||
<div className="h-full flex justify-center items-center gap-2">
|
||||
<BsmCheckbox className="h-5 aspect-square relative z-[1]" checked={launchOptions.oculus} onChange={e => setLaunchOptions({...launchOptions, oculus: e})} />
|
||||
<Tippy className="!bg-main-color-1" content={t("pages.version-viewer.launch-mods.oculus-description")} delay={[300, 0]} arrow={false}>
|
||||
<span className="font-bold cursor-help">{t("pages.version-viewer.launch-mods.oculus")}</span>
|
||||
</Tippy>
|
||||
</div>
|
||||
)}
|
||||
<div className="h-full flex justify-center items-center gap-2">
|
||||
<BsmCheckbox className="h-5 aspect-square relative z-[1]" checked={launchOptions.desktop} onChange={e => setLaunchOptions({...launchOptions, desktop: e})} />
|
||||
<Tippy className="!bg-main-color-1" content={t("pages.version-viewer.launch-mods.desktop-description")} delay={[300, 0]} arrow={false}>
|
||||
<span className="font-bold cursor-help">{t("pages.version-viewer.launch-mods.desktop")}</span>
|
||||
</Tippy>
|
||||
</div>
|
||||
<div className="h-full flex justify-center items-center gap-2">
|
||||
<BsmCheckbox className="h-5 aspect-square relative z-[1]" checked={launchOptions.debug} onChange={e => setLaunchOptions({...launchOptions, debug: e})} />
|
||||
<Tippy className="!bg-main-color-1" content={t("pages.version-viewer.launch-mods.debug-description")} delay={[300, 0]} arrow={false}>
|
||||
<span className="font-bold cursor-help">{t("pages.version-viewer.launch-mods.debug")}</span>
|
||||
</Tippy>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full rounded-md bg-light-main-color-1 dark:bg-main-color-1">
|
||||
<div className="flex items-center justify-between cursor-pointer pl-3 pr-1 py-1" onClick={() => setAdvanced(prev => !prev)}>
|
||||
<span className="font-bold">{t("modals.create-launch-shortcut.advanced-launch")}</span>
|
||||
<ChevronTopIcon className={`h-8 transition-transform ${advanced ? "rotate-180" : ""}`}/>
|
||||
</div>
|
||||
<div className={`grid grid-rows-[0fr] transition-[grid-template-rows] ${advanced ? "!grid-rows-[1fr]" : ""}`}>
|
||||
<div className="overflow-hidden">
|
||||
<div className="p-2">
|
||||
<input
|
||||
type="text"
|
||||
className="w-full rounded-md text-center outline-none bg-light-main-color-3 dark:bg-main-color-3"
|
||||
placeholder={t("pages.version-viewer.launch-mods.advanced-launch.placeholder")}
|
||||
value={additionalArgsString}
|
||||
onChange={e => setAdditionalArgsString(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-flow-col grid-cols-2 gap-4 mt-2">
|
||||
<BsmButton typeColor="cancel" className="rounded-md text-center transition-all" onClick={() => resolver({ exitCode: ModalExitCode.CANCELED })} withBar={false} text="misc.cancel" />
|
||||
<BsmButton typeColor="primary" className="rounded-md text-center transition-all" onClick={completeModal} withBar={false} text="modals.create-launch-shortcut.valid-btn" />
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
@@ -59,8 +59,9 @@ import { ModelTypePlatformIcon } from "./icons/model-type-platform-icon.componen
|
||||
import { ModelTypeBloqIcon } from "./icons/model-type-bloq-icon.component";
|
||||
import { ChevronTopIcon } from "./icons/chevron-top-icon.component";
|
||||
import { EyeCrossIcon } from "./icons/eye-cross-icon.component";
|
||||
import { ShortcutIcon } from "./icons/shortcut-icon.component";
|
||||
|
||||
export type BsmIconType = BsvMapCharacteristic | MSModelType | ("settings" | "trash" | "favorite" | "folder" | "bsNote" | "check" | "three-dots" | "twitch" | "eye" | "play" | "checkCircleIcon" | "discord" | "info" | "eye-cross" | "terminal" | "desktop" | "oculus" | "add" | "cross" | "task" | "github" | "close" | "thumbUpFill" | "timerFill" | "pause" | "twitter" | "sync" | "chevron-top" | "copy" | "steam" | "edit" | "export" | "patreon" | "search" | "bsMapDifficulty" | "link" | "unlink" | "download" | "filter" | "mee6" | "volume-up" | "volume-off" | "volume-down" | "fr-FR-flag" | "es-ES-flag" | "en-US-flag" | "en-EN-flag" | "de-DE-flag");
|
||||
export type BsmIconType = BsvMapCharacteristic | MSModelType | ("settings" | "trash" | "favorite" | "folder" | "bsNote" | "check" | "three-dots" | "twitch" | "eye" | "play" | "checkCircleIcon" | "discord" | "info" | "eye-cross" | "terminal" | "desktop" | "oculus" | "add" | "cross" | "task" | "github" | "close" | "thumbUpFill" | "timerFill" | "pause" | "twitter" | "sync" | "chevron-top" | "copy" | "steam" | "edit" | "export" | "patreon" | "search" | "bsMapDifficulty" | "link" | "unlink" | "download" | "filter" | "mee6" | "volume-up" | "volume-off" | "volume-down" | "shortcut" | "fr-FR-flag" | "es-ES-flag" | "en-US-flag" | "en-EN-flag" | "de-DE-flag");
|
||||
|
||||
export const BsmIcon = memo(({ className, icon, style }: { className?: string; icon: BsmIconType; style?: CSSProperties }) => {
|
||||
// TODO : Very ugly very messy, need to find a better way to do this
|
||||
@@ -229,6 +230,10 @@ export const BsmIcon = memo(({ className, icon, style }: { className?: string; i
|
||||
return <EyeCrossIcon className={className} style={style} />;
|
||||
}
|
||||
|
||||
if (icon === "shortcut") {
|
||||
return <ShortcutIcon className={className} style={style} />;
|
||||
}
|
||||
|
||||
if (icon === MSModelType.Avatar) {
|
||||
return <ModelTypeAvatarIcon className={className} style={style} />;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { CSSProperties } from "react";
|
||||
|
||||
export function ShortcutIcon(props: { className?: string; style?: CSSProperties }) {
|
||||
return (
|
||||
<svg className={props.className} style={props.style} xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="currentColor">
|
||||
<path d="M189.058-113.304q-30.994 0-53.374-22.38-22.38-22.38-22.38-53.374v-581.884q0-31.06 22.38-53.486 22.38-22.427 53.374-22.427h239.065q16 0 26.938 11.066Q466-824.724 466-808.688t-10.939 26.891q-10.938 10.855-26.938 10.855H189.058v581.884h581.884v-239.065q0-16 10.972-26.938Q792.886-466 808.922-466t26.984 10.939q10.949 10.938 10.949 26.938v239.065q0 30.994-22.427 53.374-22.426 22.38-53.486 22.38H189.058Zm581.884-604.168L416.58-362.949q-11.146 11.021-26.425 10.568-15.278-.452-26.3-11.474t-11.022-26.402q0-15.381 11.022-26.402l354.283-354.283H570.543q-16 0-26.939-10.972-10.938-10.972-10.938-27.008t10.938-26.984q10.939-10.949 26.939-10.949h276.312v276.312q0 16-11.066 26.939-11.065 10.938-27.101 10.938t-26.891-10.938q-10.855-10.939-10.855-26.939v-146.929Z"/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -16,13 +16,21 @@ import { UninstallModal } from "renderer/components/modal/modal-types/uninstall-
|
||||
import { MapsPlaylistsPanel } from "renderer/components/maps-mangement-components/maps-playlists-panel.component";
|
||||
import { ShareFoldersModal } from "renderer/components/modal/modal-types/share-folders-modal.component";
|
||||
import { ModelsPanel } from "renderer/components/models-management/models-panel.component";
|
||||
import { useService } from "renderer/hooks/use-service.hook";
|
||||
import { BSLauncherService } from "renderer/services/bs-launcher.service";
|
||||
import { CreateLaunchShortcutModal } from "renderer/components/modal/modal-types/create-launch-shortcut-modal.component";
|
||||
import { lastValueFrom } from "rxjs";
|
||||
import { NotificationService } from "renderer/services/notification.service";
|
||||
|
||||
export function VersionViewer() {
|
||||
|
||||
const bsUninstallerService = BSUninstallerService.getInstance();
|
||||
const bsVersionManagerService = BSVersionManagerService.getInstance();
|
||||
const modalService = ModalService.getInsance();
|
||||
const bsDownloaderService = BsDownloaderService.getInstance();
|
||||
const ipcService = IpcService.getInstance();
|
||||
const bsLauncher = useService(BSLauncherService);
|
||||
const notification = useService(NotificationService);
|
||||
|
||||
const { state } = useLocation() as { state: BSVersion };
|
||||
const navigate = useNavigate();
|
||||
@@ -74,6 +82,23 @@ export function VersionViewer() {
|
||||
modalService.openModal(ShareFoldersModal, state);
|
||||
};
|
||||
|
||||
const createLaunchShortcut = async () => {
|
||||
const { exitCode, data } = await modalService.openModal(CreateLaunchShortcutModal, state);
|
||||
if(exitCode !== ModalExitCode.COMPLETED){ return; }
|
||||
|
||||
lastValueFrom(bsLauncher.createLaunchShortcut(data)).then(() => {
|
||||
notification.notifySuccess({
|
||||
title: "notifications.create-launch-shortcut.success.title",
|
||||
desc: "notifications.create-launch-shortcut.success.msg"
|
||||
});
|
||||
}).catch(() => {
|
||||
notification.notifyError({
|
||||
title: "notifications.types.error",
|
||||
desc: "notifications.create-launch-shortcut.error.msg"
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<BsmImage className="absolute w-full h-full top-0 left-0 object-cover" image={state.ReleaseImg || DefautVersionImage} errorImage={DefautVersionImage} />
|
||||
@@ -90,7 +115,14 @@ export function VersionViewer() {
|
||||
<ModsSlide version={state} onDisclamerDecline={handleModsDisclaimerDecline} />
|
||||
</div>
|
||||
</div>
|
||||
<BsmDropdownButton className="absolute top-3 right-4 h-9 w-9 bg-light-main-color-2 dark:bg-main-color-2 rounded-md" items={[{ text: "pages.version-viewer.dropdown.open-folder", icon: "folder", onClick: openFolder }, !state.steam && !state.oculus && { text: "pages.version-viewer.dropdown.verify-files", icon: "task", onClick: verifyFiles }, !state.steam && !state.oculus && { text: "pages.version-viewer.dropdown.edit", icon: "edit", onClick: edit }, !state.oculus && { text: "pages.version-viewer.dropdown.clone", icon: "copy", onClick: clone }, { text: "pages.version-viewer.dropdown.shared-folders", icon: "link", onClick: openShareFolderModal }, !state.steam && !state.oculus && { text: "pages.version-viewer.dropdown.uninstall", icon: "trash", onClick: uninstall }]} />
|
||||
<BsmDropdownButton className="absolute top-3 right-4 h-9 w-9 bg-light-main-color-2 dark:bg-main-color-2 rounded-md" items={[
|
||||
{ text: "pages.version-viewer.dropdown.open-folder", icon: "folder", onClick: openFolder },
|
||||
!state.steam && !state.oculus && { text: "pages.version-viewer.dropdown.verify-files", icon: "task", onClick: verifyFiles },
|
||||
!state.steam && !state.oculus && { text: "pages.version-viewer.dropdown.edit", icon: "edit", onClick: edit },
|
||||
!state.oculus && { text: "pages.version-viewer.dropdown.clone", icon: "copy", onClick: clone },
|
||||
{ text: "pages.version-viewer.dropdown.shared-folders", icon: "link", onClick: openShareFolderModal },
|
||||
{ text: "pages.version-viewer.dropdown.create-shortcut", icon: "shortcut", onClick: createLaunchShortcut },
|
||||
!state.steam && !state.oculus && { text: "pages.version-viewer.dropdown.uninstall", icon: "trash", onClick: uninstall }]} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ export class BsDownloaderService {
|
||||
}
|
||||
|
||||
public async download(bsVersion: BSVersion, isVerification?: boolean, isFirstCall = true): Promise<IpcResponse<DownloadEvent>> {
|
||||
// TODO : to remake cause we don't need recursion anymore
|
||||
// TODO : to remake cause we don't need recursion anymore (will be rework with qr code)
|
||||
|
||||
if (isFirstCall && !this.progressBarService.require()) {
|
||||
return { success: false };
|
||||
|
||||
@@ -3,8 +3,10 @@ import { BSVersion } from 'shared/bs-version.interface';
|
||||
import { IpcService } from "./ipc.service";
|
||||
import { NotificationService } from "./notification.service";
|
||||
import { BsDownloaderService } from "./bs-downloader.service";
|
||||
import { BehaviorSubject, Observable, filter } from "rxjs";
|
||||
import { BehaviorSubject, Observable, filter, of } from "rxjs";
|
||||
import { NotificationResult } from "shared/models/notification/notification.model";
|
||||
import { ConfigurationService } from "./configuration.service";
|
||||
import { ThemeService } from "./theme.service";
|
||||
|
||||
export class BSLauncherService {
|
||||
private static instance: BSLauncherService;
|
||||
@@ -12,6 +14,8 @@ export class BSLauncherService {
|
||||
private readonly ipcService: IpcService;
|
||||
private readonly notificationService: NotificationService;
|
||||
private readonly bsDownloaderService: BsDownloaderService;
|
||||
private readonly config: ConfigurationService;
|
||||
private readonly theme: ThemeService;
|
||||
|
||||
public readonly versionRunning$: BehaviorSubject<BSVersion> = new BehaviorSubject(null);
|
||||
|
||||
@@ -24,48 +28,20 @@ export class BSLauncherService {
|
||||
this.ipcService = IpcService.getInstance();
|
||||
this.notificationService = NotificationService.getInstance();
|
||||
this.bsDownloaderService = BsDownloaderService.getInstance();
|
||||
this.config = ConfigurationService.getInstance();
|
||||
this.theme = ThemeService.getInstance();
|
||||
}
|
||||
|
||||
// TODO REMOVE
|
||||
private listenBsExit(): void{
|
||||
this.ipcService.watch("bs-launch.exit").subscribe(res => {
|
||||
const version = this.versionRunning$.value;
|
||||
this.versionRunning$.next(null);
|
||||
if(res.success){ return; }
|
||||
this.notificationService.notifyError({title: "notifications.bs-launch.errors.titles.EXIT", desc: "notifications.bs-launch.errors.msg.EXIT", actions: [{id: "0", title: "misc.verify"}]}).then(res => {
|
||||
if(res === "0"){ this.bsDownloaderService.download(version, true); }
|
||||
});
|
||||
});
|
||||
public getLaunchOptions(version: BSVersion): LaunchOption{
|
||||
return {
|
||||
version,
|
||||
oculus: this.config.get(LaunchMods.OCULUS_MOD),
|
||||
desktop: this.config.get(LaunchMods.DESKTOP_MOD),
|
||||
debug: this.config.get(LaunchMods.DEBUG_MOD),
|
||||
additionalArgs: (this.config.get<string>("additionnal-args") || "").split(";").map(arg => arg.trim()).filter(arg => arg.length > 0)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO : Rework with shortcuts implementation
|
||||
public launch_old(version: BSVersion, oculus: boolean, desktop: boolean, debug: boolean, additionalArgs?: string[]): Promise<NotificationResult|string>{
|
||||
const lauchOption: LaunchOption = {debug, oculus, desktop, version, additionalArgs};
|
||||
if(this.versionRunning$.value){ return this.notificationService.notifyError({title: "notifications.bs-launch.errors.titles.BS_ALREADY_RUNNING"}); }
|
||||
this.versionRunning$.next(version);
|
||||
return this.ipcService.send<LaunchResult>("bs-launch.launch", {args: lauchOption}).then(res => {
|
||||
|
||||
if(res.data === "LAUNCHED"){ return this.notificationService.notifySuccess({title: "notifications.bs-launch.success.titles.launching"}); }
|
||||
|
||||
this.versionRunning$.next(null);
|
||||
if(!res.success){
|
||||
return this.notificationService.notifyError({title: "notifications.bs-launch.errors.titles.UNABLE_TO_LAUNCH", desc: res.error.title});
|
||||
}
|
||||
if (res.data === "EXE_NOT_FINDED") {
|
||||
return this.notificationService.notifyError({ title: "notifications.bs-launch.errors.titles.EXE_NOT_FINDED", desc: "notifications.bs-launch.errors.msg.EXE_NOT_FINDED", actions: [{ id: "0", title: "misc.verify" }] }).then(res => {
|
||||
if (res === "0") {
|
||||
this.bsDownloaderService.download(version, true);
|
||||
}
|
||||
return res;
|
||||
});
|
||||
}
|
||||
if (res.data) {
|
||||
return this.notificationService.notifyError({ title: `notifications.bs-launch.errors.titles.${res.data}` });
|
||||
}
|
||||
return this.notificationService.notifyError({title: res.data || res.error.title});
|
||||
});
|
||||
}
|
||||
|
||||
public doLaunch(launchOptions: LaunchOption): Observable<BSLaunchEventData>{
|
||||
return this.ipcService.sendV2<BSLaunchEventData, LaunchOption>("bs-launch.launch", {args: launchOptions});
|
||||
}
|
||||
@@ -96,6 +72,11 @@ export class BSLauncherService {
|
||||
return launchState$;
|
||||
}
|
||||
|
||||
public createLaunchShortcut(launchOptions: LaunchOption): Observable<void>{
|
||||
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});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export enum LaunchMods {
|
||||
|
||||
@@ -20,11 +20,6 @@ export class NotificationService {
|
||||
private constructor() {
|
||||
this.ipc = IpcService.getInstance();
|
||||
this.notifications$ = new BehaviorSubject<ResolvableNotification[]>([]);
|
||||
|
||||
// TODO : Make actions work and adapt with "watch" remork
|
||||
this.ipc.watch<Notification>("show-notification").subscribe(notification => {
|
||||
this.notify(notification as unknown as Notification);
|
||||
});
|
||||
}
|
||||
|
||||
public notify(notification: Notification): Promise<NotificationResult | string> {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DefaultConfigKey, ThemeConfig } from "renderer/config/default-configuration.config";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
import { Observable } from "rxjs";
|
||||
import { ConfigurationService } from "./configuration.service";
|
||||
|
||||
export class ThemeService {
|
||||
@@ -7,7 +7,7 @@ export class ThemeService {
|
||||
|
||||
private readonly configService: ConfigurationService;
|
||||
|
||||
public readonly theme$: BehaviorSubject<ThemeConfig>;
|
||||
public readonly theme$: Observable<ThemeConfig>;
|
||||
|
||||
public static getInstance(): ThemeService {
|
||||
if (!ThemeService.instance) {
|
||||
@@ -29,6 +29,10 @@ export class ThemeService {
|
||||
return this.configService.get("theme" as DefaultConfigKey);
|
||||
}
|
||||
|
||||
public getBsmColors(): [string, string]{
|
||||
return [this.configService.get("first-color" as DefaultConfigKey), this.configService.get("second-color" as DefaultConfigKey)];
|
||||
}
|
||||
|
||||
public get isLight() {
|
||||
return this.configService.get("theme" as DefaultConfigKey) === ("light" as ThemeConfig);
|
||||
}
|
||||
|
||||
@@ -63,9 +63,9 @@ export default function ShortcutLaunch() {
|
||||
},
|
||||
error: err => {
|
||||
if(!Object.values(BSLaunchError).includes(err.type)){
|
||||
notification.notifySystem({title: t("bs-launch.errors.titles.UNKNOWN_ERROR"), body: t("bs-launch.errors.msg.UNKNOWN_ERROR")});
|
||||
notification.notifySystem({title: t("notifications.bs-launch.errors.titles.UNKNOWN_ERROR"), body: t("notifications.bs-launch.errors.msg.UNKNOWN_ERROR")});
|
||||
} else {
|
||||
notification.notifySystem({title: t(`bs-launch.errors.titles.${err.type}`), body: t(`bs-launch.errors.msg.${err.type}`)})
|
||||
notification.notifySystem({title: t(`notifications.bs-launch.errors.titles.${err.type}`), body: t(`notifications.bs-launch.errors.msg.${err.type}`)})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,9 +2,9 @@ import { BSVersion } from "shared/bs-version.interface";
|
||||
|
||||
export interface LaunchOption {
|
||||
version: BSVersion,
|
||||
oculus: boolean,
|
||||
desktop: boolean,
|
||||
debug: boolean,
|
||||
oculus?: boolean,
|
||||
desktop?: boolean,
|
||||
debug?: boolean,
|
||||
additionalArgs?: string[],
|
||||
skipAlreadyRunning?: boolean
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user