diff --git a/ROADMAP.md b/ROADMAP.md
index 88d17a0..03b85c4 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -1,9 +1,6 @@
# Mod Manager
-- Improved made possible by new architecture
- - Auto-scroll console to latest message while intalling mods
- - Show progress bar (mod N/M) while installing mods
- - Show progress bar while downloading update
+- Show progress bar while downloading update
- Mod config actions
- Typeahead config
- Check for mod updates via GitHub
diff --git a/src/main/worker/BridgeAPI.ts b/src/main/worker/BridgeAPI.ts
index ecb2c06..28291bc 100644
--- a/src/main/worker/BridgeAPI.ts
+++ b/src/main/worker/BridgeAPI.ts
@@ -34,6 +34,7 @@ import { Relative } from 'bridge/Relative';
import type { TSVDataRow } from 'bridge/TSV';
import packageManifest from '../../../release/app/package.json';
import { getAppPath, getHomePath } from './AppInfoAPI';
+import { BroadcastAPI } from './BroadcastAPI';
import { dwordPtr, getCascLib, processErrorCode, voidPtrPtr } from './CascLib';
import { provideAPI } from './IPC';
import { InstallationRuntime } from './InstallationRuntime';
@@ -1021,6 +1022,11 @@ const config = JSON.parse(D2RMM.getConfigJSON());
}
for (let i = 0; i < runtime.modsToInstall.length; i = i + 1) {
+ BroadcastAPI.send(
+ 'installationProgress',
+ i,
+ runtime.modsToInstall.length,
+ );
runtime.mod = runtime.modsToInstall[i];
let code: string = '';
let sourceMap: string = '';
@@ -1095,6 +1101,12 @@ const config = JSON.parse(D2RMM.getConfigJSON());
}
runtime.mod = null;
+ BroadcastAPI.send(
+ 'installationProgress',
+ runtime.modsToInstall.length,
+ runtime.modsToInstall.length,
+ );
+
if (!runtime.options.isPreExtractedData) {
await BridgeAPI.closeStorage();
}
diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx
index 26412d4..8f64e03 100644
--- a/src/renderer/App.tsx
+++ b/src/renderer/App.tsx
@@ -7,6 +7,7 @@ import { Box, Divider, Tab } from '@mui/material';
import './App.css';
import ErrorBoundary from './ErrorBoundary';
import { InstallContextProvider } from './InstallContext';
+import InstallationProgressBar from './InstallationProgressBar';
import { LogsProvider } from './Logs';
import ModList from './ModList';
import ModManagerLogs from './ModManagerLogs';
@@ -58,11 +59,17 @@ function D2RMMRootView() {
overflow: 'hidden',
}}
>
- setTab(value)}>
-
-
-
-
+
+ setTab(value)}>
+
+
+
+
+
+
+
diff --git a/src/renderer/InstallContext.tsx b/src/renderer/InstallContext.tsx
index 2b62030..684c481 100644
--- a/src/renderer/InstallContext.tsx
+++ b/src/renderer/InstallContext.tsx
@@ -1,10 +1,14 @@
-import React, { useMemo } from 'react';
+import React, { useContext, useEffect, useMemo, useState } from 'react';
+import { BroadcastAPI } from './BroadcastAPI';
type SetIsInstalling = React.Dispatch>;
+type SetProgress = React.Dispatch>;
export type IInstallContext = {
isInstalling: boolean;
setIsInstalling: SetIsInstalling;
+ progress: number;
+ setProgress: SetProgress;
};
const InstallContext = React.createContext(null);
@@ -14,13 +18,27 @@ export function InstallContextProvider({
}: {
children: React.ReactNode;
}): JSX.Element {
- const [isInstalling, setIsInstalling] = React.useState(false);
+ const [isInstalling, setIsInstalling] = useState(false);
+ const [progress, setProgress] = useState(0);
const context = useMemo(
- () => ({ isInstalling, setIsInstalling }),
- [isInstalling, setIsInstalling],
+ () => ({ isInstalling, setIsInstalling, progress, setProgress }),
+ [isInstalling, setIsInstalling, progress, setProgress],
);
+ useEffect(() => {
+ const listener = async (
+ installedModsCount: unknown,
+ totalModsCount: unknown,
+ ) =>
+ setProgress(
+ ((installedModsCount as number) / (totalModsCount as number)) * 100,
+ );
+ BroadcastAPI.addEventListener('installationProgress', listener);
+ return () =>
+ BroadcastAPI.removeEventListener('installationProgress', listener);
+ }, [setProgress]);
+
return (
{children}
@@ -29,9 +47,19 @@ export function InstallContextProvider({
}
export function useIsInstalling(): [boolean, SetIsInstalling] {
- const context = React.useContext(InstallContext);
+ const context = useContext(InstallContext);
if (context == null) {
throw new Error('useIsInstalling used outside of a InstallContextProvider');
}
return [context.isInstalling, context.setIsInstalling];
}
+
+export function useInstallationProgress(): [number, SetProgress] {
+ const context = useContext(InstallContext);
+ if (context == null) {
+ throw new Error(
+ 'useInstallationProgress used outside of a InstallContextProvider',
+ );
+ }
+ return [context.progress, context.setProgress];
+}
diff --git a/src/renderer/InstallationProgressBar.tsx b/src/renderer/InstallationProgressBar.tsx
new file mode 100644
index 0000000..8a6e147
--- /dev/null
+++ b/src/renderer/InstallationProgressBar.tsx
@@ -0,0 +1,20 @@
+import { Box, LinearProgress, Tab } from '@mui/material';
+import { useInstallationProgress, useIsInstalling } from './InstallContext';
+
+export default function InstallationProgressBar() {
+ const [isInstalling] = useIsInstalling();
+ const [installationProgress] = useInstallationProgress();
+
+ if (!isInstalling) {
+ return null;
+ }
+
+ return (
+ <>
+
+
+
+
+ >
+ );
+}