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 ( + <> + + + + + + ); +}