From 372771a228c9fbb31ec4a943dd6cfa9915741c6c Mon Sep 17 00:00:00 2001
From: Party Wumpus <48649272+PartyWumpus@users.noreply.github.com>
Date: Mon, 13 May 2024 14:42:55 +0100
Subject: plugin install progress (#614)

* Frontend progress bars

* Backend bit

* closure is stale i think so no closure for you

* Fix formatting of the progress svgs

* Reset progress bar when new plugin starts downloading
---
 .../modals/MultiplePluginsInstallModal.tsx         | 55 ++++++++++++++++++++--
 1 file changed, 52 insertions(+), 3 deletions(-)

(limited to 'frontend/src/components/modals/MultiplePluginsInstallModal.tsx')

diff --git a/frontend/src/components/modals/MultiplePluginsInstallModal.tsx b/frontend/src/components/modals/MultiplePluginsInstallModal.tsx
index 73b8acb1..ba49ba92 100644
--- a/frontend/src/components/modals/MultiplePluginsInstallModal.tsx
+++ b/frontend/src/components/modals/MultiplePluginsInstallModal.tsx
@@ -1,6 +1,7 @@
-import { ConfirmModal, Navigation, QuickAccessTab } from '@decky/ui';
-import { FC, useMemo, useState } from 'react';
+import { ConfirmModal, Navigation, ProgressBarWithInfo, QuickAccessTab } from '@decky/ui';
+import { FC, useEffect, useMemo, useState } from 'react';
 import { useTranslation } from 'react-i18next';
+import { FaCheck, FaDownload } from 'react-icons/fa';
 
 import { InstallType } from '../../plugin';
 
@@ -27,8 +28,42 @@ const MultiplePluginsInstallModal: FC<MultiplePluginsInstallModalProps> = ({
   closeModal,
 }) => {
   const [loading, setLoading] = useState<boolean>(false);
+  const [percentage, setPercentage] = useState<number>(0);
+  const [pluginsCompleted, setPluginsCompleted] = useState<string[]>([]);
+  const [pluginInProgress, setInProgress] = useState<string | null>();
+  const [downloadInfo, setDownloadInfo] = useState<string | null>(null);
   const { t } = useTranslation();
 
+  function updateDownloadState(percent: number, trans_text: string | undefined, trans_info: Record<string, string>) {
+    setPercentage(percent);
+    if (trans_text === undefined) {
+      setDownloadInfo(null);
+    } else {
+      setDownloadInfo(t(trans_text, trans_info));
+    }
+  }
+
+  function startDownload(name: string) {
+    setInProgress(name);
+    setPercentage(0);
+  }
+
+  function finishDownload(name: string) {
+    setPluginsCompleted((list) => [...list, name]);
+  }
+
+  useEffect(() => {
+    DeckyBackend.addEventListener('loader/plugin_download_info', updateDownloadState);
+    DeckyBackend.addEventListener('loader/plugin_download_start', startDownload);
+    DeckyBackend.addEventListener('loader/plugin_download_finish', finishDownload);
+
+    return () => {
+      DeckyBackend.removeEventListener('loader/plugin_download_info', updateDownloadState);
+      DeckyBackend.removeEventListener('loader/plugin_download_start', startDownload);
+      DeckyBackend.removeEventListener('loader/plugin_download_finish', finishDownload);
+    };
+  }, []);
+
   // used as part of the title translation
   // if we know all operations are of a specific type, we can show so in the title to make decision easier
   const installTypeGrouped = useMemo((): TitleTranslationMapping => {
@@ -66,7 +101,10 @@ const MultiplePluginsInstallModal: FC<MultiplePluginsInstallModalProps> = ({
 
             return (
               <li key={i} style={{ display: 'flex', flexDirection: 'column' }}>
-                <div>{description}</div>
+                <span>
+                  {description}{' '}
+                  {(pluginsCompleted.includes(name) && <FaCheck />) || (name === pluginInProgress && <FaDownload />)}
+                </span>
                 {hash === 'False' && (
                   <div style={{ color: 'red', paddingLeft: '10px' }}>{t('PluginInstallModal.no_hash')}</div>
                 )}
@@ -74,6 +112,17 @@ const MultiplePluginsInstallModal: FC<MultiplePluginsInstallModalProps> = ({
             );
           })}
         </ul>
+        {/* TODO: center the progress bar and make it 80% width */}
+        {loading && (
+          <ProgressBarWithInfo
+            // when the key changes, react considers this a new component so resets the progress without the smoothing animation
+            key={pluginInProgress}
+            bottomSeparator="none"
+            focusable={false}
+            nProgress={percentage}
+            sOperationText={downloadInfo}
+          />
+        )}
       </div>
     </ConfirmModal>
   );
-- 
cgit v1.2.3

