From c9be32287ad5b72fcd86b10fa726d09dbd97d7fd Mon Sep 17 00:00:00 2001
From: xXJSONDeruloXx <danielhimebauch@gmail.com>
Date: Sun, 6 Sep 2026 17:09:54 -0400
Subject: refactor: organize plugin into native tabs

---
 src/components/FlatpaksTab.tsx | 195 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 195 insertions(+)
 create mode 100644 src/components/FlatpaksTab.tsx

(limited to 'src/components/FlatpaksTab.tsx')

diff --git a/src/components/FlatpaksTab.tsx b/src/components/FlatpaksTab.tsx
new file mode 100644
index 0000000..dc73858
--- /dev/null
+++ b/src/components/FlatpaksTab.tsx
@@ -0,0 +1,195 @@
+import { useEffect, useState } from "react";
+import {
+  ButtonItem,
+  ConfirmModal,
+  Field,
+  PanelSection,
+  PanelSectionRow,
+  Spinner,
+  Toggle,
+  showModal,
+} from "@decky/ui";
+import { FaCheck, FaCog, FaDownload, FaTimes, FaTrash } from "react-icons/fa";
+import {
+  checkFlatpakExtensionStatus,
+  FlatpakApp,
+  FlatpakAppInfo,
+  FlatpakExtensionStatus,
+  getFlatpakApps,
+  installFlatpakExtension,
+  removeFlatpakAppOverride,
+  setFlatpakAppOverride,
+  uninstallFlatpakExtension,
+} from "../api/lsfgApi";
+import { showErrorToast } from "../utils/toastUtils";
+import t from "../i18n/i18n";
+
+const runtimeVersions = [
+  { version: "23.08", key: "installed_23_08" },
+  { version: "24.08", key: "installed_24_08" },
+  { version: "25.08", key: "installed_25_08" },
+] as const;
+
+interface RuntimeRowProps {
+  version: string;
+  installed: boolean;
+  busy: boolean;
+  onAction: () => void;
+}
+
+function RuntimeRow({ version, installed, busy, onAction }: RuntimeRowProps) {
+  return (
+    <PanelSectionRow>
+      <Field
+        label={`Runtime ${version}`}
+        description={installed ? t("FLATPAK_INSTALLED", "Installed") : t("FLATPAK_NOT_INSTALLED", "Not installed")}
+        icon={installed ? <FaCheck style={{ color: "green" }} /> : <FaTimes style={{ color: "red" }} />}
+      >
+        <ButtonItem layout="below" onClick={onAction} disabled={busy}>
+          {busy ? <Spinner /> : installed ? <><FaTrash /> {t("FLATPAK_UNINSTALL_BTN", "Uninstall")}</> : <><FaDownload /> {t("FLATPAK_INSTALL_BTN", "Install")}</>}
+        </ButtonItem>
+      </Field>
+    </PanelSectionRow>
+  );
+}
+
+interface AppRowProps {
+  app: FlatpakApp;
+  busy: boolean;
+  onToggle: () => void;
+}
+
+function AppRow({ app, busy, onToggle }: AppRowProps) {
+  const configured = app.has_filesystem_override && app.has_env_override;
+  const partial = app.has_filesystem_override || app.has_env_override;
+  const status = configured
+    ? t("FLATPAK_STATUS_CONFIGURED", "Configured")
+    : partial
+      ? t("FLATPAK_STATUS_PARTIAL", "Partial")
+      : t("FLATPAK_STATUS_NO_OVERRIDES", "No overrides");
+
+  return (
+    <PanelSectionRow>
+      <Field
+        label={app.app_name || app.app_id}
+        description={`${app.app_id} - ${status}`}
+        icon={<FaCog style={{ color: configured ? "green" : partial ? "orange" : "red" }} />}
+      >
+        <Toggle value={configured} onChange={onToggle} disabled={busy} />
+      </Field>
+    </PanelSectionRow>
+  );
+}
+
+export function FlatpaksTab() {
+  const [extensionStatus, setExtensionStatus] = useState<FlatpakExtensionStatus | null>(null);
+  const [apps, setApps] = useState<FlatpakAppInfo | null>(null);
+  const [loading, setLoading] = useState(true);
+  const [operation, setOperation] = useState<string | null>(null);
+  const [error, setError] = useState<string | null>(null);
+
+  const load = async () => {
+    setLoading(true);
+    try {
+      const [nextStatus, nextApps] = await Promise.all([
+        checkFlatpakExtensionStatus(),
+        getFlatpakApps(),
+      ]);
+      setExtensionStatus(nextStatus);
+      setApps(nextApps);
+    } catch (loadError) {
+      setError(String(loadError));
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  useEffect(() => {
+    void load();
+  }, []);
+
+  const runExtensionOperation = async (version: string, installed: boolean) => {
+    const action = installed ? "uninstall" : "install";
+    setOperation(`${action}-${version}`);
+    setError(null);
+    try {
+      const result = installed
+        ? await uninstallFlatpakExtension(version)
+        : await installFlatpakExtension(version);
+      if (!result.success) throw new Error(result.error || result.message);
+      setExtensionStatus(await checkFlatpakExtensionStatus());
+    } catch (operationError) {
+      const message = String(operationError);
+      setError(message);
+      showErrorToast("Flatpak operation failed", message);
+    } finally {
+      setOperation(null);
+    }
+  };
+
+  const confirmExtensionOperation = (version: string, installed: boolean) => {
+    if (!installed) {
+      void runExtensionOperation(version, false);
+      return;
+    }
+    showModal(
+      <ConfirmModal
+        strTitle={t("FLATPAK_UNINSTALL_TITLE", "Uninstall Runtime Extension")}
+        strDescription={`${t("FLATPAK_UNINSTALL_CONFIRM_PREFIX", "Are you sure you want to uninstall the")} ${version} ${t("FLATPAK_UNINSTALL_CONFIRM_SUFFIX", "runtime extension?")}`}
+        onOK={() => void runExtensionOperation(version, true)}
+        onCancel={() => {}}
+      />,
+    );
+  };
+
+  const toggleApp = async (app: FlatpakApp) => {
+    const configured = app.has_filesystem_override && app.has_env_override;
+    setOperation(`app-${app.app_id}`);
+    setError(null);
+    try {
+      const result = configured
+        ? await removeFlatpakAppOverride(app.app_id)
+        : await setFlatpakAppOverride(app.app_id);
+      if (!result.success) throw new Error(result.error || result.message);
+      setApps(await getFlatpakApps());
+    } catch (operationError) {
+      const message = String(operationError);
+      setError(message);
+      showErrorToast("Flatpak override failed", message);
+    } finally {
+      setOperation(null);
+    }
+  };
+
+  if (loading) {
+    return <PanelSection title={t("FLATPAK_MODAL_TITLE", "Flatpak Extensions")}><PanelSectionRow><Spinner /></PanelSectionRow></PanelSection>;
+  }
+
+  return (
+    <>
+      <PanelSection title={t("FLATPAK_RUNTIME_INSTALLER", "Runtime Extension Installer")}>
+        {error && <PanelSectionRow><Field label={t("FLATPAK_OPERATION_ERROR", "Operation failed")} description={error} icon={<FaTimes style={{ color: "red" }} />} /></PanelSectionRow>}
+        {extensionStatus?.success ? runtimeVersions.map(({ version, key }) => (
+          <RuntimeRow
+            key={version}
+            version={version}
+            installed={extensionStatus[key]}
+            busy={operation === `${extensionStatus[key] ? "uninstall" : "install"}-${version}`}
+            onAction={() => confirmExtensionOperation(version, extensionStatus[key])}
+          />
+        )) : <PanelSectionRow><Field label={t("FLATPAK_ERROR", "Error")} description={extensionStatus?.error || error || t("FLATPAK_ERROR_STATUS", "Failed to check extension status")} /></PanelSectionRow>}
+      </PanelSection>
+
+      <PanelSection title={t("FLATPAK_APPS_TITLE", "Flatpak Applications")}>
+        {apps?.success ? apps.apps.length ? apps.apps.map((app) => (
+          <AppRow
+            key={app.app_id}
+            app={app}
+            busy={operation === `app-${app.app_id}`}
+            onToggle={() => void toggleApp(app)}
+          />
+        )) : <PanelSectionRow><Field label={t("FLATPAK_NO_APPS", "No Flatpak Apps Found")} description={t("FLATPAK_NO_APPS_DESC", "No Flatpak applications are currently installed")} /></PanelSectionRow> : <PanelSectionRow><Field label={t("FLATPAK_ERROR", "Error")} description={apps?.error || error || t("FLATPAK_ERROR_APPS", "Failed to load Flatpak applications")} /></PanelSectionRow>}
+      </PanelSection>
+    </>
+  );
+}
-- 
cgit v1.2.3

