mirror of
https://github.com/Zagrios/bs-manager.git
synced 2026-07-03 14:08:25 +02:00
Merge pull request #691 from silentrald/feat/blacklist-shared-folders
[feat] added blacklist for shared folders
This commit is contained in:
@@ -606,6 +606,10 @@
|
||||
"ENOSPC": "Die Festplatte ist voll, machen Sie Platz und versuchen Sie es erneut.",
|
||||
"UNKNOWN_ERROR": "Ein unbekannter Fehler ist beim Verknüpfen des Ordners aufgetreten."
|
||||
}
|
||||
},
|
||||
"adding-error": {
|
||||
"title": "Das Hinzufügen des freigegebenen Ordners ist fehlgeschlagen",
|
||||
"msg": "Sie können \"{folder}\" nicht zu freigegebenen Ordnern hinzufügen."
|
||||
}
|
||||
},
|
||||
"create-launch-shortcut": {
|
||||
@@ -805,9 +809,14 @@
|
||||
"add-folder": "Ordner hinzufügen",
|
||||
"link-folder": "Ordner verknüpfen",
|
||||
"unlink-folder": "Ordner-Verknüpfung aufheben",
|
||||
"link-all": "Alle verlinken"
|
||||
"link-all": "Alle verlinken",
|
||||
"remove-from-the-list": "Aus der Liste entfernen"
|
||||
}
|
||||
},
|
||||
"adding-shared-folder": {
|
||||
"title": "Freigegebenen Ordner hinzufügen",
|
||||
"description": "Beim Verknüpfen des Ordners \"{folder}\" können Probleme auftreten. Möchten Sie ihn wirklich hinzufügen?"
|
||||
},
|
||||
"create-launch-shortcut": {
|
||||
"title": "Verknüpfung erstellen",
|
||||
"desc": "Das Erstellen einer Verknüpfung ermöglicht es dir, Beat Saber mit den ausgewählten Optionen zu starten, ohne durch BSManager zu gehen.",
|
||||
|
||||
@@ -606,6 +606,10 @@
|
||||
"ENOSPC": "The disk is full, make some space and try again.",
|
||||
"UNKNOWN_ERROR": "An unknown error has occurred while linking the folder."
|
||||
}
|
||||
},
|
||||
"adding-error": {
|
||||
"title": "Adding shared folder failed",
|
||||
"msg": "You can't add \"{folder}\" to shared folders."
|
||||
}
|
||||
},
|
||||
"create-launch-shortcut": {
|
||||
@@ -800,9 +804,14 @@
|
||||
"add-folder": "Add Folder",
|
||||
"link-folder": "Link Folder",
|
||||
"unlink-folder": "Unlink Folder",
|
||||
"link-all": "Link all"
|
||||
"link-all": "Link all",
|
||||
"remove-from-the-list": "Remove from the list"
|
||||
}
|
||||
},
|
||||
"adding-shared-folder": {
|
||||
"title": "Adding Shared Folder",
|
||||
"description": "You might experience issues when linking the \"{folder}\" folder. Are you sure you want to add it?"
|
||||
},
|
||||
"create-launch-shortcut": {
|
||||
"title": "Create a shortcut",
|
||||
"desc": "Creating a shortcut will allow you to start Beat Saber with the chosen options without going through BSManager.",
|
||||
|
||||
@@ -606,6 +606,10 @@
|
||||
"ENOSPC": "El disco está lleno, libera espacio e inténtalo de nuevo.",
|
||||
"UNKNOWN_ERROR": "Se ha producido un error desconocido al enlazar la carpeta."
|
||||
}
|
||||
},
|
||||
"adding-error": {
|
||||
"title": "Error al agregar una carpeta compartida",
|
||||
"msg": "No puedes agregar \"{folder}\" a las carpetas compartidas."
|
||||
}
|
||||
},
|
||||
"create-launch-shortcut": {
|
||||
@@ -805,9 +809,14 @@
|
||||
"add-folder": "Añadir Carpeta",
|
||||
"link-folder": "Enlazar Carpeta",
|
||||
"unlink-folder": "Desenlazar Carpeta",
|
||||
"link-all": "Enlazar todo"
|
||||
"link-all": "Enlazar todo",
|
||||
"remove-from-the-list": "Eliminar de la lista"
|
||||
}
|
||||
},
|
||||
"adding-shared-folder": {
|
||||
"title": "Agregar una carpeta compartida",
|
||||
"description": "Es posible que experimentes problemas al vincular la carpeta \"{folder}\". ¿Estás seguro de que deseas agregarla?"
|
||||
},
|
||||
"create-launch-shortcut": {
|
||||
"title": "Crear un atajo",
|
||||
"desc": "Crear un atajo te permitirá iniciar Beat Saber con las opciones seleccionadas sin pasar por BSManager.",
|
||||
|
||||
@@ -606,6 +606,10 @@
|
||||
"ENOSPC": "Le disque est plein, faites de la place et réessayez.",
|
||||
"UNKNOWN_ERROR": "Une erreur inconnue est survenue lors de la liaison du dossier."
|
||||
}
|
||||
},
|
||||
"adding-error": {
|
||||
"title": "L'ajout du dossier partagé a échoué",
|
||||
"msg": "Vous ne pouvez pas ajouter \"{folder}\" aux dossiers partagés."
|
||||
}
|
||||
|
||||
},
|
||||
@@ -806,9 +810,14 @@
|
||||
"add-folder": "Ajouter un dossier",
|
||||
"link-folder": "Lier le dossier",
|
||||
"unlink-folder": "Délier le dossier",
|
||||
"link-all": "Tout lier"
|
||||
"link-all": "Tout lier",
|
||||
"remove-from-the-list": "Retirer de la liste"
|
||||
}
|
||||
},
|
||||
"adding-shared-folder": {
|
||||
"title": "Ajout d'un dossier partagé",
|
||||
"description": "Vous pourriez rencontrer des problèmes lors de la liaison du dossier \"{folder}\". Êtes-vous sûr de vouloir l'ajouter ?"
|
||||
},
|
||||
"create-launch-shortcut": {
|
||||
"title": "Créer un raccourci",
|
||||
"desc": "Créer un raccourci te permettra de démarrer Beat Saber avec les options choisies sans passer par BSManager.",
|
||||
|
||||
@@ -606,6 +606,10 @@
|
||||
"ENOSPC": "ディスクがいっぱいです。空き容量を作ってもう一度試してください。",
|
||||
"UNKNOWN_ERROR": "フォルダをリンク中に不明なエラーが発生しました。"
|
||||
}
|
||||
},
|
||||
"adding-error": {
|
||||
"title": "共有フォルダの追加に失敗しました",
|
||||
"msg": "「{folder}」を共有フォルダに追加することはできません。"
|
||||
}
|
||||
},
|
||||
"create-launch-shortcut": {
|
||||
@@ -805,9 +809,14 @@
|
||||
"add-folder": "フォルダーを追加",
|
||||
"link-folder": "フォルダーをリンク",
|
||||
"unlink-folder": "フォルダーのリンクを解除",
|
||||
"link-all": "すべてリンク"
|
||||
"link-all": "すべてリンク",
|
||||
"remove-from-the-list": "リストから削除"
|
||||
}
|
||||
},
|
||||
"adding-shared-folder": {
|
||||
"title": "共有フォルダの追加",
|
||||
"description": "「{folder}」フォルダをリンクするときに問題が発生する可能性があります。追加してもよろしいですか?"
|
||||
},
|
||||
"create-launch-shortcut": {
|
||||
"title": "ショートカットを作成",
|
||||
"desc": "ショートカットを作成すると、BSManagerを起動せずに、直接Beat Saberを起動できます。",
|
||||
|
||||
@@ -606,6 +606,10 @@
|
||||
"ENOSPC": "디스크가 가득 찼습니다. 공간을 확보하고 다시 시도하세요.",
|
||||
"UNKNOWN_ERROR": "폴더를 연결하는 중 알 수 없는 오류가 발생했습니다."
|
||||
}
|
||||
},
|
||||
"adding-error": {
|
||||
"title": "공유 폴더 추가에 실패했습니다.",
|
||||
"msg": "공유 폴더에 「{folder}」을 추가할 수 없습니다."
|
||||
}
|
||||
},
|
||||
"create-launch-shortcut": {
|
||||
@@ -805,9 +809,14 @@
|
||||
"add-folder": "폴더 추가",
|
||||
"link-folder": "폴더 연결",
|
||||
"unlink-folder": "폴더 연결 해제",
|
||||
"link-all": "모두 연결"
|
||||
"link-all": "모두 연결",
|
||||
"remove-from-the-list": "목록에서 제거"
|
||||
}
|
||||
},
|
||||
"adding-shared-folder": {
|
||||
"title": "공유 폴더 추가",
|
||||
"description": "「{folder}」폴더를 연결할 때 문제가 발생할 수 있습니다. 정말 추가하시겠습니까?"
|
||||
},
|
||||
"create-launcher-shortcut": {
|
||||
"title": "런처 바로가기 만들기",
|
||||
"description": "바탕화면에 실행 파일의 바로가기를 만들어 BSManager를 손쉽게 실행하세요.",
|
||||
|
||||
@@ -606,6 +606,10 @@
|
||||
"ENOSPC": "Диск заполнен, освободите место и попробуйте снова.",
|
||||
"UNKNOWN_ERROR": "Произошла неизвестная ошибка при связывании папки."
|
||||
}
|
||||
},
|
||||
"adding-error": {
|
||||
"title": "Не удалось добавить общую папку.",
|
||||
"msg": "Вы не можете добавлять \"{folder}\" в общие папки."
|
||||
}
|
||||
},
|
||||
"create-launch-shortcut": {
|
||||
@@ -805,9 +809,14 @@
|
||||
"add-folder": "Добавить папку",
|
||||
"link-folder": "Связать папку",
|
||||
"unlink-folder": "Отвязать папку",
|
||||
"link-all": "Связать всё"
|
||||
"link-all": "Связать всё",
|
||||
"remove-from-the-list": "Удалить из списка"
|
||||
}
|
||||
},
|
||||
"adding-shared-folder": {
|
||||
"title": "Добавление общей папки",
|
||||
"description": "У вас могут возникнуть проблемы при связывании папки \"{folder}\". Вы уверены, что хотите ее добавить?"
|
||||
},
|
||||
"create-launch-shortcut": {
|
||||
"title": "Создать ярлык",
|
||||
"desc": "Ярлык позволит вам запустить Beat Saber без помощи BSManager.",
|
||||
|
||||
@@ -606,6 +606,10 @@
|
||||
"ENOSPC": "磁碟已滿,請騰出空間後再試。",
|
||||
"UNKNOWN_ERROR": "連結文件夾時發生未知錯誤。"
|
||||
}
|
||||
},
|
||||
"adding-error": {
|
||||
"title": "添加共享文件夹失败",
|
||||
"msg": "您无法将\"{folder}\"添加到共享文件夹。"
|
||||
}
|
||||
},
|
||||
"create-launch-shortcut": {
|
||||
@@ -805,9 +809,14 @@
|
||||
"add-folder": "新增文件夾",
|
||||
"link-folder": "關聯文件夾",
|
||||
"unlink-folder": "取消關聯文件夾",
|
||||
"link-all": "關聯所有"
|
||||
"link-all": "關聯所有",
|
||||
"remove-from-the-list": "從列表中移除"
|
||||
}
|
||||
},
|
||||
"adding-shared-folder": {
|
||||
"title": "添加共享文件夹",
|
||||
"description": "链接\"{folder}\"文件夹时可能会遇到问题。您确定要添加它吗?"
|
||||
},
|
||||
"create-launch-shortcut": {
|
||||
"title": "創建捷徑",
|
||||
"desc": "創建捷徑使得你可以在不通過 BSManager 的情況下以特定選項啟動 BeatSaber",
|
||||
|
||||
@@ -606,6 +606,10 @@
|
||||
"ENOSPC": "磁盘已满,请腾出空间后再试。",
|
||||
"UNKNOWN_ERROR": "链接文件夹时发生未知错误。"
|
||||
}
|
||||
},
|
||||
"adding-error": {
|
||||
"title": "添加共享文件夹失败",
|
||||
"msg": "您无法将\"{folder}\"添加到共享文件夹。"
|
||||
}
|
||||
},
|
||||
"create-launch-shortcut": {
|
||||
@@ -805,9 +809,14 @@
|
||||
"add-folder": "添加文件夹",
|
||||
"link-folder": "关联文件夹",
|
||||
"unlink-folder": "取消关联文件夹",
|
||||
"link-all": "关联所有"
|
||||
"link-all": "关联所有",
|
||||
"remove-from-the-list": "从列表中移除"
|
||||
}
|
||||
},
|
||||
"adding-shared-folder": {
|
||||
"title": "添加共享文件夹",
|
||||
"description": "链接\"{folder}\"文件夹时可能会遇到问题。您确定要添加它吗?"
|
||||
},
|
||||
"create-launch-shortcut": {
|
||||
"title": "创建快捷方式",
|
||||
"desc": "创建快捷方式使得你可以在不通过 BSManager 的情况下以特定选项启动 BeatSaber",
|
||||
|
||||
@@ -21,19 +21,24 @@ export class FolderLinkerService {
|
||||
private readonly installLocationService = InstallationLocationService.getInstance();
|
||||
private readonly staticConfig: StaticConfigurationService;
|
||||
|
||||
private linkingType: "junction" | "symlink" = "junction";
|
||||
// Only Windows support "junction", this is disregarded in other os'es
|
||||
private linkingType: "junction" | "symlink" =
|
||||
process.platform === "win32" ? "junction" : "symlink";
|
||||
|
||||
private constructor() {
|
||||
this.installLocationService = InstallationLocationService.getInstance();
|
||||
this.staticConfig = StaticConfigurationService.getInstance();
|
||||
|
||||
this.linkingType = this.staticConfig.get("use-symlinks") === true ? "symlink" : "junction";
|
||||
log.info(`Linking type is set to ${this.linkingType}`);
|
||||
if (process.platform === "win32") {
|
||||
// Only Windows support "junction", this is disregarded in other os'es
|
||||
this.linkingType = this.staticConfig.get("use-symlinks") === true ? "symlink" : "junction";
|
||||
log.info(`Linking type is set to ${this.linkingType}`);
|
||||
|
||||
this.staticConfig.$watch("use-symlinks").subscribe((useSymlink) => {
|
||||
this.linkingType = useSymlink === true ? "symlink" : "junction";
|
||||
log.info(`Linking type set to ${this.linkingType}`);
|
||||
});
|
||||
this.staticConfig.$watch("use-symlinks").subscribe((useSymlink) => {
|
||||
this.linkingType = useSymlink === true ? "symlink" : "junction";
|
||||
log.info(`Linking type set to ${this.linkingType}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public sharedFolder(): string {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { BsmButton, BsmButtonType } from "renderer/components/shared/bsm-button.component";
|
||||
import { BsmImage } from "renderer/components/shared/bsm-image.component";
|
||||
import { cn } from "renderer/helpers/css-class.helpers";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
import { useTranslationV2 } from "renderer/hooks/use-translation.hook";
|
||||
import { ModalComponent, ModalExitCode } from "renderer/services/modale.service";
|
||||
|
||||
type BasicModalOptions = {
|
||||
@@ -12,7 +12,11 @@ type BasicModalOptions = {
|
||||
id: string;
|
||||
text: string;
|
||||
type: BsmButtonType,
|
||||
isCancel?: boolean;
|
||||
/**
|
||||
* true - ModalExitCode.COMPLETED
|
||||
* undefined/false - ModalExitCode.CANCELED
|
||||
*/
|
||||
onClick?: () => boolean;
|
||||
}[];
|
||||
buttonsLayout?: "row" | "column";
|
||||
};
|
||||
@@ -21,20 +25,32 @@ export const BasicModal: ModalComponent<BasicModalOptions["buttons"][0]["id"], B
|
||||
data: { title, image, body, buttons, buttonsLayout = "column" }
|
||||
} }) => {
|
||||
|
||||
const t = useTranslation();
|
||||
const t = useTranslationV2();
|
||||
|
||||
const handleClick = (button: BasicModalOptions["buttons"][0]) => {
|
||||
resolver({ exitCode: button.isCancel ? ModalExitCode.CANCELED : ModalExitCode.COMPLETED, data: button.id });
|
||||
resolver({
|
||||
exitCode: button.onClick?.()
|
||||
? ModalExitCode.COMPLETED
|
||||
: ModalExitCode.CANCELED,
|
||||
data: button.id
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<form className="text-gray-900 dark:text-white">
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">{t(title)}</h1>
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">{t.text(title)}</h1>
|
||||
<BsmImage className="mx-auto h-24" image={image} />
|
||||
{ body && <p className="w-full">{t(body)}</p> }
|
||||
{ body && <p className="w-full">{t.text(body)}</p> }
|
||||
<div className={cn("grid gap-2 mt-4")} style={{ gridAutoFlow: buttonsLayout, ...(buttonsLayout === "row" ? { gridTemplateRows: `repeat(${buttons.length}, 1fr)` } : { gridTemplateColumns: `repeat(${buttons.length}, 1fr)` }) }}>
|
||||
{buttons.map(button => (
|
||||
<BsmButton key={button.id} typeColor={button.type} className="h-8 rounded-md text-center flex justify-center items-center" onClick={() => handleClick(button)} withBar={false} text={button.text} />
|
||||
<BsmButton
|
||||
key={button.id}
|
||||
typeColor={button.type}
|
||||
className="h-8 rounded-md text-center flex justify-center items-center"
|
||||
onClick={() => handleClick(button)}
|
||||
withBar={false}
|
||||
text={button.text}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -6,30 +6,45 @@ import { BsmButton } from "renderer/components/shared/bsm-button.component";
|
||||
import { useObservable } from "renderer/hooks/use-observable.hook";
|
||||
import { useService } from "renderer/hooks/use-service.hook";
|
||||
import { useThemeColor } from "renderer/hooks/use-theme-color.hook";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
import { useTranslationV2 } from "renderer/hooks/use-translation.hook";
|
||||
import { BSVersionManagerService } from "renderer/services/bs-version-manager.service";
|
||||
import { ConfigurationService } from "renderer/services/configuration.service";
|
||||
import { IpcService } from "renderer/services/ipc.service";
|
||||
import { ModalComponent } from "renderer/services/modale.service";
|
||||
import { ModalComponent, ModalService } from "renderer/services/modale.service";
|
||||
import { FolderLinkState, VersionFolderLinkerService, VersionLinkerActionType } from "renderer/services/version-folder-linker.service";
|
||||
import { lastValueFrom } from "rxjs";
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
import { NotificationService } from "renderer/services/notification.service";
|
||||
import { BasicModal } from "../basic-modal.component";
|
||||
import BeatConflict from "../../../../../assets/images/apngs/beat-conflict.png";
|
||||
|
||||
export const ShareFoldersModal: ModalComponent<void, BSVersion> = ({ options: {data} }) => {
|
||||
const SHARED_FOLDERS_KEY = "default-shared-folders";
|
||||
const SHARED_FOLDERS_KEY = "default-shared-folders";
|
||||
const SHARED_FOLDER_BLACKLIST = {
|
||||
error: [
|
||||
".DepotDownloader",
|
||||
"Beat Saber_Data",
|
||||
"IPA",
|
||||
"Libs",
|
||||
"Plugins",
|
||||
"MonoBleedingEdge",
|
||||
],
|
||||
warn: [
|
||||
"DLC",
|
||||
"Logs",
|
||||
],
|
||||
};
|
||||
|
||||
export const ShareFoldersModal: ModalComponent<void, BSVersion> = ({ options: { data: version } }) => {
|
||||
const config = useService(ConfigurationService);
|
||||
const ipc = useService(IpcService);
|
||||
const linker = useService(VersionFolderLinkerService);
|
||||
const versionManager = useService(BSVersionManagerService);
|
||||
|
||||
const t = useTranslation();
|
||||
const t = useTranslationV2();
|
||||
|
||||
const [folders, setFolders] = useState<string[]>(Array.from(new Set([...config.get<string[]>(SHARED_FOLDERS_KEY)]).values()));
|
||||
|
||||
useEffect(() => {
|
||||
linker
|
||||
.getLinkedFolders(data, { relative: true })
|
||||
.getLinkedFolders(version, { relative: true })
|
||||
.toPromise()
|
||||
.then(linkedFolders => {
|
||||
setFolders(prev => Array.from(new Set([...prev, ...linkedFolders]).values()));
|
||||
@@ -45,42 +60,24 @@ export const ShareFoldersModal: ModalComponent<void, BSVersion> = ({ options: {d
|
||||
config.set(SHARED_FOLDERS_KEY, folders);
|
||||
}, [folders]);
|
||||
|
||||
const addFolder = async () => {
|
||||
const versionPath = await lastValueFrom(versionManager.getVersionPath(data));
|
||||
const folder = await lastValueFrom(ipc.sendV2("choose-folder", {
|
||||
defaultPath: versionPath
|
||||
}));
|
||||
|
||||
if (!folder || folder.canceled || !folder.filePaths?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const relativeFolder = await lastValueFrom(ipc.sendV2("full-version-path-to-relative", { version: data, fullPath: folder.filePaths[0] }));
|
||||
|
||||
if (folders.includes(relativeFolder)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setFolders(pre => [...pre, relativeFolder]);
|
||||
};
|
||||
|
||||
const removeFolder = (index: number) => {
|
||||
setFolders(prev => prev.filter((_, i) => i !== index));
|
||||
};
|
||||
|
||||
const linkAll = () => {
|
||||
folders.forEach(relativeFolder => linker.linkVersionFolder({ version: data, relativeFolder, type: VersionLinkerActionType.Link }));
|
||||
folders.forEach(relativeFolder => linker.linkVersionFolder({ version, relativeFolder, type: VersionLinkerActionType.Link }));
|
||||
};
|
||||
|
||||
return (
|
||||
<form className="w-full max-w-md ">
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center text-gray-800 dark:text-gray-200">{t("modals.shared-folders.title")}</h1>
|
||||
<p className="my-3">{t("modals.shared-folders.description")}</p>
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center text-gray-800 dark:text-gray-200">{t.text("modals.shared-folders.title")}</h1>
|
||||
<p className="my-3">{t.text("modals.shared-folders.description")}</p>
|
||||
<ul className="flex flex-col gap-1 mb-2 h-[300px] max-h-[300px] overflow-scroll scrollbar-default px-1">
|
||||
{folders.map((folder, index) => (
|
||||
<FolderItem
|
||||
key={folder}
|
||||
version={data}
|
||||
version={version}
|
||||
relativeFolder={folder}
|
||||
onDelete={() => {
|
||||
removeFolder(index);
|
||||
@@ -89,13 +86,93 @@ export const ShareFoldersModal: ModalComponent<void, BSVersion> = ({ options: {d
|
||||
))}
|
||||
</ul>
|
||||
<div className="grid grid-flow-col gap-3 grid-cols-2">
|
||||
<BsmButton icon="add" className="h-8 rounded-md flex justify-center items-center font-bold bg-light-main-color-1 dark:bg-main-color-1" iconClassName="h-6 aspect-square text-current" onClick={addFolder} withBar={false} text="modals.shared-folders.buttons.add-folder" />
|
||||
<AddFolderButton
|
||||
version={version}
|
||||
folders={folders}
|
||||
setFolders={setFolders}
|
||||
/>
|
||||
<BsmButton icon="link" className="h-8 rounded-md flex justify-center items-center font-bold" typeColor="primary" iconClassName="h-6 aspect-square text-current -rotate-45" onClick={linkAll} withBar={false} text="modals.shared-folders.buttons.link-all" />
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
function AddFolderButton({
|
||||
version,
|
||||
folders,
|
||||
setFolders,
|
||||
}: Readonly<{
|
||||
version: BSVersion;
|
||||
folders: string[];
|
||||
setFolders: (value: string[]) => void;
|
||||
}>) {
|
||||
const config = useService(ConfigurationService);
|
||||
const ipc = useService(IpcService);
|
||||
const versionManager = useService(BSVersionManagerService);
|
||||
const notification = useService(NotificationService);
|
||||
const modal = useService(ModalService);
|
||||
|
||||
const t = useTranslationV2();
|
||||
|
||||
const addFolder = async () => {
|
||||
const versionPath = await lastValueFrom(versionManager.getVersionPath(version));
|
||||
const folder = await lastValueFrom(ipc.sendV2("choose-folder", {
|
||||
defaultPath: versionPath
|
||||
}));
|
||||
|
||||
if (!folder || folder.canceled || !folder.filePaths?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const relativeFolder = await lastValueFrom(ipc.sendV2("full-version-path-to-relative", { version, fullPath: folder.filePaths[0] }));
|
||||
if (folders.includes(relativeFolder)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (SHARED_FOLDER_BLACKLIST.error.includes(relativeFolder)) {
|
||||
notification.notifyError({
|
||||
title: "notifications.shared-folder.adding-error.title",
|
||||
desc: t.text("notifications.shared-folder.adding-error.msg", {
|
||||
folder: relativeFolder
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (SHARED_FOLDER_BLACKLIST.warn.includes(relativeFolder)) {
|
||||
await modal.openModal(BasicModal, { data: {
|
||||
title: "modals.adding-shared-folder.title",
|
||||
body: t.text("modals.adding-shared-folder.description", {
|
||||
folder: relativeFolder
|
||||
}),
|
||||
image: BeatConflict,
|
||||
buttons: [
|
||||
{ id: "cancel", text: "misc.cancel", type: "cancel" },
|
||||
{
|
||||
id: "confirm", text: "misc.confirm", type: "primary",
|
||||
onClick() {
|
||||
config.set(SHARED_FOLDERS_KEY, [...folders, relativeFolder]);
|
||||
return true;
|
||||
},
|
||||
}
|
||||
]
|
||||
}});
|
||||
return;
|
||||
}
|
||||
|
||||
setFolders([...folders, relativeFolder]);
|
||||
};
|
||||
|
||||
return <BsmButton
|
||||
icon="add"
|
||||
className="h-8 rounded-md flex justify-center items-center font-bold bg-light-main-color-1 dark:bg-main-color-1"
|
||||
iconClassName="h-6 aspect-square text-current"
|
||||
onClick={addFolder}
|
||||
withBar={false}
|
||||
text="modals.shared-folders.buttons.add-folder"
|
||||
/>;
|
||||
}
|
||||
|
||||
// -------- FOLDER ITEM --------
|
||||
|
||||
type FolderProps = {
|
||||
@@ -107,7 +184,7 @@ type FolderProps = {
|
||||
const FolderItem = ({ version, relativeFolder, onDelete }: FolderProps) => {
|
||||
const linker = useService(VersionFolderLinkerService);
|
||||
|
||||
const t = useTranslation();
|
||||
const t = useTranslationV2();
|
||||
|
||||
const color = useThemeColor("first-color");
|
||||
const state = useObservable(() => linker.$folderLinkedState(version, relativeFolder), FolderLinkState.Unlinked, [version, relativeFolder]);
|
||||
@@ -139,7 +216,7 @@ const FolderItem = ({ version, relativeFolder, onDelete }: FolderProps) => {
|
||||
{name}
|
||||
</span>
|
||||
<div className="flex flex-row gap-1.5">
|
||||
<Tippy placement="left" content={t(`modals.shared-folders.buttons.${state === FolderLinkState.Linked ? "unlink-folder" : "link-folder"}`)} arrow={false}>
|
||||
<Tippy placement="top" theme="default" content={t.text(`modals.shared-folders.buttons.${state === FolderLinkState.Linked ? "unlink-folder" : "link-folder"}`)}>
|
||||
<LinkButton
|
||||
className="p-0.5 h-7 shrink-0 aspect-square blur-0 cursor-pointer hover:brightness-75"
|
||||
state={state}
|
||||
@@ -164,15 +241,17 @@ const FolderItem = ({ version, relativeFolder, onDelete }: FolderProps) => {
|
||||
);
|
||||
}
|
||||
return (
|
||||
<BsmButton
|
||||
className="aspect-square h-7 rounded-md p-1"
|
||||
icon="trash"
|
||||
withBar={false}
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
onDelete?.();
|
||||
}}
|
||||
/>
|
||||
<Tippy content={t.text("modals.shared-folders.buttons.remove-from-the-list")} theme="default" hideOnClick={false} placement="top">
|
||||
<BsmButton
|
||||
className="aspect-square h-7 rounded-md p-1"
|
||||
icon="trash"
|
||||
withBar={false}
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
onDelete?.();
|
||||
}}
|
||||
/>
|
||||
</Tippy>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ReactNode } from "react";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
import { useTranslationV2 } from "renderer/hooks/use-translation.hook";
|
||||
|
||||
type Props = {
|
||||
id?: string;
|
||||
@@ -8,11 +8,11 @@ type Props = {
|
||||
minorTitle?: string;
|
||||
description?: string;
|
||||
children?: ReactNode;
|
||||
os?: string;
|
||||
os?: "win32" | "linux";
|
||||
};
|
||||
|
||||
export function SettingContainer({ id, className, title, minorTitle, description, children, os }: Props) {
|
||||
const t = useTranslation();
|
||||
const t = useTranslationV2();
|
||||
|
||||
if (os && os !== window.electron.platform) {
|
||||
return undefined;
|
||||
@@ -20,9 +20,9 @@ export function SettingContainer({ id, className, title, minorTitle, description
|
||||
|
||||
return (
|
||||
<div id={id} className={className || "relative mb-5"}>
|
||||
{title && <h1 className="mb-1 text-2xl font-bold tracking-wide">{t(title)}</h1>}
|
||||
{minorTitle && <h2 className="mb-1 font-bold tracking-wide text-gray-600 dark:text-gray-300">{t(minorTitle)}</h2>}
|
||||
{description && <p className="mb-3 text-sm text-gray-600 dark:text-gray-400">{t(description)}</p>}
|
||||
{title && <h1 className="mb-1 text-2xl font-bold tracking-wide">{t.text(title)}</h1>}
|
||||
{minorTitle && <h2 className="mb-1 font-bold tracking-wide text-gray-600 dark:text-gray-300">{t.text(minorTitle)}</h2>}
|
||||
{description && <p className="mb-3 text-sm text-gray-600 dark:text-gray-400">{t.text(description)}</p>}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ToogleSwitch } from "../shared/toogle-switch.component";
|
||||
|
||||
type Item = {
|
||||
export type Item = {
|
||||
text: string;
|
||||
desc?: string;
|
||||
checked?: boolean;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { motion } from "framer-motion";
|
||||
import { useThemeColor } from "renderer/hooks/use-theme-color.hook";
|
||||
import { BsmIcon } from "../svgs/bsm-icon.component";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
import { useTranslationV2 } from "renderer/hooks/use-translation.hook";
|
||||
import { FolderLinkState } from "renderer/services/version-folder-linker.service";
|
||||
import React from "react";
|
||||
import React, { forwardRef } from "react";
|
||||
|
||||
export type LinkBtnProps = {
|
||||
className?: string;
|
||||
@@ -12,8 +12,8 @@ export type LinkBtnProps = {
|
||||
onClick?: () => unknown;
|
||||
};
|
||||
|
||||
export function LinkButton({className, title, state, onClick}: LinkBtnProps) {
|
||||
const t = useTranslation();
|
||||
export const LinkButton = forwardRef<HTMLDivElement, LinkBtnProps>(({className, title, state, onClick}, forwardedRef) => {
|
||||
const { text: t } = useTranslationV2();
|
||||
|
||||
const color = useThemeColor("first-color");
|
||||
const disabled = state === FolderLinkState.Processing || state === FolderLinkState.Pending;
|
||||
@@ -41,6 +41,7 @@ export function LinkButton({className, title, state, onClick}: LinkBtnProps) {
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
ref={forwardedRef}
|
||||
variants={{ hover: { rotate: 22.5 }, tap: { rotate: 45 } }}
|
||||
whileHover="hover"
|
||||
whileTap="tap"
|
||||
@@ -55,4 +56,4 @@ export function LinkButton({className, title, state, onClick}: LinkBtnProps) {
|
||||
<BsmIcon className="p-1 absolute top-0 left-0 h-full w-full !bg-transparent -rotate-45 brightness-150" icon={state === FolderLinkState.Linked ? "link" : "unlink"} style={{ color: btnColor() }} />
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -34,3 +34,4 @@ export const defaultConfiguration: {
|
||||
};
|
||||
|
||||
export type ThemeConfig = "dark" | "light" | "os";
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ import Tippy from "@tippyjs/react";
|
||||
import { MapsManagerService } from "renderer/services/maps-manager.service";
|
||||
import { PlaylistsManagerService } from "renderer/services/playlists-manager.service";
|
||||
import { ModelsManagerService } from "renderer/services/models-management/models-manager.service";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
import { useTranslation, useTranslationV2 } from "renderer/hooks/use-translation.hook";
|
||||
import { VersionFolderLinkerService } from "renderer/services/version-folder-linker.service";
|
||||
import { useService } from "renderer/hooks/use-service.hook";
|
||||
import { lastValueFrom } from "rxjs";
|
||||
@@ -40,7 +40,7 @@ import { SteamIcon } from "renderer/components/svgs/icons/steam-icon.component";
|
||||
import { OculusIcon } from "renderer/components/svgs/icons/oculus-icon.component";
|
||||
import { BsDownloaderService } from "renderer/services/bs-version-download/bs-downloader.service";
|
||||
import BeatConflict from "../../../assets/images/apngs/beat-conflict.png";
|
||||
import { SettingToogleSwitchGrid } from "renderer/components/settings/setting-toogle-switch-grid.component";
|
||||
import { Item, SettingToogleSwitchGrid } from "renderer/components/settings/setting-toogle-switch-grid.component";
|
||||
import { BasicModal } from "renderer/components/modal/basic-modal.component";
|
||||
import { StaticConfigurationService } from "renderer/services/static-configuration.service";
|
||||
import { tryit } from "shared/helpers/error.helpers";
|
||||
@@ -96,8 +96,6 @@ export function SettingsPage() {
|
||||
const [playlistsDeepLinkEnabled, setPlaylistsDeepLinkEnabled] = useState(false);
|
||||
const [modelsDeepLinkEnabled, setModelsDeepLinkEnabled] = useState(false);
|
||||
const [hasDownloaderSession, setHasDownloaderSession] = useState(false);
|
||||
const [hardwareAccelerationEnabled, setHardwareAccelerationEnabled] = useState(true);
|
||||
const [useSymlink, setUseSymlink] = useState(false);
|
||||
const appVersion = useObservable(() => autoUpdater.getAppVersion());
|
||||
|
||||
useEffect(() => {
|
||||
@@ -107,8 +105,6 @@ export function SettingsPage() {
|
||||
playlistsManager.isDeepLinksEnabled().then(enabled => setPlaylistsDeepLinkEnabled(() => enabled));
|
||||
modelsManager.isDeepLinksEnabled().then(enabled => setModelsDeepLinkEnabled(() => enabled));
|
||||
|
||||
staticConfig.get("disable-hadware-acceleration").then(disabled =>setHardwareAccelerationEnabled(() => disabled !== true));
|
||||
staticConfig.get("use-symlinks").then(useSymlinks => setUseSymlink(() => useSymlinks));
|
||||
staticConfig.get("proton-folder").then(setProtonFolder);
|
||||
}, []);
|
||||
|
||||
@@ -233,66 +229,6 @@ export function SettingsPage() {
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeHardwareAcceleration = async (newHardwareAccelerationEnabled: boolean) => {
|
||||
if(newHardwareAccelerationEnabled === hardwareAccelerationEnabled){ return; }
|
||||
|
||||
const res = await modalService.openModal(BasicModal, { data: {
|
||||
title: "pages.settings.advanced.hardware-acceleration.modal.title",
|
||||
body: "pages.settings.advanced.hardware-acceleration.modal.body",
|
||||
image: BeatConflict,
|
||||
buttons: [
|
||||
{ id: "cancel", text: "misc.cancel", type: "cancel", isCancel: true },
|
||||
{ id: "confirm", text: "pages.settings.advanced.hardware-acceleration.modal.confirm-btn", type: "error" }
|
||||
]
|
||||
}});
|
||||
|
||||
if(res.exitCode !== ModalExitCode.COMPLETED || res.data !== "confirm"){ return; }
|
||||
|
||||
const { error } = await tryit(() => staticConfig.set("disable-hadware-acceleration", !newHardwareAccelerationEnabled));
|
||||
|
||||
if(error){
|
||||
notificationService.notifyError({ title: "notifications.types.error", desc: "pages.settings.advanced.hardware-acceleration.error-notification.message" });
|
||||
setHardwareAccelerationEnabled(() => !newHardwareAccelerationEnabled);
|
||||
return;
|
||||
}
|
||||
|
||||
setHardwareAccelerationEnabled(() => newHardwareAccelerationEnabled);
|
||||
|
||||
if(!progressBarService.require()){
|
||||
return;
|
||||
}
|
||||
|
||||
await lastValueFrom(ipcService.sendV2("restart-app"));
|
||||
};
|
||||
|
||||
const onChangeUseSymlinks = async (newUseSymlink: boolean) => {
|
||||
|
||||
if(newUseSymlink === useSymlink){ return; }
|
||||
|
||||
if(newUseSymlink){
|
||||
const res = await modalService.openModal(BasicModal, { data: {
|
||||
title: "pages.settings.advanced.use-symlinks.modal.title",
|
||||
body: "pages.settings.advanced.use-symlinks.modal.body",
|
||||
image: BeatConflict,
|
||||
buttons: [
|
||||
{ id: "cancel", text: "misc.cancel", type: "cancel", isCancel: true },
|
||||
{ id: "confirm", text: "pages.settings.advanced.use-symlinks.modal.confirm-btn", type: "error" }
|
||||
]
|
||||
}});
|
||||
|
||||
if(res.exitCode !== ModalExitCode.COMPLETED || res.data !== "confirm"){ return; }
|
||||
}
|
||||
|
||||
const { error } = await tryit(() => staticConfig.set("use-symlinks", newUseSymlink));
|
||||
|
||||
if(error){
|
||||
notificationService.notifyError({ title: "notifications.types.error", desc: "pages.settings.advanced.use-symlinks.error-notification.message" });
|
||||
return;
|
||||
}
|
||||
|
||||
setUseSymlink(() => newUseSymlink);
|
||||
}
|
||||
|
||||
const toogleShowSupporters = () => {
|
||||
setShowSupporters(show => !show);
|
||||
};
|
||||
@@ -585,12 +521,7 @@ export function SettingsPage() {
|
||||
</SettingContainer>
|
||||
</SettingContainer>
|
||||
|
||||
<SettingContainer title="pages.settings.advanced.title" description="pages.settings.advanced.description">
|
||||
<SettingToogleSwitchGrid items={[
|
||||
{ checked: hardwareAccelerationEnabled, text: t("pages.settings.advanced.hardware-acceleration.title"), desc: t("pages.settings.advanced.hardware-acceleration.description"), onChange: onChangeHardwareAcceleration },
|
||||
{ checked: useSymlink, text: t("pages.settings.advanced.use-symlinks.title"), desc: t("pages.settings.advanced.use-symlinks.description"), onChange: onChangeUseSymlinks },
|
||||
]}/>
|
||||
</SettingContainer>
|
||||
<AdvancedSettings />
|
||||
|
||||
<span className="bg-light-main-color-1 dark:bg-main-color-1 rounded-md py-1 px-2 font-bold float-right mb-5">v{appVersion}</span>
|
||||
</div>
|
||||
@@ -598,3 +529,104 @@ export function SettingsPage() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function AdvancedSettings() {
|
||||
const ipc = useService(IpcService);
|
||||
const modal = useService(ModalService);
|
||||
const notification = useService(NotificationService);
|
||||
const progressBar = useService(ProgressBarService);
|
||||
const staticConfig = useService(StaticConfigurationService);
|
||||
|
||||
const t = useTranslationV2();
|
||||
|
||||
const [hardwareAccelerationEnabled, setHardwareAccelerationEnabled] = useState(true);
|
||||
const [useSymlink, setUseSymlink] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
staticConfig.get("disable-hadware-acceleration").then(disabled =>setHardwareAccelerationEnabled(() => disabled !== true));
|
||||
staticConfig.get("use-symlinks").then(useSymlinks => setUseSymlink(() => useSymlinks));
|
||||
}, []);
|
||||
|
||||
const onChangeHardwareAcceleration = async (newHardwareAccelerationEnabled: boolean) => {
|
||||
if(newHardwareAccelerationEnabled === hardwareAccelerationEnabled){ return; }
|
||||
|
||||
const res = await modal.openModal(BasicModal, { data: {
|
||||
title: "pages.settings.advanced.hardware-acceleration.modal.title",
|
||||
body: "pages.settings.advanced.hardware-acceleration.modal.body",
|
||||
image: BeatConflict,
|
||||
buttons: [
|
||||
{ id: "cancel", text: "misc.cancel", type: "cancel" },
|
||||
{ id: "confirm", text: "pages.settings.advanced.hardware-acceleration.modal.confirm-btn", type: "error", onClick: () => true },
|
||||
]
|
||||
}});
|
||||
|
||||
if(res.exitCode !== ModalExitCode.COMPLETED || res.data !== "confirm"){ return; }
|
||||
|
||||
const { error } = await tryit(() => staticConfig.set("disable-hadware-acceleration", !newHardwareAccelerationEnabled));
|
||||
|
||||
if(error){
|
||||
notification.notifyError({ title: "notifications.types.error", desc: "pages.settings.advanced.hardware-acceleration.error-notification.message" });
|
||||
setHardwareAccelerationEnabled(() => !newHardwareAccelerationEnabled);
|
||||
return;
|
||||
}
|
||||
|
||||
setHardwareAccelerationEnabled(() => newHardwareAccelerationEnabled);
|
||||
|
||||
if(!progressBar.require()){
|
||||
return;
|
||||
}
|
||||
|
||||
await lastValueFrom(ipc.sendV2("restart-app"));
|
||||
};
|
||||
|
||||
const onChangeUseSymlinks = async (newUseSymlink: boolean) => {
|
||||
|
||||
if (window.electron.platform !== "win32" || newUseSymlink === useSymlink) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(newUseSymlink){
|
||||
const res = await modal.openModal(BasicModal, { data: {
|
||||
title: "pages.settings.advanced.use-symlinks.modal.title",
|
||||
body: "pages.settings.advanced.use-symlinks.modal.body",
|
||||
image: BeatConflict,
|
||||
buttons: [
|
||||
{ id: "cancel", text: "misc.cancel", type: "cancel" },
|
||||
{ id: "confirm", text: "pages.settings.advanced.use-symlinks.modal.confirm-btn", type: "error", onClick: () => true }
|
||||
]
|
||||
}});
|
||||
|
||||
if(res.exitCode !== ModalExitCode.COMPLETED || res.data !== "confirm"){ return; }
|
||||
}
|
||||
|
||||
const { error } = await tryit(() => staticConfig.set("use-symlinks", newUseSymlink));
|
||||
|
||||
if(error){
|
||||
notification.notifyError({ title: "notifications.types.error", desc: "pages.settings.advanced.use-symlinks.error-notification.message" });
|
||||
return;
|
||||
}
|
||||
|
||||
setUseSymlink(() => newUseSymlink);
|
||||
}
|
||||
|
||||
const advancedItems: Item[] = [{
|
||||
checked: hardwareAccelerationEnabled,
|
||||
text: t.text("pages.settings.advanced.hardware-acceleration.title"),
|
||||
desc: t.text("pages.settings.advanced.hardware-acceleration.description"),
|
||||
onChange: onChangeHardwareAcceleration
|
||||
}];
|
||||
if (window.electron.platform === "win32") {
|
||||
advancedItems.push({
|
||||
checked: useSymlink,
|
||||
text: t.text("pages.settings.advanced.use-symlinks.title"),
|
||||
desc: t.text("pages.settings.advanced.use-symlinks.description"),
|
||||
onChange: onChangeUseSymlinks
|
||||
});
|
||||
}
|
||||
|
||||
return <SettingContainer title="pages.settings.advanced.title" description="pages.settings.advanced.description">
|
||||
<SettingToogleSwitchGrid items={advancedItems}/>
|
||||
</SettingContainer>
|
||||
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -3,7 +3,7 @@ import { webUtils } from "electron";
|
||||
declare global {
|
||||
interface Window {
|
||||
electron: {
|
||||
platform: "win32"|"linux"|"darwin",
|
||||
platform: "win32" | "linux",
|
||||
ipcRenderer: {
|
||||
sendMessage(channel: string, args: any): void;
|
||||
on(channel: string, func: (...args: any) => void): (() => void) | undefined;
|
||||
|
||||
Reference in New Issue
Block a user