fix(plugin): handle fetch errors, race conditions, and error states in library panel

This commit is contained in:
2026-07-13 05:21:28 +08:00
parent 66d16bc25c
commit d63ec4a42e
@@ -1,4 +1,4 @@
import { useEffect, useState } from "react"
import { useEffect, useState, useRef } from "react"
import { PanelSection, PanelSectionRow, DropdownItem, staticClasses } from "@decky/ui"
import { FaCheck, FaTimes, FaChartLine } from "react-icons/fa"
import {
@@ -56,16 +56,34 @@ export default function LibraryAppPanel({ appId, title, hardwareSlug, baseUrl }:
const [devices, setDevices] = useState<PluginDeviceRow[]>([])
const [loading, setLoading] = useState(true)
const [device, setDevice] = useState<string>(hardwareSlug ?? "") // "" = all devices
const [fetchError, setFetchError] = useState<string>("")
const reqIdRef = useRef(0)
useEffect(() => {
setPluginApiBaseUrl(baseUrl)
let cancelled = false
const id = ++reqIdRef.current
async function load() {
setLoading(true)
const d = await fetchPluginGame(appId, device || null, 3)
if (!cancelled) { setData(d); setLoading(false) }
const devs = await fetchPluginDevices(appId)
if (!cancelled) setDevices(devs)
try {
const d = await fetchPluginGame(appId, device || null, 3)
if (cancelled || id !== reqIdRef.current) return
if (d.error && !d.game) {
setFetchError(d.error)
} else {
setFetchError("")
}
setData(d)
setLoading(false)
const devs = await fetchPluginDevices(appId)
if (!cancelled && id === reqIdRef.current) setDevices(devs)
} catch (e) {
if (!cancelled && id === reqIdRef.current) {
setData(null)
setFetchError("Request failed")
setLoading(false)
}
}
}
load()
return () => { cancelled = true }
@@ -89,6 +107,18 @@ export default function LibraryAppPanel({ appId, title, hardwareSlug, baseUrl }:
.map((d) => ({ label: `${d.name} (${d.count})`, data: d.slug })),
]
if (fetchError) {
return (
<PanelSection title="DeckyVault">
<PanelSectionRow>
<div className={staticClasses.Text} style={{ fontSize: "12px", padding: "6px 0", opacity: 0.7, color: "#e74c3c" }}>
<FaTimes /> Could not load DeckyVault data. Please try again later.
</div>
</PanelSectionRow>
</PanelSection>
)
}
if (!data || !data.game) {
return (
<PanelSection title="DeckyVault">