import { useEffect, useState } from "react"; import { ConfirmModal, Field, PanelSection, PanelSectionRow, ToggleField, showModal, } from "@decky/ui"; 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 ( onAction()} disabled={busy} /> ); } 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 ( ); } export function FlatpaksTab() { const [extensionStatus, setExtensionStatus] = useState(null); const [apps, setApps] = useState(null); const [loading, setLoading] = useState(true); const [operation, setOperation] = useState(null); const [error, setError] = useState(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( 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 ; } return ( <> {error && } {extensionStatus?.success ? runtimeVersions.map(({ version, key }) => ( confirmExtensionOperation(version, extensionStatus[key])} /> )) : } {apps?.success ? apps.apps.length ? apps.apps.map((app) => ( void toggleApp(app)} /> )) : : } ); }