Repository navigation
fix: wrap receive tab swipes around - #1027
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
[Low impact] The change is limited to navigation within the Receive workflow. The PR appears safe to merge, with non-blocking improvements to the USDT wrap preview and journey coverage. FindingsSummaryAdds wraparound swipes to Receive.
Two non-blocking improvements remain: show the USDT content during the wrap drag, and cover repeated wrapping with USDT disabled. No runtime UI checks were run; this Linux environment cannot launch the iOS app.
Reviews (1) · Last reviewed commit: "fix: wrap receive tab swipes around" · Reviewed by Greptile |
talosmachina
left a comment
There was a problem hiding this comment.
No findings. Adds wrap pages before the first and after the last tab of the Receive TabView, and wrapAround for the USDT page swipe. Reviewed 6fbf185, full tier, reasoned from the code and CI (iOS does not build on this side).
What I checked, and 4 candidates I ruled out
Read in full: ReceiveQr.swift, View+SwipeGestures.swift, the UsdtReceiveView.swift change, both journeys
Call sites traced: swipeSegmentedTabs (Settings, Shop, All Activity keep the default wrapAround: false), selectedTabBinding
Ruled out
- Duplicate tags: the wrap pages are tagged
.wrapToLast/.wrapToFirst, distinct from every.tab(_), so theTabViewnever sees two pages with the same tag. - Selection stuck on a wrap page: the getter always returns
.tab(selectedTab), so after the setter maps a wrap page to the end tab the pager snaps to the real page. - Wrap page duplicates side effects:
tabContentside effects (hardware load, detail reset) hang offonChange(of: selectedTab)in the parent, not off the page, so the extra page does not trigger them twice. - USDT as the wrap target:
wrapPagerendersColor.clearfor USDT, matching the existing USDT placeholder page.
Merge confidence: 4/5, no findings, but the swipe behaviour has no automated test and Run Tests was still running when this was written.
|
Pushed f9085b4, answering the review threads:
The empty USDT page while swiping predates this PR and is tracked in #1029. |
jvsena42
left a comment
There was a problem hiding this comment.
Code scan: no findings.
Checked and clean:
- No hidden tabs. The pager setter maps
.wrapToLastand.wrapToFirstthroughavailableTabItems, and the getter always returns.tab(selectedTab). - Content follows the tab. The wrap pages render
tabContent(for:)of the tab at the other end, so the QR, copy and edit values match the tab that gets selected. - USDT page. It wraps only on
.address.
Worth checking on the e2e run once this targets master, not a finding: when USDT is off and the last tab has a QR, the wrap page puts a second SpecifyInvoiceButton and ReceiveCopyQR ahead of the real first page in the view order. Several e2e specs tap('SpecifyInvoiceButton'), so if XCUITest takes the first match it could edit the off-screen tab's invoice. All iOS e2e jobs were skipped on this stacked PR.
Device gate (partial): not run yet. Pending: receive/receive-tabs-wraparound.xml and usdt-receive-swipe-tabs.xml, plus Receive open time with USDT off, since the two wrap pages render full QR content. The device result will follow.
@jvsena42 Pushed 4934e28: both wrap pages are now |
…o fix/receive-tabs-wraparound # Conflicts: # Bitkit/Extensions/View+SwipeGestures.swift # Bitkit/Views/Wallets/UsdtReceiveView.swift
|
Pushed 02fad0c, merging the base branch's 047370c (swipe down from the USDT QR). The conflict was in The |
jvsena42
left a comment
There was a problem hiding this comment.
Delta since f9085b4 (4934e28 plus the merge of 047370c): no findings. My e2e note is addressed: non-USDT wrap pages are .accessibilityHidden(true) (ReceiveQr.swift:379), so XCUITest sees only the real tab's SpecifyInvoiceButton and ReceiveCopyQR. pageSelection, wrapPage and tabContent are untouched, so a wrap still lands on the selected tab's content and skips hidden tabs. The merge keeps both wrap branches in swipeSegmentedTabs, and USDT passes wrapAround: true, simultaneous: true, so swipe-down still closes the sheet.
Device gate (partial): not run yet. Pending: receive/receive-tabs-wraparound.xml and receive/usdt-receive-swipe-tabs.xml on the simulator. The Android twin synonymdev/bitkit-android#1593 passed both.
|
Pushed 364756f: the wraparound journey now says the wrap slides on to the other end like a swipe between neighbouring tabs, matching the Android twin. No code change. I also re-recorded the Preview on 02fad0c with every frame kept: both wraps follow the finger, a swipe released above the sheet keeps Receive open, and a swipe down closes the sheet from Savings and from USDT. |
|
Pushed 3fec826: the wraparound journey's two pager steps now swipe in 1200 ms, kept identical to the Android twin (synonymdev/bitkit-android#1608). Journey only, no code change. |
…o fix/receive-tabs-wraparound # Conflicts: # Bitkit/Views/Wallets/Receive/ReceiveQr.swift
jvsena42
left a comment
There was a problem hiding this comment.
Code scan at ae4f35f: no issues found. 364756f and 3fec826 are journey text only. The ae4f35f merge re-wraps the USDT if in ReceiveQr and keeps .tag(ReceivePage.tab(.usdt)), both wrap pages, pageSelection, wrapPage and tabContent unchanged.
Notes:
- The red SwiftFormat check is on
ActivityRow.swift:104-105, which this PR doesn't touch. It comes from master and is fixed by #1035. - This PR is based on #1020 and has to merge after it.
- The Android twin synonymdev/bitkit-android#1608 has a tab-then-swipe regression that iOS doesn't have: the
TabViewselection jumps straight to the page.
Device gate (partial): ae4f35f — not run yet. Pending:
receive/receive-tabs-wraparound.xmlandreceive/usdt-receive-swipe-tabs.xmlon the simulator;- Receive open time with USDT off, since the two wrap pages render full tab content.
Device results will follow in a separate review.
|
@jvsena42 thanks, the Android tab-then-swipe issue you noted is fixed in synonymdev/bitkit-android@8488077 on synonymdev/bitkit-android#1608: a tap on the USDT tab now lands the pager on the USDT page, so the next swipe slides one page. |
|
Pushed cf51fb2: the wraparound journey gains the two USDT-tab-then-leave steps, kept identical to the Android twin. Journey only. |
|
Device gate (partial):
Transitions out of USDT, both to Spending and the wrap to Savings, are crossfades rather than slides because USDT is a separate view. That matches the journey wording. Still running: Receive open time with USDT off, PR vs master. |
|
Device gate (partial 2): Receive open time, tap → Savings QR settled, from
The sheet starts moving after a median 490 ms on the PR and 534 ms on master. I measured no regression. The two master outliers (848 and 871 ms) coincide with load from the parallel emulator. Still running: the two actions |
jvsena42
left a comment
There was a problem hiding this comment.
Approving. One LOW on the new journey wording is inline; it can be fixed here or in a follow-up, your choice. Please reply on the thread with which.
Device gate: cf51fb2 — journeys: 23 passed, 0 failed (receive/receive-tabs-wraparound.xml 16/16 including the two cf51fb2 actions, receive/usdt-receive-swipe-tabs.xml 7/7); perf: Receive open, tap → settled, median 606 ms (n=11) vs master 627 ms (n=6), no regression; Figma: n/a (no new UI); l10n: no UI text changed; Android: same journey file on synonymdev/bitkit-android#1608.
The app code is the same as at ae4f35f, which ran the first 21 actions. cf51fb2 only adds the two actions, and I drove those on the same build. Ran on the iPhone 17 simulator with the tabs Savings, Spending and USDT, with USDT on and off.
The red SwiftFormat check is on ActivityRow.swift, which this PR doesn't touch; it's fixed by #1035. Run Tests is still pending. This PR must merge after #1020.
|
Pushed 912ac74: the two USDT-exit journey checks now read "goes straight from the USDT QR to the Savings QR" and "the same direct change", which hold on both platforms; the journey notes the iOS crossfade. Journey and description only. |
|
Manual testing confirms that selection wraps, but the transition at the ends is abrupt or unanimated. The device QA above also notes that leaving USDT crossfades rather than slides. Changing the journey wording does not address that interaction. I recommend using one persistent circular pager for the Receive tabs, with real USDT content in the pager, instead of duplicate end pages plus selection redirection and a separate USDT view replacing the pager. A feasible native approach is a SwiftUI wrapper around UIPageViewController using its scrolling transition style:
This uses the pager's supported neighbour-navigation API rather than private APIs or duplicate-page reset timing. Apple documents the data-source mechanism here: https://developer.apple.com/library/archive/documentation/WindowsViews/Conceptual/ViewControllerCatalog/Chapters/PageViewControllers.html It would also address the architectural cause of the blank USDT preview tracked in #1029. This is a recommendation, not a claim that a replacement has been implemented or device-verified. Verification should cover repeated wraps in both directions, cancelled/slow swipes, tab-tap followed by swipe, USDT on/off, Auto/Hardware configurations, QR copy/edit, accessibility, and swipe-down dismissal. |
@pwltr thanks. Out of scope: tracked in #1038 (3.2.0), because a circular On the ends: my simulator recordings in the Preview show the wrap page following the finger and sliding on, with the swap to the real page after it settles. Which steps or device showed the abrupt change? If it is the end tabs with USDT off on a device, I'd like to look at that here. |
jvsena42
left a comment
There was a problem hiding this comment.
Approving again. 912ac74 changes only journey text, and my LOW is fixed.
Device gate: 912ac74 (app code ae4f35f) — journeys: 23 passed, 0 failed (receive/receive-tabs-wraparound.xml 16/16, receive/usdt-receive-swipe-tabs.xml 7/7); perf: Receive open, tap → settled, median 606 ms vs master 627 ms; Figma: n/a; l10n: no UI text changed; Android: same journey on synonymdev/bitkit-android#1608.
The red SwiftFormat check is on ActivityRow.swift:104, which is the same on master (#1035 closed without merging) and isn't touched here. It needs a fix on master before merge.
The merge-base changed after approval.
The merge-base changed after approval.
jvsena42
left a comment
There was a problem hiding this comment.
Approving again. The new head 12f6356 is a master merge (#923's CI move) with no conflict resolution, so my earlier review and device gate stand: 912ac74 (app code ae4f35f), 23/23 journey actions passed, Receive open median 606 ms against master 627 ms. CI is re-running.
Closes #1024
Twin: synonymdev/bitkit-android#1593
Stacked on #1020
This PR makes the Receive tabs wrap around: a swipe right on the first tab selects the last one, and a swipe left on the last tab selects the first one.
Expected behaviour, as specified: "we can swipe fw to next, bw to prev, and if on last + fw it 'rotates' back, if on first + bw it rotates vice-versa".
Description
ReceiveQr: the pagedTabViewholds an extra page before the first tab and after the last one, each showing the tab at the other end. Paging onto one selects that tab, so the swipe follows the finger as it does between the other tabs, and a swipe down still closes the sheet. With USDT off, the pager returns to the real page after a wrap, so the next swipe moves to the adjacent tab. The wrap pages are hidden from accessibility, so UI tests and VoiceOver see each tab's buttons once.UsdtReceiveView: the USDT address page passeswrapAroundtoswipeSegmentedTabs, so a swipe left on USDT selects the first tab instead of doing nothing.swipeSegmentedTabsgains thewrapAroundoption, off by default, so Settings, Shop and All Activity keep their current swipes.Sheet dismissal: a sideways swipe released above the sheet does not close Receive on iOS, with the swipes in the Preview.
Leaving USDT, to Spending or by the wrap to Savings, crossfades rather than slides, because USDT replaces the pager with its own view (as since fix: swipe between receive tabs from the usdt qr #1020). The Android twin slides there.
Out of Scope
ReceiveQr: one circular pager with USDT inside it, so leaving USDT slides and the ends need no copy pages — bug: receive tabs crossfade instead of sliding out of usdt #1038, raised in fix: wrap receive tab swipes around #1027 (comment).Design
N/A — no UI changes.
Preview
iPhone 17 Pro simulator (iOS 26.5), dev wallet with USDT enabled, tabs Savings, Spending and USDT, swipes with AXe; recordings played at 2x.
A tap on the Savings QR still copies.
A swipe right on Savings follows the finger onto the wrap page and selects USDT
ios-a-savings-right2x.mp4
A swipe left on USDT selects Savings
ios-b-usdt-left2x.mp4
A swipe left on Savings selects Spending
ios-c-savings-left2x.mp4
A swipe right on Spending selects Savings
ios-d-spending-right2x.mp4
A swipe left on Spending selects USDT
ios-e-spending-left2x.mp4
A swipe right on USDT selects Spending
ios-f-usdt-right2x.mp4
A swipe left on USDT released above the sheet selects Savings; Receive stays open
ios-h-usdt-left-above2x.mp4
A swipe right on Savings released above the sheet selects USDT; Receive stays open
ios-i-savings-right-above2x.mp4
A swipe down on Savings still closes the sheet
ios-l-savings-swipe-down2x.mp4
A swipe down on USDT still closes the sheet
ios-m-usdt-swipe-down2x.mp4
QA Notes
Journeys
receive-tabs-wraparound.xml— swipes wrap from Savings to USDT and back, a swipe released above the sheet keeps Receive open, a tap on the QR still copies, and with USDT off Savings and Spending wrap both waysusdt-receive-swipe-tabs.xml— a swipe left on USDT selects Savings instead of staying on USDTManual Tests
N/A
Automated Checks
N/A