fix(plugin): handle fetch errors, race conditions, and error states in library panel
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState, useRef } from "react"
|
||||||
import { PanelSection, PanelSectionRow, DropdownItem, staticClasses } from "@decky/ui"
|
import { PanelSection, PanelSectionRow, DropdownItem, staticClasses } from "@decky/ui"
|
||||||
import { FaCheck, FaTimes, FaChartLine } from "react-icons/fa"
|
import { FaCheck, FaTimes, FaChartLine } from "react-icons/fa"
|
||||||
import {
|
import {
|
||||||
@@ -56,16 +56,34 @@ export default function LibraryAppPanel({ appId, title, hardwareSlug, baseUrl }:
|
|||||||
const [devices, setDevices] = useState<PluginDeviceRow[]>([])
|
const [devices, setDevices] = useState<PluginDeviceRow[]>([])
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
const [device, setDevice] = useState<string>(hardwareSlug ?? "") // "" = all devices
|
const [device, setDevice] = useState<string>(hardwareSlug ?? "") // "" = all devices
|
||||||
|
const [fetchError, setFetchError] = useState<string>("")
|
||||||
|
const reqIdRef = useRef(0)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setPluginApiBaseUrl(baseUrl)
|
setPluginApiBaseUrl(baseUrl)
|
||||||
let cancelled = false
|
let cancelled = false
|
||||||
|
const id = ++reqIdRef.current
|
||||||
async function load() {
|
async function load() {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
const d = await fetchPluginGame(appId, device || null, 3)
|
try {
|
||||||
if (!cancelled) { setData(d); setLoading(false) }
|
const d = await fetchPluginGame(appId, device || null, 3)
|
||||||
const devs = await fetchPluginDevices(appId)
|
if (cancelled || id !== reqIdRef.current) return
|
||||||
if (!cancelled) setDevices(devs)
|
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()
|
load()
|
||||||
return () => { cancelled = true }
|
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 })),
|
.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) {
|
if (!data || !data.game) {
|
||||||
return (
|
return (
|
||||||
<PanelSection title="DeckyVault">
|
<PanelSection title="DeckyVault">
|
||||||
|
|||||||
Reference in New Issue
Block a user