diff --git a/daemon/web/src/lib/action_errors.svelte.ts b/daemon/web/src/lib/action_errors.svelte.ts new file mode 100644 index 0000000..0847135 --- /dev/null +++ b/daemon/web/src/lib/action_errors.svelte.ts @@ -0,0 +1,24 @@ +export class ActionError extends Error { + // The number of this an identical error has happened. + // This is shown as a number next to the error in the UI. + times = $state(1); + + constructor(message: string, cause: Error) { + super(message); + this.cause = cause; + } +} + +export const action_errors: ActionError[] = $state([]); + +export function add_error(e: Error, msg: string): void { + for (const existing of action_errors) { + if (existing.message === msg) { + existing.times += 1; + return; + } + } + const action_error = new ActionError(msg, e); + action_errors.unshift(action_error); + console.log(action_errors.length); +} diff --git a/daemon/web/src/lib/components/ActionErrors.svelte b/daemon/web/src/lib/components/ActionErrors.svelte new file mode 100644 index 0000000..fb42a13 --- /dev/null +++ b/daemon/web/src/lib/components/ActionErrors.svelte @@ -0,0 +1,86 @@ + + +{#if action_errors.length > 0} +
+
+ + + Error Completing Action {current_error.times > 1 ? `x${current_error.times}` : ''} + +
+ {pos + 1}/{action_errors.length} + + + +
+
+ {current_error.message} + {#if current_error.cause} +
+ Details + {current_error.cause} +
+ {/if} +
+{/if} diff --git a/daemon/web/src/lib/components/ApiRequestButton.svelte b/daemon/web/src/lib/components/ApiRequestButton.svelte index a059d74..b43b60c 100644 --- a/daemon/web/src/lib/components/ApiRequestButton.svelte +++ b/daemon/web/src/lib/components/ApiRequestButton.svelte @@ -1,5 +1,5 @@ diff --git a/daemon/web/src/lib/components/ManifestCard.svelte b/daemon/web/src/lib/components/ManifestCard.svelte index df9e0ff..002228f 100644 --- a/daemon/web/src/lib/components/ManifestCard.svelte +++ b/daemon/web/src/lib/components/ManifestCard.svelte @@ -91,6 +91,7 @@ {/if} diff --git a/daemon/web/src/lib/components/ManifestTableRow.svelte b/daemon/web/src/lib/components/ManifestTableRow.svelte index 211a7b2..d081577 100644 --- a/daemon/web/src/lib/components/ManifestTableRow.svelte +++ b/daemon/web/src/lib/components/ManifestTableRow.svelte @@ -60,6 +60,7 @@ {/if} diff --git a/daemon/web/src/lib/components/ReAnalyzeButton.svelte b/daemon/web/src/lib/components/ReAnalyzeButton.svelte index 002cb89..eda7def 100644 --- a/daemon/web/src/lib/components/ReAnalyzeButton.svelte +++ b/daemon/web/src/lib/components/ReAnalyzeButton.svelte @@ -35,6 +35,7 @@ variant="blue" onclick={handleReAnalyze} ariaLabel="re-analyze" + errorMessage="Error re-analyzing recoding" > {#snippet icon()} diff --git a/daemon/web/src/lib/components/RecordingControls.svelte b/daemon/web/src/lib/components/RecordingControls.svelte index e1b189c..7c00f94 100644 --- a/daemon/web/src/lib/components/RecordingControls.svelte +++ b/daemon/web/src/lib/components/RecordingControls.svelte @@ -1,6 +1,5 @@
{#if server_is_recording} - + {#snippet icon()} {:else} - + {#snippet icon()} { } } +// A wrapper around req that reports errors to the UI +export async function user_action_req( + method: string, + url: string, + error_msg: string +): Promise { + try { + return await req(method, url); + } catch (error) { + if (error instanceof Error) { + console.log('beeeo'); + add_error(error, error_msg); + } + return undefined; + } +} + export async function get_manifest(): Promise { const manifest_json = JSON.parse(await req('GET', '/api/qmdl-manifest')); return new Manifest(manifest_json); diff --git a/daemon/web/src/routes/+page.svelte b/daemon/web/src/routes/+page.svelte index b1f4793..66845bf 100644 --- a/daemon/web/src/routes/+page.svelte +++ b/daemon/web/src/routes/+page.svelte @@ -9,22 +9,33 @@ import DeleteAllButton from '$lib/components/DeleteAllButton.svelte'; import RecordingControls from '$lib/components//RecordingControls.svelte'; import ConfigForm from '$lib/components/ConfigForm.svelte'; + import ActionErrors from '$lib/components/ActionErrors.svelte'; let manager: AnalysisManager = new AnalysisManager(); let loaded = $state(false); let entries: ManifestEntry[] = $state([]); let current_entry: ManifestEntry | undefined = $state(undefined); let system_stats: SystemStats | undefined = $state(undefined); + let update_error: string | undefined = $state(undefined); $effect(() => { const interval = setInterval(async () => { - await manager.update(); - let new_manifest = await get_manifest(); - await new_manifest.set_analysis_status(manager); - entries = new_manifest.entries; - current_entry = new_manifest.current_entry; + try { + await manager.update(); + let new_manifest = await get_manifest(); + await new_manifest.set_analysis_status(manager); + entries = new_manifest.entries; + current_entry = new_manifest.current_entry; - system_stats = await get_system_stats(); - loaded = true; + system_stats = await get_system_stats(); + update_error = undefined; + loaded = true; + } catch (error) { + if (error instanceof Error) { + update_error = error.message; + } else { + update_error = ''; + } + } }, 1000); return () => clearInterval(interval); @@ -84,6 +95,41 @@
+ {#if update_error !== undefined} +
+ + + Connection Error + + This webpage is not currently receiving updates from your Rayhunter device. This + could be do loss of connection or some issue with your device. + {#if update_error} +
+ Error + {update_error} +
+ {/if} +
+ {/if} + {#if loaded}
{#if current_entry}