summaryrefslogtreecommitdiff
path: root/src/components/FGModInstallerSection.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/FGModInstallerSection.tsx')
-rw-r--r--src/components/FGModInstallerSection.tsx103
1 files changed, 103 insertions, 0 deletions
diff --git a/src/components/FGModInstallerSection.tsx b/src/components/FGModInstallerSection.tsx
new file mode 100644
index 0000000..f9f905e
--- /dev/null
+++ b/src/components/FGModInstallerSection.tsx
@@ -0,0 +1,103 @@
+import { useState, useEffect } from "react";
+import { PanelSection, PanelSectionRow, ButtonItem } from "@decky/ui";
+import { checkFGModPath, runInstallFGMod, runUninstallFGMod } from "../api";
+import { ResultDisplay, OperationResult } from "./ResultDisplay";
+import { createAutoCleanupTimer, safeAsyncOperation } from "../utils";
+import { TIMEOUTS, MESSAGES } from "../utils/constants";
+
+export function FGModInstallerSection() {
+ const [installing, setInstalling] = useState(false);
+ const [uninstalling, setUninstalling] = useState(false);
+ const [installResult, setInstallResult] = useState<OperationResult | null>(null);
+ const [uninstallResult, setUninstallResult] = useState<OperationResult | null>(null);
+ const [pathExists, setPathExists] = useState<boolean | null>(null);
+
+ useEffect(() => {
+ const checkPath = async () => {
+ const result = await safeAsyncOperation(
+ async () => await checkFGModPath(),
+ 'useEffect -> checkPath'
+ );
+ if (result) setPathExists(result.exists);
+ };
+
+ checkPath(); // Initial check
+ const intervalId = setInterval(checkPath, TIMEOUTS.pathCheck); // Check every 3 seconds
+ return () => clearInterval(intervalId); // Cleanup interval on component unmount
+ }, []);
+
+ useEffect(() => {
+ if (installResult) {
+ return createAutoCleanupTimer(() => setInstallResult(null), TIMEOUTS.resultDisplay);
+ }
+ return () => {}; // Ensure a cleanup function is always returned
+ }, [installResult]);
+
+ useEffect(() => {
+ if (uninstallResult) {
+ return createAutoCleanupTimer(() => setUninstallResult(null), TIMEOUTS.resultDisplay);
+ }
+ return () => {}; // Ensure a cleanup function is always returned
+ }, [uninstallResult]);
+
+ const handleInstallClick = async () => {
+ try {
+ setInstalling(true);
+ const result = await runInstallFGMod();
+ setInstallResult(result);
+ } catch (e) {
+ console.error(e);
+ } finally {
+ setInstalling(false);
+ }
+ };
+
+ const handleUninstallClick = async () => {
+ try {
+ setUninstalling(true);
+ const result = await runUninstallFGMod();
+ setUninstallResult(result);
+ } catch (e) {
+ console.error(e);
+ } finally {
+ setUninstalling(false);
+ }
+ };
+
+ return (
+ <PanelSection>
+ {pathExists !== null ? (
+ <PanelSectionRow>
+ <div style={{ color: pathExists ? "green" : "red" }}>
+ {pathExists ? MESSAGES.modInstalled : MESSAGES.modNotInstalled}
+ </div>
+ </PanelSectionRow>
+ ) : null}
+
+ {pathExists === false ? (
+ <PanelSectionRow>
+ <ButtonItem layout="below" onClick={handleInstallClick} disabled={installing}>
+ {installing ? MESSAGES.installing : MESSAGES.installButton}
+ </ButtonItem>
+ </PanelSectionRow>
+ ) : null}
+
+ {pathExists === true ? (
+ <PanelSectionRow>
+ <ButtonItem layout="below" onClick={handleUninstallClick} disabled={uninstalling}>
+ {uninstalling ? MESSAGES.uninstalling : MESSAGES.uninstallButton}
+ </ButtonItem>
+ </PanelSectionRow>
+ ) : null}
+
+ <ResultDisplay result={installResult} />
+ <ResultDisplay result={uninstallResult} />
+
+ <PanelSectionRow>
+ <div>
+ {MESSAGES.instructionText}
+ </div>
+ </PanelSectionRow>
+ </PanelSection>
+ );
+}