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 { CollapsibleItemGroup, collapsibleItemGroupStyles, usePersistentCollapsed } from "./CollapsibleItemGroup"; 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; } const ENABLED_COLLAPSED_KEY = "lsfg-flatpak-enabled-collapsed-v2"; const AVAILABLE_COLLAPSED_KEY = "lsfg-flatpak-available-collapsed-v2"; 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), []); const [enabledCollapsed, toggleEnabled] = usePersistentCollapsed(ENABLED_COLLAPSED_KEY); const [availableCollapsed, toggleAvailable] = usePersistentCollapsed(AVAILABLE_COLLAPSED_KEY); const enabledApps = useMemo( () => apps.filter((app) => app.enabled).sort((a, b) => a.app_name.localeCompare(b.app_name)), [apps], ); const availableApps = useMemo( () => apps.filter((app) => !app.enabled).sort((a, b) => a.app_name.localeCompare(b.app_name)), [apps], ); const itemFor = (app: FlatpakApp) => ({ id: app.app_id, label: app.app_name, description: `${app.app_id} · ${app.prepared && !app.owned ? "Prepared externally" : "Available"}`, }); if (!selectedAppId) { return ( ({ id: app.app_id, label: app.app_name, description: `${app.app_id}${app.app_id === runningApp?.app_id ? " · Running" : ""}`, }))} collapsed={enabledCollapsed} onToggle={toggleEnabled} onSelect={setSelectedAppId} /> {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"} )}
); }