1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
import { ButtonItem, ConfirmModal, Field, PanelSectionRow, showModal } from "@decky/ui";
import { useEffect, useRef } from "react";
import { GameTarget } from "../hooks/useGameConfiguration";
import { CollapsibleItemGroup, collapsibleItemGroupStyles, usePersistentCollapsed } from "./CollapsibleItemGroup";
interface Props {
targets: GameTarget[];
runningGame: GameTarget | null;
onSelect: (appid: string) => void;
onEnableAll: () => Promise<void>;
onResetAll: () => Promise<void>;
focusConfiguredToggle?: boolean;
onConfiguredToggleFocused?: () => void;
}
const ENABLED_COLLAPSED_KEY = "lsfg-enabled-games-collapsed-v4";
const AVAILABLE_COLLAPSED_KEY = "lsfg-available-games-collapsed-v3";
function targetDescription(game: GameTarget): string {
return game.nonSteam ? "Non-Steam" : "Steam";
}
export function GameConfigurationSelector({
targets,
runningGame,
onSelect,
onEnableAll,
onResetAll,
focusConfiguredToggle = false,
onConfiguredToggleFocused,
}: 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 enabledGames = sortGames(targets.filter((game) => game.configured));
const availableGames = sortGames(targets.filter((game) => !game.configured));
const toItem = (game: GameTarget) => ({
id: game.appid,
label: game.name,
description: targetDescription(game),
});
const [enabledCollapsed, toggleEnabled] = usePersistentCollapsed(ENABLED_COLLAPSED_KEY);
const [availableCollapsed, toggleAvailable] = usePersistentCollapsed(AVAILABLE_COLLAPSED_KEY);
const enabledToggleRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!focusConfiguredToggle) return;
const frame = requestAnimationFrame(() => {
enabledToggleRef.current?.querySelector<HTMLElement>('[role="button"], button')?.focus();
onConfiguredToggleFocused?.();
});
return () => cancelAnimationFrame(frame);
}, [enabledGames.length, focusConfiguredToggle, onConfiguredToggleFocused]);
const confirmResetAll = () => {
showModal(
<ConfirmModal
strTitle="Remove all profiles?"
strOKButtonText="Remove all"
strCancelButtonText="Cancel"
onOK={() => void onResetAll()}
onCancel={() => {}}
/>,
);
};
const confirmEnableAll = () => {
showModal(
<ConfirmModal
strTitle="Enable all available games?"
strDescription="Create individual LSFG-VK profiles for every available Steam game and non-Steam shortcut. Flatpak profiles are managed separately in the Flatpak tab."
strOKButtonText="Enable all"
strCancelButtonText="Cancel"
onOK={() => void onEnableAll()}
onCancel={() => {}}
/>,
);
};
return (
<>
<style>
{collapsibleItemGroupStyles}
</style>
{targets.length === 0 && (
<PanelSectionRow>
<Field label="No installed games" description="Steam has not reported any eligible games" />
</PanelSectionRow>
)}
<CollapsibleItemGroup
title="Enabled"
items={enabledGames.map(toItem)}
collapsed={enabledCollapsed}
onToggle={toggleEnabled}
onSelect={onSelect}
toggleRef={enabledToggleRef}
/>
<CollapsibleItemGroup
title="Available"
items={availableGames.map(toItem)}
collapsed={availableCollapsed}
onToggle={toggleAvailable}
onSelect={onSelect}
/>
{availableGames.length > 0 && (
<PanelSectionRow>
<ButtonItem layout="below" onClick={confirmEnableAll}>
Enable all available games
</ButtonItem>
</PanelSectionRow>
)}
<PanelSectionRow>
<ButtonItem
layout="below"
onClick={confirmResetAll}
disabled={!targets.some((target) => target.configured)}
>
Remove all profiles
</ButtonItem>
</PanelSectionRow>
</>
);
}
|