From 6671a5c51fef98c07369f5e5be5ce498a317e868 Mon Sep 17 00:00:00 2001 From: Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com> Date: Thu, 10 Sep 2026 16:31:58 -0400 Subject: feat: add flatpak profiles tab --- src/components/FlatpakTab.tsx | 178 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 src/components/FlatpakTab.tsx (limited to 'src/components') diff --git a/src/components/FlatpakTab.tsx b/src/components/FlatpakTab.tsx new file mode 100644 index 0000000..6392d59 --- /dev/null +++ b/src/components/FlatpakTab.tsx @@ -0,0 +1,178 @@ +import { ButtonItem, DialogButton, Field, Focusable, PanelSection, PanelSectionRow, gamepadDialogClasses } from "@decky/ui"; +import { useCallback, useMemo, useState } from "react"; +import { FaArrowLeft } from "react-icons/fa"; +import type { FlatpakApp, LsfgConfig, WorkaroundState } from "../api/lsfgApi"; +import { ConfigurationSection } from "./ConfigurationSection"; +import { FlatpakWorkaroundsSection } from "./FlatpakWorkaroundsSection"; +import { FpsMultiplierControl } from "./FpsMultiplierControl"; +import { ProfileDetails } from "./ProfileDetails"; + +interface Props { + apps: FlatpakApp[]; + runningApp: FlatpakApp | null; + loading: boolean; + busyAppId: string; + onRefresh: () => Promise; + onEnable: (appId: string) => Promise; + onRemove: (appId: string) => Promise; + onConfigChange: (appId: string, config: LsfgConfig) => Promise; + onWorkaroundChange: (appId: string, state: WorkaroundState) => Promise; +} + +export function FlatpakTab({ + apps, + runningApp, + loading, + busyAppId, + onRefresh, + onEnable, + onRemove, + onConfigChange, + onWorkaroundChange, +}: Props) { + const [selectedAppId, setSelectedAppId] = useState(null); + const selected = useMemo( + () => selectedAppId ? apps.find((app) => app.app_id === selectedAppId) || null : null, + [apps, selectedAppId], + ); + const close = useCallback(() => setSelectedAppId(null), []); + + if (!selectedAppId) { + return ( + + + + + {apps.map((app) => { + const status = app.enabled + ? app.app_id === runningApp?.app_id ? "Enabled · Running" : "Enabled" + : app.prepared && !app.owned ? "Prepared externally" : "Available"; + return ( + + setSelectedAppId(app.app_id)} + highlightOnFocus + /> + + ); + })} + {apps.length === 0 && !loading && ( + + + + )} + + void onRefresh()}> + {loading ? "Refreshing..." : "Refresh Flatpaks"} + + + + ); + } + + if (!selected) { + return ( + + + Back + + + + + + ); + } + + const busy = busyAppId === selected.app_id; + const config = selected.config; + const external = selected.prepared && !selected.owned; + const profileDescription = [ + selected.app_id, + selected.runtime_branch ? `runtime ${selected.runtime_branch}` : null, + selected.enabled ? `profile ${selected.profile}` : null, + selected.app_id === runningApp?.app_id ? "Running" : null, + ].filter(Boolean).join(" · "); + + const changeConfig = async ( + field: keyof LsfgConfig, + value: boolean | number | string | string[], + ) => { + if (!config) return; + await onConfigChange(selected.app_id, { ...config, [field]: value }); + }; + + return ( + + + +
+ + + + + +
+ {selected.app_name} +
+
+
+
+ {!selected.enabled && ( + + + void onEnable(selected.app_id)} + > + {busy ? "Enabling..." : external ? "Prepared externally" : "Enable LSFG-VK"} + + + {selected.error && ( + + + + )} + + )} + {selected.enabled && config && ( + <> + + + onWorkaroundChange(selected.app_id, state)} + /> + + void onRemove(selected.app_id)}> + {busy ? "Removing..." : "Remove Flatpak profile"} + + + + )} + +
+ ); +} -- cgit v1.2.3