summaryrefslogtreecommitdiff
path: root/src/components/GameConfigurationSelector.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/GameConfigurationSelector.tsx')
-rw-r--r--src/components/GameConfigurationSelector.tsx107
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"