fix: prevent node details hang when device hardware API is unreachable#5514
Merged
jamesarich merged 2 commits intoMay 20, 2026
Merged
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 Flowcombinetransform. 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:
Key design decisions:
staleWhileRevalidateFlowutility incore:data-- cold Flow, no scope ownership neededMutexsingle-flight guard prevents concurrent collectors from duplicating the full-table API fetchobserveDeviceHardware()Flow method added toDeviceHardwareRepositoryinterface for use in combine transforms; existing suspendgetDeviceHardwareByModel()retained for one-shot callers (firmware update screen)FirmwareReleaseRepositoryImplrefactored to use the same utility for consistencyCoroutineDispatchersfor testabilityChanges
core/data/.../util/StaleWhileRevalidateFlow.kt-- new reusable utilitycore/repository/.../DeviceHardwareRepository.kt-- addedobserveDeviceHardware()Flow APIcore/data/.../DeviceHardwareRepositoryImpl.kt-- full rewrite with Mutex, stale-while-revalidatecore/data/.../FirmwareReleaseRepositoryImpl.kt-- refactored to same pattern + injected dispatchersfeature/node/.../CommonGetNodeDetailsUseCase.kt-- hardware lookup is now a Flow input to combine (not a blocking suspend call inside it)core/domain/.../IsOtaCapableUseCase.kt-- switched toobserveDeviceHardware().map {}core/testing/.../FakeDeviceHardwareRepository.kt-- addedobserveDeviceHardwarestubcore/domain/.../IsOtaCapableUseCaseTest.kt-- updated mocksTesting
All affected module tests pass:
core:data,core:domain,core:testing,feature:node,feature:firmware. Detekt and spotless clean.