diff options
| author | xXJSONDeruloXx <danielhimebauch@gmail.com> | 2026-09-06 23:12:28 -0400 |
|---|---|---|
| committer | xXJSONDeruloXx <danielhimebauch@gmail.com> | 2026-09-06 23:12:28 -0400 |
| commit | d1f8263bf9f22753ad53e1b0105172a3bd0faf5f (patch) | |
| tree | 5e744fd0d2d61f6ea118d1d53b94a602fed3111b /src/components/GameConfigurationSelector.tsx | |
| parent | 7bc5f685186b1ff03ce0f7c99e7bec411beabaeb (diff) | |
| download | decky-lsfg-vk-d1f8263bf9f22753ad53e1b0105172a3bd0faf5f.tar.gz decky-lsfg-vk-d1f8263bf9f22753ad53e1b0105172a3bd0faf5f.zip | |
feat: organize game profiles and focus enabled settings
Diffstat (limited to 'src/components/GameConfigurationSelector.tsx')
| -rw-r--r-- | src/components/GameConfigurationSelector.tsx | 107 |
1 files changed, 84 insertions, 23 deletions
diff --git a/src/components/GameConfigurationSelector.tsx b/src/components/GameConfigurationSelector.tsx index fcdd0aa..982fcef 100644 --- a/src/components/GameConfigurationSelector.tsx +++ b/src/components/GameConfigurationSelector.tsx @@ -1,4 +1,6 @@ import { ButtonItem, Field, PanelSectionRow } from "@decky/ui"; +import { useEffect, useState } from "react"; +import { RiArrowDownSFill, RiArrowUpSFill } from "react-icons/ri"; import { GameTarget } from "../hooks/useGameConfiguration"; interface Props { @@ -8,42 +10,101 @@ interface Props { onResetAll: () => Promise<void>; } -const profileDescription = (target: GameTarget, running: boolean, active: boolean) => [ - running ? "Now playing" : "", - target.nonSteam ? "Non-Steam" : "Steam", - target.configured - ? active ? "Profile active" : "Profile saved · applies next launch" - : "Not configured · changes apply next launch", -].filter(Boolean).join(" · "); +const CONFIGURED_COLLAPSED_KEY = "lsfg-configured-games-collapsed"; +const AVAILABLE_COLLAPSED_KEY = "lsfg-available-games-collapsed"; -export function GameConfigurationSelector({ targets, runningGame, onSelect, onResetAll }: Props) { - const games = [...targets].sort((a, b) => { - if (a.appid === runningGame?.appid) return -1; - if (b.appid === runningGame?.appid) return 1; - return a.name.localeCompare(b.name); +function usePersistentCollapsed(key: string) { + const [collapsed, setCollapsed] = useState(() => { + try { + return localStorage.getItem(key) === "true"; + } catch { + return false; + } }); + useEffect(() => { + try { + localStorage.setItem(key, String(collapsed)); + } catch { + // Persisting the view preference is optional. + } + }, [collapsed, key]); + + return [collapsed, () => setCollapsed((value) => !value)] as const; +} + +function GameGroup({ + title, + games, + collapsed, + onToggle, + onSelect, +}: { + title: string; + games: GameTarget[]; + collapsed: boolean; + onToggle: () => void; + onSelect: (appid: string) => void; +}) { + if (games.length === 0) return null; + return ( <> - {games.length === 0 && ( - <PanelSectionRow> - <Field label="No installed games" description="Steam has not reported any eligible games" /> - </PanelSectionRow> - )} - {games.map((game) => ( + <PanelSectionRow> + <ButtonItem + layout="below" + bottomSeparator={collapsed ? "standard" : "none"} + onClick={onToggle} + > + {collapsed ? <RiArrowDownSFill /> : <RiArrowUpSFill />} {title} ({games.length}) + </ButtonItem> + </PanelSectionRow> + {!collapsed && games.map((game) => ( <PanelSectionRow key={game.appid}> <Field label={game.name} - description={profileDescription( - game, - game.appid === runningGame?.appid, - game.appid === runningGame?.appid ? runningGame.configured : game.configured, - )} + description={game.nonSteam ? "Non-Steam" : "Steam"} onActivate={() => onSelect(game.appid)} highlightOnFocus /> </PanelSectionRow> ))} + </> + ); +} + +export function GameConfigurationSelector({ targets, runningGame, onSelect, onResetAll }: Props) { + const sortGames = (games: GameTarget[]) => [...games].sort((a, b) => { + if (a.appid === runningGame?.appid) return -1; + if (b.appid === runningGame?.appid) return 1; + return a.name.localeCompare(b.name); + }); + const configuredGames = sortGames(targets.filter((game) => game.configured)); + const availableGames = sortGames(targets.filter((game) => !game.configured)); + const [configuredCollapsed, toggleConfigured] = usePersistentCollapsed(CONFIGURED_COLLAPSED_KEY); + const [availableCollapsed, toggleAvailable] = usePersistentCollapsed(AVAILABLE_COLLAPSED_KEY); + + return ( + <> + {targets.length === 0 && ( + <PanelSectionRow> + <Field label="No installed games" description="Steam has not reported any eligible games" /> + </PanelSectionRow> + )} + <GameGroup + title="Configured" + games={configuredGames} + collapsed={configuredCollapsed} + onToggle={toggleConfigured} + onSelect={onSelect} + /> + <GameGroup + title="Available games" + games={availableGames} + collapsed={availableCollapsed} + onToggle={toggleAvailable} + onSelect={onSelect} + /> <PanelSectionRow> <ButtonItem layout="below" |
