Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 51ea1b130b | |||
| d020b72f20 | |||
| f2ac952cd9 | |||
| 189d2388fb | |||
| 1c989a2402 | |||
| 959e60127d | |||
| 0faa0f1288 | |||
| 5ad25f58d5 | |||
| 8ace5e6a7a | |||
| 185d01e7b4 | |||
| 755e407372 | |||
| 4f123b69ef | |||
| 7e2334f8fc | |||
| 5be533ccfb | |||
| b8eafd5f9b | |||
| 3f3bb959c4 | |||
| f30cad74d7 | |||
| ede82393da | |||
| 9ee468af03 | |||
| c4d5db809b | |||
| 8ecc9d30b3 | |||
| ca722b2a18 | |||
| c219e9655d | |||
| a6c1ba9fee | |||
| 6e9ea1bd9a | |||
| 84f27d571b | |||
| 51549161cd | |||
| 198002238e |
@@ -13,7 +13,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows-latest]
|
||||
os: [windows-latest, ubuntu-latest]
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
@@ -30,5 +30,5 @@ jobs:
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: release
|
||||
name: release-${{ matrix.os }}
|
||||
path: release
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
# This workflow will...
|
||||
|
||||
name: Realease Linux
|
||||
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.x
|
||||
cache: "npm"
|
||||
- run: npm ci
|
||||
- run: npm run publish:linux
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: release
|
||||
path: release
|
||||
@@ -679,8 +679,7 @@
|
||||
"ReleaseURL": "https://steamcommunity.com/games/620980/announcements/detail/3865840827122760836",
|
||||
"ReleaseImg": "https://clan.akamai.steamstatic.com/images/32055887/ae11d195bbe41a0f244d7b4d055a0ab89f88e5bb.png",
|
||||
"ReleaseDate": "1702309893",
|
||||
"year": "2023",
|
||||
"recommended": true
|
||||
"year": "2023"
|
||||
},
|
||||
{
|
||||
"BSVersion": "1.34.5",
|
||||
@@ -734,7 +733,8 @@
|
||||
"ReleaseURL": "https://store.steampowered.com/news/app/620980/view/4161968170153497460",
|
||||
"ReleaseImg": "https://clan.akamai.steamstatic.com/images//32055887/f82d34c7582137a6748a28c5b02670ff4b235356.png",
|
||||
"ReleaseDate": "1717513346",
|
||||
"year": "2024"
|
||||
"year": "2024",
|
||||
"recommended": true
|
||||
},
|
||||
{
|
||||
"BSVersion": "1.37.1",
|
||||
|
||||
@@ -1 +1,8 @@
|
||||
{}
|
||||
{
|
||||
"1.5.0-alpha.1":{
|
||||
"htmlBody" :"<strong>BSManager continues to get better, thanks to your continued support and contributions!</strong><center>🎈🎉🎈</center><p>Note: This is an alpha release, encountering bugs is expected. Please file new issues for any bugs you find.</p><h2>Features</h2><ul><li>Added playlist support</li><li>Added Linux support (Thanks to @Insprill)</li><li>Added reset button to reset values when editing a Beat Saber version (Thanks to @Liborsaf)</li><li>Added a changelog modal</li></ul><h2>Fixes</h2><ul><li>Fixed an issue where switching between Beat Saber versions too quickly displayed mods for the wrong version</li><li>Fixed an issue where the launch window from a shortcut would never close</li><li>Advanced launch arguments are used even when the input field is closed</li><li>Fixed an issue where having many maps caused some maps to not have their information loaded</li></ul><h2>Other changes</h2><ul><li>.NET Framework is no longer required</li><li>Clear English translations for the Steam credentials popup (Thanks to @Aeywoo)</li><li>NSFW models are now blurred when browsing for models</li><li>When no mods are available for a Beat Saber version, the version number is now shown</li><li>Mods load faster</li><li>After a first full maps loading, maps load almost instantly</li><li>Improved performance in lists with lot of contents</li></ul>",
|
||||
"title" : "What's new ?",
|
||||
"timestamp" : 1721048400,
|
||||
"version" : "1.5.0-alpha.1"
|
||||
}
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 945 B |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 442 KiB After Width: | Height: | Size: 442 KiB |
@@ -2,7 +2,7 @@
|
||||
"name": "bs-manager",
|
||||
"description": "Manage maps, mods and more for Beat Saber",
|
||||
"main": "./src/main/main.ts",
|
||||
"version": "1.5.0",
|
||||
"version": "1.5.0-alpha.1",
|
||||
"scripts": {
|
||||
"build-rust-scripts": "node -r esbuild-register ./.erb/scripts/build-rust-scripts.js",
|
||||
"build": "concurrently \"npm run build:main\" \"npm run build:renderer\"",
|
||||
@@ -18,7 +18,8 @@
|
||||
"start:preload": "cross-env NODE_ENV=development webpack --config ./.erb/configs/webpack.config.preload.dev.ts",
|
||||
"start:renderer": "cross-env NODE_ENV=development webpack serve --config ./.erb/configs/webpack.config.renderer.dev.ts",
|
||||
"test": "jest",
|
||||
"publish": "npm run build && electron-builder -c.win.certificateSha1=842a817a51e2a1d360fcd62f54bf5f9193e919e1 --publish always --win --x64"
|
||||
"publish": "npm run build && electron-builder -c.win.certificateSha1=842a817a51e2a1d360fcd62f54bf5f9193e919e1 --publish always --win --x64",
|
||||
"publish:linux": "npm run build && electron-builder --publish always --linux --x64"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,ts,tsx}": [
|
||||
@@ -36,7 +37,6 @@
|
||||
},
|
||||
"build": {
|
||||
"extraResources": [
|
||||
"./assets/favicon.ico",
|
||||
"./assets/jsons/bs-versions.json",
|
||||
"./assets/jsons/patreons.json",
|
||||
"./assets/proto/song_details_cache_v1.proto"
|
||||
@@ -56,8 +56,9 @@
|
||||
"nsis",
|
||||
"nsis-web"
|
||||
],
|
||||
"icon": "assets/favicon.ico",
|
||||
"icon": "./build/icons/win/favicon.ico",
|
||||
"extraResources": [
|
||||
"./build/icons/win",
|
||||
"./assets/scripts/*.exe"
|
||||
]
|
||||
},
|
||||
@@ -65,8 +66,10 @@
|
||||
"target": [
|
||||
"AppImage"
|
||||
],
|
||||
"category": "Development",
|
||||
"icon": "./build/icons/png",
|
||||
"category": "Utility;Game;",
|
||||
"extraResources": [
|
||||
"./build/icons/png",
|
||||
"./assets/scripts/DepotDownloader"
|
||||
]
|
||||
},
|
||||
@@ -180,7 +183,7 @@
|
||||
"css-minimizer-webpack-plugin": "^6.0.0",
|
||||
"detect-port": "^1.5.1",
|
||||
"electron": "^28.2.4",
|
||||
"electron-builder": "^24.12.0",
|
||||
"electron-builder": "^24.13.3",
|
||||
"electron-devtools-installer": "^3.2.0",
|
||||
"electronmon": "^2.0.2",
|
||||
"esbuild": "^0.20.1",
|
||||
@@ -243,20 +246,20 @@
|
||||
"electron-debug": "^3.2.0",
|
||||
"electron-log": "^4.4.8",
|
||||
"electron-store": "^8.1.0",
|
||||
"electron-updater": "^6.1.8",
|
||||
"electron-updater": "^6.2.1",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"framer-motion": "^11.2.6",
|
||||
"fs-extra": "^11.2.0",
|
||||
"got": "^14.2.0",
|
||||
"got": "^14.4.1",
|
||||
"history": "^5.3.0",
|
||||
"is-elevated": "^4.0.0",
|
||||
"jszip": "^3.10.1",
|
||||
"md5-file": "^5.0.0",
|
||||
"node-abi": "^3.56.0",
|
||||
"node-abi": "^3.65.0",
|
||||
"node-fetch": "^3.3.2",
|
||||
"node-stream-zip": "^1.15.0",
|
||||
"pako": "^2.1.0",
|
||||
"protobufjs": "^7.3.0",
|
||||
"protobufjs": "^7.3.2",
|
||||
"qrcode.react": "^3.1.0",
|
||||
"query-process": "^0.0.3",
|
||||
"react": "^18.2.0",
|
||||
@@ -264,17 +267,17 @@
|
||||
"react-colorful": "^5.6.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-range": "^1.8.14",
|
||||
"react-router-dom": "^6.22.1",
|
||||
"react-virtualized-auto-sizer": "^1.0.23",
|
||||
"react-router-dom": "^6.24.1",
|
||||
"react-virtualized-auto-sizer": "^1.0.24",
|
||||
"react-window": "^1.8.10",
|
||||
"recursive-readdir": "^2.2.3",
|
||||
"rfdc": "^1.3.1",
|
||||
"rxjs": "^7.8.1",
|
||||
"sanitize-filename": "^1.6.3",
|
||||
"semver": "^7.6.0",
|
||||
"semver": "^7.6.2",
|
||||
"serialize-error": "^11.0.3",
|
||||
"striptags": "^4.0.0-alpha.4",
|
||||
"tailwind-merge": "^2.3.0",
|
||||
"tailwind-merge": "^2.4.0",
|
||||
"tailwind-scrollbar-hide": "^1.1.7",
|
||||
"tailwindcss-scoped-groups": "^2.0.0",
|
||||
"tippy.js": "^6.3.7",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "bs-manager",
|
||||
"version": "1.5.0",
|
||||
"version": "1.5.0-alpha.1",
|
||||
"description": "BSManager",
|
||||
"main": "./dist/main/main.js",
|
||||
"author": {
|
||||
|
||||
@@ -14,6 +14,7 @@ export class BeatModsApiService {
|
||||
|
||||
private readonly aliasesCache = new Map<string, BSVersion[]>();
|
||||
private readonly versionModsCache = new Map<string, Mod[]>();
|
||||
private readonly modsHashCache = new Map<string, Mod>();
|
||||
|
||||
private allModsCache: Mod[];
|
||||
|
||||
@@ -66,6 +67,29 @@ export class BeatModsApiService {
|
||||
return mod;
|
||||
}
|
||||
|
||||
private updateModsHashCache(mods: Mod[]): void {
|
||||
|
||||
if(!Array.isArray(mods)){
|
||||
return;
|
||||
}
|
||||
|
||||
for (const mod of mods) {
|
||||
for (const downloads of (mod.downloads ?? [])) {
|
||||
for (const hashMd5 of (downloads.hashMd5 ?? [])) {
|
||||
this.modsHashCache.set(hashMd5.hash, mod);
|
||||
}
|
||||
}
|
||||
|
||||
for (const dep of (mod.dependencies ?? [])) {
|
||||
for (const downloads of (dep.downloads ?? [])) {
|
||||
for (const hashMd5 of (downloads.hashMd5 ?? [])) {
|
||||
this.modsHashCache.set(hashMd5.hash, dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async getVersionMods(version: BSVersion): Promise<Mod[]> {
|
||||
if (this.versionModsCache.has(version.BSVersion)) {
|
||||
return this.versionModsCache.get(version.BSVersion);
|
||||
@@ -76,6 +100,9 @@ export class BeatModsApiService {
|
||||
return this.requestService.getJSON<Mod[]>(this.getVersionModsUrl(alias)).then(mods => {
|
||||
mods = mods.map(mod => this.asignDependencies(mod, mods));
|
||||
this.versionModsCache.set(version.BSVersion, mods);
|
||||
|
||||
this.updateModsHashCache(mods);
|
||||
|
||||
return mods;
|
||||
});
|
||||
}
|
||||
@@ -89,4 +116,15 @@ export class BeatModsApiService {
|
||||
return this.allModsCache;
|
||||
});
|
||||
}
|
||||
|
||||
public getModByHash(hash: string): Promise<Mod> {
|
||||
if (this.modsHashCache.has(hash)) {
|
||||
return Promise.resolve(this.modsHashCache.get(hash));
|
||||
}
|
||||
|
||||
return this.requestService.getJSON<Mod[]>(`${this.BEAT_MODS_API_URL}mod?hash=${hash}`).then(mods => {
|
||||
this.updateModsHashCache(mods);
|
||||
return mods.at(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import { lastValueFrom } from "rxjs";
|
||||
import JSZip from "jszip";
|
||||
import { extractZip } from "../../helpers/zip.helpers";
|
||||
import recursiveReadDir from "recursive-readdir";
|
||||
import { minToMs } from "../../../shared/helpers/time.helpers";
|
||||
import { sToMs } from "../../../shared/helpers/time.helpers";
|
||||
import { ensureDir } from "fs-extra";
|
||||
|
||||
export class BsModsManagerService {
|
||||
@@ -48,23 +48,14 @@ export class BsModsManagerService {
|
||||
}
|
||||
|
||||
private async getModFromHash(hash: string): Promise<Mod> {
|
||||
const allMods = await this.beatModsApi.getAllMods();
|
||||
return allMods.find(mod => {
|
||||
if (mod.name.toLowerCase() === "bsipa") {
|
||||
return false;
|
||||
}
|
||||
return mod.downloads.some(download => download.hashMd5.some(md5 => md5.hash === hash));
|
||||
});
|
||||
}
|
||||
|
||||
private async getIpaFromHash(hash: string): Promise<Mod> {
|
||||
const allMods = await this.beatModsApi.getAllMods();
|
||||
return allMods.find(mod => {
|
||||
if (mod.name.toLowerCase() !== "bsipa") {
|
||||
return false;
|
||||
}
|
||||
return mod.downloads.some(download => download.hashMd5.some(md5 => md5.hash === hash));
|
||||
});
|
||||
const mod = await this.beatModsApi.getModByHash(hash);
|
||||
|
||||
if(mod?.name?.toLowerCase() === "bsipa"){
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
private async getModsInDir(version: BSVersion, modsDir: ModsInstallFolder): Promise<Mod[]> {
|
||||
@@ -84,7 +75,6 @@ export class BsModsManagerService {
|
||||
return undefined;
|
||||
}
|
||||
const hash = await md5File(filePath);
|
||||
|
||||
const mod = await this.getModFromHash(hash);
|
||||
|
||||
if (!mod) {
|
||||
@@ -107,7 +97,7 @@ export class BsModsManagerService {
|
||||
});
|
||||
|
||||
const mods = await Promise.all(promises);
|
||||
return mods.filter(Boolean);
|
||||
return mods.filter(Boolean);
|
||||
}
|
||||
|
||||
private async getBsipaInstalled(version: BSVersion): Promise<Mod> {
|
||||
@@ -117,7 +107,7 @@ export class BsModsManagerService {
|
||||
return undefined;
|
||||
}
|
||||
const injectorMd5 = await md5File(injectorPath);
|
||||
return this.getIpaFromHash(injectorMd5);
|
||||
return this.beatModsApi.getModByHash(injectorMd5);
|
||||
}
|
||||
|
||||
private async downloadZip(zipUrl: string): Promise<JSZip> {
|
||||
@@ -157,12 +147,18 @@ export class BsModsManagerService {
|
||||
return new Promise<boolean>(resolve => {
|
||||
const cmd = process.platform === 'linux'
|
||||
? `screen -dmS "BSIPA" dotnet ${ipaPath} ${args.join(" ")}` // Must run through screen, otherwise BSIPA tries to move console cursor and crashes.
|
||||
: `start /wait /min "" "${ipaPath}" ${args.join(" ")}`;
|
||||
: `${ipaPath} ${args.join(" ")}`;
|
||||
|
||||
log.info("START IPA PROCESS", cmd);
|
||||
const processIPA = spawn(cmd, { cwd: versionPath, detached: true, shell: true });
|
||||
|
||||
const timemout = setTimeout(() => {
|
||||
log.info("Ipa process timeout");
|
||||
resolve(false)
|
||||
}, sToMs(30));
|
||||
|
||||
processIPA.once("exit", code => {
|
||||
clearTimeout(timemout);
|
||||
if (code === 0) {
|
||||
log.info("Ipa process exist with code 0");
|
||||
return resolve(true);
|
||||
@@ -171,10 +167,7 @@ export class BsModsManagerService {
|
||||
resolve(false);
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
log.info("Ipa process timeout");
|
||||
resolve(false)
|
||||
}, minToMs(1));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@@ -322,8 +315,10 @@ export class BsModsManagerService {
|
||||
|
||||
public async getInstalledMods(version: BSVersion): Promise<Mod[]> {
|
||||
this.manifestMatches = [];
|
||||
await this.beatModsApi.getAllMods();
|
||||
|
||||
const bsipa = await this.getBsipaInstalled(version);
|
||||
|
||||
|
||||
return Promise.all([this.getModsInDir(version, ModsInstallFolder.PLUGINS_PENDING), this.getModsInDir(version, ModsInstallFolder.LIBS_PENDING), this.getModsInDir(version, ModsInstallFolder.PLUGINS), this.getModsInDir(version, ModsInstallFolder.LIBS)]).then(dirMods => {
|
||||
const modsDict = new Map<string, Mod>();
|
||||
|
||||
|
||||
@@ -14,12 +14,13 @@ export const ChangelogModal: ModalComponent<void, ChangelogVersion> = ({ options
|
||||
const openTwitter = () => linkOpener.open("https://twitter.com/BSManager_");
|
||||
const openSupportPage = () => linkOpener.open("https://www.patreon.com/bsmanager");
|
||||
const openDiscord = () => linkOpener.open("https://discord.gg/uSqbHVpKdV");
|
||||
const openWebSite = () => linkOpener.open("https://bsmanager.io/");
|
||||
const date = changelog?.timestamp ? new Date(changelog.timestamp * 1000).toLocaleDateString() : '';
|
||||
|
||||
return (
|
||||
<form className="w-[350px] text-gray-800 dark:text-gray-200 h-[70vh] flex flex-col justify-between">
|
||||
<h1 className=" p-4 pt-1 text-3xl uppercase tracking-wide w-full text-center text-gray-800 dark:text-gray-200 font-bold">{changelog?.title}</h1>
|
||||
<div className=" overflow-y-scroll h-full content grow" dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(changelog?.htmlBody) }}/>
|
||||
<div className=" overflow-y-scroll h-full content grow scrollbar-default" dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(changelog?.htmlBody) }}/>
|
||||
<span className="block w-[100%] mx-auto mt-0 mb-4 h-1 rounded-full bg-main-color-1" />
|
||||
<div className="flex flex-row justify-between">
|
||||
<div className="my-auto flex flex-col text-sm">
|
||||
@@ -39,6 +40,9 @@ export const ChangelogModal: ModalComponent<void, ChangelogVersion> = ({ options
|
||||
<Tippy content="Patreon" placement="top" className="font-bold !bg-neutral-900" duration={[200, 0]} arrow={false}>
|
||||
<div><BsmButton onClick={openSupportPage} className="rounded-md p-1 w-7 h-7 " icon="patreon" withBar={false} iconColor="#fff" color="#000"/></div>
|
||||
</Tippy>
|
||||
<Tippy content="Web Site" placement="top" className="font-bold !bg-neutral-900" duration={[200, 0]} arrow={false}>
|
||||
<div><BsmButton onClick={openWebSite} className="rounded-md p-1 w-7 h-7 " icon="web-site" withBar={false} iconColor="#fff" color="#000"/></div>
|
||||
</Tippy>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -62,7 +62,7 @@ export const DownloadPlaylistModal: ModalComponent<void, {version: BSVersion, ow
|
||||
}, [searchParams]);
|
||||
|
||||
const handleNewSearch = (value: Omit<PlaylistSearchParams, "page">) => {
|
||||
setPlaylists(() => []);
|
||||
setPlaylists(() => undefined);
|
||||
setSearchParams(() => ({ ...value, page: 0}));
|
||||
};
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ import { SyncIcon } from "./icons/sync-icon.component";
|
||||
import { VolumeUpIcon } from "./icons/volume-up-icon.component";
|
||||
import { VolumeOffIcon } from "./icons/volume-off-icon.component";
|
||||
import { VolumeDownIcon } from "./icons/volume-down-icon.component";
|
||||
import { WebSiteIcon } from "./icons/web-site-icon.component";
|
||||
import { GermanIcon } from "./flags/german-icon.component";
|
||||
import { RussianIcon } from "./flags/russian-icon.component";
|
||||
import { ChineseIcon } from "./flags/chinese-icon.component";
|
||||
@@ -61,7 +62,8 @@ import { ShortcutIcon } from "./icons/shortcut-icon.component";
|
||||
import { BackupRestoreIcon } from "./icons/backup-restore-icon.component";
|
||||
import { SongDetailDiffCharactertistic } from "shared/models/maps/song-details-cache/song-details-cache.model";
|
||||
|
||||
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" | "fr-FR-flag" | "es-ES-flag" | "en-US-flag" | "en-EN-flag" | "de-DE-flag" | "ru-RU-flag" | "zh-CN-flag" | "zh-TW-flag" | "ja-JP-flag");
|
||||
|
||||
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" | "fr-FR-flag" | "es-ES-flag" | "en-US-flag" | "en-EN-flag" | "de-DE-flag" | "ru-RU-flag" | "zh-CN-flag" | "zh-TW-flag" | "ja-JP-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
|
||||
@@ -250,7 +252,13 @@ export const BsmIcon = memo(({ className, icon, style }: { className?: string; i
|
||||
if (icon === "backup-restore") {
|
||||
return <BackupRestoreIcon className={className} style={style} />;
|
||||
}
|
||||
|
||||
if(icon === "web-site") {
|
||||
return <WebSiteIcon className={className} style={style} />;
|
||||
}
|
||||
return <TrashIcon className={className} style={style} />;
|
||||
|
||||
|
||||
};
|
||||
|
||||
return <>{renderIcon()}</>;
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { CSSProperties } from "react";
|
||||
|
||||
export function WebSiteIcon(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="M480-80q-82 0-155-31.5t-127.5-86Q143-252 111.5-325T80-480q0-83 31.5-155.5t86-127Q252-817 325-848.5T480-880q83 0 155.5 31.5t127 86q54.5 54.5 86 127T880-480q0 82-31.5 155t-86 127.5q-54.5 54.5-127 86T480-80Zm0-82q26-36 45-75t31-83H404q12 44 31 83t45 75Zm-104-16q-18-33-31.5-68.5T322-320H204q29 50 72.5 87t99.5 55Zm208 0q56-18 99.5-55t72.5-87H638q-9 38-22.5 73.5T584-178ZM170-400h136q-3-20-4.5-39.5T300-480q0-21 1.5-40.5T306-560H170q-5 20-7.5 39.5T160-480q0 21 2.5 40.5T170-400Zm216 0h188q3-20 4.5-39.5T580-480q0-21-1.5-40.5T574-560H386q-3 20-4.5 39.5T380-480q0 21 1.5 40.5T386-400Zm268 0h136q5-20 7.5-39.5T800-480q0-21-2.5-40.5T790-560H654q3 20 4.5 39.5T660-480q0 21-1.5 40.5T654-400Zm-16-240h118q-29-50-72.5-87T584-782q18 33 31.5 68.5T638-640Zm-234 0h152q-12-44-31-83t-45-75q-26 36-45 75t-31 83Zm-200 0h118q9-38 22.5-73.5T376-782q-56 18-99.5 55T204-640Z"/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -9,8 +9,8 @@ import { BsmButton } from "renderer/components/shared/bsm-button.component";
|
||||
import BeatWaitingImg from "../../../../../../assets/images/apngs/beat-waiting.png";
|
||||
import BeatConflictImg from "../../../../../../assets/images/apngs/beat-conflict.png";
|
||||
import { useObservable } from "renderer/hooks/use-observable.hook";
|
||||
import { skip, filter, map } from "rxjs/operators";
|
||||
import { Observable, Subscription, combineLatest, lastValueFrom, noop } from "rxjs";
|
||||
import { skip, filter } from "rxjs/operators";
|
||||
import { Subscription, lastValueFrom, noop } from "rxjs";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
import { LinkOpenerService } from "renderer/services/link-opener.service";
|
||||
import { useInView } from "framer-motion";
|
||||
@@ -96,24 +96,27 @@ export function ModsSlide({ version, onDisclamerDecline }: { version: BSVersion;
|
||||
});
|
||||
|
||||
modsManager.installMods(modsToInstall, version).then(() => {
|
||||
lastValueFrom(loadMods());
|
||||
loadMods();
|
||||
});
|
||||
};
|
||||
|
||||
const loadMods = (): Observable<void> => {
|
||||
const loadMods = (): Promise<void> => {
|
||||
if (os.isOffline) {
|
||||
return new Observable<void>(subscriber => subscriber.complete());
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return combineLatest([
|
||||
modsManager.getAvailableMods(version),
|
||||
modsManager.getInstalledMods(version)
|
||||
]).pipe(map(([available, installed]) => {
|
||||
const promise = async () => {
|
||||
const available = await lastValueFrom(modsManager.getAvailableMods(version));
|
||||
const installed = await lastValueFrom(modsManager.getInstalledMods(version));
|
||||
return [available, installed];
|
||||
}
|
||||
|
||||
return promise().then(([available, installed]) => {
|
||||
const defaultMods = configService.get<string[]>("default_mods" as DefaultConfigKey);
|
||||
setModsAvailable(() => modsToCategoryMap(available));
|
||||
setModsSelected(() => available.filter(m => m.required || defaultMods.some(d => m.name.toLowerCase() === d.toLowerCase()) || installed.some(i => m.name === i.name)));
|
||||
setModsInstalled(() => modsToCategoryMap(installed));
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@@ -141,14 +144,14 @@ export function ModsSlide({ version, onDisclamerDecline }: { version: BSVersion;
|
||||
return onDisclamerDecline?.();
|
||||
}
|
||||
|
||||
subs.push(loadMods().subscribe());
|
||||
loadMods();
|
||||
|
||||
subs.push(
|
||||
modsManager.isUninstalling$.pipe(
|
||||
skip(1),
|
||||
filter(uninstalling => !uninstalling)
|
||||
).subscribe(() => {
|
||||
subs.push(loadMods().subscribe());
|
||||
loadMods();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||