Merge branch 'master' into bugfix/beatmods-connection

This commit is contained in:
Zagrios
2025-02-13 16:09:40 +01:00
29 changed files with 2994 additions and 446 deletions
+5 -5
View File
@@ -1,6 +1,7 @@
import { BsModsManagerService } from "../services/mods/bs-mods-manager.service";
import { IpcService } from "../services/ipc.service";
import { from } from "rxjs";
import { BeatModsApiService } from "main/services/mods/beat-mods-api.service";
const ipc = IpcService.getInstance();
@@ -34,8 +35,7 @@ ipc.on("bs-mods.uninstall-all-mods", (args, reply) => {
reply(modsManager.uninstallAllMods(args));
});
ipc.on("bs-mods.get-mods-grid-status", (_, reply) => {
const modsManager = BsModsManagerService.getInstance();
reply(from(modsManager.getModsGridStatus()));
});
ipc.on("bs-mods.beatmods-up", (_, reply) => {
const beatMods = BeatModsApiService.getInstance();
reply(from(beatMods.isUp()));
})
+5
View File
@@ -9,3 +9,8 @@ ipc.on("linux.verify-proton-folder", (_, reply) => {
reply(of(linuxService.verifyProtonPath()));
});
ipc.on("linux.get-wine-prefix-path", (_, reply) => {
const linuxService = LinuxService.getInstance();
reply(of(linuxService.getWinePrefixPath()));
});
@@ -106,10 +106,10 @@ export class BSLocalVersionService {
const versionFilePath = path.join(bsPath, 'Beat Saber_Data', 'globalgamemanagers');
const folderVersion = await this.getVersionFromGlobalGameManagerFile(versionFilePath);
folderVersion.path = bsPath;
if(!folderVersion){ return null; }
folderVersion.path = bsPath;
if(options?.steam || options?.oculus){
return {...folderVersion, ...options};
}
@@ -19,7 +19,6 @@ import crypto from "crypto";
import { BsmZipExtractor } from "main/models/bsm-zip-extractor.class";
import { BsmShellLog, bsmSpawn } from "main/helpers/os.helpers";
import { BbmFullMod, BbmModVersion, ExternalMod } from "../../../shared/models/mods/mod.interface";
import { ModsGridStatus } from "shared/models/mods/mod-ipc.model";
export class BsModsManagerService {
private static instance: BsModsManagerService;
@@ -565,24 +564,6 @@ export class BsModsManagerService {
.finally(() => obs.complete());
});
}
public async getModsGridStatus(): Promise<ModsGridStatus> {
if (process.platform === "linux") {
const wineprefix = this.linuxService.getWinePrefixPath();
if (!wineprefix) {
log.error("Could not find BSManager WINEPREFIX path");
return ModsGridStatus.NO_WINEPREFIX;
}
}
const beatModsUp = await this.beatModsApi.isUp();
if (!beatModsUp) {
return ModsGridStatus.BEATMODS_DOWN;
}
return ModsGridStatus.OK;
}
}
const enum ModsInstallFolder {
@@ -12,7 +12,7 @@ import { FranceIcon } from "./flags/france-icon.component";
import { SpainIcon } from "./flags/spain-icon.component";
import { ItalyIcon } from "./flags/italy-icon.component";
import { UsaIcon } from "./flags/usa-icon.component";
import { UkIcon } from "./flags/uk-icon.component";
import { UkraineIcon } from "./flags/ukraine-icon.component";
import { TaskIcon } from "./icons/task-icon.component";
import { CopyIcon } from "./icons/copy-icon.component";
import { SteamIcon } from "./icons/steam-icon.component";
@@ -70,10 +70,12 @@ import { AddFileIcon } from "./icons/add-file-icon.component";
import { CancelIcon } from "./icons/cancel-icon.component";
import { WarningIcon } from "./icons/warning-icon.component";
import { ArrowUpwardIcon } from "./icons/arrow-upward-icon.component";
import { UkIcon } from "./flags/uk-icon.component";
import { PhilippineIcon } from "./flags/philippine-icon.component";
export type BsmIconType = SongDetailDiffCharactertistic | ("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" | "backup-restore" | "web-site" | "clean" | "browse" | "add-file" | "arrow-upward" | "cancel" | "warning" | "fr-FR-flag" | "es-ES-flag" | "it-IT-flag" | "en-US-flag" | "en-EN-flag" | "de-DE-flag" | "ru-RU-flag" | "zh-CN-flag" | "zh-TW-flag" | "ja-JP-flag" | "ko-KR-flag" | "pt-BR-flag" | "uk-UA-flag" | "tl-PH-flag" | "null" );
export type BsmIconType = SongDetailDiffCharactertistic | ("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" | "backup-restore" | "web-site" | "clean" | "browse" | "add-file" | "arrow-upward" | "cancel" | "warning" | "fr-FR-flag" | "es-ES-flag" | "it-IT-flag" | "en-US-flag" | "en-EN-flag" | "de-DE-flag" | "ru-RU-flag" | "zh-CN-flag" | "zh-TW-flag" | "ja-JP-flag" | "ko-KR-flag" | "pt-BR-flag" | "null" );
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
@@ -146,6 +148,12 @@ export const BsmIcon = memo(({ className, icon, style }: { className?: string; i
if (icon === "pt-BR-flag"){
return <BrazilIcon className={className} style={style} />;
}
if (icon === "uk-UA-flag"){
return <UkraineIcon className={className} style={style} />;
}
if (icon === "tl-PH-flag") {
return <PhilippineIcon className={className} style={style} />;
}
if (icon === "task") {
return <TaskIcon className={className} style={style} />;
@@ -0,0 +1,14 @@
import { createSvgIcon } from "../svg-icon.type";
export const PhilippineIcon = createSvgIcon((props, ref) => {
return (
<svg ref={ref} {...props} width="71" height="48" viewBox="0 0 71 48" xmlns="http://www.w3.org/2000/svg">
<g fill="none">
<path d="M63.89.12H6.61A6.36 6.36 0 0 0 .25 6.48v17.64h70V6.48A6.36 6.36 0 0 0 63.89.12" fill="#4758A9"/>
<path d="M.25 40.75a6.36 6.36 0 0 0 6.36 6.36h57.27a6.36 6.36 0 0 0 6.36-6.36V24.12h-70z" fill="#ED5565"/>
<path d="M2.15 1.95a6.34 6.34 0 0 0-1.9 4.54v34.27a6.34 6.34 0 0 0 1.95 4.57l23.05-21.21z" fill="#FFF"/>
<circle fill="#F6D660" cx="10.25" cy="23.62" r="4"/>
</g>
</svg>
);
});
@@ -0,0 +1,14 @@
import { createSvgIcon } from "../svg-icon.type";
// Uploaded from: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools
export const UkraineIcon = createSvgIcon((props, ref) => {
return (
<svg ref={ref} {...props} width="71" height="48" viewBox="0 0 71 48" xmlns="http://www.w3.org/2000/svg">
<g fill="none">
<path d="M64.27 47.7H7a6.36 6.36 0 0 1-6.36-6.36V23.7h70v17.64a6.36 6.36 0 0 1-6.37 6.36" fill="#F6D660"/>
<path d="M.63 7.06A6.36 6.36 0 0 1 7 .7h57.27a6.36 6.36 0 0 1 6.36 6.36V23.7h-70z" fill="#4A89DC"/>
</g>
</svg>
);
})
@@ -35,7 +35,7 @@ type Props = { version: BSVersion; isActive?: boolean, onDisclamerDecline: () =>
export const ModsSlide = forwardRef<ModsSlideRef, Props>(({ version, isActive, onDisclamerDecline }, forwaredRef) => {
const ACCEPTED_DISCLAIMER_KEY = "accepted-mods-disclaimer";
const { text: t } = useTranslationV2();
const { text: t, element: te } = useTranslationV2();
const modsManager = useService(BsModsManagerService);
const configService = useService(ConfigurationService);
@@ -189,7 +189,7 @@ export const ModsSlide = forwardRef<ModsSlideRef, Props>(({ version, isActive, o
setModsSelected(() => []);
}
const getAcceptDisclaimerKey = async () => {
const ensureDisclaimerAccepted = async (): Promise<boolean> => {
if (configService.get<boolean>(ACCEPTED_DISCLAIMER_KEY)) {
return true;
}
@@ -228,17 +228,16 @@ export const ModsSlide = forwardRef<ModsSlideRef, Props>(({ version, isActive, o
return noop();
}
getAcceptDisclaimerKey()
.then(async (canLoad) => {
if (!canLoad) {
return onDisclamerDecline?.();
}
ensureDisclaimerAccepted().then(async canLoad => {
if (!canLoad) {
return onDisclamerDecline?.();
}
const status = await modsManager.getModsGridStatus();
setGridStatus(status);
const status = await modsManager.getModsGridStatus();
setGridStatus(() => status);
loadMods();
});
loadMods();
});
return () => {
setMoreInfoMod(null);
@@ -271,11 +270,9 @@ export const ModsSlide = forwardRef<ModsSlideRef, Props>(({ version, isActive, o
const renderStatus = () => {
if (gridStatus === ModsGridStatus.BEATMODS_DOWN) {
const [ textStart, textEnd ] = t("pages.version-viewer.mods.status.beatmods-down")
.split("{links}");
return <ModStatus image={BeatConflictImg}>
<span className="text-xl text-center px-2 mt-3 italic">
{textStart}
{te("pages.version-viewer.mods.status.beatmods-down", {links: (<>
<BsmLink className="text-blue-500 underline" href={DISCORD_URL}>
Discord
</BsmLink>
@@ -283,7 +280,7 @@ export const ModsSlide = forwardRef<ModsSlideRef, Props>(({ version, isActive, o
<BsmLink className="text-blue-500 underline" href={GITHUB_URL}>
GitHub
</BsmLink>
{textEnd}
</>)})}
</span>
</ModStatus>
}
@@ -25,7 +25,7 @@ export const defaultConfiguration: {
"second-color": "#ff4444",
theme: "os",
language: window.navigator.language.length <= 2 ? `${window.navigator.language}-${window.navigator.language.toLocaleUpperCase()}` : window.navigator.language,
supported_languages: ["en-US", "en-EN", "fr-FR", "es-ES", "it-IT", "de-DE", "ru-RU", "zh-CN", "zh-TW", "ja-JP", "ko-KR", "pt-BR"],
supported_languages: ["en-US", "en-EN", "fr-FR", "es-ES", "it-IT", "de-DE", "ru-RU", "zh-CN", "zh-TW", "ja-JP", "ko-KR", "pt-BR", "uk-UA", "tl-PH"],
default_mods: ["SongCore", "WhyIsThereNoLeaderboard", "BeatSaverDownloader", "BeatSaverVoting", "PlaylistManager"],
"default-shared-folders": [
window.electron.path.join("Beat Saber_Data", "CustomLevels"),
@@ -508,7 +508,8 @@ export function SettingsPage() {
<SettingContainer className="mt-3" description="pages.settings.discord.description">
<div className="flex gap-2">
<BsmButton className="flex w-fit rounded-md h-8 px-2 font-bold py-1 !text-white" withBar={false} text="Discord" icon="discord" iconClassName="p-0.5 mr-1" color="#5865f2" onClick={openDiscord} />
<BsmButton className="flex w-fit rounded-md h-8 px-2 font-bold py-1 !text-white" withBar={false} text="Twitter" icon="twitter" iconClassName="p-0.5 mr-1" color="#000" onClick={openTwitter} />
<BsmButton className="flex w-fit rounded-md h-8 px-2 font-bold py-1 !text-white" withBar={false} text="Twitter" icon="twitter" iconClassName="p-0.5 mr-1" color="#171717" onClick={openTwitter} />
<BsmButton className="flex w-fit rounded-md h-8 px-2 font-bold py-1 !text-white" withBar={false} text="GitHub" icon="github" iconClassName="p-0.5 mr-1 h-full w-full" color="#171717" onClick={openGithub} />
</div>
</SettingContainer>
<SettingContainer className="pt-3" description="pages.settings.contribution.description">
@@ -519,7 +520,6 @@ export function SettingsPage() {
</div>
<div className="flex px-2 gap-2">
<BsmButton onClick={openLogs} className="shrink-0 whitespace-nowrap px-2 font-bold italic text-sm rounded-md" text="pages.settings.contribution.buttons.open-logs" withBar={false} />
<BsmButton onClick={openGithub} className="shrink-0 px-2 rounded-md" icon="github" title="GitHub" withBar={false} />
</div>
</div>
</SettingContainer>
@@ -9,6 +9,7 @@ import { ProgressionInterface } from "shared/models/progress-bar";
import { BbmFullMod, BbmModVersion } from "shared/models/mods/mod.interface";
import { logRenderError } from "renderer";
import { ModsGridStatus } from "shared/models/mods/mod-ipc.model";
import { LinuxService } from "./linux.service";
export class BsModsManagerService {
private static instance: BsModsManagerService;
@@ -18,6 +19,7 @@ export class BsModsManagerService {
private readonly ipcService: IpcService;
private readonly progressBar: ProgressBarService;
private readonly notifications: NotificationService;
private readonly linux: LinuxService
public readonly isUninstalling$: BehaviorSubject<boolean> = new BehaviorSubject(false);
@@ -32,6 +34,7 @@ export class BsModsManagerService {
this.ipcService = IpcService.getInstance();
this.progressBar = ProgressBarService.getInstance();
this.notifications = NotificationService.getInstance();
this.linux = LinuxService.getInstance();
}
public getAvailableMods(version: BSVersion): Observable<BbmFullMod[]> {
@@ -206,11 +209,21 @@ export class BsModsManagerService {
}
public async getModsGridStatus(): Promise<ModsGridStatus> {
return lastValueFrom(this.ipcService.sendV2("bs-mods.get-mods-grid-status"))
.catch(error => {
logRenderError(error);
return ModsGridStatus.UNKNOWN;
});
if(window.electron.platform === "linux"){
const winePrefix = await lastValueFrom(this.linux.getWinePrefixPath());
if(!winePrefix){
logRenderError("Could not find BSManager WINEPREFIX path");
return ModsGridStatus.NO_WINEPREFIX;
}
}
const beatModsUp = await lastValueFrom(this.ipcService.sendV2("bs-mods.beatmods-up")).catch(() => false);
if (!beatModsUp) {
return ModsGridStatus.BEATMODS_DOWN;
}
return ModsGridStatus.OK;
}
}
+26
View File
@@ -0,0 +1,26 @@
import { Observable } from "rxjs";
import { IpcService } from "./ipc.service";
export class LinuxService {
private static instance: LinuxService;
public static getInstance(): LinuxService {
if (!LinuxService.instance) {
LinuxService.instance = new LinuxService();
}
return LinuxService.instance;
}
private readonly ipc: IpcService;
private constructor() {
this.ipc = IpcService.getInstance();
}
public getWinePrefixPath(): Observable<string> {
return this.ipc.sendV2("linux.get-wine-prefix-path");
}
}
+2 -2
View File
@@ -21,7 +21,6 @@ import { LocalBPList, LocalBPListsDetails } from "../playlists/local-playlist.mo
import { StaticConfigGetIpcRequestResponse, StaticConfigKeys, StaticConfigSetIpcRequest } from "main/services/static-configuration.service";
import { BbmFullMod, BbmModVersion, ExternalMod } from "../mods/mod.interface";
import { OculusDownloadInfo } from "main/services/bs-version-download/bs-oculus-downloader.service";
import { ModsGridStatus } from "../mods/mod-ipc.model";
export type IpcReplier<T> = (data: Observable<T>) => void;
@@ -89,7 +88,7 @@ export interface IpcChannelMapping {
"bs-mods.install-mods": { request: { mods: BbmFullMod[]; version: BSVersion }, response: Progression };
"bs-mods.uninstall-mods": { request: { mods: BbmFullMod[]; version: BSVersion }, response: Progression };
"bs-mods.uninstall-all-mods": { request: BSVersion, response: Progression };
"bs-mods.get-mods-grid-status": { request: BSVersion, response: ModsGridStatus };
"bs-mods.beatmods-up": { request: void, response: boolean };
/* ** bs-playlist-ipcs ** */
"one-click-install-playlist": { request: string, response: Progression<DownloadPlaylistProgressionData> };
@@ -161,6 +160,7 @@ export interface IpcChannelMapping {
/* ** linux.ipcs ** */
"linux.verify-proton-folder": { request: void, response: boolean };
"linux.get-wine-prefix-path": { request: void, response: string };
/* ** oculus.ipcs ** */
"is-oculus-sideloaded-apps-enabled": { request: void, response: boolean };