From d1f8263bf9f22753ad53e1b0105172a3bd0faf5f Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Sun, 6 Sep 2026 23:12:28 -0400 Subject: feat: organize game profiles and focus enabled settings --- src/components/GameConfigurationSelector.tsx | 107 +++++++++++++++++++++------ 1 file changed, 84 insertions(+), 23 deletions(-) (limited to 'src/components/GameConfigurationSelector.tsx') 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; } -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 && ( - - - - )} - {games.map((game) => ( + + + {collapsed ? : } {title} ({games.length}) + + + {!collapsed && games.map((game) => ( onSelect(game.appid)} highlightOnFocus /> ))} + + ); +} + +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 && ( + + + + )} + +