feat(ui): mesh Refresh/Broadcast feedback, Set Recommended modal with probe progress bar, live list refresh
Demo images / Build & push demo images (push) Successful in 3m43s

- Refresh button: real handler — calls the new mesh.refresh (radio
  re-query) plus contacts/federation/outbox re-reads, disabled with a
  spinner while running (was an unawaited cache repaint with no feedback
  that skipped half the list's data sources).
- Broadcast button: success ('Sent ✓') and failure states with the error
  in the tooltip; failures no longer vanish as unhandled rejections.
- The store's 5s status poll no longer wipes the error banner each tick.
- Contacts/aliases, federation nodes and the outbox badge refresh every
  ~30s (were mount-only and went permanently stale).
- Peer-list empty state keys on the merged list, so federation rows and
  the channel rows still render with no radio attached.
- Device setup modal: 'Set Recommended' naming, probe progress bar with
  stage labels instead of an anonymous spinner.
- Device panel: name save clears properly (empty = fall back to server
  name) and the confirmation reflects the new live apply.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-07-28 16:37:16 -04:00
co-authored by Claude Fable 5
parent a8c4694c36
commit 3f76b4960a
5 changed files with 138 additions and 17 deletions
+19 -2
View File
@@ -274,12 +274,16 @@ export const useMeshStore = defineStore('mesh', () => {
async function fetchStatus() {
try {
loading.value = true
error.value = null
const res = await rpcClient.call<MeshStatus>({ method: 'mesh.status' })
status.value = res
trackDetectedDevices(res)
} catch (err: unknown) {
error.value = err instanceof Error ? err.message : 'Failed to fetch mesh status'
// Don't clobber a user-action error (broadcast/configure/send) — this
// runs on a 5s poll, and the old `error.value = null` on entry meant
// any real error banner survived at most one poll tick.
if (!error.value) {
error.value = err instanceof Error ? err.message : 'Failed to fetch mesh status'
}
} finally {
loading.value = false
}
@@ -975,6 +979,18 @@ export const useMeshStore = defineStore('mesh', () => {
await Promise.all([fetchStatus(), fetchPeers(), fetchMessages(), fetchDeadmanStatus(), fetchBlockHeaders()])
}
/** Ask the backend to actively re-query the radio's contact table (and by
* extension re-drain daemon events for Reticulum) — the server-side half
* of the Refresh button; refreshAll() alone only re-reads caches. */
async function refreshRadio(): Promise<boolean> {
try {
const res = await rpcClient.call<{ refreshed: boolean }>({ method: 'mesh.refresh' })
return !!res.refreshed
} catch {
return false
}
}
return {
status,
peers,
@@ -1002,6 +1018,7 @@ export const useMeshStore = defineStore('mesh', () => {
broadcastIdentity,
configure,
refreshAll,
refreshRadio,
markChatRead,
clearViewingChat,
sendInvoice,