summaryrefslogtreecommitdiff
path: root/src/components/FlatpakSetupSection.tsx
blob: 01192501a7cd61a07e0b7ec8cc6f62606f075b06 (plain)
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
import { ButtonItem, Field, PanelSection, PanelSectionRow } from "@decky/ui";
import { useCallback, useEffect, useState } from "react";
import {
  getFlatpakApps,
  prepareFlatpakApp,
  removeFlatpakApp,
  type FlatpakApp,
} from "../api/lsfgApi";
import { showErrorToast } from "../utils/toastUtils";

interface Props {
  enabled: boolean;
}

export function FlatpakSetupSection({ enabled }: Props) {
  const [apps, setApps] = useState<FlatpakApp[]>([]);
  const [loading, setLoading] = useState(false);
  const [busyApp, setBusyApp] = useState("");
  const [error, setError] = useState<string | null>(null);

  const load = useCallback(async () => {
    if (!enabled) {
      setApps([]);
      setError(null);
      return;
    }
    setLoading(true);
    try {
      const result = await getFlatpakApps();
      if (!result.success) throw new Error(result.error || "Could not list Flatpak applications");
      setApps(result.apps || []);
      setError(null);
    } catch (loadError) {
      const message = loadError instanceof Error ? loadError.message : String(loadError);
      setError(message);
    } finally {
      setLoading(false);
    }
  }, [enabled]);

  useEffect(() => {
    void load();
  }, [load]);

  const toggle = async (app: FlatpakApp) => {
    if (busyApp || (app.prepared && !app.owned)) return;
    setBusyApp(app.app_id);
    try {
      const result = app.prepared
        ? await removeFlatpakApp(app.app_id)
        : await prepareFlatpakApp(app.app_id);
      if (!result.success) throw new Error(result.error || "Flatpak setup failed");
      await load();
    } catch (operationError) {
      const message = operationError instanceof Error ? operationError.message : String(operationError);
      showErrorToast("Flatpak setup failed", message);
    } finally {
      setBusyApp("");
    }
  };

  if (!enabled) return null;

  return (
    <PanelSection title="Flatpak Setup">
      <PanelSectionRow>
        <Field
          label="Flatpak applications"
          description="Prepare an emulator or launcher once. Steam game profiles remain separate."
        />
      </PanelSectionRow>
      {error && (
        <PanelSectionRow>
          <Field label="Flatpak unavailable" description={error} />
        </PanelSectionRow>
      )}
      {apps.map((app) => {
        const busy = busyApp === app.app_id;
        const description = [
          app.app_id,
          app.runtime_branch ? `runtime ${app.runtime_branch}` : null,
          app.error,
        ].filter(Boolean).join(" · ");
        const label = busy
          ? "Working..."
          : app.prepared
            ? app.owned ? "Remove" : "Prepared externally"
            : app.error ? "Unavailable" : "Prepare";
        return (
          <PanelSectionRow key={app.app_id}>
            <Field label={app.app_name} description={description}>
              <ButtonItem
                layout="below"
                disabled={busy || Boolean(app.error) || (app.prepared && !app.owned)}
                onClick={() => void toggle(app)}
              >
                {label}
              </ButtonItem>
            </Field>
          </PanelSectionRow>
        );
      })}
      <PanelSectionRow>
        <ButtonItem layout="below" disabled={loading || Boolean(busyApp)} onClick={() => void load()}>
          {loading ? "Refreshing..." : "Refresh Flatpaks"}
        </ButtonItem>
      </PanelSectionRow>
    </PanelSection>
  );
}