mirror of
https://github.com/Zagrios/bs-manager.git
synced 2026-07-03 14:08:25 +02:00
Merge branch 'master' into chore/add-beatsaber-desktop-version-info
This commit is contained in:
@@ -142,5 +142,11 @@
|
||||
{
|
||||
"username": "Arts Rimuro Suraimu",
|
||||
"type": "gold"
|
||||
},
|
||||
{
|
||||
"username": "Ricardo Boss"
|
||||
},
|
||||
{
|
||||
"username": "Jesper Norsted"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -144,6 +144,7 @@
|
||||
"mods-grid": {
|
||||
"header-bar": {
|
||||
"name": "Name",
|
||||
"size": "Größe",
|
||||
"installed": "Installiert",
|
||||
"latest": "Neueste",
|
||||
"description": "Beschreibung",
|
||||
@@ -152,6 +153,10 @@
|
||||
"uninstall-all": "Alle deinstallieren",
|
||||
"unselect-all": "Alle abwählen"
|
||||
}
|
||||
},
|
||||
"mod-item": {
|
||||
"this-is-a-large-mod": "Das ist ein großer Mod!",
|
||||
"this-is-a-very-large-mod": "Das ist ein sehr großer Mod!"
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -144,6 +144,7 @@
|
||||
"mods-grid": {
|
||||
"header-bar": {
|
||||
"name": "Name",
|
||||
"size": "Size",
|
||||
"installed": "Installed",
|
||||
"latest": "Latest",
|
||||
"description": "Description",
|
||||
@@ -152,6 +153,10 @@
|
||||
"uninstall-all": "Uninstall all",
|
||||
"unselect-all": "Unselect all"
|
||||
}
|
||||
},
|
||||
"mod-item":{
|
||||
"this-is-a-large-mod": "This is a large mod!",
|
||||
"this-is-a-very-large-mod": "This is a very large mod!"
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -144,6 +144,7 @@
|
||||
"mods-grid": {
|
||||
"header-bar": {
|
||||
"name": "Nombre",
|
||||
"size": "Tamaño",
|
||||
"installed": "Instalado",
|
||||
"latest": "Último",
|
||||
"description": "Descripción",
|
||||
@@ -152,6 +153,10 @@
|
||||
"uninstall-all": "Desinstalar todos",
|
||||
"unselect-all": "Deseleccionar todo"
|
||||
}
|
||||
},
|
||||
"mod-item": {
|
||||
"this-is-a-large-mod": "¡Este es un mod grande!",
|
||||
"this-is-a-very-large-mod": "¡Este es un mod muy grande!"
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -144,6 +144,7 @@
|
||||
"mods-grid": {
|
||||
"header-bar": {
|
||||
"name": "Nom",
|
||||
"size": "Taille",
|
||||
"installed": "Installé",
|
||||
"latest": "Récent",
|
||||
"description": "Description",
|
||||
@@ -152,6 +153,10 @@
|
||||
"uninstall-all": "Tout désinstaller",
|
||||
"unselect-all": "Tout désélectionner"
|
||||
}
|
||||
},
|
||||
"mod-item": {
|
||||
"this-is-a-large-mod": "C'est un mod volumineux !",
|
||||
"this-is-a-very-large-mod": "C'est un mod très volumineux !"
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -144,6 +144,7 @@
|
||||
"mods-grid": {
|
||||
"header-bar": {
|
||||
"name": "Nome",
|
||||
"size": "Dimensione",
|
||||
"installed": "Installata",
|
||||
"latest": "Ultima Versione",
|
||||
"description": "Descrizione",
|
||||
@@ -152,6 +153,10 @@
|
||||
"uninstall-all": "Disinstalla tutto",
|
||||
"unselect-all": "Deseleziona tutto"
|
||||
}
|
||||
},
|
||||
"mod-item": {
|
||||
"this-is-a-large-mod": "Questo è un mod grande!",
|
||||
"this-is-a-very-large-mod": "Questo è un mod molto grande!"
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -144,6 +144,7 @@
|
||||
"mods-grid": {
|
||||
"header-bar": {
|
||||
"name": "名前",
|
||||
"size": "サイズ",
|
||||
"installed": "インストール済み",
|
||||
"latest": "最新",
|
||||
"description": "說明",
|
||||
@@ -152,6 +153,10 @@
|
||||
"uninstall-all": "全てアンインストールする",
|
||||
"unselect-all": "すべて選択解除"
|
||||
}
|
||||
},
|
||||
"mod-item": {
|
||||
"this-is-a-large-mod": "これは大きなModです!",
|
||||
"this-is-a-very-large-mod": "これは非常に大きなModです!"
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -144,6 +144,7 @@
|
||||
"mods-grid": {
|
||||
"header-bar": {
|
||||
"name": "이름",
|
||||
"size": "크기",
|
||||
"installed": "설치됨",
|
||||
"latest": "최신",
|
||||
"description": "설명",
|
||||
@@ -152,6 +153,10 @@
|
||||
"uninstall-all": "모두 제거",
|
||||
"unselect-all": "모두 선택 해제"
|
||||
}
|
||||
},
|
||||
"mod-item": {
|
||||
"this-is-a-large-mod": "이것은 큰 모드입니다!",
|
||||
"this-is-a-very-large-mod": "이것은 매우 큰 모드입니다!"
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -144,6 +144,7 @@
|
||||
"mods-grid": {
|
||||
"header-bar": {
|
||||
"name": "Nome",
|
||||
"size": "tamanho",
|
||||
"installed": "Instalado",
|
||||
"latest": "Mais recente",
|
||||
"description": "Descrição",
|
||||
@@ -152,6 +153,10 @@
|
||||
"uninstall-all": "Desinstalar todos",
|
||||
"unselect-all": "Desmarcar todos"
|
||||
}
|
||||
},
|
||||
"mod-item": {
|
||||
"this-is-a-large-mod": "Este é um mod grande!",
|
||||
"this-is-a-very-large-mod": "Este é um mod muito grande!"
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -144,6 +144,7 @@
|
||||
"mods-grid": {
|
||||
"header-bar": {
|
||||
"name": "Имя",
|
||||
"size": "Размер",
|
||||
"installed": "Установлено",
|
||||
"latest": "Latest",
|
||||
"description": "Описание",
|
||||
@@ -152,6 +153,10 @@
|
||||
"uninstall-all": "Удалить всё",
|
||||
"unselect-all": "Снять все выделения"
|
||||
}
|
||||
},
|
||||
"mod-item": {
|
||||
"this-is-a-large-mod": "Это большой мод!",
|
||||
"this-is-a-very-large-mod": "Это очень большой мод!"
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -144,6 +144,7 @@
|
||||
"mods-grid": {
|
||||
"header-bar": {
|
||||
"name": "Pangalan",
|
||||
"size": "laki",
|
||||
"installed": "Naka-install",
|
||||
"latest": "Pinakabago",
|
||||
"description": "Deskripsyon",
|
||||
@@ -152,6 +153,10 @@
|
||||
"uninstall-all": "I-uninstall ang lahat",
|
||||
"unselect-all": "I-unselect and lahat"
|
||||
}
|
||||
},
|
||||
"mod-item": {
|
||||
"this-is-a-large-mod": "Ito ay isang malaking mod!",
|
||||
"this-is-a-very-large-mod": "Ito ay isang napakalaking mod!"
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -144,6 +144,7 @@
|
||||
"mods-grid": {
|
||||
"header-bar": {
|
||||
"name": "Назва",
|
||||
"size": "розмір",
|
||||
"installed": "Встановлено",
|
||||
"latest": "Остання",
|
||||
"description": "Опис",
|
||||
@@ -152,6 +153,10 @@
|
||||
"uninstall-all": "Видалити всі",
|
||||
"unselect-all": "Зняти виділення з всіх"
|
||||
}
|
||||
},
|
||||
"mod-item": {
|
||||
"this-is-a-large-mod": "Це великий мод!",
|
||||
"this-is-a-very-large-mod": "Це дуже великий мод!"
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -144,6 +144,7 @@
|
||||
"mods-grid": {
|
||||
"header-bar": {
|
||||
"name": "名稱",
|
||||
"size": "大小",
|
||||
"installed": "已安裝",
|
||||
"latest": "最新",
|
||||
"description": "描述",
|
||||
@@ -152,6 +153,10 @@
|
||||
"uninstall-all": "全部移除",
|
||||
"unselect-all": "取消全選"
|
||||
}
|
||||
},
|
||||
"mod-item": {
|
||||
"this-is-a-large-mod": "這是一個大型Mod!",
|
||||
"this-is-a-very-large-mod": "這是一個非常大型的Mod!"
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -144,6 +144,7 @@
|
||||
"mods-grid": {
|
||||
"header-bar": {
|
||||
"name": "名称",
|
||||
"size": "大小",
|
||||
"installed": "已安装",
|
||||
"latest": "最新",
|
||||
"description": "描述",
|
||||
@@ -152,6 +153,10 @@
|
||||
"uninstall-all": "全部卸载",
|
||||
"unselect-all": "取消全选"
|
||||
}
|
||||
},
|
||||
"mod-item": {
|
||||
"this-is-a-large-mod": "这是一个大型Mod!",
|
||||
"this-is-a-very-large-mod": "这是一个非常大型的Mod!"
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
import log from "electron-log";
|
||||
import { isPromise } from "../../shared/helpers/promise.helpers";
|
||||
import { tryit } from "../../shared/helpers/error.helpers";
|
||||
|
||||
type LogOptions = {
|
||||
logInput?: boolean;
|
||||
logOutput?: boolean;
|
||||
logArgs?: boolean;
|
||||
};
|
||||
|
||||
const stringifyArgs = (args: unknown[]) => {
|
||||
return args?.map(a => JSON.stringify(a)).join(', ');
|
||||
};
|
||||
|
||||
const logInfo = (message: string, propertyKey: string, args: unknown[], logArgs: boolean, result: unknown) => {
|
||||
log.info(`[${message}] ${propertyKey}(${logArgs ? stringifyArgs(args) : ''})`, result);
|
||||
};
|
||||
|
||||
const logError = (propertyKey: string, args: unknown[], logArgs: boolean, error: unknown) => {
|
||||
log.error(`[ERROR] ${propertyKey}(${logArgs ? stringifyArgs(args) : ''})`, error);
|
||||
throw error;
|
||||
};
|
||||
|
||||
export function Log(options?: LogOptions){
|
||||
|
||||
const logInput = options?.logInput ?? false;
|
||||
const logOutput = options?.logOutput ?? true;
|
||||
const logArgs = options?.logArgs ?? true;
|
||||
|
||||
return (target: unknown, propertyKey: string, descriptor: PropertyDescriptor) => {
|
||||
const originalMethod = descriptor.value;
|
||||
|
||||
descriptor.value = function(...args: unknown[]) {
|
||||
|
||||
if (logInput) {
|
||||
logInfo('INPUT', propertyKey, args, logArgs, null);
|
||||
}
|
||||
|
||||
const outcome = tryit(() => originalMethod.apply(this, args));
|
||||
|
||||
if (isPromise(outcome)) {
|
||||
return outcome.then(({ result, error }) => {
|
||||
|
||||
if (error) {
|
||||
logError(propertyKey, args, logArgs, error);
|
||||
}
|
||||
if (logOutput) {
|
||||
logInfo('OUTPUT', propertyKey, args, logArgs, result);
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
const { result, error } = outcome;
|
||||
|
||||
if (error) {
|
||||
logError(propertyKey, args, logArgs, error);
|
||||
}
|
||||
|
||||
if (logOutput) {
|
||||
logInfo('OUTPUT', propertyKey, args, logArgs, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -3,7 +3,6 @@ import { access, mkdir, readdir, lstat, readlink } from "fs/promises";
|
||||
import path from "path";
|
||||
import { Observable, concatMap, from } from "rxjs";
|
||||
import log from "electron-log";
|
||||
import { BsmException } from "shared/models/bsm-exception.model";
|
||||
import crypto from "crypto";
|
||||
import { execSync } from "child_process";
|
||||
import { tryit } from "../../shared/helpers/error.helpers";
|
||||
@@ -181,7 +180,7 @@ export function isSubdirectory(parent: string, child: string): boolean {
|
||||
|
||||
export async function copyDirectoryWithJunctions(src: string, dest: string, options?: CopyOptions): Promise<void> {
|
||||
if (isSubdirectory(src, dest)) {
|
||||
throw { message: `Cannot copy directory '${src}' into itself '${dest}'.`, code: "COPY_TO_SUBPATH" } as BsmException;
|
||||
throw new CustomError(`Cannot copy directory '${src}' into itself '${dest}'.`, "COPY_TO_SUBPATH");
|
||||
}
|
||||
|
||||
await ensureDir(dest);
|
||||
|
||||
@@ -19,7 +19,6 @@ export class SongCacheService {
|
||||
private readonly mapsInfoCache: JsonCache<CachedMapInfoWithHash>;
|
||||
|
||||
private constructor(){
|
||||
console.log(this.MAPS_INFO_CACHE_PATH);
|
||||
this.mapsInfoCache = new JsonCache(this.MAPS_INFO_CACHE_PATH);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ import useDoubleClick from "use-double-click";
|
||||
import { useOnUpdate } from "renderer/hooks/use-on-update.hook";
|
||||
import striptags from "striptags";
|
||||
import { safeGt } from "shared/helpers/semver.helpers";
|
||||
import Tippy from "@tippyjs/react";
|
||||
import { useTranslationV2 } from "renderer/hooks/use-translation.hook";
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
@@ -21,6 +23,68 @@ type Props = {
|
||||
onUninstall?: () => void;
|
||||
};
|
||||
|
||||
type FileSizeProps = {
|
||||
fileSize?: number;
|
||||
wantInfoStyle: CSSProperties;
|
||||
};
|
||||
|
||||
function FileSizeText({ fileSize, wantInfoStyle }: Readonly<FileSizeProps>) {
|
||||
|
||||
const { text: t } = useTranslationV2();
|
||||
|
||||
const verifyFileSize = fileSize !== undefined;
|
||||
|
||||
const isMediumMod = verifyFileSize && fileSize > 1024 * 1024 * 50; // 50MB
|
||||
|
||||
const isLargeMod = verifyFileSize && fileSize > 1024 * 1024 * 100; // 100MB
|
||||
|
||||
const getFormattedSize = () : string => {
|
||||
if (!verifyFileSize || fileSize === 0)
|
||||
return `-`;
|
||||
if (fileSize < 1024 * 1024)
|
||||
return `${(fileSize/1024).toFixed(2)}KB`;
|
||||
return `${(fileSize/1024/1024).toFixed(2)}MB`;
|
||||
};
|
||||
|
||||
const getWarningText = () : string => {
|
||||
if (isLargeMod)
|
||||
return `pages.version-viewer.mods.mods-grid.mod-item.this-is-a-very-large-mod`;
|
||||
if (isMediumMod)
|
||||
return `pages.version-viewer.mods.mods-grid.mod-item.this-is-a-large-mod`;
|
||||
return "";
|
||||
};
|
||||
|
||||
const getTheme = () : string => {
|
||||
if (isLargeMod)
|
||||
return 'red';
|
||||
if (isMediumMod)
|
||||
return 'yellow';
|
||||
return "";
|
||||
};
|
||||
|
||||
const getTextColor = () : string => {
|
||||
if (isLargeMod)
|
||||
return "text-red-400";
|
||||
if (isMediumMod)
|
||||
return "text-yellow-400";
|
||||
return "";
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
<Tippy
|
||||
content={t(getWarningText())}
|
||||
placement="left"
|
||||
theme={getTheme()}
|
||||
disabled={!(isLargeMod || isMediumMod)}
|
||||
delay={[50, 0]} >
|
||||
<span className={`min-w-0 text-center bg-inherit py-2 px-1 text-sm border-t-2 border-b-2 group-hover:brightness-90 ${getTextColor()}`} style={wantInfoStyle}>
|
||||
{getFormattedSize()}
|
||||
</span>
|
||||
</Tippy>
|
||||
);
|
||||
}
|
||||
|
||||
export function ModItem({ className, mod, installedVersion, isDependency, isSelected, onChange, wantInfo, onWantInfo, disabled, onUninstall }: Props) {
|
||||
|
||||
const themeColor = useThemeColor("second-color");
|
||||
@@ -65,6 +129,7 @@ export function ModItem({ className, mod, installedVersion, isDependency, isSele
|
||||
<span className="min-w-0 text-center bg-inherit py-2 px-1 text-sm border-t-2 border-b-2 group-hover:brightness-90" style={wantInfoStyle}>
|
||||
{mod.version.modVersion}
|
||||
</span>
|
||||
<FileSizeText fileSize={mod.version.fileSize} wantInfoStyle={wantInfoStyle} />
|
||||
<span title={striptags(mod.mod?.description ?? "", { tagReplacementText: " " })} className="px-3 bg-inherit whitespace-nowrap text-ellipsis overflow-hidden py-2 text-sm border-t-2 border-b-2 group-hover:brightness-90" style={wantInfoStyle}>
|
||||
{striptags(mod.mod?.summary ?? "", { tagReplacementText: " " })}
|
||||
</span>
|
||||
|
||||
@@ -58,14 +58,15 @@ export function ModsGrid({ modsMap, installed, modsSelected, onModChange, moreIn
|
||||
|
||||
return (
|
||||
modsMap && (
|
||||
<div className="grid gap-y-1 grid-cols-[40px_min-content_min-content_min-content_1fr_min-content] bg-light-main-color-2 dark:bg-main-color-2 text-main-color-1 dark:text-light-main-color-1">
|
||||
<div className="grid gap-y-1 grid-cols-[40px_min-content_min-content_min-content_min-content_1fr_min-content] bg-light-main-color-2 dark:bg-main-color-2 text-main-color-1 dark:text-light-main-color-1">
|
||||
<span className="absolute z-10 top-0 w-full h-8 bg-inherit rounded-tl-md" />
|
||||
<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-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 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: "200px" }} 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 px-2 whitespace-nowrap">{t("pages.version-viewer.mods.mods-grid.header-bar.size")}</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>
|
||||
<span className="z-10 sticky top-0 bg-inherit border-b-2 border-main-color-1 h-8 flex justify-start items-center py-1 pl-[3px] min-w-[50px]">
|
||||
<BsmDropdownButton className="h-full aspect-square relative rounded-full bg-light-main-color-1 dark:bg-main-color-3" withBar={false} icon="three-dots" buttonClassName="!rounded-full !p-[2px] !bg-light-main-color-2 dark:!bg-main-color-2 hover:!bg-light-main-color-1 dark:hover:!bg-main-color-3" menuTranslationY="5px" items={[
|
||||
|
||||
@@ -114,6 +114,22 @@
|
||||
@apply border-r-neutral-900;
|
||||
}
|
||||
|
||||
.tippy-box[data-theme~='red'] {
|
||||
@apply bg-neutral-900;
|
||||
@apply text-red-400;
|
||||
}
|
||||
.tippy-box[data-theme~='red'][data-placement^='left'] > .tippy-arrow::before {
|
||||
@apply border-l-neutral-900;
|
||||
}
|
||||
|
||||
.tippy-box[data-theme~='yellow'] {
|
||||
@apply bg-neutral-900;
|
||||
@apply text-yellow-400;
|
||||
}
|
||||
.tippy-box[data-theme~='yellow'][data-placement^='left'] > .tippy-arrow::before {
|
||||
@apply border-l-neutral-900;
|
||||
}
|
||||
|
||||
.bg-theme-1 { @apply bg-light-main-color-1 dark:bg-main-color-1; }
|
||||
.bg-theme-2 { @apply bg-light-main-color-2 dark:bg-main-color-2; }
|
||||
.bg-theme-3 { @apply bg-light-main-color-3 dark:bg-main-color-3; }
|
||||
|
||||
@@ -42,6 +42,7 @@ export interface BbmModVersion {
|
||||
downloadCount: number;
|
||||
lastApprovedById?: number;
|
||||
lastUpdatedById?: number;
|
||||
fileSize?: number;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user