mirror of
https://github.com/Zagrios/bs-manager.git
synced 2026-07-03 14:08:25 +02:00
Merge branch 'master' into master
This commit is contained in:
@@ -121,5 +121,8 @@
|
||||
},
|
||||
{
|
||||
"username": "Furiouspupa"
|
||||
},
|
||||
{
|
||||
"username": "Maximilian"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -150,7 +150,7 @@
|
||||
}
|
||||
},
|
||||
"available-versions": {
|
||||
"title": "Wähle eine Version",
|
||||
"title": "Eine Version herunterladen",
|
||||
"steam-release": "Versionsseite",
|
||||
"dropdown": {
|
||||
"refresh": "Aktualisiere die Versionen",
|
||||
@@ -917,7 +917,8 @@
|
||||
"tags": "Stichwörter",
|
||||
"specificities": "Allgemein",
|
||||
"requirements": "Anforderungen",
|
||||
"exclude": "Ausschließen"
|
||||
"exclude": "Ausschließen",
|
||||
"leaderboard": "Rangliste"
|
||||
},
|
||||
"map-types": {
|
||||
"accuracy": "Präzision",
|
||||
@@ -968,11 +969,16 @@
|
||||
},
|
||||
"map-specificities": {
|
||||
"automapper": "KI",
|
||||
"ranked": "Ranked",
|
||||
"curated": "Curated",
|
||||
"verified": "Verifiziert",
|
||||
"fullSpread": "Full Spread"
|
||||
},
|
||||
"map-leaderboard": {
|
||||
"All": "Alle",
|
||||
"Ranked": "Ranked",
|
||||
"BeatLeader": "BeatLeader",
|
||||
"ScoreSaber": "ScoreSaber"
|
||||
},
|
||||
"map-excludes": {
|
||||
"installed": "Installiert"
|
||||
},
|
||||
|
||||
@@ -150,7 +150,7 @@
|
||||
}
|
||||
},
|
||||
"available-versions": {
|
||||
"title": "Select a version",
|
||||
"title": "Download a version",
|
||||
"steam-release": "Release page",
|
||||
"dropdown": {
|
||||
"refresh": "Refresh versions",
|
||||
@@ -912,7 +912,8 @@
|
||||
"tags": "tags",
|
||||
"specificities": "general",
|
||||
"requirements": "requirements",
|
||||
"exclude": "exclude"
|
||||
"exclude": "exclude",
|
||||
"leaderboard": "leaderboard"
|
||||
},
|
||||
"map-types": {
|
||||
"accuracy": "accuracy",
|
||||
@@ -963,11 +964,16 @@
|
||||
},
|
||||
"map-specificities": {
|
||||
"automapper": "AI",
|
||||
"ranked": "ranked",
|
||||
"curated": "curated",
|
||||
"verified": "verified",
|
||||
"fullSpread": "full spread"
|
||||
},
|
||||
"map-leaderboard": {
|
||||
"All": "All",
|
||||
"Ranked": "Ranked",
|
||||
"BeatLeader": "BeatLeader",
|
||||
"ScoreSaber": "ScoreSaber"
|
||||
},
|
||||
"map-excludes": {
|
||||
"installed": "installed"
|
||||
},
|
||||
|
||||
@@ -150,7 +150,7 @@
|
||||
}
|
||||
},
|
||||
"available-versions": {
|
||||
"title": "Selecciona una versión",
|
||||
"title": "Descargar una versión",
|
||||
"steam-release": "Anuncio en Steam",
|
||||
"dropdown": {
|
||||
"refresh": "Actualizar las versiones",
|
||||
@@ -917,7 +917,8 @@
|
||||
"tags": "tags",
|
||||
"specificities": "general",
|
||||
"requirements": "requisitos",
|
||||
"exclude": "excluir"
|
||||
"exclude": "excluir",
|
||||
"leaderboard": "tabla de clasificación"
|
||||
},
|
||||
"map-types": {
|
||||
"accuracy": "precisión",
|
||||
@@ -968,11 +969,16 @@
|
||||
},
|
||||
"map-specificities": {
|
||||
"automapper": "IA",
|
||||
"ranked": "rankado",
|
||||
"curated": "recomendado",
|
||||
"verified": "verificado",
|
||||
"fullSpread": "panel completo"
|
||||
},
|
||||
"map-leaderboard": {
|
||||
"All": "Todos",
|
||||
"Ranked": "Rankado",
|
||||
"BeatLeader": "BeatLeader",
|
||||
"ScoreSaber": "ScoreSaber"
|
||||
},
|
||||
"map-excludes": {
|
||||
"installed": "instalado"
|
||||
},
|
||||
|
||||
@@ -150,7 +150,7 @@
|
||||
}
|
||||
},
|
||||
"available-versions": {
|
||||
"title": "Choisis une version",
|
||||
"title": "Télécharger une version",
|
||||
"steam-release": "Notes de version",
|
||||
"dropdown": {
|
||||
"refresh": "Actualiser les versions",
|
||||
@@ -918,7 +918,8 @@
|
||||
"tags": "tags",
|
||||
"specificities": "général",
|
||||
"requirements": "requis",
|
||||
"exclude": "exclure"
|
||||
"exclude": "exclure",
|
||||
"leaderboard": "tableau des leaders"
|
||||
},
|
||||
"map-types": {
|
||||
"accuracy": "précision",
|
||||
@@ -969,11 +970,16 @@
|
||||
},
|
||||
"map-specificities": {
|
||||
"automapper": "IA",
|
||||
"ranked": "classée",
|
||||
"curated": "recommandée",
|
||||
"verified": "vérifiée",
|
||||
"fullSpread": "panel complet"
|
||||
},
|
||||
"map-leaderboard": {
|
||||
"All": "Tout",
|
||||
"Ranked": "Classée",
|
||||
"BeatLeader": "BeatLeader",
|
||||
"ScoreSaber": "ScoreSaber"
|
||||
},
|
||||
"map-excludes": {
|
||||
"installed": "installé"
|
||||
},
|
||||
|
||||
@@ -150,7 +150,7 @@
|
||||
}
|
||||
},
|
||||
"available-versions": {
|
||||
"title": "バージョンを選択",
|
||||
"title": "バージョンをダウンロード",
|
||||
"steam-release": "リリースページ",
|
||||
"dropdown": {
|
||||
"refresh": "バージョンの更新",
|
||||
@@ -917,7 +917,8 @@
|
||||
"tags": "タグ",
|
||||
"specificities": "一般",
|
||||
"requirements": "要Mod",
|
||||
"exclude": "除外する"
|
||||
"exclude": "除外する",
|
||||
"leaderboard": "ランキング"
|
||||
},
|
||||
"map-types": {
|
||||
"accuracy": "正確",
|
||||
@@ -968,11 +969,16 @@
|
||||
},
|
||||
"map-specificities": {
|
||||
"automapper": "AI",
|
||||
"ranked": "ランク入賞",
|
||||
"curated": "厳選済み",
|
||||
"verified": "認証済み",
|
||||
"fullSpread": "フルスプレッド"
|
||||
},
|
||||
"map-leaderboard": {
|
||||
"All": "すべて",
|
||||
"Ranked": "ランク入賞",
|
||||
"BeatLeader": "BeatLeader",
|
||||
"ScoreSaber": "ScoreSaber"
|
||||
},
|
||||
"map-excludes": {
|
||||
"installed": "インストール済み"
|
||||
},
|
||||
|
||||
@@ -150,7 +150,7 @@
|
||||
}
|
||||
},
|
||||
"available-versions": {
|
||||
"title": "Выберите версию",
|
||||
"title": "Скачать версию",
|
||||
"steam-release": "Список изменений",
|
||||
"dropdown": {
|
||||
"refresh": "Обновить версии",
|
||||
@@ -916,7 +916,8 @@
|
||||
"tags": "тэги",
|
||||
"specificities": "основное",
|
||||
"requirements": "требуемые моды",
|
||||
"exclude": "исключить"
|
||||
"exclude": "исключить",
|
||||
"leaderboard": "Таблица лидеров"
|
||||
},
|
||||
"map-types": {
|
||||
"accuracy": "точность",
|
||||
@@ -967,11 +968,16 @@
|
||||
},
|
||||
"map-specificities": {
|
||||
"automapper": "ИИ",
|
||||
"ranked": "с рейтингом",
|
||||
"curated": "оценённые куратором",
|
||||
"verified": "проверенные авторы",
|
||||
"fullSpread": "полный набор"
|
||||
},
|
||||
"map-leaderboard": {
|
||||
"All": "все",
|
||||
"Ranked": "с рейтингом",
|
||||
"BeatLeader": "BeatLeader",
|
||||
"ScoreSaber": "ScoreSaber"
|
||||
},
|
||||
"map-excludes": {
|
||||
"installed": "установленный"
|
||||
},
|
||||
|
||||
@@ -150,7 +150,7 @@
|
||||
}
|
||||
},
|
||||
"available-versions": {
|
||||
"title": "選擇版本",
|
||||
"title": "下載一個版本",
|
||||
"steam-release": "發布頁面",
|
||||
"dropdown": {
|
||||
"refresh": "刷新版本",
|
||||
@@ -917,7 +917,8 @@
|
||||
"tags": "標籤",
|
||||
"specificities": "general",
|
||||
"requirements": "要求",
|
||||
"exclude": "排除"
|
||||
"exclude": "排除",
|
||||
"leaderboard": "排行榜"
|
||||
},
|
||||
"map-types": {
|
||||
"accuracy": "精確度",
|
||||
@@ -968,11 +969,16 @@
|
||||
},
|
||||
"map-specificities": {
|
||||
"automapper": "AI",
|
||||
"ranked": "Ranked",
|
||||
"curated": "Curated",
|
||||
"verified": "Verified",
|
||||
"fullSpread": "Full spread"
|
||||
},
|
||||
"map-leaderboard": {
|
||||
"All": "所有",
|
||||
"Ranked": "排名的",
|
||||
"BeatLeader": "BeatLeader",
|
||||
"ScoreSaber": "ScoreSaber"
|
||||
},
|
||||
"map-excludes": {
|
||||
"installed": "已安裝"
|
||||
},
|
||||
|
||||
@@ -150,7 +150,7 @@
|
||||
}
|
||||
},
|
||||
"available-versions": {
|
||||
"title": "选择版本",
|
||||
"title": "下载一个版本",
|
||||
"steam-release": "发布页面",
|
||||
"dropdown": {
|
||||
"refresh": "刷新版本",
|
||||
@@ -917,7 +917,8 @@
|
||||
"tags": "标签",
|
||||
"specificities": "general",
|
||||
"requirements": "要求",
|
||||
"exclude": "排除"
|
||||
"exclude": "排除",
|
||||
"leaderboard": "排行榜"
|
||||
},
|
||||
"map-types": {
|
||||
"accuracy": "精确度",
|
||||
@@ -968,11 +969,16 @@
|
||||
},
|
||||
"map-specificities": {
|
||||
"automapper": "AI",
|
||||
"ranked": "ranked",
|
||||
"curated": "curated",
|
||||
"verified": "verified",
|
||||
"fullSpread": "full spread"
|
||||
},
|
||||
"map-leaderboard": {
|
||||
"All": "所有",
|
||||
"Ranked": "排名的",
|
||||
"BeatLeader": "BeatLeader",
|
||||
"ScoreSaber": "ScoreSaber"
|
||||
},
|
||||
"map-excludes": {
|
||||
"installed": "已安装"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import log from 'electron-log';
|
||||
import { RegDwordValue, RegSzValue } from "regedit-rs"
|
||||
import { execOnOs } from "../helpers/env.helpers";
|
||||
import { execOnOs } from "./env.helpers";
|
||||
import { bootstrap } from 'global-agent';
|
||||
import { StaticConfigurationService } from "../services/static-configuration.service";
|
||||
|
||||
@@ -14,7 +14,7 @@ async function isProxyEnabled(): Promise<boolean>{
|
||||
if(!key.exists){ throw new Error("Key \"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\" not exist"); }
|
||||
const registryValue = key.values.ProxyEnable as RegDwordValue;
|
||||
if(!registryValue){ throw new Error("Value \"ProxyEnable\" not exist"); }
|
||||
return (1 === registryValue.value);
|
||||
return registryValue.value === 1;
|
||||
}
|
||||
|
||||
async function getProxyServer(): Promise<string>{
|
||||
|
||||
@@ -12,3 +12,7 @@ ipc.on("static-configuration.get", (args, reply) => {
|
||||
ipc.on("static-configuration.set", (args, reply) => {
|
||||
reply(from(staticConfig.set(args.key, args.value)));
|
||||
});
|
||||
|
||||
ipc.on("static-configuration.delete", (key, reply) => {
|
||||
reply(of(staticConfig.delete(key)));
|
||||
});
|
||||
|
||||
@@ -21,6 +21,7 @@ import { OculusLauncherService } from "./oculus-launcher.service";
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
import { BsStore } from "../../../shared/models/bs-store.enum";
|
||||
import { LaunchMod, LaunchMods } from "shared/models/bs-launch/launch-option.interface";
|
||||
import { StaticConfigurationService } from "../static-configuration.service";
|
||||
|
||||
export class BSLauncherService {
|
||||
private static instance: BSLauncherService;
|
||||
@@ -32,6 +33,7 @@ export class BSLauncherService {
|
||||
private readonly remoteVersion: BSVersionLibService;
|
||||
private readonly steamLauncher: SteamLauncherService;
|
||||
private readonly oculusLauncher: OculusLauncherService;
|
||||
private readonly staticConfig: StaticConfigurationService;
|
||||
|
||||
public static getInstance(): BSLauncherService {
|
||||
if (!BSLauncherService.instance) {
|
||||
@@ -48,6 +50,7 @@ export class BSLauncherService {
|
||||
this.remoteVersion = BSVersionLibService.getInstance();
|
||||
this.steamLauncher = SteamLauncherService.getInstance();
|
||||
this.oculusLauncher = OculusLauncherService.getInstance();
|
||||
this.staticConfig = StaticConfigurationService.getInstance();
|
||||
|
||||
this.bsmProtocolService.on("launch", link => {
|
||||
log.info("Launch from bsm protocol", link.toString());
|
||||
@@ -73,6 +76,8 @@ export class BSLauncherService {
|
||||
return throwError(() => new Error("Unable to get launcher for the provided version"));
|
||||
}
|
||||
|
||||
this.staticConfig.set("last-version-launched", launchOptions.version);
|
||||
|
||||
return launcher.launch(launchOptions);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import { Observable, Subject, catchError, finalize, from, map, switchMap, throwE
|
||||
import { BsStore } from "../../shared/models/bs-store.enum";
|
||||
import { CustomError } from "../../shared/models/exceptions/custom-error.class";
|
||||
import crypto from "crypto";
|
||||
import { StaticConfigurationService } from "./static-configuration.service";
|
||||
|
||||
|
||||
export class BSLocalVersionService {
|
||||
@@ -32,6 +33,7 @@ export class BSLocalVersionService {
|
||||
private readonly remoteVersionService: BSVersionLibService;
|
||||
private readonly configService: ConfigurationService;
|
||||
private readonly linker: FolderLinkerService;
|
||||
private readonly staticConfig: StaticConfigurationService;
|
||||
private readonly _loadedVersions$: Subject<BSVersion[]>;
|
||||
|
||||
public static getInstance(): BSLocalVersionService {
|
||||
@@ -48,6 +50,7 @@ export class BSLocalVersionService {
|
||||
this.remoteVersionService = BSVersionLibService.getInstance();
|
||||
this.configService = ConfigurationService.getInstance();
|
||||
this.linker = FolderLinkerService.getInstance();
|
||||
this.staticConfig = StaticConfigurationService.getInstance();
|
||||
|
||||
this._loadedVersions$ = new Subject<BSVersion[]>();
|
||||
}
|
||||
@@ -171,6 +174,24 @@ export class BSLocalVersionService {
|
||||
this.setCustomVersions([...this.getCustomVersions() ?? [], version]);
|
||||
}
|
||||
|
||||
private updateLastVersionLaunched(version: BSVersion, editedVersion: BSVersion): void {
|
||||
const lastVersion = this.staticConfig.get("last-version-launched");
|
||||
if (!lastVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
version.BSVersion !== lastVersion.BSVersion
|
||||
|| version.name !== lastVersion.name
|
||||
|| version.steam !== lastVersion.steam
|
||||
|| version.oculus !== lastVersion.oculus
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.staticConfig.set("last-version-launched", editedVersion);
|
||||
}
|
||||
|
||||
private getCustomVersions(): BSVersion[]{
|
||||
return this.configService.get<BSVersion[]>(this.CUSTOM_VERSIONS_KEY) || [];
|
||||
}
|
||||
@@ -317,6 +338,7 @@ export class BSLocalVersionService {
|
||||
if(oldPath === newPath){
|
||||
this.deleteCustomVersion(version);
|
||||
this.addCustomVersion(editedVersion);
|
||||
this.updateLastVersionLaunched(version, editedVersion);
|
||||
return editedVersion;
|
||||
}
|
||||
|
||||
@@ -327,6 +349,7 @@ export class BSLocalVersionService {
|
||||
return rename(oldPath, newPath).then(() => {
|
||||
this.deleteCustomVersion(version);
|
||||
this.addCustomVersion(editedVersion);
|
||||
this.updateLastVersionLaunched(version, editedVersion);
|
||||
return editedVersion;
|
||||
}).catch((err: Error) => {
|
||||
log.error("edit version error", err, version, name, color);
|
||||
|
||||
@@ -89,6 +89,7 @@ export interface StaticConfigKeyValues {
|
||||
"disable-hadware-acceleration": boolean;
|
||||
"use-symlinks": boolean;
|
||||
"use-system-proxy": boolean;
|
||||
"last-version-launched": BSVersion;
|
||||
|
||||
// Linux Specific static configs
|
||||
"proton-folder": string;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BsvMapDetail } from "shared/models/maps";
|
||||
import { BsvPlaylistPage, MapFilter, PlaylistSearchParams, PlaylistSearchResponse, SearchParams, SearchResponse } from "shared/models/maps/beat-saver.model";
|
||||
import { BsvPlaylistPage, MapFilter, MapLeaderboard, PlaylistSearchParams, PlaylistSearchResponse, SearchParams, SearchResponse } from "shared/models/maps/beat-saver.model";
|
||||
import { RequestService } from "../../request.service";
|
||||
import { CustomError } from "shared/models/exceptions/custom-error.class";
|
||||
|
||||
@@ -32,6 +32,10 @@ export class BeatSaverApiService {
|
||||
return new URLSearchParams();
|
||||
}
|
||||
|
||||
if (!filter.leaderboard) {
|
||||
filter.leaderboard = MapLeaderboard.All;
|
||||
}
|
||||
|
||||
const enbledTagsString = filter.enabledTags ? Array.from(filter.enabledTags) : null;
|
||||
const excludedTagsString = filter.excludedTags ? Array.from(filter.excludedTags).map(tag => `!${tag}`) : null;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BsvMapDetail, MapFilter, MapRequirement, MapSpecificity, MapStyle, MapTag, MapType } from "shared/models/maps/beat-saver.model";
|
||||
import { BsvMapDetail, MapFilter, MapLeaderboard, MapRequirement, MapSpecificity, MapStyle, MapTag, MapType } from "shared/models/maps/beat-saver.model";
|
||||
import { motion } from "framer-motion";
|
||||
import { MutableRefObject, useEffect, useRef, useState } from "react";
|
||||
import { BsmCheckbox } from "../../shared/bsm-checkbox.component";
|
||||
@@ -14,6 +14,8 @@ import { BsmLocalMap } from "shared/models/maps/bsm-local-map.interface";
|
||||
import { SongDetails } from "shared/models/maps";
|
||||
import formatDuration from "format-duration";
|
||||
import { MapInfo } from "shared/models/maps/info/map-info.model";
|
||||
import { BsmSelect, BsmSelectOption } from "renderer/components/shared/bsm-select.component";
|
||||
import { useConstant } from "renderer/hooks/use-constant.hook";
|
||||
|
||||
export type Props = {
|
||||
className?: string;
|
||||
@@ -45,6 +47,13 @@ export function FilterPanel({ className, ref, playlist = false, filter, localDat
|
||||
const isTagActivated = (tag: MapTag): boolean => filter?.enabledTags?.has(tag) || filter?.excludedTags?.has(tag);
|
||||
const isTagExcluded = (tag: MapTag): boolean => filter?.excludedTags?.has(tag);
|
||||
|
||||
const leaderboardOptions: BsmSelectOption<MapLeaderboard>[] = useConstant(
|
||||
() => Object.values(MapLeaderboard).map(key => ({
|
||||
text: `maps.map-leaderboard.${key}`,
|
||||
value: key,
|
||||
}))
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (firstRun.current) {
|
||||
firstRun.current = false;
|
||||
@@ -137,10 +146,6 @@ export function FilterPanel({ className, ref, playlist = false, filter, localDat
|
||||
return t(`maps.map-styles.${style}`);
|
||||
};
|
||||
|
||||
const translateMapSpecificity = (specificity: MapSpecificity): string => {
|
||||
return t(`maps.map-specificities.${specificity}`);
|
||||
};
|
||||
|
||||
type BooleanKeys<T> = { [k in keyof T]: T[k] extends boolean ? k : never }[keyof T];
|
||||
|
||||
const handleCheckbox = (key: BooleanKeys<MapFilter>) => {
|
||||
@@ -153,6 +158,12 @@ export function FilterPanel({ className, ref, playlist = false, filter, localDat
|
||||
onChange(newFilter);
|
||||
};
|
||||
|
||||
const handleLeaderboardChange = (value: MapLeaderboard) => {
|
||||
const newFilter = { ...(filter ?? {}) };
|
||||
newFilter.leaderboard = value;
|
||||
onChange(newFilter);
|
||||
}
|
||||
|
||||
const handleApply = () => {
|
||||
onApply(filter);
|
||||
setHaveChanged(() => false);
|
||||
@@ -173,9 +184,18 @@ export function FilterPanel({ className, ref, playlist = false, filter, localDat
|
||||
{Object.values(MapSpecificity).map(specificity => (
|
||||
<div key={specificity} className="flex justify-start items-center h-[22px] z-20 relative py-0.5 cursor-pointer" onClick={() => handleCheckbox(specificity)}>
|
||||
<BsmCheckbox className="h-full aspect-square relative bg-inherit mr-1" checked={filter?.[specificity]} onChange={() => handleCheckbox(specificity)} />
|
||||
<span className="grow capitalize">{translateMapSpecificity(specificity)}</span>
|
||||
<span className="grow capitalize">{t(`maps.map-specificities.${specificity}`)}</span>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<h2 className="mb-1 uppercase text-sm">{t("maps.map-filter-panel.leaderboard")}</h2>
|
||||
<BsmSelect
|
||||
className="rounded-md bg-theme-1"
|
||||
options={leaderboardOptions}
|
||||
selected={filter.leaderboard || MapLeaderboard.All}
|
||||
onChange={handleLeaderboardChange}
|
||||
/>
|
||||
|
||||
<h2 className="my-1 uppercase text-sm">{t("maps.map-filter-panel.requirements")}</h2>
|
||||
{Object.values(MapRequirement).map(requirement => (
|
||||
<div key={requirement} className="flex justify-start items-center h-[22px] z-20 relative py-0.5 cursor-pointer" onClick={() => handleCheckbox(requirement)}>
|
||||
@@ -287,10 +307,23 @@ function isFitAutomapper(filter: MapFilter, automapper: boolean): boolean {
|
||||
return automapper;
|
||||
}
|
||||
|
||||
function isFitLeaderboard(filter: MapFilter, ranked: boolean, blRanked: boolean): boolean {
|
||||
switch (filter?.leaderboard) {
|
||||
case MapLeaderboard.All:
|
||||
return true;
|
||||
|
||||
function isFitRanked(filter: MapFilter, ranked: boolean): boolean {
|
||||
if (!filter?.ranked) { return true; }
|
||||
return ranked;
|
||||
case MapLeaderboard.Ranked:
|
||||
return ranked || blRanked;
|
||||
|
||||
case MapLeaderboard.ScoreSaber:
|
||||
return ranked;
|
||||
|
||||
case MapLeaderboard.BeatLeader:
|
||||
return blRanked;
|
||||
|
||||
default: // filter is undefined
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function isFitCurated(filter: MapFilter, curated: boolean): boolean {
|
||||
@@ -322,7 +355,7 @@ export const isLocalMapFitMapFilter = ({filter, map, search}: { filter: MapFilte
|
||||
if (!isFitChroma(filter, map.songDetails?.difficulties.some(diff => !!diff.chroma))) { return false; }
|
||||
if (!isFitFullSpread(filter, map.songDetails?.difficulties.length)) { return false; }
|
||||
if (!isFitAutomapper(filter, map.songDetails?.automapper)){ return false; }
|
||||
if (!isFitRanked(filter, map.songDetails?.ranked || map.songDetails?.blRanked)) { return false; }
|
||||
if (!isFitLeaderboard(filter, map.songDetails?.ranked, map.songDetails?.blRanked)) { return false; }
|
||||
if (!isFitCurated(filter, map.songDetails?.curated)) { return false; }
|
||||
if (!isFitVerified(filter, map.songDetails?.uploader.verified)) { return false; }
|
||||
if (!isFitSearch(search, {songName: map.mapInfo?.songName, songAuthorName: map.mapInfo?.songAuthorName, levelMappers: map.mapInfo?.levelMappers})) { return false; }
|
||||
@@ -343,7 +376,7 @@ export const isBsvMapFitMapFilter = ({filter, map, search}: { filter: MapFilter,
|
||||
if (!isFitChroma(filter, map.versions?.at(0)?.diffs.some(diff => !!diff.chroma))) { return false; }
|
||||
if (!isFitFullSpread(filter, map.versions?.at(0)?.diffs.length)) { return false; }
|
||||
if (!isFitAutomapper(filter, map.automapper)){ return false; }
|
||||
if (!isFitRanked(filter, map.ranked || map.blRanked)) { return false; }
|
||||
if (!isFitLeaderboard(filter, map.ranked, map.blRanked)) { return false; }
|
||||
if (!isFitCurated(filter, !!map.curator)) { return false; }
|
||||
if (!isFitVerified(filter, !!map.curatedAt)) { return false; }
|
||||
if (!isFitSearch(search, {songName: map.name, songAuthorName: map.metadata.songAuthorName, levelMappers: [map.metadata.levelAuthorName]})) { return false; }
|
||||
@@ -363,7 +396,7 @@ export const isSongDetailsFitMapFilter = ({filter, map, search}: { filter: MapFi
|
||||
if (!isFitChroma(filter, map.difficulties.some(diff => !!diff.chroma))) { return false; }
|
||||
if (!isFitFullSpread(filter, map.difficulties.length)) { return false; }
|
||||
if (!isFitAutomapper(filter, map.automapper)){ return false; }
|
||||
if (!isFitRanked(filter, map.ranked || map.blRanked)) { return false; }
|
||||
if (!isFitLeaderboard(filter, map.ranked, map.blRanked)) { return false; }
|
||||
if (!isFitCurated(filter, map.curated)) { return false; }
|
||||
if (!isFitVerified(filter, map.uploader.verified)) { return false; }
|
||||
if (!isFitSearch(search, {songName: map.name, songAuthorName: map.uploader.name, levelMappers: [map.uploader.name]})) { return false; }
|
||||
|
||||
@@ -324,12 +324,12 @@ export function MapItemComponent <T = unknown>({ hash, title, autor, songAutor,
|
||||
<motion.div className="w-full h-5 pb-1 pr-7 flex items-center gap-1" onHoverStart={bottomBarHoverStart} onHoverEnd={bottomBarHoverEnd}>
|
||||
{ranked && (
|
||||
<div className="text-yellow-300 bg-current rounded-full px-1 h-full flex items-center justify-center">
|
||||
<span className="uppercase text-xs font-bold tracking-wide brightness-[.25]">{t("maps.map-specificities.ranked")}</span>
|
||||
<span className="uppercase text-xs font-bold tracking-wide brightness-[.25]">{t("maps.map-leaderboard.Ranked")}</span>
|
||||
</div>
|
||||
)}
|
||||
{blRanked && (
|
||||
<div className="bg-pink-400 bg-current rounded-full px-1 h-full flex items-center justify-center">
|
||||
<span className="uppercase text-xs font-bold tracking-wide brightness-[.25]">{t("maps.map-specificities.ranked")}</span>
|
||||
<span className="uppercase text-xs font-bold tracking-wide brightness-[.25]">{t("maps.map-leaderboard.Ranked")}</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="h-full grow flex items-start content-start">{renderDiffPreview()}</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
import { BehaviorSubject, Observable, Subscription, lastValueFrom, shareReplay, throwError } from "rxjs";
|
||||
import { BehaviorSubject, Observable, Subscription, lastValueFrom, map, share, shareReplay, throwError } from "rxjs";
|
||||
import { IpcService } from "./ipc.service";
|
||||
import { ModalExitCode, ModalService } from "./modale.service";
|
||||
import { NotificationService } from "./notification.service";
|
||||
@@ -19,6 +19,7 @@ export class BSVersionManagerService {
|
||||
private readonly progressBar: ProgressBarService;
|
||||
private readonly modals: ModalService;
|
||||
|
||||
private askInstalledVersionsObserver$: Observable<BSVersion[]> | null = null;
|
||||
public readonly installedVersions$: BehaviorSubject<BSVersion[]> = new BehaviorSubject([]);
|
||||
public readonly availableVersions$: BehaviorSubject<BSVersion[]> = new BehaviorSubject([]);
|
||||
|
||||
@@ -28,7 +29,9 @@ export class BSVersionManagerService {
|
||||
this.notification = NotificationService.getInstance();
|
||||
this.progressBar = ProgressBarService.getInstance();
|
||||
this.modals = ModalService.getInstance();
|
||||
this.askAvailableVersions().then(() => this.askInstalledVersions());
|
||||
|
||||
this.askAvailableVersions();
|
||||
this.askInstalledVersions();
|
||||
}
|
||||
|
||||
public static getInstance() {
|
||||
@@ -54,15 +57,47 @@ export class BSVersionManagerService {
|
||||
});
|
||||
}
|
||||
|
||||
public askInstalledVersions(): Promise<BSVersion[]> {
|
||||
return lastValueFrom(this.ipcService.sendV2("bs-version.installed-versions")).then(res => {
|
||||
this.setInstalledVersions(res);
|
||||
return res;
|
||||
});
|
||||
public async askInstalledVersions(): Promise<BSVersion[]> {
|
||||
if (this.askInstalledVersionsObserver$) {
|
||||
return lastValueFrom(this.askInstalledVersionsObserver$);
|
||||
}
|
||||
|
||||
this.askInstalledVersionsObserver$ = this.ipcService
|
||||
.sendV2("bs-version.installed-versions")
|
||||
.pipe(
|
||||
map(versions => {
|
||||
let processed = BSVersionManagerService.sortVersions(versions);
|
||||
processed = BSVersionManagerService.removeDuplicateVersions(processed);
|
||||
return processed;
|
||||
}),
|
||||
share(),
|
||||
);
|
||||
|
||||
return lastValueFrom(this.askInstalledVersionsObserver$)
|
||||
.then(versions => {
|
||||
this.setInstalledVersions(versions);
|
||||
return versions;
|
||||
})
|
||||
.finally(() => {
|
||||
this.askInstalledVersionsObserver$ = null;
|
||||
});
|
||||
}
|
||||
|
||||
public isVersionInstalled(version: BSVersion): boolean {
|
||||
return !!this.getInstalledVersions().find(v => v.BSVersion === version.BSVersion && v.steam === version.steam && v.oculus === version.oculus);
|
||||
public async isVersionInstalled(version: BSVersion): Promise<boolean> {
|
||||
try {
|
||||
const versions: BSVersion[] = this.askInstalledVersionsObserver$
|
||||
? await lastValueFrom(this.askInstalledVersionsObserver$)
|
||||
: this.getInstalledVersions();
|
||||
|
||||
return !!versions.find(v =>
|
||||
v.BSVersion === version.BSVersion
|
||||
&& v.name === version.name
|
||||
&& v.steam === version.steam
|
||||
&& v.oculus === version.oculus
|
||||
);
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async editVersion(version: BSVersion): Promise<BSVersion> {
|
||||
|
||||
@@ -27,4 +27,8 @@ export class StaticConfigurationService {
|
||||
return lastValueFrom(this.ipc.sendV2("static-configuration.set", { key, value }));
|
||||
}
|
||||
|
||||
public delete<K extends StaticConfigKeys>(key: K): Promise<void> {
|
||||
return lastValueFrom(this.ipc.sendV2("static-configuration.delete", key));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ import { ConfigurationService } from "renderer/services/configuration.service";
|
||||
import { OsDiagnosticService } from "renderer/services/os-diagnostic.service";
|
||||
import { useService } from "renderer/hooks/use-service.hook";
|
||||
import { SetupService } from "renderer/services/setup.service";
|
||||
import { StaticConfigurationService } from "renderer/services/static-configuration.service";
|
||||
import { BSVersionManagerService } from "renderer/services/bs-version-manager.service";
|
||||
|
||||
export default function App() {
|
||||
|
||||
@@ -34,6 +36,8 @@ export default function App() {
|
||||
const notification = useService(NotificationService);
|
||||
const config = useService(ConfigurationService);
|
||||
const setup = useService(SetupService);
|
||||
const staticConfig = useService(StaticConfigurationService);
|
||||
const versionManager = useService(BSVersionManagerService);
|
||||
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
@@ -41,10 +45,25 @@ export default function App() {
|
||||
useEffect(() => {
|
||||
setup.check()
|
||||
.then(() => {
|
||||
navigateToDefaultPage();
|
||||
checkOneClicks();
|
||||
})
|
||||
});
|
||||
}, []);
|
||||
|
||||
const navigateToDefaultPage = async () => {
|
||||
const version = await staticConfig.get("last-version-launched");
|
||||
if (!version) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!await versionManager.isVersionInstalled(version)) {
|
||||
await staticConfig.delete("last-version-launched");
|
||||
return;
|
||||
}
|
||||
|
||||
navigate(`/bs-version/${version.BSVersion}`, { state: version });
|
||||
};
|
||||
|
||||
const checkOneClicks = async () => {
|
||||
|
||||
if (config.get("not-remind-oneclick") === true) {
|
||||
|
||||
@@ -156,6 +156,7 @@ export interface IpcChannelMapping {
|
||||
/* ** static-configuration.ipcs ** */
|
||||
"static-configuration.get": StaticConfigGetIpcRequestResponse<StaticConfigKeys>;
|
||||
"static-configuration.set": StaticConfigSetIpcRequest<StaticConfigKeys>;
|
||||
"static-configuration.delete": { request: StaticConfigKeys; response: void };
|
||||
|
||||
/* ** linux.ipcs ** */
|
||||
"linux.verify-proton-folder": { request: void, response: boolean };
|
||||
|
||||
@@ -209,11 +209,16 @@ export enum MapRequirement {
|
||||
|
||||
export enum MapSpecificity {
|
||||
Automapper = "automapper",
|
||||
Ranked = "ranked",
|
||||
Curated = "curated",
|
||||
Verified = "verified",
|
||||
FullSpread = "fullSpread"
|
||||
}
|
||||
|
||||
export enum MapLeaderboard {
|
||||
All = "All",
|
||||
Ranked = "Ranked",
|
||||
BeatLeader = "BeatLeader",
|
||||
ScoreSaber = "ScoreSaber",
|
||||
}
|
||||
|
||||
// [ Admin, Uploader, SageScore, None ]
|
||||
@@ -235,7 +240,7 @@ export interface MapFilter {
|
||||
from?: number;
|
||||
to?: number;
|
||||
fullSpread?: boolean;
|
||||
ranked?: boolean;
|
||||
leaderboard?: MapLeaderboard;
|
||||
installed?: boolean;
|
||||
minDuration?: number;
|
||||
maxDuration?: number;
|
||||
|
||||
Reference in New Issue
Block a user