mirror of
https://github.com/Zagrios/bs-manager.git
synced 2026-07-03 14:08:25 +02:00
Merge branch 'master' into bugfix/527
This commit is contained in:
@@ -787,5 +787,14 @@
|
||||
"ReleaseDate": "1728399815",
|
||||
"year": "2024",
|
||||
"recommended": true
|
||||
},
|
||||
{
|
||||
"BSVersion": "1.39.0",
|
||||
"BSManifest": "5365094543246905054",
|
||||
"OculusBinaryId": "7978663688900007",
|
||||
"ReleaseURL": "https://steamcommunity.com/games/620980/announcements/detail/4451339035198095638",
|
||||
"ReleaseImg": "https://clan.cloudflare.steamstatic.com/images/32055887/fd8e2c783e46decdf5e10fed1e16f124616d9321.png",
|
||||
"ReleaseDate": "1731426932",
|
||||
"year": "2024"
|
||||
}
|
||||
]
|
||||
@@ -114,5 +114,9 @@
|
||||
},
|
||||
{
|
||||
"username": "clapxz"
|
||||
},
|
||||
{
|
||||
"username": "Reflected Chop",
|
||||
"type": "gold"
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
"copied": "Kopiert!",
|
||||
"confirm": "Bestätigen",
|
||||
"choose-folder": "Ordner Wählen",
|
||||
"unknown": "Ein unbekannter Fehler ist aufgetreten ¯\\_(ツ)_/¯"
|
||||
"unknown": "Ein unbekannter Fehler ist aufgetreten ¯\\_(ツ)_/¯",
|
||||
"warning": "Achtung",
|
||||
"continue": "Weiter"
|
||||
},
|
||||
"nav-bar": {
|
||||
"add-version": "Version hinzufügen",
|
||||
@@ -840,6 +842,9 @@
|
||||
"proton-folder-description": "Unter Linux benötigt BSManager Proton, um zu funktionieren. Wählen Sie den Installationsordner von Proton, um fortzufahren.",
|
||||
"proton-folder-placeholder": "Proton-Installationsordner",
|
||||
"where-is-proton-installed": "Wo ist Proton installiert?"
|
||||
},
|
||||
"bs-version-outdated": {
|
||||
"body": "Die Version {outdatedVersion} ist veraltet, und einige Mods oder Funktionen funktionieren möglicherweise nicht mehr wie erwartet. Bitte lade die neueste empfohlene Version ({recommendedVersion}) von Beat Saber herunter, um die neuesten Funktionen und Fehlerbehebungen zu nutzen."
|
||||
}
|
||||
},
|
||||
"maps": {
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
"copied": "Copied!",
|
||||
"confirm": "Confirm",
|
||||
"choose-folder": "Choose folder",
|
||||
"unknown": "An unknown error occurred ¯\\_(ツ)_/¯"
|
||||
"unknown": "An unknown error occurred ¯\\_(ツ)_/¯",
|
||||
"warning": "Warning",
|
||||
"continue": "Continue"
|
||||
},
|
||||
"nav-bar": {
|
||||
"add-version": "Add a version",
|
||||
@@ -840,6 +842,9 @@
|
||||
"proton-folder-description": "On Linux, BSManager requires Proton to work. Choose the Proton installation folder to continue.",
|
||||
"proton-folder-placeholder": "Proton installation folder",
|
||||
"where-is-proton-installed": "Where is Proton installed?"
|
||||
},
|
||||
"bs-version-outdated": {
|
||||
"body": "The version {outdatedVersion} is outdated, and some mods or features may no longer work as expected. Please download the latest recommended version ({recommendedVersion}) of Beat Saber to enjoy the latest features and bugfixes."
|
||||
}
|
||||
},
|
||||
"maps": {
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
"copied": "¡Copiado!",
|
||||
"confirm": "Confirmar",
|
||||
"choose-folder": "Elige la carpeta",
|
||||
"unknown": "Se ha producido un error desconocido ¯\\_(ツ)_/¯"
|
||||
"unknown": "Se ha producido un error desconocido ¯\\_(ツ)_/¯",
|
||||
"warning": "Advertencia",
|
||||
"continue": "Continuar"
|
||||
},
|
||||
"nav-bar": {
|
||||
"add-version": "Agregar una versión",
|
||||
@@ -840,6 +842,9 @@
|
||||
"proton-folder-description": "En Linux, BSManager necesita Proton para funcionar. Elija la carpeta de instalación de Proton para continuar.",
|
||||
"proton-folder-placeholder": "Carpeta de instalación de Proton",
|
||||
"where-is-proton-installed": "¿Dónde está instalado Proton?"
|
||||
},
|
||||
"bs-version-outdated": {
|
||||
"body": "La versión {outdatedVersion} está obsoleta, y algunos mods o funciones pueden no funcionar como se espera. Por favor, descarga la última versión recomendada ({recommendedVersion}) de Beat Saber para disfrutar de las últimas funciones y correcciones de errores."
|
||||
}
|
||||
},
|
||||
"maps": {
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
"copied": "Copié !",
|
||||
"confirm": "Confirmer",
|
||||
"choose-folder": "Choisir un dossier",
|
||||
"unknown": "Une erreur inconnue s'est produite ¯\\_(ツ)_/¯"
|
||||
"unknown": "Une erreur inconnue s'est produite ¯\\_(ツ)_/¯",
|
||||
"warning": "Attention",
|
||||
"continue": "Continuer"
|
||||
},
|
||||
"nav-bar": {
|
||||
"add-version": "Ajouter une version",
|
||||
@@ -841,6 +843,9 @@
|
||||
"proton-folder-description": "Sous Linux, BSManager a besoin de Proton pour fonctionner. Choisissez le dossier d'installation de Proton pour continuer.",
|
||||
"proton-folder-placeholder": "Dossier d'installation de Proton",
|
||||
"where-is-proton-installed": "Où est installé Proton ?"
|
||||
},
|
||||
"bs-version-outdated": {
|
||||
"body": "La version {outdatedVersion} est obsolète, et certains mods ou fonctionnalités peuvent ne plus fonctionner comme prévu. Veuillez télécharger la dernière version recommandée ({recommendedVersion}) de Beat Saber pour profiter des dernières fonctionnalités et corrections de bugs."
|
||||
}
|
||||
},
|
||||
"maps": {
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
"copied": "コピー済み!",
|
||||
"confirm": "確認",
|
||||
"choose-folder": "フォルダーを選択",
|
||||
"unknown": "不明なエラーが発生しました (´・ω・`)"
|
||||
"unknown": "不明なエラーが発生しました (´・ω・`)",
|
||||
"warning": "警告",
|
||||
"continue": "続ける"
|
||||
},
|
||||
"nav-bar": {
|
||||
"add-version": "バージョンを追加",
|
||||
@@ -840,6 +842,9 @@
|
||||
"proton-folder-description": "Linuxでは、BSManagerを動作させるためにProtonが必要です。続行するには、Protonのインストールフォルダを選択してください。",
|
||||
"proton-folder-placeholder": "Protonのインストールフォルダ",
|
||||
"where-is-proton-installed": "Protonはどこにインストールされていますか?"
|
||||
},
|
||||
"bs-version-outdated": {
|
||||
"body": "バージョン {outdatedVersion} は古いため、一部のモッドや機能が期待通りに動作しない場合があります。最新の機能やバグ修正を楽しむために、推奨される最新バージョンの Beat Saber ({recommendedVersion}) をダウンロードしてください。"
|
||||
}
|
||||
},
|
||||
"maps": {
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
"copied": "복사 완료!",
|
||||
"confirm": "확인",
|
||||
"choose-folder": "폴더를 선택하세요",
|
||||
"unknown": "알 수 없는 오류가 발생했습니다 (´・ω・`)"
|
||||
"unknown": "알 수 없는 오류가 발생했습니다 (´・ω・`)",
|
||||
"warning": "경고",
|
||||
"continue": "계속"
|
||||
},
|
||||
"nav-bar": {
|
||||
"add-version": "버전 추가",
|
||||
@@ -237,7 +239,7 @@
|
||||
"modal": {
|
||||
"title": "재시작 필요",
|
||||
"body": "하드웨어 가속 설정을 변경하면 BSManager가 종료 후 재시작됩니다. 계속하시겠습니까?",
|
||||
"confirm-btn": "네, 맞습니다"
|
||||
"confirm-btn": "네!"
|
||||
},
|
||||
"error-notification": {
|
||||
"message": "오류가 발생했습니다. 하드웨어 가속을 비활성화할 수 없습니다."
|
||||
@@ -249,7 +251,7 @@
|
||||
"modal": {
|
||||
"title": "심볼릭 링크 권한",
|
||||
"body": "심볼릭 링크를 생성하려면 BSManager는 관리자 권한 또는 시스템에 활성화된 개발자 모드를 필요로 합니다. 계속하시겠습니까?",
|
||||
"confirm-btn": "네, 맞습니다"
|
||||
"confirm-btn": "네!"
|
||||
},
|
||||
"error-notification": {
|
||||
"message": "오류가 발생했습니다. 심볼릭 링크 설정을 변경할 수 없습니다."
|
||||
@@ -338,7 +340,7 @@
|
||||
"VERIFY_INTEGRITY_FAILED": "파일 검증 중 오류가 발생했습니다.",
|
||||
"SOME_FILES_FAILED_TO_DOWNLOAD": "일부 파일을 다운로드하지 못했습니다.",
|
||||
"OCULUS_LOGIN_TIMED_OUT": "로그인 토큰을 얻는 데 시간이 너무 오래 걸렸습니다.",
|
||||
"OCULUS_LOGIN_WINDOW_CLOSED_BY_USER": "메타 로그인 창이 닫혔습니다.",
|
||||
"OCULUS_LOGIN_WINDOW_CLOSED_BY_USER": "Meta 로그인 창이 닫혔습니다.",
|
||||
"NO_META_AUTH_TOKEN": "다운로드에 필요한 Meta 로그인 토큰을 얻을 수 없습니다.",
|
||||
"UNKNOWN_ERROR": "알 수 없는 오류가 발생했습니다."
|
||||
}
|
||||
@@ -490,8 +492,8 @@
|
||||
"mods": {
|
||||
"install-mods": {
|
||||
"titles": {
|
||||
"success": "Mod 설치 완료 🎉",
|
||||
"warning": "Mod 설치 완료 🤔"
|
||||
"success": "모드 설치 완료 🎉",
|
||||
"warning": "모드 설치 완료 🤔"
|
||||
},
|
||||
"msg": {
|
||||
"success": "모든 MOD가 설치되었습니다.",
|
||||
@@ -504,7 +506,7 @@
|
||||
},
|
||||
"uninstall-mod": {
|
||||
"titles": {
|
||||
"success": "Mod 제거 완료 🎉"
|
||||
"success": "모드 제거 완료 🎉"
|
||||
},
|
||||
"msg": {
|
||||
"errors": {
|
||||
@@ -514,7 +516,7 @@
|
||||
},
|
||||
"uninstall-all-mods": {
|
||||
"titles": {
|
||||
"success": "Mod 제거 완료 🎉"
|
||||
"success": "모드 제거 완료 🎉"
|
||||
},
|
||||
"msg": {
|
||||
"success": "모든 Mod가 제거되었습니다.",
|
||||
@@ -831,7 +833,7 @@
|
||||
},
|
||||
"ask-install-path": {
|
||||
"title": "설치 폴더",
|
||||
"choose-folder-description": "BSManager에 의해 다운로드된 모든 콘텐츠 (버전, MOD, 맵, 재생 목록 등)를 포함하는 폴더를 선택하세요.",
|
||||
"choose-folder-description": "BSManager에 의해 다운로드된 모든 콘텐츠 (버전, 모드, 맵, 재생 목록 등)를 포함하는 폴더를 선택하세요.",
|
||||
"default": "기본값",
|
||||
"default-tooltip": "기본적으로 홈 폴더로 설정됩니다"
|
||||
},
|
||||
@@ -840,6 +842,9 @@
|
||||
"proton-folder-description": "Linux에서는 BSManager가 작동하려면 Proton이 필요합니다. 계속하려면 Proton 설치 폴더를 선택하세요.",
|
||||
"proton-folder-placeholder": "Proton 설치 폴더",
|
||||
"where-is-proton-installed": "Proton이 설치된 위치는 어디인가요?"
|
||||
},
|
||||
"bs-version-outdated": {
|
||||
"body": "버전 {outdatedVersion}은(는) 오래되어 일부 모드나 기능이 예상대로 작동하지 않을 수 있습니다. 최신 기능과 버그 수정 사항을 즐기기 위해 Beat Saber의 최신 권장 버전 ({recommendedVersion})을 다운로드하십시오."
|
||||
}
|
||||
},
|
||||
"maps": {
|
||||
@@ -882,7 +887,7 @@
|
||||
"comedy-meme": "코미디 & 밈",
|
||||
"instrumental": "기악",
|
||||
"hardcore": "하드코어",
|
||||
"k-pop": "K-팝",
|
||||
"k-pop": "K-POP",
|
||||
"indie": "인디",
|
||||
"techno": "테크노",
|
||||
"house": "하우스",
|
||||
@@ -893,15 +898,15 @@
|
||||
"metal": "메탈",
|
||||
"anime": "애니메이션",
|
||||
"hip-hop-rap": "힙합 & 랩",
|
||||
"j-pop": "J-팝",
|
||||
"j-pop": "J-POP",
|
||||
"rock": "록",
|
||||
"pop": "팝",
|
||||
"electronic": "일렉트로닉",
|
||||
"classical-orchestral": "클래식 & 오케스트라"
|
||||
},
|
||||
"map-specificities": {
|
||||
"automapper": "AI",
|
||||
"ranked": "랭크 인",
|
||||
"automapper": "오토매핑",
|
||||
"ranked": "랭크",
|
||||
"curated": "선별됨",
|
||||
"verified": "인증됨",
|
||||
"fullSpread": "풀 스프레드"
|
||||
@@ -917,8 +922,8 @@
|
||||
"ExpertPlus": "전문가+"
|
||||
},
|
||||
"map-item": {
|
||||
"by": "{songAutor} 작성",
|
||||
"mapped-by": "작성자 ",
|
||||
"by": "{songAutor} 작곡",
|
||||
"mapped-by": "매퍼: ",
|
||||
"delete": "삭제",
|
||||
"preview": "맵 미리보기",
|
||||
"bsr-code": "BSR 코드",
|
||||
@@ -967,7 +972,7 @@
|
||||
"modals": {
|
||||
"delete-model": {
|
||||
"title": "모델 삭제",
|
||||
"desc": "정말로 모델 {modelName}를 삭제하시겠습니까?",
|
||||
"desc": "정말로 모델 {modelName}을(를) 삭제하시겠습니까?",
|
||||
"linked-annotation": "이 모델은 모든 링크된 버전에서 삭제됩니다."
|
||||
},
|
||||
"delete-models": {
|
||||
@@ -1184,7 +1189,7 @@
|
||||
"synchronize-playlist-tip": "이 작업은 재생목록을 업데이트하고 부족한 맵을 다운로드합니다. 몇 분이 걸릴 수 있습니다.",
|
||||
"synchronize": "동기화",
|
||||
"curated": "추천",
|
||||
"verified-mapper": "인증된 맵퍼",
|
||||
"verified-mapper": "인증된 매퍼",
|
||||
"empty-playlists": "빈 재생목록",
|
||||
"search-playlist": "재생목록 검색",
|
||||
"no-playlists-found": "재생목록이 없습니다",
|
||||
@@ -1220,7 +1225,7 @@
|
||||
"playlist-is-empty": "재생목록이 비어 있습니다",
|
||||
"continue": "계속",
|
||||
"nb-maps": "맵 수",
|
||||
"nb-mappers": "맵퍼 수",
|
||||
"nb-mappers": "매퍼 수",
|
||||
"duration": "지속 시간",
|
||||
"nps": "초당 음표 수",
|
||||
"date-picker": {
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
"copied": "Скопировано!",
|
||||
"confirm": "Подтвердить",
|
||||
"choose-folder": "Изменить папку",
|
||||
"unknown": "Неизвестная ошибка ¯\\_(ツ)_/¯"
|
||||
"unknown": "Неизвестная ошибка ¯\\_(ツ)_/¯",
|
||||
"warning": "Внимание",
|
||||
"continue": "Продолжить"
|
||||
},
|
||||
"nav-bar": {
|
||||
"add-version": "Добавить версию игры",
|
||||
@@ -839,6 +841,9 @@
|
||||
"proton-folder-description": "В Linux BSManager требует Proton для работы. Выберите папку установки Proton, чтобы продолжить.",
|
||||
"proton-folder-placeholder": "Папка установки Proton",
|
||||
"where-is-proton-installed": "Где установлен Proton?"
|
||||
},
|
||||
"bs-version-outdated": {
|
||||
"body": "Версия {outdatedVersion} устарела, и некоторые моды или функции могут работать некорректно. Пожалуйста, скачайте последнюю рекомендуемую версию ({recommendedVersion}) Beat Saber, чтобы воспользоваться новейшими функциями и исправлениями ошибок."
|
||||
}
|
||||
},
|
||||
"maps": {
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
"copied": "已複製!",
|
||||
"confirm": "確認",
|
||||
"choose-folder": "選擇文件夾",
|
||||
"unknown": "發生了一個錯誤 ¯\\_(ツ)_/¯"
|
||||
"unknown": "發生了一個錯誤 ¯\\_(ツ)_/¯",
|
||||
"warning": "警告",
|
||||
"continue": "繼續"
|
||||
},
|
||||
"nav-bar": {
|
||||
"add-version": "新增版本",
|
||||
@@ -840,6 +842,9 @@
|
||||
"proton-folder-description": "在Linux上,BSManager需要Proton才能運行。請選擇Proton的安裝資料夾以繼續。",
|
||||
"proton-folder-placeholder": "Proton安裝資料夾",
|
||||
"where-is-proton-installed": "Proton安裝在哪裡?"
|
||||
},
|
||||
"bs-version-outdated": {
|
||||
"body": "版本 {outdatedVersion} 已過時,某些模組或功能可能無法正常運作。請下載最新推薦的 Beat Saber 版本 ({recommendedVersion}),以享受最新功能和修正。"
|
||||
}
|
||||
},
|
||||
"maps": {
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
"copied": "已复制!",
|
||||
"confirm": "确认",
|
||||
"choose-folder": "选择文件夹",
|
||||
"Unknown": "发生了一个错误 ¯\\_(ツ)_/¯"
|
||||
"Unknown": "发生了一个错误 ¯\\_(ツ)_/¯",
|
||||
"warning": "警告",
|
||||
"continue": "继续"
|
||||
},
|
||||
"nav-bar": {
|
||||
"add-version": "添加版本",
|
||||
@@ -840,6 +842,9 @@
|
||||
"proton-folder-description": "在Linux上,BSManager需要Proton才能运行。请选择Proton的安装文件夹以继续。",
|
||||
"proton-folder-placeholder": "Proton安装文件夹",
|
||||
"where-is-proton-installed": "Proton安装在哪里?"
|
||||
},
|
||||
"bs-version-outdated": {
|
||||
"body": "版本 {outdatedVersion} 已过时,某些模组或功能可能无法正常工作。请下载最新推荐的 Beat Saber 版本 ({recommendedVersion}),以享受最新功能和修复。"
|
||||
}
|
||||
},
|
||||
"maps": {
|
||||
|
||||
@@ -13,7 +13,9 @@ const { list } = (execOnOs({ win32: () => require("regedit-rs") }, true) ?? {})
|
||||
|
||||
export class SteamService {
|
||||
|
||||
private static readonly PROCESS_NAME = "steam";
|
||||
private static readonly PROCESS_NAME: string = process.platform === "linux"
|
||||
? "steam-runtime-launcher-service"
|
||||
: "steam";
|
||||
|
||||
private static instance: SteamService;
|
||||
|
||||
@@ -116,7 +118,7 @@ export class SteamService {
|
||||
|
||||
public async openSteam(): Promise<void> {
|
||||
|
||||
await shell.openPath("steam://open/games");
|
||||
await shell.openExternal("steam://open/games");
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
// Every 3 seconds check if steam is running
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { BsmButton } from "renderer/components/shared/bsm-button.component";
|
||||
import { BsmImage } from "renderer/components/shared/bsm-image.component";
|
||||
import { ModalComponent, ModalExitCode } from "renderer/services/modale.service";
|
||||
import BeatConflict from "../../../../../assets/images/apngs/beat-conflict.png";
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
import { BsmCheckbox } from "renderer/components/shared/bsm-checkbox.component";
|
||||
import { useTranslationV2 } from "renderer/hooks/use-translation.hook";
|
||||
import { useState } from "react";
|
||||
import { ConfigurationService } from "renderer/services/configuration.service";
|
||||
import { useService } from "renderer/hooks/use-service.hook";
|
||||
|
||||
export const BSVersionOutdatedModal: ModalComponent<void, { outdated: BSVersion, recommended: BSVersion }> = ({ resolver, options: {data : { outdated, recommended }}}) => {
|
||||
|
||||
const t = useTranslationV2();
|
||||
const config = useService(ConfigurationService);
|
||||
|
||||
const [remember, setRemember] = useState(false);
|
||||
|
||||
const handleContinue = () => {
|
||||
if (remember) {
|
||||
config.set("not-show-bs-version-outdated", true);
|
||||
}
|
||||
|
||||
resolver({ exitCode: ModalExitCode.COMPLETED });
|
||||
};
|
||||
|
||||
return (
|
||||
<form className="text-gray-800 dark:text-gray-200 max-w-sm">
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">{t.text("misc.warning")}</h1>
|
||||
<BsmImage className="mx-auto h-24" image={BeatConflict} />
|
||||
<p>{t.element("modals.bs-version-outdated.body", { outdatedVersion: <b>{outdated.BSVersion}</b>, recommendedVersion: <b>{recommended.BSVersion}</b> })}</p>
|
||||
<div className="flex items-center relative py-2 gap-1 mt-1">
|
||||
<BsmCheckbox className="h-5 relative z-[1]" checked={remember} onChange={val => setRemember(() => val)} />
|
||||
<span className="italic">{t.text("modals.misc.remember-my-choice")}</span>
|
||||
</div>
|
||||
<div className="grid grid-flow-col grid-cols-2 gap-4 mt-4 h-8">
|
||||
<BsmButton typeColor="cancel" className="rounded-md flex justify-center items-center transition-all" onClick={() => resolver({ exitCode: ModalExitCode.CANCELED })} withBar={false} text="misc.cancel" />
|
||||
<BsmButton typeColor="primary" className="rounded-md flex justify-center items-center transition-all" onClick={handleContinue} withBar={false} text="misc.continue" />
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
@@ -61,7 +61,7 @@ export function ModsGrid({ modsMap, installed, modsSelected, onModChange, moreIn
|
||||
<span className="z-10 sticky flex items-center justify-end top-0 bg-inherit border-b-2 border-main-color-1 rounded-tl-md">
|
||||
<BsmButton className="rounded-full h-6 w-6 p-[2px]" withBar={false} icon="search" onClick={handleToogleFilter} />
|
||||
</span>
|
||||
<span className="z-10 sticky top-0 flex items-center bg-inherit border-main-color-1 border-b-2 h-8 px-1 whitespace-nowrap">{filterEnabled ? <motion.input autoFocus className="bg-main-color-1 rounded-md h-6 px-2" initial={{ width: 0 }} animate={{ width: "250px" }} transition={{ ease: "easeInOut", duration: 0.15 }} onChange={e => handleInput(e.target.value)} /> : <span className="w-full text-center">{t("pages.version-viewer.mods.mods-grid.header-bar.name")}</span>}</span>
|
||||
<span className="z-10 sticky top-0 flex items-center bg-inherit border-main-color-1 border-b-2 h-8 px-1 whitespace-nowrap">{filterEnabled ? <motion.input autoFocus className="bg-light-main-color-1 dark:bg-main-color-1 rounded-md h-6 px-2" initial={{ width: 0 }} animate={{ width: "250px" }} transition={{ ease: "easeInOut", duration: 0.15 }} onChange={e => handleInput(e.target.value)} /> : <span className="w-full text-center">{t("pages.version-viewer.mods.mods-grid.header-bar.name")}</span>}</span>
|
||||
<span className="z-10 sticky flex items-center justify-center top-0 bg-inherit border-b-2 border-main-color-1 h-8 px-2 whitespace-nowrap">{t("pages.version-viewer.mods.mods-grid.header-bar.installed")}</span>
|
||||
<span className="z-10 sticky flex items-center justify-center top-0 bg-inherit border-b-2 border-main-color-1 h-8 px-2 whitespace-nowrap">{t("pages.version-viewer.mods.mods-grid.header-bar.latest")}</span>
|
||||
<span className="z-10 sticky flex items-center justify-center top-0 bg-inherit border-b-2 border-main-color-1 h-8 whitespace-nowrap">{t("pages.version-viewer.mods.mods-grid.header-bar.description")}</span>
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { I18nService } from "renderer/services/i18n.service";
|
||||
import { useService } from "./use-service.hook";
|
||||
import { useObservable } from "./use-observable.hook";
|
||||
import { useConstant } from "./use-constant.hook";
|
||||
import { createElement, Fragment } from "react";
|
||||
|
||||
/**
|
||||
* @deprecated Use useTranslationV2 instead
|
||||
*/
|
||||
export function useTranslation(): (translationKey: string, args?: Record<string, string>) => string {
|
||||
|
||||
const i18nService = useService(I18nService);
|
||||
@@ -15,3 +20,35 @@ export function useTranslation(): (translationKey: string, args?: Record<string,
|
||||
return i18nService.translate(key, args);
|
||||
};
|
||||
}
|
||||
|
||||
type TranslateFunctions = {
|
||||
text: (translationKey: string, args?: Record<string, string>) => string;
|
||||
element: (translationKey: string, args?: Record<string, JSX.Element>) => JSX.Element;
|
||||
}
|
||||
|
||||
export function useTranslationV2(): TranslateFunctions {
|
||||
|
||||
const i18nService = useService(I18nService);
|
||||
|
||||
useObservable(() => i18nService.currentLanguage$);
|
||||
|
||||
const text = useConstant(() => {
|
||||
return (key: string, args?: Record<string, string>) => {
|
||||
if (!key) {
|
||||
return key;
|
||||
}
|
||||
return i18nService.translate(key, args);
|
||||
};
|
||||
});
|
||||
|
||||
const element = useConstant(() => {
|
||||
return (key: string, args?: Record<string, JSX.Element>) => {
|
||||
if (!key) {
|
||||
return createElement(Fragment, null, key);
|
||||
}
|
||||
return i18nService.translateElement(key, args);
|
||||
};
|
||||
});
|
||||
|
||||
return { text, element };
|
||||
}
|
||||
|
||||
@@ -12,6 +12,11 @@ import { BSVersion } from "shared/bs-version.interface";
|
||||
import { useObservable } from "renderer/hooks/use-observable.hook";
|
||||
import { BsDownloaderService } from "renderer/services/bs-version-download/bs-downloader.service";
|
||||
import { logRenderError } from "renderer";
|
||||
import { ModalExitCode, ModalService } from "renderer/services/modale.service";
|
||||
import { BSVersionOutdatedModal } from "renderer/components/modal/modal-types/bs-version-outdated-modal.component";
|
||||
import { ConfigurationService } from "renderer/services/configuration.service";
|
||||
import { coerce, lt, valid } from "semver";
|
||||
import { tryit } from "shared/helpers/error.helpers";
|
||||
|
||||
export const AvailableVersionsContext = createContext<{ selectedVersion: BSVersion; setSelectedVersion: (version: BSVersion) => void }>(null);
|
||||
|
||||
@@ -19,6 +24,8 @@ export function AvailableVersionsList() {
|
||||
|
||||
const bsDownloader = useService(BsDownloaderService);
|
||||
const versionManager = useService(BSVersionManagerService);
|
||||
const modals = useService(ModalService);
|
||||
const config = useService(ConfigurationService);
|
||||
|
||||
const [selectedVersion, setSelectedVersion] = useState<BSVersion>(null);
|
||||
const contextValue = useMemo(() => ({ selectedVersion, setSelectedVersion }), [selectedVersion]);
|
||||
@@ -26,9 +33,24 @@ export function AvailableVersionsList() {
|
||||
const downloading = useObservable(() => bsDownloader.downloadingVersion$.pipe(map(v => !!v)));
|
||||
const t = useTranslation();
|
||||
|
||||
const startDownload = async () => {
|
||||
const startDownload = async (version: BSVersion) => {
|
||||
const showOutdated = !config.get("not-show-bs-version-outdated");
|
||||
const recommendedVersion = showOutdated ? versionManager.availableVersions$.value.find(v => v.recommended) : undefined;
|
||||
|
||||
return bsDownloader.downloadVersion(selectedVersion)
|
||||
const isVersionOutdated = recommendedVersion?.BSVersion ? (
|
||||
tryit(() => lt(valid(coerce(version.BSVersion)), valid(coerce(recommendedVersion.BSVersion)))).result ?? false
|
||||
) : false;
|
||||
|
||||
if (showOutdated && isVersionOutdated) {
|
||||
|
||||
const res = await modals.openModal(BSVersionOutdatedModal, {data: { outdated: version, recommended: recommendedVersion }});
|
||||
|
||||
if (res.exitCode !== ModalExitCode.COMPLETED) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return bsDownloader.downloadVersion(version)
|
||||
.catch(logRenderError)
|
||||
.finally(() => setSelectedVersion(null));
|
||||
};
|
||||
@@ -56,7 +78,7 @@ export function AvailableVersionsList() {
|
||||
|
||||
<AnimatePresence>
|
||||
{selectedVersion && !downloading && (
|
||||
<motion.div initial={{ y: "150%" }} animate={{ y: "0%" }} exit={{ y: "150%" }} className="absolute bottom-5" onClick={startDownload}>
|
||||
<motion.div initial={{ y: "150%" }} animate={{ y: "0%" }} exit={{ y: "150%" }} className="absolute bottom-5" onClick={() => startDownload(selectedVersion)}>
|
||||
<BsmButton text="misc.download" className="relative text-gray-800 dark:text-gray-100 rounded-md text-3xl font-bold italic tracking-wide px-3 pb-2 pt-1 shadow-md shadow-black" />
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
@@ -405,7 +405,7 @@ export function SettingsPage() {
|
||||
<span className="font-extrabold">{t("notifications.settings.additional-content.deep-link.select-all")}</span>
|
||||
</div>
|
||||
<div className="flex h-full gap-2">
|
||||
<Tippy content="BeatSaver" placement="top" className="font-bold bg-main-color-3" arrow={false} duration={[200, 0]}>
|
||||
<Tippy content="BeatSaver" placement="top" className="font-bold bg-main-color-3" theme="default" arrow={false} duration={[200, 0]}>
|
||||
<BsmImage
|
||||
className="h-8 cursor-pointer"
|
||||
image={beatSaverIcon}
|
||||
@@ -415,7 +415,7 @@ export function SettingsPage() {
|
||||
}}
|
||||
/>
|
||||
</Tippy>
|
||||
<Tippy content="BeastSaber" placement="top" className="font-bold bg-main-color-3" arrow={false} duration={[200, 0]}>
|
||||
<Tippy content="BeastSaber" placement="top" className="font-bold bg-main-color-3" theme="default" arrow={false} duration={[200, 0]}>
|
||||
<BsmImage
|
||||
className="h-8 rounded-md cursor-pointer"
|
||||
image={beastSaberIcon}
|
||||
@@ -425,7 +425,7 @@ export function SettingsPage() {
|
||||
}}
|
||||
/>
|
||||
</Tippy>
|
||||
<Tippy content="ScoreSaber" placement="top" className="font-bold bg-main-color-3" arrow={false} duration={[200, 0]}>
|
||||
<Tippy content="ScoreSaber" placement="top" className="font-bold bg-main-color-3" theme="default" arrow={false} duration={[200, 0]}>
|
||||
<BsmImage
|
||||
className="h-8 cursor-pointer"
|
||||
image={scoreSaberIcon}
|
||||
@@ -435,7 +435,7 @@ export function SettingsPage() {
|
||||
}}
|
||||
/>
|
||||
</Tippy>
|
||||
<Tippy content="BeatLeader" placement="top" className="font-bold bg-main-color-3" arrow={false} duration={[200, 0]}>
|
||||
<Tippy content="BeatLeader" placement="top" className="font-bold bg-main-color-3" theme="default" arrow={false} duration={[200, 0]}>
|
||||
<BsmImage
|
||||
className="h-8 cursor-pointer"
|
||||
image={beatleaderIcon}
|
||||
@@ -445,7 +445,7 @@ export function SettingsPage() {
|
||||
}}
|
||||
/>
|
||||
</Tippy>
|
||||
<Tippy content="ModelSaber" placement="top" className="font-bold bg-main-color-3" arrow={false} duration={[200, 0]}>
|
||||
<Tippy content="ModelSaber" placement="top" className="font-bold bg-main-color-3" theme="default" arrow={false} duration={[200, 0]}>
|
||||
<BsmImage
|
||||
className="h-8 cursor-pointer"
|
||||
image={modelSaberIcon}
|
||||
@@ -464,7 +464,7 @@ export function SettingsPage() {
|
||||
<span className="font-extrabold">{t("misc.maps")}</span>
|
||||
</div>
|
||||
<div className="flex h-full gap-2">
|
||||
<Tippy content="BeatSaver" placement="top" className="font-bold bg-main-color-3" arrow={false} duration={[200, 0]}>
|
||||
<Tippy content="BeatSaver" placement="top" className="font-bold bg-main-color-3" theme="default" arrow={false} duration={[200, 0]}>
|
||||
<BsmImage
|
||||
className="h-8 cursor-pointer"
|
||||
image={beatSaverIcon}
|
||||
@@ -474,7 +474,7 @@ export function SettingsPage() {
|
||||
}}
|
||||
/>
|
||||
</Tippy>
|
||||
<Tippy content="BeastSaber" placement="top" className="font-bold bg-main-color-3" arrow={false} duration={[200, 0]}>
|
||||
<Tippy content="BeastSaber" placement="top" className="font-bold bg-main-color-3" theme="default" arrow={false} duration={[200, 0]}>
|
||||
<BsmImage
|
||||
className="h-8 rounded-md cursor-pointer"
|
||||
image={beastSaberIcon}
|
||||
@@ -484,7 +484,7 @@ export function SettingsPage() {
|
||||
}}
|
||||
/>
|
||||
</Tippy>
|
||||
<Tippy content="ScoreSaber" placement="top" className="font-bold bg-main-color-3" arrow={false} duration={[200, 0]}>
|
||||
<Tippy content="ScoreSaber" placement="top" className="font-bold bg-main-color-3" theme="default" arrow={false} duration={[200, 0]}>
|
||||
<BsmImage
|
||||
className="h-8 cursor-pointer"
|
||||
image={scoreSaberIcon}
|
||||
@@ -494,7 +494,7 @@ export function SettingsPage() {
|
||||
}}
|
||||
/>
|
||||
</Tippy>
|
||||
<Tippy content="BeatLeader" placement="top" className="font-bold bg-main-color-3" arrow={false} duration={[200, 0]}>
|
||||
<Tippy content="BeatLeader" placement="top" className="font-bold bg-main-color-3" theme="default" arrow={false} duration={[200, 0]}>
|
||||
<BsmImage
|
||||
className="h-8 cursor-pointer"
|
||||
image={beatleaderIcon}
|
||||
@@ -511,8 +511,8 @@ export function SettingsPage() {
|
||||
<BsmCheckbox className="relative z-[1] h-5 w-5" onChange={() => tooglePlaylistsDeepLinks()} checked={playlistsDeepLinkEnabled} />
|
||||
<span className="font-extrabold">{t("misc.playlists")}</span>
|
||||
</div>
|
||||
<div className="flex h-full">
|
||||
<Tippy content="BeatSaver" placement="top" className="font-bold bg-main-color-3" arrow={false} duration={[200, 0]}>
|
||||
<div className="flex h-full gap-2">
|
||||
<Tippy content="BeatSaver" placement="top" className="font-bold bg-main-color-3" theme="default" arrow={false} duration={[200, 0]}>
|
||||
<BsmImage
|
||||
className="h-8 cursor-pointer"
|
||||
image={beatSaverIcon}
|
||||
@@ -522,6 +522,16 @@ export function SettingsPage() {
|
||||
}}
|
||||
/>
|
||||
</Tippy>
|
||||
<Tippy content="BeatLeader" placement="top" className="font-bold bg-main-color-3" theme="default" arrow={false} duration={[200, 0]}>
|
||||
<BsmImage
|
||||
className="h-8 cursor-pointer"
|
||||
image={beatleaderIcon}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
linkOpener.open("https://www.beatleader.xyz/");
|
||||
}}
|
||||
/>
|
||||
</Tippy>
|
||||
</div>
|
||||
</li>
|
||||
<li className="bg-light-main-color-1 dark:bg-main-color-1 rounded-md group-one flex justify-between items-center basis-0 py-2 px-3 cursor-pointer" onClick={() => toogleModelsDeepLinks()}>
|
||||
@@ -530,7 +540,7 @@ export function SettingsPage() {
|
||||
<span className="font-extrabold">{t("misc.models")}</span>
|
||||
</div>
|
||||
<div className="flex h-full">
|
||||
<Tippy content="ModelSaber" placement="top" className="font-bold bg-main-color-3" arrow={false} duration={[200, 0]}>
|
||||
<Tippy content="ModelSaber" placement="top" className="font-bold bg-main-color-3" theme="default" arrow={false} duration={[200, 0]}>
|
||||
<BsmImage
|
||||
className="h-8 cursor-pointer"
|
||||
image={modelSaberIcon}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Observable } from "rxjs";
|
||||
import { ConfigurationService } from "./configuration.service";
|
||||
import { getProperty } from "dot-prop";
|
||||
import { i18n } from "dateformat";
|
||||
import { createElement, Fragment } from "react";
|
||||
|
||||
export class I18nService {
|
||||
private static instance: I18nService;
|
||||
@@ -90,4 +91,30 @@ export class I18nService {
|
||||
|
||||
return translated;
|
||||
}
|
||||
|
||||
public translateElement(translationKey: string, args?: Record<string, JSX.Element>): JSX.Element {
|
||||
let translated = this.cache.get(translationKey);
|
||||
if (!translated) {
|
||||
translated = getProperty(this.dictionary, translationKey) ?? translationKey;
|
||||
this.cache.set(translationKey, translated);
|
||||
}
|
||||
|
||||
if (!args) {
|
||||
return createElement(Fragment, null, translated);
|
||||
}
|
||||
|
||||
// Sort keys by length in descending order to avoid partial matches within keys
|
||||
const sortedKeys = Object.keys(args).sort((a, b) => b.length - a.length);
|
||||
|
||||
const pattern = sortedKeys.map(key => `\\{${key}\\}`).join('|');
|
||||
const regex = new RegExp(`(${pattern})`, 'g');
|
||||
const segments = translated.split(regex);
|
||||
|
||||
const children = segments.map((segment) => {
|
||||
const key = segment.slice(1, -1); // Remove surrounding {}
|
||||
return args[key] || segment;
|
||||
});
|
||||
|
||||
return createElement(Fragment, null, ...children);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user