feat: refresh fx rates on pull to refresh - #1281
Conversation
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Regtest APKDownload bitkit-dev-debug universal APK (expires in 30 days). |
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
jvsena42
left a comment
There was a problem hiding this comment.
Approved — no HIGH/MEDIUM findings at head 6c1340658. (Posted as a comment because GitHub does not allow approving your own PR.)
Checked: Traced HomeViewModel.onPullToRefresh() into CurrencyRepo.triggerRefresh() -> refresh() and the other callers (2-minute poll in repoScope, ContentView foreground refresh, setSelectedCurrency, dev settings). The if (isRefreshing) return early exit sits before the try, so a caller that skips because another refresh is in flight does not clear the flag owned by that other refresh; only the owner resets it in finally, including on cancellation. runSuspendCatching only changes behaviour for CancellationException: CurrencyService.fetchLatestRates() either rethrows it through the retry delay or throws it as lastError, and Ktor 3.3's HttpRequestTimeoutException is an IOException, so network timeouts are still recorded as rates errors and cannot escape into the polling collector and stop the poll loop. The pull-to-refresh indicator is still bound only to wallet sync state in HomeScreen, and the rates and widget refreshes are fire-and-forget in viewModelScope, so neither can leave the spinner stuck; a Home entry torn down mid-fetch now cancels cleanly without a false stale-rates toast.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Verdict: ✅ Approve
Reaudit: diff 2 files.
Findings:
N/A
Audit:
Already done in comment.
Coverage:
QA: journeys and manual tests await all reviewers to approve, author can run it now via comment: @ovi-reviewer test
Reviewed by claude-opus-5-xhigh via gh-pr-review-loop skill
Commands: @ovi-reviewer test · retest · audit (author or owner)
Fixes #622
Related to #1275
This PR makes pull to refresh on Home also refresh exchange rates.
Description
CurrencyRepo.refresh()torunSuspendCatchingso a cancelled caller scope no longer records aCancellationExceptionas a rates error; theisRefreshingreset moves into afinallyso the re-entrancy guard is not left latched when cancellation propagates. Home is the first lifecycle-bound caller oftriggerRefresh(), which is what makes the latent case reachable.Out of Scope
CurrencyRepo.kt: theisRefreshingguard is a non-atomic check-then-set, so a pull landing right as a poll starts can trigger a duplicate fetch. Pre-existing and harmless beyond the extra request.Design
N/A — no UI changes.
Preview
No layout change. Screenshots from the Pixel_9 emulator on this branch after two back-to-back pulls on Home: the refresh spinner shows and no "Rates currently unavailable" toast appears.
QA Notes
Manual Tests
regression:Home → pull to refresh: wallet sync spinner shows and widgets (Price, News, Blocks, etc.) still refresh.Verified on the Pixel_9 emulator (dev build): before this branch a Home pull refreshed the wallet and widgets but logged no rates refresh; on this branch a single pull logged the rates refresh alongside the widget updates, and double pulls produced one rates refresh with no error. No existing journey covered Home pull to refresh, so this adds one, committed on the branch as
journeys/home/pull-to-refresh-rates.xmlwith its rows in thejourneys/README.mdsuites and cross-platform tables (Android-only until the iOS parity work lands). Inline below for review:journeys/home/pull-to-refresh-rates.xml (committed)
Automated Checks
HomeViewModelTest.ktverifies pull to refresh triggers both the rates refresh and the enabled widgets refresh, and that the widgets refresh starts while the rates refresh is still pending.CurrencyRepoTest.ktverifies a cancelled caller is not recorded as a rates error and does not latch the refresh guard.just compile,just test file CurrencyRepoTest,just test,just lint. Both new tests were checked against the pre-fix code and fail there. One fulljust testrun hit a timing-sensitive failure inQuickPayRepoTest.kt(unrelated); it passed on targeted rerun and on a second full run.