Skip to content

fix: improve home pull-to-refresh - #532

Open
pwltr wants to merge 3 commits into
masterfrom
fix/pull-to-refresh
Open

pwltr wants to merge 3 commits into
masterfrom
fix/pull-to-refresh

Conversation

@pwltr

@pwltr pwltr commented Apr 30, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #519
Closes #344

Description

  • Replaces the native Home refresh indicator with a stable custom indicator and reserved spacing, avoiding the layout jump when refresh completes.
  • Keeps wallet and activity synchronization on the existing refresh lifecycle.
  • Refreshes exchange rates on every eligible Home pull and keeps visible rate-refresh feedback active until the request finishes or 10 seconds elapse; a slower request continues in the background.
  • Adds a shared Home scroll identifier and an iOS journey for deterministic rate-refresh and indicator verification.

Out of Scope

Design

N/A — no design available.

Preview

Empty:

Simulator.Screen.Recording.-.iPhone.17.-.2026-09-24.at.21.55.15.mov

Populated:

Simulator.Screen.Recording.-.iPhone.17.-.2026-09-24.at.21.54.43.mov

QA Notes

Journeys

  • new pull-to-refresh-rates.xml — Home pull visibly refreshes exchange rates, records a successful update, and verifies the indicator appears and clears

Manual Tests

N/A

Automated Checks

  • HomePullRefreshFeedbackTests — 2 passed; normal completion and capped feedback with a continuing background refresh
  • ran simulator frame-difference analysis — refresh completion has no isolated layout jump

@pwltr pwltr changed the title WIP fix(ui): add a custom pull-to-refresh progress indicator Apr 30, 2026
@pwltr
pwltr force-pushed the fix/pull-to-refresh branch 2 times, most recently from ee3b578 to 9de80cf Compare September 24, 2026 19:52
@pwltr pwltr changed the title fix(ui): add a custom pull-to-refresh progress indicator fix: improve home pull-to-refresh Sep 24, 2026
@pwltr
pwltr marked this pull request as ready for review September 24, 2026 19:56
@greptile-apps

greptile-apps Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 4/5

The PR should not merge until the refresh-completion layout movement is addressed.

Findings

  1. P1 Refresh completion moves wallet content ▶
  2. P2 Rates-only pulls lack feedback ▶
  3. P2 Duplicate assertion depends on timing ▶

Summary

The PR replaces Home’s native pull-to-refresh control with a custom gesture observer and spinner, adds an independent exchange-rate refresh, and provides a log-based QA journey.

  • The conditional wallet padding still moves content when refresh completes.
  • Rates-only pulls have no visible refresh feedback, and the journey’s duplicate-fetch assertion is timing-dependent.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Home pull ends] --> B{Wallet page?}
  B -- No --> Z[Ignore]
  B -- Yes --> C[Start rate-refresh task]
  C --> D{Wallet node running?}
  D -- No --> E[Return without spinner]
  D -- Yes --> F{Home refresh already active?}
  F -- Yes --> G[Skip wallet and activity sync]
  F -- No --> H[Show spinner and add wallet padding]
  H --> I[Sync wallet, then activity]
  I --> J[Hide spinner and remove padding]
Loading

Reviews (1) · Last reviewed commit: "fix: improve pull-to-refresh feedback"

Comment thread Bitkit/Views/HomeScreen.swift
Comment thread Bitkit/Views/HomeScreen.swift Outdated
Comment thread journeys/home/pull-to-refresh-rates.xml Outdated
@pwltr
pwltr force-pushed the fix/pull-to-refresh branch from 9de80cf to 165c525 Compare September 24, 2026 20:13
@pwltr
pwltr requested review from a team, ben-kaufman and piotr-iohk and removed request for a team September 25, 2026 10:11
@piotr-iohk

Copy link
Copy Markdown
Collaborator

@pwltr conflicts.

@pwltr
pwltr force-pushed the fix/pull-to-refresh branch from 165c525 to b418053 Compare September 25, 2026 13:41
@pwltr
pwltr force-pushed the fix/pull-to-refresh branch from b418053 to f04ec51 Compare September 25, 2026 13:42

@jvsena42 jvsena42 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two low-severity observations inline, nothing blocking.

Checked and clean:

  • Concurrent pulls: beginRefreshing() is main-actor and synchronous, endRefreshing runs in defer, and the fade-out completion checks isRefreshing so a late animation cannot stop a newer spinner.
  • CurrencyViewModel.refresh() sets isRefreshing before the first await, so a pull cannot race the polling timer into publishing an older response.
  • Node stopped or wiped mid-pull: sync throws and toasts, no hang.
  • Pull trigger only fires on page 0 with at least 80pt overscroll; the pan target detaches and reattaches with the window.
  • Journey log paths and UTC timestamps match the existing hardware-wallet journeys; HomeScrollView matches the Android testTag.

Follow-up outside this diff: the Android journey at https://github.com/synonymdev/bitkit-android/blob/master/journeys/home/pull-to-refresh-rates.xml still says "iOS does not refresh rates on pull". It needs updating once this merges.

Comment thread Bitkit/Views/HomeScreen.swift Outdated
Comment thread journeys/home/pull-to-refresh-rates.xml

@piotr-iohk piotr-iohk left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QA reviewed on f04ec51.

QA review

Reviewed the full PR diff against its merge base, at f04ec51.

No new actionable code findings.

Unresolved: Home pull keeps the indicator for the full rates retry budget. Still present at this revision. A Home pull that starts a rates fetch leaves the custom indicator and the 60-point wallet spacing up until currency.refresh() returns, and beginRefreshing() ignores later pulls for that whole time. fetchLatestRates tries three times through URLSession.shared, with 1 second and then 2 seconds between failures. The shared session's request timeout is 60 seconds by URLSessionConfiguration default, so a host that never answers would hold this indicator for about three minutes. A rates failure surfaces as a toast only through the existing stale-data path, after 10 minutes without a successful refresh. Source analysis at f04ec51; the hung-host wait was not executed in this review. Home should release the indicator and accept another pull without waiting out that retry budget. The rates request can continue in the background.

Additional test cases

  • iOS simulator, onboarded wallet, Home wallet page (HomeScrollView), at least 20 seconds after the last Currency rates refreshed successfully log: pull down to refresh and, within about a second, take a screenshot. A spinner should be visible under the header, with the wallet content shifted down. The indicator is hidden from snapshot-ui. After that success line, the spinner and the extra top space should be gone. This is the visual check for #519. The new journey only asserts the log line. Discussion.
  • Same screen, with the rates host not answering before the request timeout: one pull. The indicator should clear and a second pull should be accepted while the first rates attempt is still retrying.

Rates refresh on Home pull matches the in-flight dedupe and success log from bitkit-android#1281. Widget refresh is outside the scope stated in this PR. The Android journey still says iOS does not refresh rates on pull; that file is not in this diff.

Device testing: not performed in this review. Unit, integration, and e2e checks on this revision succeeded. This review inspected those results and did not re-run them. Those checks do not drive this pull gesture.

@pwltr
pwltr force-pushed the fix/pull-to-refresh branch from 129e58c to 341c8ff Compare September 28, 2026 13:49

@piotr-iohk piotr-iohk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QA reviewed on 341c8ff.

QA review

1 actionable finding — resolve or provide an evidence-backed rebuttal.

QA review

Re-reviewed changes since f04ec51, including affected refresh paths and prior findings, at 341c8ff. This is a follow-up to the previous QA review. Base c906d5a and merge-base 018d904 are unchanged. This pass read the new feedback timeout, its unit tests, and the journey screenshot steps, and inventoried the rest of the PR diff (Home gesture and indicator, rates success log, changelog).

1 actionable finding

  • LOW: Cleared-spinner journey check runs when the rates log appears, before wallet sync finishes

Home pull keeps the indicator for the full rates retry budget does not match this revision: HomePullRefreshFeedback.wait returns after 10 seconds and leaves currency.refresh() running. The spinner journey check now has screenshot steps; the cleared-spinner step is the finding above.

Home pull still dedupes an in-flight rates fetch the same way as bitkit-android#1281, and still logs Currency rates refreshed successfully. Widget refresh is outside the scope stated in this PR. The Android journey still says iOS does not refresh rates on pull.

Additional test cases

  • iOS simulator, onboarded wallet, Home wallet page (HomeScrollView), rates host not answering: pull once. About 10 seconds later the spinner and the extra space above the wallet should be gone, with no success line yet. Pull again. The indicator should show and wallet sync should run, without a second rates request while the first currency.refresh() is still in flight, and without a "Rates currently unavailable" toast from that attempt.
  • Same wallet with Widgets turned off, so Home is a single page: pull down. A spinner should appear under the header and the log should gain a Currency rates refreshed successfully line. This checks that removing .refreshable still leaves a pull gesture when the page does not scroll.
  • Same wallet, normal network, node running, with wallet sync slower than the rates request: when the success line appears, the spinner and extra top space should still be visible, and both should clear only after wallet sync and activity sync finish.

Device testing: not performed in this review. Run Tests and Run Integration Tests succeeded on this revision. This review inspected those conclusions and did not re-run the tests. Those jobs do not drive this pull gesture. e2e-tests-local was still queued and does not cover this journey. No new Appium spec: this pull belongs in the Home journey. Short-content bounce with widgets off was not executed here.

Findings

  • [LOW] Cleared-spinner journey check runs when the rates log appears — inline at journeys/home/pull-to-refresh-rates.xml:19.

Comment thread journeys/home/pull-to-refresh-rates.xml
@pwltr
pwltr requested a review from piotr-iohk September 28, 2026 15:28

@piotr-iohk piotr-iohk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QA reviewed on bea4740.

QA review

No new actionable code findings.

QA review

Re-reviewed changes since 341c8ff, including the Home refresh path and prior findings, at bea47403. This is a follow-up to the previous QA review. Base c906d5a and merge-base 018d904 are unchanged. The new commit only adds the journey wait step. This pass re-read refresh(), the indicator, the rates fetch, and journeys/home/pull-to-refresh-rates.xml, and inventoried the rest of the PR diff.

No new actionable code findings in the follow-up.

Cleared-spinner journey check runs when the rates log appears does not match this revision. The journey now waits for wallet and activity synchronization and the spinner fade-out before the cleared-state screenshot. refresh() still hides the indicator only after wallet.sync(), activity.syncLdkNodePayments(), and HomePullRefreshFeedback.wait finish. That wait ends when currency.refresh() returns, or after 10 seconds, and the rates request keeps running.

Home pull keeps the indicator for the full rates retry budget stays fixed by that same 10-second feedback wait.

Home pull still dedupes an in-flight rates fetch the same way as bitkit-android#1281, and still logs Currency rates refreshed successfully. Widget refresh is outside the scope stated in this PR. The Android journey still says iOS does not refresh rates on pull.

Additional test cases

  • iOS simulator, onboarded wallet on the Home wallet page (HomeScrollView), with bitkit.stag0.blocktank.to not answering: pull once. About 10 seconds later the spinner and the extra space above the wallet should be gone, with no Currency rates refreshed successfully line yet. Pull again. The indicator should show, the log should not gain a second Refreshing rates line while the first fetch is still retrying, and no "Rates currently unavailable" toast should appear from that attempt.
  • Same wallet, Settings → General → Widgets (WidgetsSettings), turn off Show widgets (ShowWidgets), then return to Home so the pager is a single page: pull down. A spinner should appear under the header and the log should gain a Currency rates refreshed successfully line. This checks that removing .refreshable still leaves a pull when the page does not scroll.

Device testing: not performed in this review. Run Tests succeeded on this revision. This review inspected that conclusion and did not re-run the tests. Those tests cover HomePullRefreshFeedback.wait only; they do not drive the pull gesture. Run Integration Tests was still in progress. e2e-tests-local was still pending and does not cover this journey. No new Appium spec: this pull belongs in the Home journey.

Ready for device testing.

@piotr-iohk

Copy link
Copy Markdown
Collaborator

Device result: passed (iOS only, user_override) on bea47403.

iPhone 17 / C0525820-E7A8-44DD-8912-9ABF7A825153. Build: local worktree at that SHA, BACKEND=regtest TREZOR_BRIDGE=true. Setup: qa-fixture.sh ios empty, then overlay the PR Bitkit.app.

Case iOS
pull-to-refresh-rates (rates + spinner appear/clear) passed. Pull → Currency rates refreshed successfully within ~2s; no failure line. Spinner under header captured on a burst frame; cleared after refresh.
Exploration: second pull during/after refresh passed. Back-to-back pulls both logged rates success on the fast path; no lock-out observed.

Notes: a single screenshot right after swipe often missed the spinner (refresh finished in ~2s); burst capture was needed. Hung-host / 10s-cap path not induced. Android not tested. Earlier code-review threads on an older head are unchanged by this run — this is not a review clear of those threads by itself.

@piotr-iohk piotr-iohk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving after iOS device pass on bea4740 (Home pull-to-refresh rates + spinner journey and second-pull exploration). Android not tested in this session.

@pwltr
pwltr enabled auto-merge (squash) September 28, 2026 16:14

@jvsena42 jvsena42 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

341c8ff and bea4740: both of my threads are fixed. HomePullRefreshFeedback.wait caps the feedback at 10 s while the deduplicated currency request keeps running, and the timeout task is cancelled on normal completion. The journey now checks the spinner by screenshot and waits for wallet/activity sync before the cleared-state check. No new findings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Re-fetch currency rates on pull-to-refresh Design review notes for v2.2.0 (v60)

3 participants