Merge pull request #569 from silentrald/feat/76

[feat-76] support drag and drop of mod files (zip/dll files only)
This commit is contained in:
MathieuG-P
2024-12-08 18:22:46 +01:00
committed by GitHub
16 changed files with 356 additions and 21 deletions
+16
View File
@@ -106,6 +106,7 @@
"latest": "Neueste",
"description": "Beschreibung",
"dropdown": {
"import-mods": "Mods importieren",
"uninstall-all": "Alle deinstallieren",
"unselect-all": "Alle abwählen"
}
@@ -116,6 +117,10 @@
"title": "Mods bereits installiert",
"description": "Alle ausgewählten Mods sind bereits installiert"
}
},
"drop-zone": {
"text": "Importiere deine Mods",
"subtext": "Ziehe deine \"zip\"- oder \"dll\"-Dateien hierher, um sie zu importieren"
}
},
"dropdown": {
@@ -526,6 +531,17 @@
"no-mods": "Keine Mods sind in dieser Version installiert 😑"
}
}
},
"import-mod": {
"titles": {
"success": "Mod-Import abgeschlossen",
"error": "Es ist ein Fehler beim Importieren der Mods aufgetreten"
},
"msgs": {
"success": "Mods erfolgreich importiert.",
"some-success": "Einige Mods wurden erfolgreich importiert.",
"no-dlls": "Die Datei(en) enthalten keine \"dll\"-Dateien."
}
}
},
"maps": {
+16
View File
@@ -106,6 +106,7 @@
"latest": "Latest",
"description": "Description",
"dropdown": {
"import-mods": "Import mods",
"uninstall-all": "Uninstall all",
"unselect-all": "Unselect all"
}
@@ -116,6 +117,10 @@
"title": "Mods already installed",
"description": "All selected mods are already installed"
}
},
"drop-zone": {
"text": "Import your mods",
"subtext": "Drop your \"zip\" or \"dll\" files here to import them"
}
},
"dropdown": {
@@ -526,6 +531,17 @@
"no-mods": "No mods are installed in this version 😑"
}
}
},
"import-mod": {
"titles": {
"success": "Mods import completed",
"error": "An error occurred during the mods import"
},
"msgs": {
"success": "Mods successfully imported.",
"some-success": "Some mods were successfully imported.",
"no-dlls": "The file(s) do not contain any \"dll\" files."
}
}
},
"maps": {
+16
View File
@@ -106,6 +106,7 @@
"latest": "Último",
"description": "Descripción",
"dropdown": {
"import-mods": "Importar mods",
"uninstall-all": "Desinstalar todos",
"unselect-all": "Deseleccionar todo"
}
@@ -116,6 +117,10 @@
"title": "Mods ya instalados",
"description": "Todos los mods seleccionados ya están instalados"
}
},
"drop-zone": {
"text": "Importa tus mods",
"subtext": "Coloca tus archivos \"zip\" o \"dll\" aquí para importarlos"
}
},
"dropdown": {
@@ -526,6 +531,17 @@
"no-mods": "No hay mods instalados en esta versión 😑"
}
}
},
"import-mod": {
"titles": {
"success": "Importación de mods completada",
"error": "Ocurrió un error durante la importación de mods"
},
"msgs": {
"success": "Mods importados con éxito.",
"some-success": "Algunos mods fueron importados con éxito.",
"no-dlls": "El(los) archivo(s) no contienen archivos \"dll\"."
}
}
},
"maps": {
+16
View File
@@ -106,6 +106,7 @@
"latest": "Récent",
"description": "Description",
"dropdown": {
"import-mods": "Importer des mods",
"uninstall-all": "Tout désinstaller",
"unselect-all": "Tout désélectionner"
}
@@ -116,6 +117,10 @@
"title": "Mods déjà installées",
"description": "Tous les mods séléctionnées sont déjà installées"
}
},
"drop-zone": {
"text": "Importez vos mods",
"subtext": "Déposez vos fichiers \"zip\" ou \"dll\" ici pour les importer"
}
},
"dropdown": {
@@ -526,6 +531,17 @@
"no-mods": "Aucun mod n'est installé dans dans cette version 😑"
}
}
},
"import-mod": {
"titles": {
"success": "Importation des mods terminée",
"error": "Une erreur est survenue lors de l'importation des mods"
},
"msgs": {
"success": "Mods importés avec succès.",
"some-success": "Certains mods ont été importés avec succès.",
"no-dlls": "Le(s) fichier(s) ne contient(ent) aucun fichier \"dll\"."
}
}
},
"maps": {
+16
View File
@@ -106,6 +106,7 @@
"latest": "最新",
"description": "說明",
"dropdown": {
"import-mods": "モッドをインポート",
"uninstall-all": "全てアンインストールする",
"unselect-all": "すべて選択解除"
}
@@ -116,6 +117,10 @@
"title": "すでにインストール済みのMOD",
"description": "選択したすべてのMODはすでにインストールされています"
}
},
"drop-zone": {
"text": "モッドをインポートする",
"subtext": "「zip」または「dll」ファイルをここにドロップしてインポートする"
}
},
"dropdown": {
@@ -526,6 +531,17 @@
"no-mods": "このバージョンのModはインストールされていません 😑"
}
}
},
"import-mod": {
"titles": {
"success": "モッドのインポートが完了しました",
"error": "モッドのインポート中にエラーが発生しました"
},
"msgs": {
"success": "モッドが正常にインポートされました。",
"some-success": "一部のモッドが正常にインポートされました。",
"no-dlls": "ファイルに \"dll\" ファイルが含まれていません。"
}
}
},
"maps": {
+16
View File
@@ -106,6 +106,7 @@
"latest": "최신",
"description": "설명",
"dropdown": {
"import-mods": "모드 가져오기",
"uninstall-all": "모두 제거",
"unselect-all": "모두 선택 해제"
}
@@ -116,6 +117,10 @@
"title": "이미 설치된 모드",
"description": "선택한 모든 모드가 이미 설치되었습니다."
}
},
"drop-zone": {
"text": "모드를 가져오기",
"subtext": "\"zip\" 또는 \"dll\" 파일을 여기에 드롭하여 가져오기"
}
},
"dropdown": {
@@ -526,6 +531,17 @@
"no-mods": "이 버전에 설치된 Mod가 없습니다 😑"
}
}
},
"import-mod": {
"titles": {
"success": "모드 임포트 완료",
"error": "모드 임포트 중 오류가 발생했습니다"
},
"msgs": {
"success": "모드가 성공적으로 임포트되었습니다.",
"some-success": "일부 모드가 성공적으로 임포트되었습니다.",
"no-dlls": "파일에 \"dll\" 파일이 포함되어 있지 않습니다."
}
}
},
"maps": {
+16
View File
@@ -106,6 +106,7 @@
"latest": "Latest",
"description": "Описание",
"dropdown": {
"import-mods": "Импортировать моды",
"uninstall-all": "Удалить всё",
"unselect-all": "Снять все выделения"
}
@@ -116,6 +117,10 @@
"title": "Моды уже установлены",
"description": "Все выбранные моды уже установлены"
}
},
"drop-zone": {
"text": "Импортируйте свои моды",
"subtext": "Перетащите ваши файлы \"zip\" или \"dll\" сюда для импорта"
}
},
"dropdown": {
@@ -526,6 +531,17 @@
"no-mods": "Для этой версии не было установлено модов 😑"
}
}
},
"import-mod": {
"titles": {
"success": "Импорт модов завершен",
"error": "Произошла ошибка при импорте модов"
},
"msgs": {
"success": "Моды успешно импортированы.",
"some-success": "Некоторые моды были успешно импортированы.",
"no-dlls": "Файл(ы) не содержат файлов \"dll\"."
}
}
},
"maps": {
+16
View File
@@ -106,6 +106,7 @@
"latest": "最新",
"description": "描述",
"dropdown": {
"import-mods": "導入模組",
"uninstall-all": "全部移除",
"unselect-all": "取消全選"
}
@@ -116,6 +117,10 @@
"title": "模組已安裝",
"description": "所有選中的模組已經安裝"
}
},
"drop-zone": {
"text": "匯入你的模組",
"subtext": "將你的 \"zip\" 或 \"dll\" 檔案拖放到這裡以進行匯入"
}
},
"dropdown": {
@@ -526,6 +531,17 @@
"no-mods": "該版本中沒有安裝 Mod 😑"
}
}
},
"import-mod": {
"titles": {
"success": "模組匯入完成",
"error": "模組匯入過程中發生錯誤"
},
"msgs": {
"success": "模組成功匯入。",
"some-success": "一些模組已成功匯入。",
"no-dlls": "檔案中不包含任何 \"dll\" 檔案。"
}
}
},
"maps": {
+16
View File
@@ -106,6 +106,7 @@
"latest": "最新",
"description": "描述",
"dropdown": {
"import-mods": "导入模组",
"uninstall-all": "全部卸载",
"unselect-all": "取消全选"
}
@@ -116,6 +117,10 @@
"title": "模组已安装",
"description": "所有选中的模组已经安装"
}
},
"drop-zone": {
"text": "导入你的模组",
"subtext": "将你的 \"zip\" 或 \"dll\" 文件拖放到这里进行导入"
}
},
"dropdown": {
@@ -526,6 +531,17 @@
"no-mods": "该版本中没有安装 Mod 😑"
}
}
},
"import-mod": {
"titles": {
"success": "模组导入完成",
"error": "模组导入过程中发生错误"
},
"msgs": {
"success": "模组成功导入。",
"some-success": "一些模组已成功导入。",
"no-dlls": "文件中不包含任何 \"dll\" 文件。"
}
}
},
"maps": {
+10 -5
View File
@@ -4,27 +4,32 @@ import { from } from "rxjs";
const ipc = IpcService.getInstance();
ipc.on("get-available-mods", (args, reply) => {
ipc.on("bs-mods.get-available-mods", (args, reply) => {
const modsManager = BsModsManagerService.getInstance();
reply(from(modsManager.getAvailableMods(args)));
});
ipc.on("get-installed-mods", (args, reply) => {
ipc.on("bs-mods.get-installed-mods", (args, reply) => {
const modsManager = BsModsManagerService.getInstance();
reply(from(modsManager.getInstalledMods(args)));
});
ipc.on("install-mods", (args, reply) => {
ipc.on("bs-mods.import-mods", (args, reply) => {
const modsManager = BsModsManagerService.getInstance();
reply(modsManager.importMods(args.paths, args.version));
});
ipc.on("bs-mods.install-mods", (args, reply) => {
const modsManager = BsModsManagerService.getInstance();
reply(modsManager.installMods(args.mods, args.version));
});
ipc.on("uninstall-mods", (args, reply) => {
ipc.on("bs-mods.uninstall-mods", (args, reply) => {
const modsManager = BsModsManagerService.getInstance();
reply(modsManager.uninstallMods(args.mods, args.version));
});
ipc.on("uninstall-all-mods", (args, reply) => {
ipc.on("bs-mods.uninstall-all-mods", (args, reply) => {
const modsManager = BsModsManagerService.getInstance();
reply(modsManager.uninstallAllMods(args));
});
@@ -11,7 +11,7 @@ import { deleteFolder, pathExist, Progression, unlinkPath } from "../../helpers/
import { lastValueFrom, Observable } from "rxjs";
import recursiveReadDir from "recursive-readdir";
import { sToMs } from "../../../shared/helpers/time.helpers";
import { pathExistsSync } from "fs-extra";
import { copyFile, ensureDir, pathExistsSync } from "fs-extra";
import { CustomError } from "shared/models/exceptions/custom-error.class";
import { popElement } from "shared/helpers/array.helpers";
import { LinuxService } from "../linux.service";
@@ -19,6 +19,7 @@ import { tryit } from "shared/helpers/error.helpers";
import crypto from "crypto";
import { BsmZipExtractor } from "main/models/bsm-zip-extractor.class";
import { bsmSpawn } from "main/helpers/os.helpers";
import { ExternalMod } from "shared/models/mods/mod.interface";
export class BsModsManagerService {
private static instance: BsModsManagerService;
@@ -320,6 +321,108 @@ export class BsModsManagerService {
return Array.from(modsDict.values());
}
private async importMod(modPath: string, destination: string): Promise<string[]> {
const extensionName = path.extname(modPath).toLowerCase();
if (extensionName === ".dll") {
const filename = path.basename(modPath);
// Defaultly copy the ".dll" to the "IPA/Pending/Plugins" folder
const fileFolder = path.join(destination, ModsInstallFolder.PLUGINS);
log.info("Copying", `${modPath}`, "to", `"${fileFolder}"`);
await ensureDir(fileFolder);
const copied = await copyFile(modPath, path.join(fileFolder, filename))
.then(() => true)
.catch(error => {
log.warn("Could not copy", `"${modPath}"`, error);
return false;
});
return copied ? [ filename ] : [];
}
if (extensionName !== ".zip") {
log.warn("Mod file is not a dll or zip file");
return [];
}
log.info("Extracting", `"${modPath}"`, "to", `"${destination}"`);
const zip = await BsmZipExtractor.fromPath(modPath);
try {
const dll = await zip.findEntry(entry => entry.fileName.endsWith(".dll"));
if (!dll) {
log.warn("No \"dll\" found in zip", modPath);
return [];
}
return await zip.extract(destination);
} catch (error) {
log.warn("Could not extract", `"${modPath}"`, error);
return [];
} finally {
zip.close();
}
}
public importMods(paths: string[], version: BSVersion): Observable<Progression<ExternalMod>> {
return new Observable<Progression<ExternalMod>>(obs => {
const progress: Progression<ExternalMod> = { total: 0, current: 0 };
const externalMod: ExternalMod = {
name: "",
files: [],
};
let modsInstalledCount = 0;
let modFilesCount = 0;
const abortController = new AbortController();
(async () => {
const versionPath = await this.bsLocalService.getVersionPath(version);
const modsPendingFolder = path.join(versionPath, ModsInstallFolder.PENDING);
for (const modPath of paths) {
if (abortController.signal?.aborted) {
log.info("Mods import has been cancelled");
return;
}
progress.total = paths.length;
obs.next(progress);
if (!pathExistsSync(modPath)) { continue; }
const modFiles = await this.importMod(modPath, modsPendingFolder);
if (modFiles.length > 0) {
++modsInstalledCount;
modFilesCount += modFiles.length;
externalMod.name = path.basename(modPath, path.extname(modPath));
externalMod.files = modFiles;
progress.data = externalMod;
} else {
progress.data = undefined;
}
++progress.current;
obs.next(progress);
}
})()
.then(() => {
if (modsInstalledCount === 0) {
throw new CustomError("No \"dll\" found in any of the files dropped", "no-dlls");
}
log.info("Successfully imported", modsInstalledCount, "mods from", paths.length, "files paths. Total files/folders added", modFilesCount);
})
.catch(error => {
obs.error(error);
})
.finally(() => obs.complete());
return () => {
abortController.abort();
}
});
}
public installMods(mods: Mod[], version: BSVersion): Observable<Progression> {
const progress = { current: 0, total: mods.length };
@@ -17,9 +17,10 @@ type Props = {
uninstallMod?: (mods: Mod) => void;
uninstallAllMods?: () => void;
unselectAllMods?: () => void;
openModsDropZone?: () => void;
};
export function ModsGrid({ modsMap, installed, modsSelected, onModChange, moreInfoMod, onWantInfos, disabled, uninstallMod, uninstallAllMods, unselectAllMods }: Props) {
export function ModsGrid({ modsMap, installed, modsSelected, onModChange, moreInfoMod, onWantInfos, disabled, uninstallMod, uninstallAllMods, unselectAllMods, openModsDropZone }: Props) {
const [filter, setFilter] = useState("");
const [filterEnabled, setFilterEnabled] = useState(false);
@@ -68,6 +69,7 @@ export function ModsGrid({ modsMap, installed, modsSelected, onModChange, moreIn
<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={[
{ text: "pages.version-viewer.mods.mods-grid.header-bar.dropdown.import-mods", icon: "download", onClick: () => openModsDropZone?.() },
{ text: "pages.version-viewer.mods.mods-grid.header-bar.dropdown.unselect-all", icon: "cancel", onClick: () => unselectAllMods?.() },
{ text: "pages.version-viewer.mods.mods-grid.header-bar.dropdown.uninstall-all", icon: "trash", onClick: () => uninstallAllMods?.() }
]} />
@@ -21,10 +21,13 @@ import { useService } from "renderer/hooks/use-service.hook";
import { NotificationService } from "renderer/services/notification.service";
import { noop } from "shared/helpers/function.helpers";
import { UninstallAllModsModal } from "renderer/components/modal/modal-types/uninstall-all-mods-modal.component";
import { Dropzone } from "renderer/components/shared/dropzone.component";
export function ModsSlide({ version, onDisclamerDecline }: { version: BSVersion; onDisclamerDecline: () => void }) {
const ACCEPTED_DISCLAIMER_KEY = "accepted-mods-disclaimer";
const t = useTranslation();
const modsManager = useService(BsModsManagerService);
const configService = useService(ConfigurationService);
const notification = useService(NotificationService);
@@ -42,6 +45,7 @@ export function ModsSlide({ version, onDisclamerDecline }: { version: BSVersion;
const isOnline = useObservable(() => os.isOnline$);
const [installing, setInstalling] = useState(false);
const [uninstalling, setUninstalling] = useState(false);
const [modsDropZoneOpen, setModsDropZoneOpen] = useState(false);
const downloadRef = useRef(null);
const [downloadWith, setDownloadWidth] = useState(0);
@@ -166,6 +170,11 @@ export function ModsSlide({ version, onDisclamerDecline }: { version: BSVersion;
}).catch(noop).finally(() => setInstalling(() => false));
};
const importMods = (files: string[]): void => {
setModsDropZoneOpen(false);
modsManager.importMods(files, version);
};
const uninstallMod = (mod: Mod): void => {
setUninstalling(() => true);
lastValueFrom(modsManager.uninstallMod(mod, version)).catch(noop).finally(() => {
@@ -192,7 +201,7 @@ export function ModsSlide({ version, onDisclamerDecline }: { version: BSVersion;
setModsSelected(() => []);
}
const loadMods = (): Promise<void> => {
const loadMods = async (): Promise<void> => {
if (os.isOffline) {
return Promise.resolve();
}
@@ -265,6 +274,7 @@ export function ModsSlide({ version, onDisclamerDecline }: { version: BSVersion;
</ModStatus>
);
}
return (
<>
<div className="grow overflow-y-scroll w-full min-h-0 scrollbar-default p-0 m-0">
@@ -279,6 +289,7 @@ export function ModsSlide({ version, onDisclamerDecline }: { version: BSVersion;
uninstallMod={uninstallMod}
uninstallAllMods={uninstallAllMods}
unselectAllMods={unselectAllMods}
openModsDropZone={() => setModsDropZoneOpen(true)}
/>
</div>
<div className="shrink-0 flex items-center justify-between px-3 py-2">
@@ -297,7 +308,23 @@ export function ModsSlide({ version, onDisclamerDecline }: { version: BSVersion;
return (
<div ref={ref} className="shrink-0 w-full h-full px-8 pb-7 flex justify-center">
<div className="relative flex flex-col grow-0 bg-light-main-color-2 dark:bg-main-color-2 size-full rounded-md shadow-black shadow-center overflow-hidden">{renderContent()}</div>
<Dropzone
className="w-full h-full shrink-0"
onFiles={importMods}
text={t("pages.version-viewer.mods.drop-zone.text")}
subtext={t("pages.version-viewer.mods.drop-zone.subtext")}
open={modsDropZoneOpen}
onClose={modsDropZoneOpen ? () => setModsDropZoneOpen(false) : undefined}
filters={[
{ name: ".zip", extensions: ["zip"] },
{ name: ".dll", extensions: ["dll"] }
]}
dialogOptions={{ dialog: {
properties: ["openFile", "multiSelections"],
}}}
>
<div className="relative flex flex-col grow-0 bg-light-main-color-2 dark:bg-main-color-2 size-full rounded-md shadow-black shadow-center overflow-hidden">{renderContent()}</div>
</Dropzone>
</div>
);
}
@@ -1,11 +1,12 @@
import { Observable, BehaviorSubject, throwError, of } from "rxjs";
import { catchError, tap } from "rxjs/operators";
import { Observable, BehaviorSubject, throwError, of, lastValueFrom } from "rxjs";
import { catchError, map, tap } from "rxjs/operators";
import { BSVersion } from "shared/bs-version.interface";
import { Mod } from "shared/models/mods";
import { IpcService } from "./ipc.service";
import { ProgressBarService } from "./progress-bar.service";
import { NotificationService } from "./notification.service";
import { Progression } from "main/helpers/fs.helpers";
import { ProgressionInterface } from "shared/models/progress-bar";
export class BsModsManagerService {
private static instance: BsModsManagerService;
@@ -32,11 +33,11 @@ export class BsModsManagerService {
}
public getAvailableMods(version: BSVersion): Observable<Mod[]> {
return this.ipcService.sendV2("get-available-mods", version);
return this.ipcService.sendV2("bs-mods.get-available-mods", version);
}
public getInstalledMods(version: BSVersion): Observable<Mod[]> {
return this.ipcService.sendV2("get-installed-mods", version);
return this.ipcService.sendV2("bs-mods.get-installed-mods", version);
}
public installMods(mods: Mod[], version: BSVersion): Observable<Progression> {
@@ -46,7 +47,7 @@ export class BsModsManagerService {
}
return new Observable<Progression>(obs => {
const install$ = this.ipcService.sendV2("install-mods", { mods, version });
const install$ = this.ipcService.sendV2("bs-mods.install-mods", { mods, version });
this.progressBar.show(install$.pipe(catchError(() => of({ current: 0, total: 0} as Progression))), { paddingLeft: "190px", paddingRight: "190px", bottom: "20px" });
const sub = install$.pipe(
@@ -77,7 +78,7 @@ export class BsModsManagerService {
}
return new Observable<Progression>(obs => {
const uninstall$ = this.ipcService.sendV2("uninstall-mods", { mods: [mod], version });
const uninstall$ = this.ipcService.sendV2("bs-mods.uninstall-mods", { mods: [mod], version });
this.progressBar.show(uninstall$.pipe(catchError(() => of({ current: 0, total: 0} as Progression))), { paddingLeft: "190px", paddingRight: "190px", bottom: "20px" });
const sub = uninstall$.pipe(
@@ -102,13 +103,57 @@ export class BsModsManagerService {
});
}
public async importMods(paths: string[], version: BSVersion): Promise<void> {
if (!this.progressBar.require()) {
throw new Error("Action already in progress");
}
// TODO: Check for conflicting mod files, and ask the user if they want to overwrite the files
const import$ = this.ipcService.sendV2("bs-mods.import-mods", {
paths, version
});
this.progressBar.show(import$.pipe(
catchError(() => of()),
map(progress => ({
progression: (progress.current / progress.total) * 100,
label: progress.data?.name
}) as ProgressionInterface)
));
return lastValueFrom(import$)
.then(progress => {
if (progress.current === 0) {
return;
}
this.notifications.notifySuccess({
title: "notifications.mods.import-mod.titles.success",
desc: progress.current === progress.total
? "notifications.mods.import-mod.msgs.success"
: "notifications.mods.import-mod.msgs.some-success",
});
})
.catch(error => {
this.notifications.notifyError({
title: "notifications.mods.import-mod.titles.error",
desc: ["no-dlls"].includes(error?.code)
? `notifications.mods.import-mod.msgs.${error.code}`
: "misc.unknown",
});
})
.finally(() => {
this.progressBar.hide();
});
}
public uninstallAllMods(version: BSVersion): Observable<Progression> {
if (!this.progressBar.require()) {
return throwError(() => new Error("Action already in progress"));
}
return new Observable<Progression>(obs => {
const uninstall$ = this.ipcService.sendV2("uninstall-all-mods", version);
const uninstall$ = this.ipcService.sendV2("bs-mods.uninstall-all-mods", version);
this.progressBar.show(uninstall$.pipe(catchError(() => of({ current: 0, total: 0} as Progression))), { paddingLeft: "190px", paddingRight: "190px", bottom: "20px" });
const sub = uninstall$.pipe(
@@ -133,4 +178,5 @@ export class BsModsManagerService {
});
}
}
+7 -5
View File
@@ -20,6 +20,7 @@ import { Supporter } from "../supporters";
import { AppWindow } from "../window-manager/app-window.model";
import { LocalBPList, LocalBPListsDetails } from "../playlists/local-playlist.models";
import { StaticConfigGetIpcRequestResponse, StaticConfigKeys, StaticConfigSetIpcRequest } from "main/services/static-configuration.service";
import { ExternalMod } from "../mods/mod.interface";
export type IpcReplier<T> = (data: Observable<T>) => void;
@@ -78,11 +79,12 @@ export interface IpcChannelMapping {
"delete-models": { request: BsmLocalModel[], response: Progression<BsmLocalModel[]> };
/* ** bs-mods-ipcs ** */
"get-available-mods": { request: BSVersion, response: Mod[] };
"get-installed-mods": { request: BSVersion, response: Mod[] };
"install-mods": { request: { mods: Mod[]; version: BSVersion }, response: Progression };
"uninstall-mods": { request: { mods: Mod[]; version: BSVersion }, response: Progression };
"uninstall-all-mods": { request: BSVersion, response: Progression };
"bs-mods.get-available-mods": { request: BSVersion, response: Mod[] };
"bs-mods.get-installed-mods": { request: BSVersion, response: Mod[] };
"bs-mods.import-mods": { request: { paths: string[]; version: BSVersion; }, response: Progression<ExternalMod> };
"bs-mods.install-mods": { request: { mods: Mod[]; version: BSVersion }, response: Progression };
"bs-mods.uninstall-mods": { request: { mods: Mod[]; version: BSVersion }, response: Progression };
"bs-mods.uninstall-all-mods": { request: BSVersion, response: Progression };
/* ** bs-playlist-ipcs ** */
"one-click-install-playlist": { request: string, response: Progression<DownloadPlaylistProgressionData> };
+6
View File
@@ -34,3 +34,9 @@ export interface FileHashes {
hash: string;
file: string;
}
// Any mods that are not supported in beatmods
export interface ExternalMod {
name: string;
files: string[];
}