Skip to content

fix: prevent node details hang when device hardware API is unreachable#5514

Merged
jamesarich merged 2 commits into
mainfrom
jamesarich/fix-node-details-hang-unknown-hardware
May 20, 2026
Merged

fix: prevent node details hang when device hardware API is unreachable#5514
jamesarich merged 2 commits into
mainfrom
jamesarich/fix-node-details-hang-unknown-hardware

Conversation

@jamesarich

Copy link
Copy Markdown
Collaborator

Problem

When the Meshtastic device hardware REST API is down or slow, the node details screen hangs with an infinite loading spinner. This was reported by QA testing a t096 (Heltec Mesh Node 096) -- a device whose hardware info IS in the bundled JSON asset but triggers a stale cache refresh on open.

The root cause: DeviceHardwareRepository.getDeviceHardwareByModel() is a suspend function called inside a Flow combine transform. The Ktor HTTP client has a 30s timeout, so the entire UI pipeline blocks waiting for a network call that will never succeed.

Approach

Rather than a band-aid timeout, this PR introduces a reusable stale-while-revalidate Flow pattern for offline-first repository access:

  1. Always emit cached/bundled data first -- the UI never waits for the network
  2. Check staleness and refresh in the background (bounded by a 5s timeout)
  3. Re-emit fresh data if the refresh succeeds and data actually changed

Key design decisions:

  • New staleWhileRevalidateFlow utility in core:data -- cold Flow, no scope ownership needed
  • Mutex single-flight guard prevents concurrent collectors from duplicating the full-table API fetch
  • observeDeviceHardware() Flow method added to DeviceHardwareRepository interface for use in combine transforms; existing suspend getDeviceHardwareByModel() retained for one-shot callers (firmware update screen)
  • FirmwareReleaseRepositoryImpl refactored to use the same utility for consistency
  • Both repos use injected CoroutineDispatchers for testability

Changes

  • core/data/.../util/StaleWhileRevalidateFlow.kt -- new reusable utility
  • core/repository/.../DeviceHardwareRepository.kt -- added observeDeviceHardware() Flow API
  • core/data/.../DeviceHardwareRepositoryImpl.kt -- full rewrite with Mutex, stale-while-revalidate
  • core/data/.../FirmwareReleaseRepositoryImpl.kt -- refactored to same pattern + injected dispatchers
  • feature/node/.../CommonGetNodeDetailsUseCase.kt -- hardware lookup is now a Flow input to combine (not a blocking suspend call inside it)
  • core/domain/.../IsOtaCapableUseCase.kt -- switched to observeDeviceHardware().map {}
  • core/testing/.../FakeDeviceHardwareRepository.kt -- added observeDeviceHardware stub
  • core/domain/.../IsOtaCapableUseCaseTest.kt -- updated mocks

Testing

All affected module tests pass: core:data, core:domain, core:testing, feature:node, feature:firmware. Detekt and spotless clean.

jamesarich and others added 2 commits May 19, 2026 19:10
When the Meshtastic API is down or slow, the node details screen hangs
with an infinite loading spinner because DeviceHardwareRepositoryImpl
blocks on refreshFromNetwork() inside the UI flow's combine transform.

Add a 5-second withTimeoutOrNull around both DeviceHardwareRepository
and FirmwareReleaseRepository network refresh calls. When the API is
unresponsive, the app now falls back gracefully to bundled/cached data
instead of blocking for up to 30+ seconds (the Ktor HTTP timeout with
retries).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Convert DeviceHardwareRepository from a blocking suspend-based API to
a reactive Flow-based API using a new staleWhileRevalidateFlow utility.
This prevents the node details screen from hanging when the Meshtastic
API is down or slow by always emitting cached/bundled data first.

Key changes:
- Add staleWhileRevalidateFlow utility (emit cache, revalidate in background)
- Add observeDeviceHardware() Flow method to DeviceHardwareRepository
- Refactor CommonGetNodeDetailsUseCase to use hardware as a Flow input
- Refactor FirmwareReleaseRepositoryImpl to use same utility
- Add Mutex single-flight guard to prevent duplicate network fetches
- Update IsOtaCapableUseCase to use reactive observeDeviceHardware

Fixes: app infinite spinner on node details for unknown hardware when
the device hardware REST API is unreachable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot added the bugfix PR tag label May 20, 2026
@jamesarich jamesarich merged commit 877909f into main May 20, 2026
16 checks passed
@jamesarich jamesarich deleted the jamesarich/fix-node-details-hang-unknown-hardware branch May 20, 2026 01:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix PR tag

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant