Skip to content

fix: stabilize contact selection and amount layouts - #1003

Open
ben-kaufman wants to merge 14 commits into
masterfrom
fix/usdt-payment-layout-stability
Open

ben-kaufman wants to merge 14 commits into
masterfrom
fix/usdt-payment-layout-stability

Conversation

@ben-kaufman

@ben-kaufman ben-kaufman commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Refs #974. Matches the contact-picker behavior in synonymdev/bitkit-android#1564.

Description

  • Disable every contact row while payment details resolve, keeping loading on the selected row and retaining the duplicate-payment guard through navigation.
  • Keep request and subscription amounts leading-aligned, group the existing controls directly below without shifting when BTC/USD changes, and separate the keypad with the existing divider.
  • Wrap controls and allow scrolling at enlarged text sizes so the keypad and Continue remain reachable.
  • Allow fresh USD request and USDT subscription amounts to accept digits immediately, preserving cents when reopening existing amounts.
  • Fit the USDT QR above Edit/Copy/Share on compact screens, keeping Network separate. Scale the shared QR logo down when needed so the displayed code stays scannable, while keeping it inside the QR image’s layout bounds; address details still scroll. Keep the USDT Receive content full-height so switching tabs does not shift the header or footer.

Out of Scope

Design

Request amount — Handoff v62 and amount layout — Refactor v63. Reuses existing typography, buttons, colors and divider; the undecided selector variant is retained.

Preview

Before/after captures were taken at 320/375-point widths with normal and accessibility text, including the scrolled keypad/Continue view. Live Receive captures confirm the network remains visible. Screenshots are available in the author’s local review gallery.

QA Notes

Setup

Use PR commit ac54f6e8d4d3e3c68aee3a6e17661f259990c026, preserving the pinned Package.resolved (Core 0.8.0-rc9 at fab768db, Paykit 0.1.0 at dd8e75e5, LDK 0.7.0-rc.77). Follow the repository’s simulator setup and payment-request fixture recipe resolved at master a61e0c44. Configure an E2E/regtest test wallet with Paykit and USDT enabled; use test identities, never a personal seed. J1 needs a saved linked test contact and a current BTC/USD rate; cancel before publishing a request. J2 needs an onboarded wallet and a paired hardware test wallet for its vendor-label step; copy/scan without creating a deposit. J3 needs two linked test installs, payable endpoints and the separate no-endpoint failure fixture described by the journey; its full recipe requires regtest funds, which were not used in this maintenance run. Preserve existing wallets and do not reset them. Review J1/J2 on compact and standard screens at normal and largest text. The deterministic J3 failure fixture remains tracked in #1031; an unavailable fixture is a gap, not a pass.

The hosted validation below uses Xcode 27.0 (27A266a), iOS 26.1, xcodebuildmcp 2.7.0, one worker and the dedicated unfunded unit-test simulator; CI uses Xcode 16.4. It does not substitute for J1–J3. IDs are newly added to the existing three cases for stable references, without changing their expectations or claiming new execution.

Journeys

  • J1: new paykit-amount-layout.xml — request/subscription alignment, fresh USDT amount entry, preserved cents, legible keypad keys, scrolling to Continue and enlarged-text reachability.
  • J2: new usdt-wallet-receive-layout.xml — cross-platform wallet transition, Receive footer and network/vendor-label parity, compact Copy hit target and displayed QR scan.
  • J3: updated contact-request-or-pay.xml — selected-row progress, all-row disabling and duplicate-payment protection.

Manual Tests

N/A

Automated Checks

  • ran the focused amount, conversion, number-pad, display and component-sizing suites — all 44 tests passed.
  • ran a temporary actual-view iOS fixture — entered 150.50 directly from zero and edited an existing 150.50 to 150.55; Continue preserved the exact amount, note and expiry. The fixture was removed after validation.
  • ran temporary UIHostingController renders — actual amount view at compact/standard widths and normal/accessibility text; verified stable BTC/USD control, divider and keypad positions, wrapping and scrolling to Continue. Captures use settled layer rendering.

Earlier validation passed three receive-sharing tests, including decoding the actual USDT and Bitcoin QR components with logos at compact and normal heights. Isolated Receive renders at 320/375 pt with normal/largest text keep actions separate from Network; all four rendered USDT codes decode correctly. Simulator Receive tab checks passed. A paired hardware-wallet journey and an end-to-end contact payment were not rerun in this follow-up; the duplicate guard and awaited navigation were source-reviewed. SwiftFormat reports no changed-file findings. At that earlier revision, translation validation reported no errors and 14 warnings verified unchanged from its base. Compiler warnings were also checked against unchanged base lines.

  • added testQrLogoPreservesSquareLayoutInTallContainer in UsdtReceiveSharingTests.swift — QR with Bitcoin/USDT logos and without a logo stays square when its parent offers extra height, at compact and normal widths. Existing rendered QR decoding coverage remains.

The focused layout validation compiled the app and all test targets with Swift warnings treated as errors. All five Receive layout/QR-sharing tests passed, including square QR layout, Bitcoin/USDT QR decoding, share contents and full-height USDT content at compact/normal widths with normal/largest text. The new full-height regression failed against the previous code and passes with the flexible spacer that anchors Network and Show Details.

The latest device review confirms stable Receive footer positions, displayed QR decoding, plain-address copying and network switching on iPhone 17 and SE at normal/largest text sizes. The shared keypad legibility issue (#1036) and variable contact latency (#868) remain explicit exceptions; the whole journeys above are therefore not marked passed.

Synced with master 039c7b51, including the merged #1040 formatter correction. Full-repository SwiftFormat 0.64.0 passes (0/831 files need changes); translation validation has zero errors and 14 warnings checked against the base. Independent integration review confirms the Receive fixes and master's swipe behavior are preserved.

CI run 39448955514 completed 2,712 tests with zero failures, plus 43 integration tests with four skips and zero failures, before cancellation during post-test cache cleanup. Its tested merge tree and this final integration differ only in Transifex docs/config/scripts and two Settings files whose changes exactly match SwiftFormat 0.64.0 output. No new local native run was performed for that formatter-equivalent integration. The CI log contains dependency linker warnings; this is not a claim of warning-free CI. Prior-head SwiftFormat CI and translation validation are green. At the prior head 7d68cf13, unit tests, the E2E build and the configured E2E CI job have passed. This does not close the outstanding manual journey assertions listed above. The separate integration run 39465856741 was cancelled at its 60-minute limit after real regtest failures: Blocktank mining/transport errors, balance waits and channel-purchase readiness assertions. It is not a passing run or a post-cache cancellation. The then-current master integration run 39459205073 independently failed the same channel-purchase flow and encountered Blocktank transport/HTTP 500 errors before timing out. That investigation found those integration tests, workflow and payment services unchanged by the PR against its then-current base. No integration assertions were weakened or unchanged jobs repeatedly rerun; this validation remains blocked.

Targeted follow-up render on 7d68cf13 confirms one trailing control row at 402 pt/default text and wrapping at 320 pt/accessibility5, with stable BTC/USD divider positions. A temporary helper-layout probe passed (158 × 28 pt); the fixture was removed and production code remains unchanged. This is targeted layout evidence, not a complete accessibility or payment-journey pass.

Current master integration — ac54f6e8

Merged actual master a61e0c44 with a normal signed merge. The sole manual conflict retains the compact QR/scrolling-details/full-height footer layout and passes master’s USDT-specific copy confirmation through both QrArea and CopyAddressCard; tab wrap behavior remains intact. Other PR changes are preserved, and everything outside the PR diff matches that master. Independent frozen-tree review found no introduced correctness or repository-pattern issue.

  • Focused hosted tests: 33 passed, including all five Receive layout, rendered QR decoding and sharing tests, plus exact-amount/conversion and contact lifecycle tests.
  • CI-selected local suite: 2,833 unit executions and 39 UI executions passed, zero failures. Three Trezor cases and one funded local-Arbitrum case were explicitly skipped. The six CI-excluded integration classes remain excluded. The result bundle groups the 30 launch configurations under one test; execution counts above come from the full XCTest log and device report.
  • Full SwiftFormat 0.64.0: 0/841 files require formatting. Translation validation: zero errors, with all 15 warnings exactly reproduced by the actual-base export. Complete build/test diagnostics and both xcresult reports were inspected. The build includes AppIntents extraction notices; runtime includes thread-priority notices in passing tests and simulator haptic-resource messages. Relevant test/production sources and project configuration were compared with master; this is not a warning-free-run claim or a speculative fix for those warnings.
  • All 14 commits against actual master a61e0c44 verify locally and match the exact GitHub PR commit set, each verified=true / reason=valid. New-head CI is running and is not yet claimed green.

Evidence is retained in the author checkpoint usdt-monitor-20261022-2149 (test arguments, full logs, xcresult reports, base-warning comparison and independent review). J1–J3 were not rerun as complete device journeys in this integration. Existing keypad/latency/failure-fixture gaps above remain open, as do paired-hardware and funded/provider validation; prior device and CI evidence remains tied to its original revisions. No payment, wallet reset or deployment was performed.

Models used

  • Planning/scoping: gpt-6-astra (reasoning: xhigh)
  • Implementation: gpt-6-astra (reasoning: xhigh)
  • Review: gpt-6-astra (reasoning: xhigh)

Model metadata for this integration and its independent frozen-tree gate was checked against the actual parent/reviewer session and turn records. Earlier review evidence remains attributed to its linked reviews.

@ovitrif ovitrif added this to the 3.0.5 milestone Oct 18, 2026
@greptile-apps

greptile-apps Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 4/5

[Medium impact] The changes affect payment entry and contact selection in a few bounded app workflows.

The PR appears safe to merge, with a non-blocking setup correction needed in the new amount journey.

Findings

  1. P2 Journey lacks required setup ▶

Summary

This PR disables contact rows while payment details resolve and updates the shared request/subscription amount screen.

  • Payment request and subscription amounts stay reachable on small screens.
  • The contact picker locks every row while a payment lookup runs.
  • The receive journey checks wallet tabs, network labels, and bottom controls.

No runtime tests or visual captures were performed. The review environment is Linux and cannot render the iOS views.

Reviews (1) · Last reviewed commit: "fix: stabilize contact selection and amo..." · Reviewed by Greptile

Comment thread journeys/paykit-amount-layout.xml Outdated

@talosmachina talosmachina left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No findings. Disables every contact row while a saved-contact payment resolves, and moves the request/subscription amount screen into a scroll view with leading-aligned amount, wrapping BTC/USD controls and a divider above the keypad. Reviewed 6ba39e0, full tier.

What I checked, and 4 candidates I ruled out

Read in full: SendContactSelectView.swift, CreatePaymentRequestView.swift (PaymentRequestAmountView), PubkyContactRow.swift, NumberPad.swift, NumberPadTextField.swift
Call sites traced: PaymentRequestAmountView from ReceiveSheet.swift:150 and SubscriptionAmountView (CreateSubscriptionView.swift:251); both keep their container identifiers, and the PaymentRequest* field, asset, unit and continue identifiers are unchanged.
CI: validate and Greptile green; Run Tests, Run Integration Tests and build-local still pending at review time.
Not built here: iOS does not build on Linux, so this review is reasoned from the code and CI, not from a simulator run.

Ruled out

  • Rows stay disabled after a failed resolve: pay clears selectedContactKey in a defer, so every exit path (toast, thrown error, cancellation, navigation) re-enables the list.
  • Spacers collapse inside the new ScrollView: the content carries .frame(minHeight: geometry.size.height, alignment: .top), so the two flexible spacers still share the leftover height and Continue sits at the bottom when everything fits.
  • Asset/unit actions lost in the stacked fallback: amountActions emits the asset HStack plus the optional unit button, and both ViewThatFits branches render the same builder, so the BTC-only unit toggle and conversion handlers are identical in either layout.
  • Keypad taps eaten by the scroll view: plausible bounce on a fixed-size pad, but not demonstrable without a device run; not posted.

Merge confidence: 4/5, no findings, but the build and test jobs had not finished and the layout was not run on a simulator.

@ovi-reviewer ovi-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: ✅ Approve: one LOW layout shift, and all six cases passed at 71342ec.

Findings:

  • LOW Bitkit/Views/PaymentRequests/CreatePaymentRequestView.swift:344: switching between BTC and USD now moves the controls. The BTC amount (NumberPadTextField, with its fiat line) is about 24 pt taller than the USD amount (NumberPadAmountText). The flexible Spacer that used to absorb the difference is now a fixed 16 pt gap.

    • Tapping USD moves the BTC/USD chips and the divider up 24 pt and the keypad up 12 pt, on both iPhone 17 Pro and iPhone SE.
    • At the largest text size the shift is 52 pt for the chips and 89 pt for the keypad, because the stacked unit button disappears too.
    • On base 7b3f093 the chips and keypad stay where they are. Giving the amount slot the BTC branch's height in both modes would keep them fixed.
    Head, BTC then USDBase, BTC then USD

QA for 71342ec:

Tested on iPhone 17 Pro and iPhone SE (3rd generation) simulators, iOS 26.5. Debug build on staging regtest, with the bitkit-docker USDT fixture and Trezor emulator. Two wallets were linked as contacts.

Test 1: contact rows lock while payment details resolve ✅ Only the tapped row spins, every row is disabled, and a failed resolve shows the error and re-enables them.
  • Failed resolve: I tapped a contact that cannot resolve. Its row alone showed the spinner, both rows dimmed, and the accessibility tree reported both rows as not enabled. A tap on Bob in the meantime opened nothing. After about 14 s the "Unable To Pay Contact / Please try again." toast appeared and both rows became tappable again.
  • Retry: Tapping Bob showed his spinner with both rows dimmed. A tap on the other row was ignored, and one amount screen opened for Bob after about 12 s.
  • Both waits come from contact resolution on staging, with the spinner shown the whole time.
test1-contact-rows.2x.mp4

Test 2: request amount layout in USD ✅ The amount sits leading under the header, the BTC/USD controls trail directly below it, a divider sits above the keypad, and details keep $1,50.

This was a request for Bob, with the simulator on a comma-decimal locale. BTC showed the same structure with the unit button beside BTC/USD, and Continue stayed at the bottom. I closed the request without sending.

test2-request-amount.2x.mp4

Test 3: subscription amount in BTC and USD ✅ Same layout in both, nothing overlaps the keypad or Continue, and accepted methods are Savings only for BTC and USDT only for USD.
  • Amounts: 1000 sats, then $1,50.
  • Exclusive methods: Tapping USDT while in BTC switched the amount to USD and turned Savings off.
  • Recipient: Bob was selectable as the recipient. I closed the sheet without proposing.
test3-subscription-amount.2x.mp4

Test 4: compact width and largest text ✅ On the iPhone SE everything fits at default text, and at the largest size one scroll brings Continue fully into view.
  • Largest text, BTC: the unit button stacks under BTC/USD, and Continue starts below the fold.
  • Scrolling: a scroll that started on the keypad entered no digit. $1,50 carried through to the details.
  • Subscription amount: behaved the same at that size.
  • Width: 375 pt is the narrowest width these simulator runtimes offer, so 320 pt was not driven.
  • Against base 7b3f093 at the largest size: base cuts the unit button to "BIT…" and leaves Continue half off-screen with no way to scroll. The 000 key reads "0…" on both builds; NumberPad is unchanged.
test4-compact-large-text.2x.mp4

Test 5: USDT wallet and Receive layout ✅ Savings and USDT open with the same opaque slide, Show Details keeps its position across Receive tabs, and USDT keeps Network: Arbitrum One after the network list.
  • Show Details: on Savings and USDT it stayed at the same position on every switch, above the home indicator. Spending shows Receive Lightning Funds in that slot.
  • Network: after going back from the network list, Show Details opened the Arbitrum One address.
  • SE, default and largest text: the network row and Show Details stayed visible. The network name truncates at the largest size.
test5-wallets-receive.2x.mp4
test5-receive-compact.2x.mp4

Test 6: hardware tab label ✅ With a paired Trezor emulator, the hardware tab reads Trezor on every switch and never changes to Hardware.

I switched nine times between Trezor, USDT and Savings. At the largest text size the tab truncates to "Tre…", and its accessibility label stays Trezor.

test6-hardware-label.2x.mp4

Not from this PR: on the iPhone SE the USDT Receive tab's Network button sits over the Edit/Copy/Share row, so tapping Copy opens the network list and leaves the clipboard unchanged. Base 7b3f093 does the same, and this PR does not touch Receive.

existing-usdt-copy-head-vs-base.2x.mp4

Covered: the full diff, 7b3f093..71342ec. Every changed path had a device case. Pulling the amount sheet down from mid-screen while at rest still dismisses it, the same as on base.

Twin: synonymdev/bitkit-android#1564 matches on the contact picker. On both, the spinner stays on the selected row and every row is disabled. Android also announces the loading row to accessibility services; iOS rows only report disabled. The amount-entry layout from #974 is not part of it.

Comment thread Bitkit/Views/PaymentRequests/CreatePaymentRequestView.swift
@ben-kaufman

Copy link
Copy Markdown
Contributor Author

Also addressed the compact Receive problem noted in the review, in f55dc6f. The USDT QR now fits above Edit/Copy/Share, as it does in the Bitcoin receive layout, so Network does not cover the actions. The shared QR logo scales down only when space is limited; the existing maximum size is retained. Address details still scroll, and copy/share/URI contents are unchanged.

Checked production-view renders at 320/375 pt and normal/largest text. All four compact QR renders decode to the original payment URI; a new regression decodes the actual USDT and Bitcoin QR components at compact/normal heights. All three receive tests pass, with no new format or translation findings. Android already bounds the QR; its five receive-copy/navigation tests pass, and #1565 has the matching QA journey.

The enlarged-text truncation in the unchanged shared buttons is separate from this overlap fix. The journey still calls for reviewer checks of the physical tap targets.

@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.

Code scan: no findings.

Checked and clean:

  • USD input uses Locale.current.decimalSeparator and BTC uses .. handleInput and parsePaykitAmountInput are unchanged.
  • The hidden amountControls copy and the always-present BTC field are out of the accessibility tree and can't be tapped.
  • QR.swift: the QR content, the share image and copyValue are unchanged. Only the logo's sizing source moved, and both QrArea callers now have a bounded height.
  • SendContactSelectView.disabled: pay() clears selectedContactKey in a defer, so every exit re-enables the rows.

Scope differs from synonymdev/bitkit-android#1565 (QR logo and the contact .disabled change are iOS-only; Android already has the latter on master). The two new journey files are byte-identical.

Device gate (partial): not run yet. Pending: the three journeys on the simulator, including whether the Bitcoin QR sits lower on tall layouts now that the logo GeometryReader makes the ZStack flexible. Perf: not relevant. The device result follows in a second review.

@ovi-reviewer ovi-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: ⚠️ Not verified: the contact handoff exceeds the journey’s three-second target.

Findings: No new code findings. The previous BTC/USD layout-shift finding is fixed.

QA for b087c50:

Built and tested on iPhone 17 Pro and iPhone SE (3rd generation) simulators, iOS 26.5, using regtest and test USDT. Nothing was paid or published.

Test 1: request and subscription amounts, normal text ✅ BTC/USD keeps the divider and keypad stationary, and 1.50 USD reaches details.

Accessibility bounds confirm the positions in both callers. A 1000-sat subscription selects Savings; selecting USDT switches the denomination to USD and deselects Savings. Both forms were closed without sending.

amount-normal-2x.2x.mp4


Test 2: compact amount entry and largest text ✅ Wrapped controls stay in place across BTC/USD, and scrolling reaches the keypad and Continue.

On the iPhone SE, the divider and keypad bounds match in both denominations for requests and subscriptions. All BTC keypad controls were tapped; after clearing input, 1000 sats and 1.50 USD reached details. Default-text compact layout was also checked. The unchanged 000 label still truncates at the largest size, as recorded in the earlier review.

amount-accessibility-2x.2x.mp4


Test 3: compact Receive Copy and QR ✅ Copy reaches the plain address without opening Network, and the displayed QR decodes at normal and largest text.

Copy occupies y483–523; Network starts at y539. Clipboard reads match the QR’s receiving address. Core Image decoding of the captured display returns the correct Arbitrum payment URI at both text sizes; Bitcoin codes also decode. The network round-trip retains Arbitrum and address details scroll. Existing button and network-label truncation remains at the largest text size.

receive-compact-2x.2x.mp4


Test 4: tall Bitcoin and USDT Receive ✅ Both displayed codes decode, their logos remain centered, and the actions stay reachable.

Core Image decoded the captured Bitcoin and USDT displays to their original payment URIs. Copy, the network round-trip and Show Details retain the USDT address and Arbitrum network. Savings, Spending and USDT switching keeps the footer reachable; Spending shows its Lightning onboarding action because this wallet has no channel.

receive-tall-2x.2x.mp4


Test 5: contact-detail Request or Pay ❌ The funded warm Pay handoff takes about 6.5 seconds against the journey’s three-second target.

Pay shows progress with Request disabled during lookup and eventually opens one send amount screen. Closing permits retry, and Request opens the request amount screen. A timed accessibility probe still sees Request Or Pay after 3.2 seconds and first sees SendAmount after 6.5 seconds. The timing assertion did not pass. This caller and its navigation helper are unchanged in the reviewed delta, so this is an unmet journey expectation rather than a new code finding on this PR.

contact-request-or-pay-2x.2x.mp4


The earlier passing report at 71342ec supplies unchanged contact-row locking/failure-retry, opaque wallet-transition and paired hardware vendor-label evidence. QR rendering was rechecked here. No planned case remains unrun. The narrowest device width driven was 375 pt; no 320 pt device or fresh Figma comparison is claimed. The conditional saved-activity/deposit-details step had no saved records to open and its routes are unchanged.

Reviewed the complete delta 71342ec..b087c50 and the prior open finding. Every changed runtime component had a device case; the test, journey and changelog edits were source-reviewed.

Pair PR: synonymdev/bitkit-android#1565 fix: stabilize usdt wallet and payment layouts matches the amount-stability intent and shared journey text. Android already bounds its Receive QR; the shared QR logo adjustment here is iOS-specific. Android was not rebuilt in this run.

@ben-kaufman

Copy link
Copy Markdown
Contributor Author

Thanks for confirming the layout fixes and measuring the contact handoff. The 6.5-second result remains an unmet latency target; I am not counting that journey assertion as passed. This caller is unchanged by the layout PR, so I’m keeping the latency investigation under #868 rather than adding speculative timing changes here. The separate fixed-request work in #1014 adds duration-only resolution/setup diagnostics for incoming requests; it does not claim to explain this contact-detail delay.

@ben-kaufman

Copy link
Copy Markdown
Contributor Author

Resolved the master conflict while retaining the compact amount layout and master’s inputValue conversion fix.

The app builds and all 44 focused amount, conversion, keypad, USDT display and component-sizing tests pass on the simulator. SwiftFormat is clean; translation validation reports no errors and the same 14 warnings as current master b45b22ad. All six PR commits are signed and verified locally and on GitHub. Existing device journeys still apply; this integration did not rerun funded flows.

@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.

The delta since my review, 2297f2c, is a merge of master. The one conflict, in PaymentRequestAmountView.body, keeps this PR's GeometryReader/ScrollView layout and master's inputValue edits (lines 405, 430). usdtInputAmount display and the locale separator are intact, and QR.swift, UsdtReceiveView.swift and SendContactSelectView.swift are untouched. No code findings. One LOW on the journey is inline.

Device gate (partial): not run yet. Pending: paykit-amount-layout.xml, usdt-wallet-receive-layout.xml and contact-request-or-pay.xml step 19 on the simulator.

Comment thread journeys/payment-requests/contact-request-or-pay.xml Outdated

@ovi-reviewer ovi-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: ⚠️ Not verified: the contact handoff still exceeds the journey’s three-second target.

Findings: No new code findings in this delta. The earlier amount-layout fix remains intact. The conditional failure/retry journey setup remains deferred to synonymdev/bitkit-ios#1031, as agreed in the review thread.

QA for 2297f2c:

Built and tested on iPhone 17 Pro and iPhone SE (3rd generation), iOS 26.5 simulators, with linked contacts and fixture USDT. No payment, request or subscription was submitted. Videos are at 2× speed.

Test 1: request and subscription amounts, normal text ✅ BTC/USD keeps the divider and keypad stationary, and 1.50 USD survives editing and reopening Amount.

Accessibility bounds confirm stable positions in both callers. The comma-decimal display retains 1,50 after conversion and draft restoration; request digit editing also returns 1,50. A 1000-sat subscription selects Savings; selecting USDT converts to USD and deselects Savings.

amount-normal-2x.2x.mp4


Test 2: compact amount entry at largest text ✅ Wrapped controls remain stationary across BTC/USD, and scrolling reaches the keypad and Continue.

Both request and subscription details retain 1.50 USD. Accessibility bounds confirm the divider and keypad positions; all BTC keypad controls were tapped after scrolling. Default-text compact request geometry was also checked. The 000 label still truncates, as in the earlier report.

amount-compact-2x.2x.mp4


Test 3: compact Receive and wallet navigation ✅ Copy reaches the plain address without opening Network, and the displayed USDT QR decodes at normal and largest text.

Clipboard reads and Core Image decoding match the receiving address and Arbitrum payment URI. Network remains Arbitrum after returning; address details and bottom actions remain reachable. Savings, USDT and saved-activity transitions stay opaque. At largest text, Network and Show Details wrap; the short Edit/Copy/Share labels still clip.

receive-2x.2x.mp4


Test 4: funded contact-detail Request or Pay ❌ The warm Pay handoff takes about 6.4 seconds against the three-second target.

Timed accessibility polling still sees Request Or Pay with Request disabled at 3.4 seconds and first sees SendAmount at 6.4 seconds. Progress is shown, one amount screen opens, closing permits retry, and Request opens PaymentRequestAmount. This caller is unchanged; the author’s response keeps the existing latency investigation under synonymdev/bitkit-ios#868. I have not counted the timing assertion as passed.

contact-handoff-2x.2x.mp4


The passing report at 71342ec supplies unchanged selected-row locking, failure/retry and paired hardware-label evidence. The report at b087c50 supplies unchanged tall Bitcoin QR evidence; compact USDT decoding was repeated here. No planned case remains unrun. The narrowest device was 375 pt; no fresh Figma comparison or 320 pt device run is claimed.

Reviewed the complete delta b087c50..2297f2c and prior open feedback. The integrated request/subscription amount paths were driven; the rest of the master integration was source-reviewed outside these layout journeys.

Pair PR: synonymdev/bitkit-android#1565 fix: stabilize usdt wallet and payment layouts matches amount stability and compact QR intent. Its contact failure setup differs as tracked in synonymdev/bitkit-ios#1031. Android was not rebuilt in this run.


Reviewed by gpt-6.1-sol-high via gh-pr-review-loop skill
Commands: @ovi-reviewer review · test · retest

@jvsena42

Copy link
Copy Markdown
Member

Device gate (partial): 2297f2c, iPhone 17 (402 pt), English. paykit-amount-layout.xml steps 1–4 pass:

  • At $1.50 the amount leads near the header, with BTC/USD trailing and the divider above the keypad.
  • Three BTC↔USD switches at default text gave identical screenshots, and 1.50 USD → ₿1,827 → $1.50 shows no drift.
  • At accessibility-XXXL the unit button wraps, but the divider and keypad keep the same y. The keypad scrolls so every key and Continue are reachable, and Continue shows $1.50.

⚠️ Not run yet: amount-layout steps 5–7 (subscription amount, accepted methods; compact width needs a compact simulator), usdt-wallet-receive-layout.xml, and contact-request-or-pay.xml.

@ben-kaufman

Copy link
Copy Markdown
Contributor Author

Thanks for the completed compact-device checks. I have kept the three-second handoff assertion explicitly unpassed and linked the 6.4-second measurement under #868 in Out of Scope. I passed the measurement to the separate performance investigation; this layout PR will not change contact preparation speculatively. The shared controls’ largest-text clipping was also recorded in the earlier base comparison and remains outside this layout delta. The deterministic failure setup remains tracked in #1031.

@ben-kaufman

Copy link
Copy Markdown
Contributor Author

Fixed the matching fresh-amount bug found on Android #1565 in e539585. Selecting USDT before opening a new subscription amount now allows digit entry immediately; existing cents and conversion behavior stay intact.

Verified the actual iOS editor with zero → 150.50 and existing 150.50 → 150.55, including the Continue result. All 44 focused regression tests passed, changed-file formatting is clean, and all PR commits are locally signed and GitHub-verified. The amount journey now covers fresh USDT selection and reopening the entered amount.

@ben-kaufman
ben-kaufman requested a review from jvsena42 October 20, 2026 01:25
@ben-kaufman

Copy link
Copy Markdown
Contributor Author

Integrated current master after #1002 merged. The only conflict was in contact-request-or-pay.xml; it now retains both selected-row loading/duplicate-flow checks and the prepared no-endpoints failure/retry scenario.

The Swift diff is unchanged from the previously tested head. SwiftFormat, journey XML parsing and whitespace checks pass. Translation validation has zero errors and the same 14 warnings as current master. Independent review confirmed both branches’ changes are preserved, and all eight PR commits verify locally and on GitHub. CI is running again; no local build or device tests were rerun for this documentation-only resolution, preserving the ongoing latency measurements.

@ben-kaufman

Copy link
Copy Markdown
Contributor Author

Integrated current master (cf2b97e) and resolved the conflicts without changing the PR’s payment behavior. App and test-target compilation pass. Changed-file formatting passes; the two full-repository formatting findings are also present on master (failed master lint). A fresh simulator test run is blocked by CoreSimulator boot hanging; previous test results remain documented in the PR. Re-requesting review.

@ben-kaufman

Copy link
Copy Markdown
Contributor Author

Applied the shared formatting-only repair for the inherited ActivityRow lint failure. The Swift code is identical apart from whitespace. Full-repository SwiftFormat 0.64.0 passes; translation validation has zero errors and the same 14 warnings as master. App/test-target compilation passed before this formatting-only edit; no new runtime test result is claimed. Re-requesting review.

@ovi-reviewer ovi-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: ⚠️ Not verified: the contact timing and largest-text keypad legibility assertions remain unmet at fff038e; the added Receive integration check is unrun.

The PR moved to 1780ff2 after this run; these results are for fff038e.

Findings: No new code findings in this delta. The failures below were already reported before this head.

QA for fff038e:

Built and tested on iPhone 17 Pro and iPhone SE (3rd generation), iOS 26.5 simulators, with linked test contacts and fixture USDT. No payment, request or subscription was submitted. Videos are at 2× speed.

Test 1: fresh USD request and USDT subscription entry ✅ Both accept 1.50 from zero without Delete, and reopening preserves the cents.

The request retains 1,50 through BTC/USD switches and reopening; editing its last digit produces 1,55 in details. Selecting USDT before opening a new subscription amount accepts 1,50 immediately.

Accessibility bounds confirm stable divider and keypad positions. A 1000-sat subscription selects Savings; selecting USDT converts to USD and deselects Savings.

amount-normal-2x.2x.mp4


Test 2: compact editor and keypad legibility at largest text ❌ The unchanged 000 key still displays as “0…”, so the journey’s legibility assertion is not passed.

Fresh USD requests and USDT-first subscriptions accept 1,50 and preserve it on reopening. BTC/USD keeps the divider and keypad stationary, including the wrapped unit control. Scrolling reaches every key and Continue; all BTC keys were tapped, and cleared input reaches 1000-sat subscription details.

The earlier review already records the same 000 truncation on base. This is an unmet assertion, not a new finding on the changed lines.

amount-compact-2x.2x.mp4


Test 3: funded contact-detail Request or Pay ❌ A repeated warm Pay handoff exceeds the three-second target.

Pay shows progress with Request disabled and eventually opens one SendAmount. Closing enables another attempt, and Request opens PaymentRequestAmount.

On the repeat, timed accessibility polling first observes progress at 1.05 seconds after the tap command, still sees Request or Pay at 4.59 seconds, and first sees SendAmount at 6.00 seconds. The author’s response keeps this existing latency under synonymdev/bitkit-ios#868 contact payment latency on staging.

contact-handoff-2x.2x.mp4


Test 4: prepared endpointless contact failure and retry ✅ Selected-row progress, all-row disabling, error recovery and retry hold.

Only the selected row spins, and accessibility reports both rows disabled. Tapping the other row during lookup opens no second flow. A linked fixture contact with its published methods withdrawn shows “Unable To Pay Contact” and “The contact you're trying to send to hasn't enabled payments.” Both rows then become enabled; retrying the payable contact opens one amount screen. Both flows were cancelled without paying.

contact-retry-2x.2x.mp4


Test 5: compact Receive after master integration ⏭️ Not driven before the PR advanced to 1780ff2.

I added this check for the integrated ReceiveQr ViewBuilder simplification. Testing stopped after the active contact case when the head moved, so this run establishes no new Receive integration result.


The report at 2297f2c supplies earlier compact Receive evidence. The report at 71342ec supplies the unchanged paired hardware vendor-label check. The narrowest driven width was 375 pt; no 320 pt device run or fresh Figma comparison is claimed.

Reviewed the complete delta 2297f2c..fff038e and prior open feedback. Amount entry and contact selection were driven; the rest of the master integration was source-reviewed outside those journeys.

Pair PR: synonymdev/bitkit-android#1565 fix: stabilize usdt wallet and payment layouts matches fresh USD initialization and the shared journey expectations. Android was not rebuilt in this run.

These results cover fff038e. The newer head 1780ff2 was not reviewed or tested here.


Reviewed by gpt-6.1-sol-high via gh-pr-review-loop skill
Commands: @ovi-reviewer review · test · retest

@ben-kaufman

Copy link
Copy Markdown
Contributor Author

Thanks for the rerun. Out of scope: the shared 000 key clipping is now tracked in #1036 for 3.1.0 because the keypad is unchanged from current master and the earlier device comparison reproduced it on base. Its legibility assertion remains unpassed. Android’s existing compact/largest-text report passes the keypad fit checks; no matching defect is established there.

The roughly six-second contact handoff remains under #868 and the separate latency investigation. The Receive integration case still needs a fresh device run; I am not counting it as passed. The change from the reviewed fff038e to 1780ff2 only formats the existing ActivityRow conditional and changes no behavior. Full-repository formatting now passes.

@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.

Code scan at 1780ff2: no issues found. I reviewed e539585 and 1780ff2, plus both merges with --remerge-diff.

  • c81f534 keeps both the duplicate-flow check and #1002's endpointless-contact failure and retry.
  • fff038e keeps this PR's GeometryReader/ScrollView body; master's side of that conflict was formatting only.

Checked and clean:

  • The zero-draft guard in .task copies the existing converted.atomic == 0 ? "" path. Continue still submits amount with the draft's endpoints, note and expiry.
  • The ActivityRow change is whitespace only.
  • QR.swift, the .disabled change in SendContactSelectView and the UsdtReceiveView logic are unchanged from my last review.
  • contact-request-or-pay.xml step 19 now walks the endpointless contact, the error and the retry, and the precondition is listed. I resolved that thread earlier.
  • Parity with synonymdev/bitkit-android#1565: same zero-draft fix, same 0.2 / 68 logo ratio, and byte-identical new journeys.

Perf: not relevant. l10n: no new keys. The Edit/Copy/Share clipping at the largest text size is on master and tracked in #1036.

Device gate (partial): 1780ff2 — not run yet. Pending: paykit-amount-layout.xml, usdt-wallet-receive-layout.xml and payment-requests/contact-request-or-pay.xml on the simulator. Device results will follow in a separate review.

@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 review

Scope: Reviewed the full PR diff against its merge base, at 1780ff2.

No new actionable code findings.

Saved-contact rows stay disabled until resolution finishes: pay clears selectedContactKey in a defer on success, failure, and navigation. A zero USD draft loads as an empty string, so a new request or USDT subscription accepts digits immediately; a non-zero amount reloads inputValue and keeps the cents. The amount row reserves the taller Bitcoin field and unit-control height, so switching BTC/USD leaves the divider and keypad in place. The shared QR logo uses the same 20% / 68 pt cap as Android, and the USDT receive code sits above Edit/Copy/Share with Network kept separate.

Compared those flows with bitkit-android#1565 at a4ce71cd: row disabling, zero-amount initialization, and the logo ratio match. Android locks the QR to a square; this was source comparison only.

Run Tests on this revision passed, including testCompactQrWithLogoRemainsScannable. SwiftFormat passed. This review did not execute those jobs or a simulator. Device testing: not performed. Prior device runs on fff038e and earlier commits still leave the contact handoff over the journey's three-second mark (#868) and the unchanged keypad 000 truncation at the largest text size (#1036).

@ovi-reviewer ovi-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: ⛔️ Request changes — two journey assertions remain unpassed at 1780ff2.

Findings: I found no new defects in the delta. The failures below reproduce the existing, explicitly deferred keypad and contact-latency issues.

QA for 1780ff2: I built and installed this head on iOS 26.5 simulators. Recordings play at 2×; timing below uses the original run.

Test 1: Compact amount entry at largest text ❌ Fresh amounts, reopening, stable controls and scrolling work; the shared 000 label still truncates.

On a 375-point iPhone SE, I entered USD 1.50 directly from zero without Delete in a request and a USDT-first subscription. Continue and reopening retained the cents. Accessibility bounds matched for the BTC/USD controls, divider and keypad across currency switches in both editors. Scrolling exposed every key and Continue; tapping 1 then 000 produced 1,000 sats, but the visible 000 label reads 0… in both editors.

The subscription selected Savings for sats; choosing USDT converted the amount to USD and deselected Savings. I selected a recipient and cancelled without proposing. The failed legibility assertion remains tracked in bitkit-ios#1036 Keep the 000 keypad label readable at largest text.

amount-compact-2x.2x.mp4
Test 2: Funded contact-detail Request or Pay ❌ Progress, Request disabling, closing and retry work; a warm repeat exceeds three seconds.

With linked staging contacts and a funded test USDT wallet, both Pay attempts opened one SendAmount screen. The progress state disabled Request. Closing restored the contact action; Request subsequently opened PaymentRequestAmount. I cancelled without sending a payment or request.

A host-local monotonic accessibility probe on the repeat recorded the physical tap completing at 0.738 s. The sheet was still loading during the 3.841–4.309 s observation, over 3.10 s after tap completion. SendAmount was first observed by 6.539 s from tap-command start. This fails the journey's three-second target; the existing latency follow-up is bitkit-ios#868 Contact payment latency on staging.

contact-handoff-2x.2x.mp4
Test 3: Compact Receive after master integration ✅ QR decoding, Copy, network round-trip, scrolling details and footer reachability pass.

At normal and largest text on the iPhone SE, I decoded the displayed Bitcoin and USDT QR captures with Core Image. USDT Copy produced the plain displayed address, checked from the clipboard, without opening Network. The network-selection round-trip retained Arbitrum, details scrolled, and the Savings, Spending and USDT footer actions remained reachable. The existing largest-text Edit/Copy/Share label clipping is tracked in the shared accessibility follow-up above.

receive-integration-2x.2x.mp4

I retained these passing reports for unchanged behavior:

Scope: I reviewed the contiguous fff038e…1780ff2 delta and outstanding QA; its only change is whitespace in ActivityRow.swift, checked in source without a separate device case. No cases remain unrun in this plan. This run adds no 320-point, Figma or physical-hardware coverage.

Twin: bitkit-android#1565 fix: stabilize usdt wallet and payment layouts at a4ce71c matches the two new journey files byte-for-byte; Android was not rebuilt in this run.


Reviewed by gpt-6.1-sol-high via gh-pr-review-loop skill
Commands: @ovi-reviewer review · test · retest

@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.

Device gate (partial): 1780ff2 on iPhone 17 (simulator, 402 pt, iOS 26.5). One LOW from the device is inline: the Bitcoin QR and its buttons sit lower than on master.

  • paykit-amount-layout.xml: steps 1-6 passed.
    • BTC/USD switching keeps the controls, divider, keypad and Continue at the same y in every state, including AX-XL and AX5.
    • At AX5, Continue is reachable by scrolling.
    • A subscription amount from 0 accepts the first digit.
    • A USD amount accepts USDT only, and a BTC amount accepts Savings only.
  • usdt-wallet-receive-layout.xml: steps 1-4 and 7 passed.
    • Opaque push transitions, and Show Details clear of the home indicator on every tab.
    • "Network: Arbitrum One" is shown, and Copy copies the plain address.
    • At AX5 the USDT QR now fits above Edit/Copy/Share, where on master it overflows under them.
  • ⚠️ Not run:
    • step 7 of paykit-amount-layout.xml and step 6 of usdt-wallet-receive-layout.xml (compact width): no compact simulator on this Mac, and I'm reusing the existing ones;
    • step 5 of usdt-wallet-receive-layout.xml (hardware tab label): needs the Trezor emulator paired;
    • payment-requests/contact-request-or-pay.xml: two-party, pending a cross-device run with Android.

Seen on master too, not from this PR:

  • At AX5 the Edit/Copy/Share labels break mid-word.
  • A USD subscription amount re-converted through BTC drifts 1,50 → 1,51.
  • With region en-BR, the USD asset uses "," while BTC-in-USD uses ".".

The remaining items, and the code/review follow-up, will come in a separate review.

Comment thread Bitkit/Components/QR.swift
@ben-kaufman
ben-kaufman requested a review from jvsena42 October 20, 2026 11:30

@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 review

Scope: Follow-up review of b17f49c since 1780ff2, using the baseline QA review. Inherited coverage: saved-contact row disable and cleanup, request and subscription amount layout, compact QR decode plus the paykit-amount-layout, usdt-wallet-receive-layout, and contact-request-or-pay journeys, and Android parity at a4ce71cd for contact disable and zero-USD initialization. This pass rechecked the shared QR logo layout and its receive callers, the new tall-container sizing test, and Android QR logo geometry at a4ce71cd12ed.

No new actionable code findings.

The logo GeometryReader is an overlay on the QR container, so Savings, Spending, hardware, and USDT codes keep the square image height. That removes the extra vertical space measured on Savings at 1780ff2 in the device comment. The logo stays centered at min(68, 20% of the shorter side), with the inner image at 50/68, matching Android QrCodeImage's square container and the same ratio. Profile and address-viewer codes pass no logo.

The author reports that all four QR/sharing tests passed on this commit, including the new square-layout case. This review did not run them. Run Tests was still in progress; Run Integration Tests had passed. Device testing was not performed in this review; the position measurements are from 1780ff2. Keypad legibility (#1036) and contact preparation latency (#868) remain the previously deferred journey gaps.

Device testing: not performed in this review.

Suggested additional test cases

  • iOS, iPhone 17 simulator, iOS 26.5, same wallet as the 1780ff2 Savings measurement: open Receive on Savings and confirm QRCode y is 258 and ReceiveCopyQR y is 644, matching master cf2b97e. Switch between Savings and USDT and confirm the vertical jump is about 6 pt. Scan the displayed Bitcoin and USDT codes.

piotr-iohk
piotr-iohk previously approved these changes Oct 20, 2026

@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.

Approve at b17f49c. Current-head review and targeted device checks confirm the QR correction; amount entry, scrolling and contact-row disabling are covered.

The remaining largest-text caption/keypad issues also reproduce on master and remain separate follow-ups, including #1036. Contact-preparation latency remains tracked in #868. These are not blockers for this PR.

@ben-kaufman

Copy link
Copy Markdown
Contributor Author

CI follow-up: the new merge-checkout formatting job fails on Electrum/RGS Settings from master 5ce560f4, not on the Receive changes. I read the full diagnostics and reproduced the same violations on those exact base files with SwiftFormat 0.64.0; neither is in this PR’s diff. Existing #1040 covers them. Local full formatting and all five Receive layout/QR-sharing tests pass. I’ll recheck merge CI once the base fix lands, without duplicating that PR or repeatedly retrying the unchanged failure.

@ovitrif

ovitrif commented Oct 20, 2026

Copy link
Copy Markdown
Collaborator

@ben-kaufman the red SwiftFormat check here comes from master's own files, which #1040 fixed (merged). Syncing this branch with master turns it green; if it still fails after that, run SwiftFormat 0.64.0 on this PR's changed files.

@ovi-reviewer ovi-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: ⛔️ Request changes — two existing journey assertions remain unpassed at 17e6533.

Findings: I found no new defects in the 1780ff2..17e6533 delta. The QR square-layout correction and USDT footer fix hold on the simulators. The failures below are the previously deferred keypad and contact-latency issues.

QA for 17e6533: I built this head and tested iPhone SE (375 pt) and iPhone 17 (402 pt) simulators on iOS 26.5, using disposable wallets and fixture USDT. No payment, request or subscription was submitted. Videos play at 2×; timings use the original run.

Test 1: Compact amount entry at largest text ❌ Fresh entry, preserved cents, stable controls and scrolling work; the shared 000 label still reads 0….

A fresh USD request and a subscription with USDT selected before opening Amount both accept 1.50 without Delete. Continue and reopening retain the cents. Accessibility bounds match for the divider and keypad across BTC/USD in both editors; scrolling fully exposes Continue. The Bitcoin keypad keys were tapped, and clearing the request then entering 1 and 000 produces 1,000 sats. The visible 000 label remains truncated, as tracked in bitkit-ios#1036 Keep the 000 keypad label readable at largest text. The subscription's USD details retain USDT selected and Savings unselected.

amount-largest-2x.2x.mp4
amount-fresh-2x.2x.mp4
amount-request-2x.2x.mp4
Test 2: Funded contact-detail Request or Pay ❌ One warm handoff exceeds three seconds; the repeat is faster.

Both attempts show progress with Request disabled, then open one SendAmount screen. Closing permits another attempt, and Request opens PaymentRequestAmount.

After more than a minute in the foreground, the first monotonic probe records the physical tap completing at 0.620 s. The sheet is still loading during the 3.968–4.365 s observation, more than 3.34 s after tap completion; SendAmount is first observed by 6.164 s from command start. On the repeat, the tap completes at 0.631 s and SendAmount is observed by 3.122 s. The first run leaves the timing assertion failed; the variable latency remains tracked in bitkit-ios#868 Contact payment latency on staging with the shared Paykit runtime: pin paykit-rs#171 and re-measure.

contact-latency-2x.2x.mp4
Test 3: Standard-size Receive footer and Bitcoin QR ✅ The Bitcoin QR returns to its earlier position, and Savings/USDT keep the footer anchored.

At normal text on iPhone 17, Savings QR y=258 and Copy y=644 match the earlier master comparison. Show Details is y=784 on both Savings and USDT. At the largest text size, their button heights differ but the bottom edges remain at y=840. Spending retains its Lightning onboarding footer because this wallet has no channel.

Core Image decodes the captured Bitcoin and USDT displays at both text sizes to their expected payment URIs. USDT Copy matches the plain address from the clipboard. The Network round-trip retains Arbitrum One and Show Details opens the address.

receive-tall-2x.2x.mp4
Test 4: Compact Receive Copy and QR ✅ The QR fits above the actions, Copy reaches the address, and the footer remains reachable.

On iPhone SE, Show Details is y=595 on both Savings and USDT at normal text. At the largest size, both bottom edges remain at y=651. USDT Copy remains separate from Network and copies the plain receiving address without opening network selection. Core Image decodes displayed Bitcoin and USDT codes at both sizes. The Arbitrum network round-trip and scrolling address details work. Existing largest-text Edit/Copy/Share caption clipping remains visible.

receive-compact-2x.2x.mp4

I retained these reports for passing assertions whose implementation the later pushes leave untouched:

Scope: I reviewed the contiguous 1780ff2…17e6533 delta and outstanding feedback. Both changed runtime components were driven; the two added hosted regressions were source-reviewed. No planned case remains unrun. Device widths were 375 and 402 points; a fresh Figma comparison was unavailable.

Twin:


Reviewed by gpt-6.1-sol-high via gh-pr-review-loop skill
Commands: @ovi-reviewer review · test · retest

@ben-kaufman

ben-kaufman commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor Author

Synced with master in 7d68cf1, including the merged #1040 correction. Full SwiftFormat 0.64.0 now passes (0/831 files), translation validation has zero errors and the same 14 base warnings, and independent review confirms the Receive spacer/QR fixes and master's swipe behavior are preserved.

The previous CI run completed its tests successfully before cancellation in post-cache cleanup. The integration is formatter-equivalent to that tested runtime tree; current-head SwiftFormat and translation validation are now green. Unit/integration tests and the E2E build remain in progress. All 13 PR commits are verified locally and on GitHub. The PR description now includes the latest successful Receive device checks while keeping #1036 and #868 explicit.

@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 review

Scope: Full reassessment of the entire PR diff against merge base 039c7b51, at 7d68cf1. No verified coverage baseline was supplied, and the comparison base changed since the saved assessment, so no earlier coverage was inherited.

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

The BTC unit control does not join the denomination buttons in the horizontal row ViewThatFits measures. Contact-row disabling, empty zero-USD entry, the QR logo overlay, and the USDT footer spacer are otherwise consistent with the behavior measured on earlier commits in this PR. The merge after those measurements adds the master swipe-between-tabs gesture on the USDT receive container and does not rewrite those views.

Run Tests, SwiftFormat, and translation validation passed on this head. Run Integration Tests was cancelled at the one-hour limit after Blocktank/regtest timeouts in channel-purchase and address-type cases. This review did not build the app or run a simulator. Device testing: not performed here. Simulator measurements on 17e6533 still show Show Details aligned across Savings and USDT, including iPhone SE, and the amount divider staying put; those views were not rewritten afterward. The contact handoff remains over the journey's three-second target (#868), and the shared keypad 000 label still truncates at the largest text size (#1036). Compared contact disabling and the amount screen with bitkit-android#1564 at afcf6ff4: Android disables every row while a contact resolves, and it keeps the unit control on its own trailing row.

Suggested additional test cases

  • iOS simulator, iPhone 17 width, normal text, USDT enabled, onboarded wallet with a rate. Open a payment-request amount, select BTC, and leave the text size at the default. Expected: BTC, USD, and the Bitcoin unit control sit on one trailing row, with no blank row between that row and the divider. Switch to USD and back. Expected: the divider and keypad do not move. Repeat at the largest accessibility text. Expected: only the unit control wraps to a second trailing-aligned row.

Findings

  • [LOW] Unit control always stacks under the denomination buttons — inline at Bitkit/Views/PaymentRequests/CreatePaymentRequestView.swift:420.

Comment thread Bitkit/Views/PaymentRequests/CreatePaymentRequestView.swift

@ovi-reviewer ovi-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: ⛔️ Request changes — the existing keypad-legibility and contact-timing journey assertions remain unpassed at 7d68cf1.

Findings: I found no new code defects in the delta. The unit-control report does not reproduce in either actual editor: the controls share one row when they fit and wrap at enlarged text, confirming the author’s rebuttal. The failures below remain the explicitly deferred shared-control and latency issues.

QA for 7d68cf1: I built this head and tested iPhone SE (375 pt) and iPhone 17 (402 pt) simulators on iOS 26.5, with mutually saved and linked disposable identities and a payer funded with fixture USDT. No payment, request or subscription was submitted. Videos play at 2×; timings use the original run.

Test 1: Request and subscription unit-control rows ✅ The unit control shares the normal-size row and wraps below it at the largest text size.

On iPhone 17, both actual editors show BTC, USD and Bitcoin in one trailing row at default text. At the largest accessibility size, only the unit button wraps into the second trailing row. BTC → USD → BTC preserves the divider and keypad positions. Accessibility measurements put the divider / first keypad row at y=343.33 / 408.33 at default text and y=539.67 / 556.67 at the largest size, in both denominations and both editors.

unit-controls-2x.2x.mp4

Subscription amount controls, default and largest text.

unit-request-2x.2x.mp4

Payment-request amount controls, default and largest text.

Test 2: Compact amount entry at largest text ❌ Fresh entry, preserved cents and scrolling work; the shared 000 label still reads 0….

On iPhone SE, a fresh USD request and a subscription with USDT selected before opening Amount both accept 1.50 without Delete. Continue and reopening retain the cents, displayed as 1,50 in this locale. Accessibility bounds confirm stable divider and keypad positions across BTC/USD. Scrolling exposes all keys and Continue; every Bitcoin keypad key was tapped in both editors. The visible 000 label still truncates, so its legibility assertion remains failed under synonymdev/bitkit-ios#1036 Keep the 000 keypad label readable at largest text.

amount-largest-2x.2x.mp4

Fresh USDT-first subscription entry, reopening and compact keypad.

amount-request-2x.2x.mp4

Fresh USD request entry, reopening and compact keypad.

Test 3: Funded warm contact Request or Pay ❌ The repeat exceeds three seconds; loading disables Request and each attempt opens one amount screen.

Both Pay attempts show progress with Request disabled and open one SendAmount screen. Closing permits retry, and Request opens PaymentRequestAmount. The send screen shows the payer’s 25 fixture USDT available balance.

The first monotonic probe records physical tap completion at 0.621 s and first observes SendAmount by 3.405 s from command start. On the repeat, the tap completes at 0.682 s; the 3.812–4.312 s observation still shows loading with Request disabled, more than 3.13 s after tap completion. SendAmount is first observed by 5.933 s. This leaves the three-second assertion failed; the existing investigation remains synonymdev/bitkit-ios#868 Contact payment latency on staging.

contact-latency-2x.2x.mp4

Warm first attempt, closing and retry, then Request opening the amount editor.

Test 4: Integrated USDT Receive swipe and Copy ✅ Swipes change tabs without a copied tooltip, and a tap still copies the address.

On iPhone SE, a right swipe over the USDT QR selects Spending and shows its Lightning onboarding content. A 600 ms left swipe returns to USDT. A left swipe over the last USDT tab leaves it selected. Immediate full-resolution captures show no copied tooltip after either USDT swipe. Tapping the QR shows the tooltip and copies the plain receiving address, independently checked from the clipboard. Network: Arbitrum One and Show Details remain visible; their compact positions remain y=539 and y=595 after the round-trip.

receive-swipes-2x.2x.mp4

Compact Receive tab swipes and QR tap-to-copy.

I retained these passing reports where the later merge leaves the tested implementation untouched:

Scope: I reviewed the contiguous 17e6533…7d68cf1 delta and outstanding feedback. The overlapping USDT swipe integration was driven; imported master settings/reset, identifier, dependency and documentation changes were source-reviewed outside these layout journeys. No planned case remains unrun. This run adds no 320-point device or fresh Figma comparison. I did not rerun the separate native integration suite; its current-head CI run remains unsuccessful.

Twin:


Reviewed by gpt-6.1-sol-high via gh-pr-review-loop skill
Commands: @ovi-reviewer review · test · retest

@ben-kaufman

Copy link
Copy Markdown
Contributor Author

Resolved the new master conflict in ac54f6e. The compact Receive QR and anchored footer remain, and master’s USDT copy confirmation is preserved on both QR and address-details paths, along with tab wrap behavior.

Validation on the final tree: 33 focused tests; 2,833 canonical unit executions plus 39 UI executions passed, with four fixture skips explicit. Full SwiftFormat is clean; all 15 translation warnings match actual master. Complete diagnostics/xcresult and a fresh independent integration review are recorded in the updated QA notes. All 14 actual-base PR commits are locally verified and GitHub-verified (true / valid). New-head CI is running.

J1–J3 remain unchecked for their previously documented device/fixture gaps; no new funded/provider journey is implied. Re-requesting ovitrif and jvsena42 for the integration.

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.

5 participants