From 61428c823374fc7a53ac294e9060d4b832e1c179 Mon Sep 17 00:00:00 2001 From: Roland Date: Wed, 29 Jul 2026 19:41:55 -0600 Subject: [PATCH] controller: restore useConfigs and useDownloads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The toggle-hook de-duplication replaced a range of customHooks.js that also contained these two unrelated hooks, breaking the production build. Restored byte-identical from master; exports now match master exactly. Jest missed this because it only compiles what tests import — `npm run build` is what catches a missing export. Co-Authored-By: Claude Fable 5 --- app/src/hooks/customHooks.js | 66 ++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/app/src/hooks/customHooks.js b/app/src/hooks/customHooks.js index 1f8b0bf1..2812a15a 100644 --- a/app/src/hooks/customHooks.js +++ b/app/src/hooks/customHooks.js @@ -1024,6 +1024,72 @@ export const useBrowseFiles = () => { return {browseFiles, openFolders, setOpenFolders, fetchFiles}; } +export const useConfigs = () => { + const [configs, setConfigs] = useState(null); + const [loading, setLoading] = useState(false); + + const fetchConfigs = async () => { + setLoading(true); + console.log('fetching configs...'); + try { + const data = await getConfigs(); + setConfigs(data['configs']); + console.debug('fetching configs successful'); + } catch (e) { + // Ignore SyntaxError because they happen when the API is down. + if (!(e instanceof SyntaxError)) { + console.error(e); + } + setConfigs(undefined); + } finally { + setLoading(false); + } + } + + const importConfig = async (fileName) => { + try { + await postImportConfig(fileName); + } catch (e) { + console.error(e); + } + } + + const saveConfig = async (fileName) => { + try { + await postDumpConfig(fileName); + } catch (e) { + console.error(e); + } + } + + return {configs, loading, fetchConfigs, importConfig, saveConfig} +} + +export const useDownloads = () => { + const [onceDownloads, setOnceDownloads] = useState(null); + const [recurringDownloads, setRecurringDownloads] = useState(null); + const [pendingOnceDownloads, setPendingOnceDownloads] = useState(null); + + const fetchDownloads = async () => { + try { + const data = await getDownloads(); + setOnceDownloads(data['once_downloads']); + setRecurringDownloads(data['recurring_downloads']); + setPendingOnceDownloads(data['pending_once_downloads']); + } catch (e) { + console.error(e); + // Display errors. + setOnceDownloads(undefined); + setRecurringDownloads(undefined); + setPendingOnceDownloads(undefined); + } + } + + useRecurringTimeout(fetchDownloads, 3 * 1000); + + return {onceDownloads, recurringDownloads, pendingOnceDownloads, fetchDownloads} +} + // Every hardware toggle works the same way: one field of the shared status object // says whether the subsystem is on, and starting/stopping it is a Controller call. // `on` is null when the status is unknown, which the UI renders as unsupported.