From d232d6a58464bc92adf3587e4d9fbdf8ca8ff6e2 Mon Sep 17 00:00:00 2001 From: benk10 Date: Sun, 18 Oct 2026 16:46:47 -0600 Subject: [PATCH 1/7] fix: improve usdt deposit accessibility --- Bitkit/Components/Scanner.swift | 4 +++- Bitkit/Views/Wallets/Activity/ActivityRow.swift | 10 ++++++---- Bitkit/Views/Wallets/UsdtDepositHistoryView.swift | 14 ++++++++++---- BitkitTests/ComponentSizingTests.swift | 15 +++++++++++++++ changelog.d/next/983.fixed.md | 1 + .../payment-requests/contact-request-or-pay.xml | 1 + journeys/payment-requests/request-summary.xml | 1 + journeys/usdt-bridge-details.xml | 1 + journeys/usdt-payment-links-and-scans.xml | 1 + journeys/usdt-receive-amount-and-units.xml | 2 +- journeys/usdt-send-review-and-details.xml | 1 + 11 files changed, 41 insertions(+), 10 deletions(-) create mode 100644 changelog.d/next/983.fixed.md diff --git a/Bitkit/Components/Scanner.swift b/Bitkit/Components/Scanner.swift index 625ed6bb6..3120777c2 100644 --- a/Bitkit/Components/Scanner.swift +++ b/Bitkit/Components/Scanner.swift @@ -239,7 +239,7 @@ struct Scanner: View { ZStack { if cameraManager.hasPermission { #if targetEnvironment(simulator) - Color.black + Color.black.allowsHitTesting(false) #else ScannerCamera( isTorchOn: isTorchOn, @@ -247,6 +247,7 @@ struct Scanner: View { await onScan(payload) } ) + .allowsHitTesting(false) #endif ScannerCornerButtons( @@ -264,6 +265,7 @@ struct Scanner: View { } } .cornerRadius(16) + .contentShape(Rectangle()) .onAppear { guard !cameraManager.hasPermission else { return } cameraManager.requestPermissionIfNeeded() diff --git a/Bitkit/Views/Wallets/Activity/ActivityRow.swift b/Bitkit/Views/Wallets/Activity/ActivityRow.swift index 4b9cc32a7..f21b4ca3e 100644 --- a/Bitkit/Views/Wallets/Activity/ActivityRow.swift +++ b/Bitkit/Views/Wallets/Activity/ActivityRow.swift @@ -88,18 +88,20 @@ struct ActivityRowContent: View { var titleLineHeight: CGFloat? var subtitleLineHeight: CGFloat? var textSpacing: CGFloat = 2 + var stacked = false @ViewBuilder var icon: () -> Icon @ViewBuilder var amount: () -> Amount var body: some View { - HStack(spacing: 16) { + let layout = stacked ? AnyLayout(VStackLayout(alignment: .leading, spacing: 16)) : AnyLayout(HStackLayout(spacing: 16)) + layout { icon() VStack(alignment: .leading, spacing: textSpacing) { - BodyMSBText(title, kerning: textKerning, lineHeight: titleLineHeight).lineLimit(1) - CaptionBText(subtitle, kerning: textKerning, lineHeight: subtitleLineHeight).lineLimit(1) + BodyMSBText(title, kerning: textKerning, lineHeight: titleLineHeight).lineLimit(stacked ? nil : 1) + CaptionBText(subtitle, kerning: textKerning, lineHeight: subtitleLineHeight).lineLimit(stacked ? nil : 1) } .fixedSize(horizontal: false, vertical: true) - Spacer(minLength: 0) + if !stacked { Spacer(minLength: 0) } amount() } } diff --git a/Bitkit/Views/Wallets/UsdtDepositHistoryView.swift b/Bitkit/Views/Wallets/UsdtDepositHistoryView.swift index e36870c2b..f8129b5b7 100644 --- a/Bitkit/Views/Wallets/UsdtDepositHistoryView.swift +++ b/Bitkit/Views/Wallets/UsdtDepositHistoryView.swift @@ -197,7 +197,10 @@ struct UsdtDepositHistoryView: View { } UsdtSupportActions(details: detail.supportDetails).disabled(busy || recoveryActive) } else { - HStack(alignment: .top, spacing: 16) { + let layout = dynamicTypeSize.isAccessibilitySize + ? AnyLayout(VStackLayout(alignment: .leading, spacing: 16)) + : AnyLayout(HStackLayout(alignment: .top, spacing: 16)) + layout { UsdtDetailCell(title: t("wallet__activity_status"), value: detail.statusText, icon: detail.statusIcon, valueColor: detail.statusColor, iconColor: detail.statusColor, valueKerning: 0) UsdtDetailCell(title: t("usdt__receiving_network"), value: UsdtDestination.arbitrum.label, icon: "usdt-network") @@ -412,6 +415,7 @@ extension UsdtDeposit { } struct UsdtDepositRow: View { + @Environment(\.dynamicTypeSize) private var dynamicTypeSize let deposit: UsdtDeposit let hideBalance: Bool @@ -424,14 +428,16 @@ struct UsdtDepositRow: View { let color: Color = completed ? .usdtAccent : .yellowAccent ActivityRowContainer { ActivityRowContent(title: deposit.networkLabel, subtitle: deposit.statusText, textKerning: 0, - titleLineHeight: 22, subtitleLineHeight: 18, textSpacing: 0) + titleLineHeight: 22, subtitleLineHeight: 18, textSpacing: 0, stacked: dynamicTypeSize.isAccessibilitySize) { CircularIcon(icon: completed ? "check-circle" : "hourglass-simple", iconColor: color, backgroundColor: color.opacity(0.16), size: 40) } amount: { - VStack(alignment: .trailing, spacing: 0) { - BodyMSBText(deposit.asset == "USDT" ? amount + " USDT" : deposit.asset, kerning: 0, lineHeight: 22).lineLimit(1) + VStack(alignment: dynamicTypeSize.isAccessibilitySize ? .leading : .trailing, spacing: 0) { + BodyMSBText(deposit.asset == "USDT" ? amount + " USDT" : deposit.asset, kerning: 0, lineHeight: 22) + .lineLimit(dynamicTypeSize.isAccessibilitySize ? nil : 1) .minimumScaleFactor(0.7) + .fixedSize(horizontal: false, vertical: true) if deposit.asset == "USDT" { CaptionBText(hideBalance ? amount : "$" + amount, textColor: .textSecondary, kerning: 0, lineHeight: 18) } diff --git a/BitkitTests/ComponentSizingTests.swift b/BitkitTests/ComponentSizingTests.swift index aef17d445..0b36e7453 100644 --- a/BitkitTests/ComponentSizingTests.swift +++ b/BitkitTests/ComponentSizingTests.swift @@ -1,4 +1,5 @@ @testable import Bitkit +import BitkitCore import SwiftUI import XCTest @@ -34,6 +35,20 @@ final class ComponentSizingTests: XCTestCase { XCTAssertGreaterThan(height(of: permission, width: 280, size: .accessibility5), height(of: permission, width: 280, size: .large)) } + func testDepositRowsGrowToShowNetworkStatusAndAmountAtAccessibilitySizes() { + let deposit = UsdtDeposit(id: "deposit", network: "bsc", asset: "USDT", amount: 50_000_001, + sourceTx: "0x" + String(repeating: "ab", count: 32), status: "held", + code: "standing_route_unavailable", refundTx: nil) + let row = UsdtDepositRow(deposit: deposit, hideBalance: false) + let normal = height(of: row, width: 320, size: .large) + let accessible = height(of: row, width: 320, size: .accessibility5) + XCTAssertGreaterThan(accessible, normal * 2) + let status = BodyMSBText(deposit.networkLabel).fixedSize(horizontal: false, vertical: true) + let amount = BodyMSBText("50.000001 USDT").fixedSize(horizontal: false, vertical: true) + XCTAssertGreaterThan(accessible, height(of: status, width: 288, size: .accessibility5) + + height(of: amount, width: 288, size: .accessibility5)) + } + private func height(of view: some View, width: CGFloat, size: DynamicTypeSize) -> CGFloat { let controller = UIHostingController(rootView: view.environment(\.dynamicTypeSize, size)) return controller.sizeThatFits(in: CGSize(width: width, height: 10000)).height diff --git a/changelog.d/next/983.fixed.md b/changelog.d/next/983.fixed.md new file mode 100644 index 000000000..343c626ff --- /dev/null +++ b/changelog.d/next/983.fixed.md @@ -0,0 +1 @@ +Improved USDT deposit readability and recipient scrolling with accessibility text sizes. diff --git a/journeys/payment-requests/contact-request-or-pay.xml b/journeys/payment-requests/contact-request-or-pay.xml index 5b39f432e..720465da8 100644 --- a/journeys/payment-requests/contact-request-or-pay.xml +++ b/journeys/payment-requests/contact-request-or-pay.xml @@ -16,5 +16,6 @@ Verify the Request or Pay sheet (id "RequestOrPaySheet") appears Tap Request on the sheet Verify the Payment Request amount screen (id "PaymentRequestAmount") opens + Open Send and choose Contact. Tap a saved contact and verify its row shows loading feedback while payment details resolve; other contacts cannot be selected meanwhile. When resolution finishes, verify the amount screen opens. If resolution fails, verify the error is shown and contact selection becomes available for retry. Cancel without paying. diff --git a/journeys/payment-requests/request-summary.xml b/journeys/payment-requests/request-summary.xml index 7ee861bd0..a627b9ca4 100644 --- a/journeys/payment-requests/request-summary.xml +++ b/journeys/payment-requests/request-summary.xml @@ -15,5 +15,6 @@ On the requester, send the payer contact a Payment Request for 5,000 sats with no note On the payer, verify the Payment Request confirmation screen (id "PaymentRequestConfirm") appears automatically with 5,000 sats Verify From (id "PaymentRequestFrom") shows the requester's contact name and For (id "PaymentRequestFor") shows "Not specified" + Open existing USDT payment request history with a verified receipt containing an exact six-decimal amount. With enlarged text and a long translated verification status, verify the contact name stays readable and the complete status wraps within the amount column. Repeat for a BTC-denominated request paid in USDT. diff --git a/journeys/usdt-bridge-details.xml b/journeys/usdt-bridge-details.xml index 17557d9cf..20aa56d20 100644 --- a/journeys/usdt-bridge-details.xml +++ b/journeys/usdt-bridge-details.xml @@ -17,5 +17,6 @@ Open USDT Receive, then Network. Verify Cross-Network Deposits (id "UsdtDepositHistory") is a full-width primary button pinned over the bottom of the network list. Scroll the list and verify the rows and artwork pass behind the button while it stays fixed. Scroll to the end and verify the last content can be brought above the button. Repeat with large text, verify the button label is not clipped, then tap it and verify deposit history opens before any Arbitrum receipt exists. Using only an isolated refund fixture, submit a refund address that the fixture rejects. Verify UsdtRefundError appears immediately below UsdtRefundAddress and remains visible through two background detail refreshes. Edit the address and verify the error clears; retry a rejected address and verify it returns. Change the selected deposit and verify the previous refund error is absent. At the largest system text size, open Details on a refundable deposit and dismiss the keyboard. Verify Request refund, Hide details, Contact support and Copy details stack vertically, can be scrolled into view and have complete readable labels. Return to normal text size and verify the paired actions share their original rows. + On iOS, select the largest accessibility text size. Open Cross-Network Deposits and verify each row shows the full source network, status and amount without truncation. Open a deposit and verify Status and Receiving Network stack vertically and remain readable. Return to the default text size and verify the compact row and two-column summary return. diff --git a/journeys/usdt-payment-links-and-scans.xml b/journeys/usdt-payment-links-and-scans.xml index 9d33cdfe2..ff82a87c2 100644 --- a/journeys/usdt-payment-links-and-scans.xml +++ b/journeys/usdt-payment-links-and-scans.xml @@ -22,5 +22,6 @@ On a compact display with large text, open USDT Send and select address scanning. Verify the camera remains usable with one scanner heading, one paste action, gallery import and flash controls. Verify denied camera permission offers its normal recovery action and the rest of the send controls remain reachable by scrolling. Cancel without paying. At the largest system text size, open USDT Send on a compact display. Verify the selected network name is fully readable. Deny camera permission and scroll to Enable camera; verify its full label is visible and the action remains usable. Select BNB Smart Chain when available and verify its complete network name is readable. Cancel without paying. On iOS with Read Clipboard enabled, open Send, then copy an owned USDT address while the sheet is open. Tap Paste Invoice and allow the system paste permission if requested. Close from Amount without opening a text field, return Home, then background and foreground the app. Verify the already-pasted address is not offered again. Repeat using USDT Send's Paste button. Copy a different supported address and verify automatic detection still offers the new content. Cancel without paying. + On iOS with camera permission enabled and the largest accessibility text size, open USDT Send. Start an upward drag inside the camera preview and verify the recipient screen scrolls so Paste, Enter Manually and the network warning are reachable. Verify the photo picker and flashlight controls still respond. Restore the default text size without sending. diff --git a/journeys/usdt-receive-amount-and-units.xml b/journeys/usdt-receive-amount-and-units.xml index 282e7436a..65a92022f 100644 --- a/journeys/usdt-receive-amount-and-units.xml +++ b/journeys/usdt-receive-amount-and-units.xml @@ -5,7 +5,7 @@ Open USDT from Home. Verify the USDT balance, teal Tether icon and existing activity rows. Open Receive. Verify Receive Dollars, the USDT tab and Network: Arbitrum One on the QR screen. Tap Edit, enter 5 and tap Back. Verify the QR has no uint256 amount. - Tap Edit. Enter 3.5. Verify the dollar amount and BTC equivalent are displayed, there is no input-unit toggle, the keypad follows the divider, and Continue stays at the bottom without overlap. + Tap Edit. Enter 3.50. Verify the dollar amount and BTC equivalent are displayed, there is no input-unit toggle, the keypad follows the divider, and Continue stays at the bottom without overlap. Continue to the QR. Verify it requests the Arbitrum USDT token, the wallet address and uint256=3500000, never Bitcoin. Tap Edit, enter a different amount and tap Back. Verify the QR still requests uint256=3500000. Tap Copy on the QR screen and verify the clipboard contains the exact payment URI including uint256=3500000. Open Share and verify the shared text includes Network: Arbitrum One and the exact payment URI. Cancel sharing. Open Show Details. Verify Network: Arbitrum One, the same Arbitrum address and teal Copy control. Verify Share includes the same network and payment URI, while Copy contains only the address. Open Network and verify Arbitrum is marked Default and all enabled bridge networks remain available, with circular USDT icons. Return to the QR. diff --git a/journeys/usdt-send-review-and-details.xml b/journeys/usdt-send-review-and-details.xml index 64186accf..b5af303ab 100644 --- a/journeys/usdt-send-review-and-details.xml +++ b/journeys/usdt-send-review-and-details.xml @@ -14,5 +14,6 @@ Open an existing sent USDT activity. Verify the heading is Sent USDT, the signed amount includes the actual fee, and summary rows show Status/Network, Date/Time and Payment/Fee with teal icons. Open Details and verify transaction ID, known sender, recipient and source/destination networks; Open Block Explorer stays at the bottom. Open an existing received activity. Verify Received USDT and a positive amount. Verify Details shows the verified sender when available and does not invent one for older unknown records. Check pending, failed and transferring rows use their matching icons, and transferring shows its destination. Prepare a contact payment that accepts multiple payment methods. Open the payment-source button and verify the balance menu shows Savings, Spending, any paired hardware wallet and USDT. Unsupported methods are disabled and shown at 30% opacity. Verify the menu has 24px outer padding, 24px asset icons, 13/18 uppercase labels and 17/22 balances, with no extra transfer icon next to Spending. Select a supported method and verify its label, available balance and payable amount update. Cancel without paying. + On Android, repeat contact, one-time request and recurring request USDT confirmation on a compact device with three-button navigation, then gesture navigation. Verify Swipe To Pay stays above the navigation area in both collapsed and expanded views. Open incoming activity Details and verify Cross-Network Deposits remains fully above the navigation area. Cancel without paying. From b3e6025d756075ddfcb66a358bbbac74fcfe8567 Mon Sep 17 00:00:00 2001 From: benk10 Date: Sun, 18 Oct 2026 17:23:09 -0600 Subject: [PATCH 2/7] test: verify every deposit row field fits large text --- BitkitTests/ComponentSizingTests.swift | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/BitkitTests/ComponentSizingTests.swift b/BitkitTests/ComponentSizingTests.swift index 0b36e7453..180fd2841 100644 --- a/BitkitTests/ComponentSizingTests.swift +++ b/BitkitTests/ComponentSizingTests.swift @@ -40,13 +40,20 @@ final class ComponentSizingTests: XCTestCase { sourceTx: "0x" + String(repeating: "ab", count: 32), status: "held", code: "standing_route_unavailable", refundTx: nil) let row = UsdtDepositRow(deposit: deposit, hideBalance: false) - let normal = height(of: row, width: 320, size: .large) - let accessible = height(of: row, width: 320, size: .accessibility5) - XCTAssertGreaterThan(accessible, normal * 2) - let status = BodyMSBText(deposit.networkLabel).fixedSize(horizontal: false, vertical: true) - let amount = BodyMSBText("50.000001 USDT").fixedSize(horizontal: false, vertical: true) - XCTAssertGreaterThan(accessible, height(of: status, width: 288, size: .accessibility5) - + height(of: amount, width: 288, size: .accessibility5)) + let amount = usdtOverviewAmount(50_000_001) + for width: CGFloat in [280, 320] { + let textWidth = width - 32 + let networkHeight = height(of: BodyMSBText(deposit.networkLabel, kerning: 0, lineHeight: 22) + .fixedSize(horizontal: false, vertical: true), width: textWidth, size: .accessibility5) + let statusHeight = height(of: CaptionBText(deposit.statusText, kerning: 0, lineHeight: 18) + .fixedSize(horizontal: false, vertical: true), width: textWidth, size: .accessibility5) + let amountHeight = height(of: BodyMSBText(amount + " USDT", kerning: 0, lineHeight: 22) + .fixedSize(horizontal: false, vertical: true), width: textWidth, size: .accessibility5) + let fiatHeight = height(of: CaptionBText("$" + amount, textColor: .textSecondary, kerning: 0, lineHeight: 18) + .fixedSize(horizontal: false, vertical: true), width: textWidth, size: .accessibility5) + let contentHeight = 40 + networkHeight + statusHeight + amountHeight + fiatHeight + XCTAssertEqual(height(of: row, width: width, size: .accessibility5), contentHeight + 64, accuracy: 1) + } } private func height(of view: some View, width: CGFloat, size: DynamicTypeSize) -> CGFloat { From 22581dc2ccdd07649e52160f589855b639aa927d Mon Sep 17 00:00:00 2001 From: benk10 Date: Sun, 18 Oct 2026 19:20:03 -0600 Subject: [PATCH 3/7] fix: keep enlarged deposit cards full width --- Bitkit/Views/Wallets/Activity/ActivityRow.swift | 1 + BitkitTests/ComponentSizingTests.swift | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/Bitkit/Views/Wallets/Activity/ActivityRow.swift b/Bitkit/Views/Wallets/Activity/ActivityRow.swift index f21b4ca3e..9b50cbc36 100644 --- a/Bitkit/Views/Wallets/Activity/ActivityRow.swift +++ b/Bitkit/Views/Wallets/Activity/ActivityRow.swift @@ -104,6 +104,7 @@ struct ActivityRowContent: View { if !stacked { Spacer(minLength: 0) } amount() } + .frame(maxWidth: stacked ? .infinity : nil, alignment: .leading) } } diff --git a/BitkitTests/ComponentSizingTests.swift b/BitkitTests/ComponentSizingTests.swift index 180fd2841..75b658391 100644 --- a/BitkitTests/ComponentSizingTests.swift +++ b/BitkitTests/ComponentSizingTests.swift @@ -56,6 +56,18 @@ final class ComponentSizingTests: XCTestCase { } } + func testDepositRowsUseTheFullAvailableWidthWithLargeText() { + for network in ["polygon", "tron"] { + let deposit = UsdtDeposit(id: "deposit", network: network, asset: "USDT", amount: 1_000_000, + sourceTx: "0x" + String(repeating: "ab", count: 32), status: "pending", code: nil, refundTx: nil) + let row = UsdtDepositRow(deposit: deposit, hideBalance: false) + for width: CGFloat in [280, 320] { + let controller = UIHostingController(rootView: row.environment(\.dynamicTypeSize, .accessibility1)) + XCTAssertEqual(controller.sizeThatFits(in: CGSize(width: width, height: 10000)).width, width, accuracy: 1) + } + } + } + private func height(of view: some View, width: CGFloat, size: DynamicTypeSize) -> CGFloat { let controller = UIHostingController(rootView: view.environment(\.dynamicTypeSize, size)) return controller.sizeThatFits(in: CGSize(width: width, height: 10000)).height From c6dadfd5457051ed289972bbf5d3dd680b9c3f1e Mon Sep 17 00:00:00 2001 From: benk10 Date: Mon, 19 Oct 2026 04:24:59 -0600 Subject: [PATCH 4/7] docs: clarify usdt journey prerequisites --- journeys/payment-requests/README.md | 5 +++++ journeys/payment-requests/contact-request-or-pay.xml | 4 ++-- journeys/payment-requests/request-summary.xml | 4 ++-- journeys/usdt-send-review-and-details.xml | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/journeys/payment-requests/README.md b/journeys/payment-requests/README.md index 0ebcb2f0e..6f614d22e 100644 --- a/journeys/payment-requests/README.md +++ b/journeys/payment-requests/README.md @@ -131,6 +131,11 @@ correctly prevents it from entering the presentation queue. ## Request summary `request-summary.xml` is ported alongside Android's matching journey. +The USDT history step in `request-summary.xml` requires both instances opted in, a payer funded +with local test USDT, and two completed requests (one USD-denominated and one BTC-denominated), +each with a verified receipt for exactly 1.234567 USDT. Establish these using the local USDT +fixture before the journey. `contact-request-or-pay.xml` also needs a third linked contact that +publishes no payment endpoints, so the failure and retry steps are reproducible. `success-dismissal.xml` matches Android's journey: after paying a Lightning request, closing success must leave no error toast or duplicate payment; closing a later unpaid request must not send it. diff --git a/journeys/payment-requests/contact-request-or-pay.xml b/journeys/payment-requests/contact-request-or-pay.xml index 720465da8..7ed0d0cf6 100644 --- a/journeys/payment-requests/contact-request-or-pay.xml +++ b/journeys/payment-requests/contact-request-or-pay.xml @@ -1,6 +1,6 @@ - Verifies that Pay on a linked contact offers Request or Pay, that paying shows progress on the sheet until the amount screen opens within a few seconds, and that Request opens the Payment Request amount screen. Requires two Bitkit instances saved as each other's contacts and linked as identities, with the payer funded; see README.md. Let the payer run for a minute after launch before starting, so start-up Paykit work does not dominate the timings. Open the contact with xcrun simctl openurl booted "bitkit://contact?pubky=<requester-public-key>". + Verifies that Pay on a linked contact offers Request or Pay, that paying shows progress on the sheet until the amount screen opens within a few seconds, and that Request opens the Payment Request amount screen. Requires two Bitkit instances saved as each other's contacts and linked as identities, with the payer funded; see README.md. Also save a third linked contact that publishes no payment endpoints, for the resolution-failure check. Let the payer run for a minute after launch before starting, so start-up Paykit work does not dominate the timings. Open the contact with xcrun simctl openurl booted "bitkit://contact?pubky=<requester-public-key>". Open bitkit://contact?pubky=<requester-public-key> on the payer @@ -16,6 +16,6 @@ Verify the Request or Pay sheet (id "RequestOrPaySheet") appears Tap Request on the sheet Verify the Payment Request amount screen (id "PaymentRequestAmount") opens - Open Send and choose Contact. Tap a saved contact and verify its row shows loading feedback while payment details resolve; other contacts cannot be selected meanwhile. When resolution finishes, verify the amount screen opens. If resolution fails, verify the error is shown and contact selection becomes available for retry. Cancel without paying. + Open Send and choose Contact. Tap a saved contact and verify its row shows loading feedback while payment details resolve; other contacts cannot be selected meanwhile. When resolution finishes, verify the amount screen opens. Cancel without paying. Reopen Send, choose Contact and select the prepared contact with no payment endpoints. Verify the error is shown and contact selection becomes available for retry. Select the original payable contact and verify its amount screen opens, then cancel without paying. diff --git a/journeys/payment-requests/request-summary.xml b/journeys/payment-requests/request-summary.xml index a627b9ca4..a643128bc 100644 --- a/journeys/payment-requests/request-summary.xml +++ b/journeys/payment-requests/request-summary.xml @@ -1,6 +1,6 @@ - Verifies that the collapsed Payment Request confirmation shows who the request is from and what it is for, keeps the note in the details, and shows "Not specified" in For when the request has no note. Requires two Bitkit instances saved as each other's contacts and linked as identities, with the payer funded to cover 21,000 sats; see README.md. + Verifies that the collapsed Payment Request confirmation shows who the request is from and what it is for, keeps the note in the details, and shows "Not specified" in For when the request has no note. Requires two Bitkit instances saved as each other's contacts and linked as identities, with the payer funded to cover 21,000 sats plus fees and local test USDT payments. Before starting, use the local USDT fixture to establish two completed requests in the requester's history: one USD-denominated and one BTC-denominated, each with a verified receipt for exactly 1.234567 USDT. Both instances must have USDT enabled. See README.md. On the requester, send the payer contact a Payment Request for 21,000 sats with the note "Lunch last week" @@ -15,6 +15,6 @@ On the requester, send the payer contact a Payment Request for 5,000 sats with no note On the payer, verify the Payment Request confirmation screen (id "PaymentRequestConfirm") appears automatically with 5,000 sats Verify From (id "PaymentRequestFrom") shows the requester's contact name and For (id "PaymentRequestFor") shows "Not specified" - Open existing USDT payment request history with a verified receipt containing an exact six-decimal amount. With enlarged text and a long translated verification status, verify the contact name stays readable and the complete status wraps within the amount column. Repeat for a BTC-denominated request paid in USDT. + Open the prepared USD-denominated request in payment history. With enlarged text and a long translated verification status, verify the contact name stays readable and the complete status wraps within the amount column. Verify the receipt status shows exactly 1.234567 USDT, using the selected locale's decimal separator. Repeat for the prepared BTC-denominated request paid in USDT. diff --git a/journeys/usdt-send-review-and-details.xml b/journeys/usdt-send-review-and-details.xml index b5af303ab..9b4021e31 100644 --- a/journeys/usdt-send-review-and-details.xml +++ b/journeys/usdt-send-review-and-details.xml @@ -1,5 +1,5 @@ - Use an onboarded wallet with USDT, a fresh BTC/USD rate, an owned recipient and existing sent/received activity. Quotes require the service with MAX support. No payment is submitted. + Use an onboarded wallet with USDT, a fresh BTC/USD rate, owned EVM and Solana recipients and existing sent/received activity. Save a linked contact that advertises Bitcoin, Lightning and USDT receiving methods. Have an unpaid one-time request and a due subscription payment from that contact, each permitting USDT. Pair a hardware wallet for the hardware-option check. Use the local USDT fixture for funding, controlled quotes and submitted-payment states. Quotes require the service with MAX support. No payment is submitted. Enable USDT in the introductory sheet or Settings, General, Payments before starting this flow. Open USDT Send and enter the owned recipient. On Amount, verify the BTC equivalent above the dollar amount, 32-point gap before Available balance, USDT badge, MAX and network pill. Enter a six-decimal amount and verify no precision is lost. With a large available balance and BNB Smart Chain selected, verify both values remain fully readable; the controls may wrap onto a second row. From aff5b7b00f86811f356a5ce990e8b22696bc9964 Mon Sep 17 00:00:00 2001 From: benk10 Date: Mon, 19 Oct 2026 05:55:39 -0600 Subject: [PATCH 5/7] fix: keep request contacts and receipts readable --- .../PaymentRequests/PaymentRequestsView.swift | 40 +++++++++++++------ changelog.d/next/983.fixed.md | 2 +- journeys/payment-requests/request-summary.xml | 2 +- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/Bitkit/Views/PaymentRequests/PaymentRequestsView.swift b/Bitkit/Views/PaymentRequests/PaymentRequestsView.swift index 8bd4e7b63..6648e5a1b 100644 --- a/Bitkit/Views/PaymentRequests/PaymentRequestsView.swift +++ b/Bitkit/Views/PaymentRequests/PaymentRequestsView.swift @@ -172,31 +172,45 @@ struct PaymentRequestCard: View { } private var header: some View { + ViewThatFits(in: .horizontal) { + HStack(spacing: 16) { + contactSummary.fixedSize(horizontal: true, vertical: false) + Spacer(minLength: 8) + amountSummary.fixedSize(horizontal: true, vertical: false) + } + VStack(alignment: .trailing, spacing: 8) { + contactSummary.frame(maxWidth: .infinity, alignment: .leading) + amountSummary.fixedSize(horizontal: false, vertical: true) + } + } + .padding(16) + .contentShape(Rectangle()) + } + + private var contactSummary: some View { HStack(spacing: 16) { avatar - VStack(alignment: .leading, spacing: 4) { BodyMSBText(title) .lineLimit(1) CaptionText(subtitle, textColor: .white64) .lineLimit(1) } + } + } - Spacer(minLength: 8) - - if let amountStatus = receipt?.statusText ?? amountStatus ?? - (request.direction == .outgoing && request.paymentProofKind == .usdt ? t("wallet__payment_request_status_pending") : nil) - { - VStack(alignment: .trailing, spacing: 2) { - PaykitAmountText(amount: request.amount, prefix: amountPrefix) - CaptionText(amountStatus, textColor: .white64) - } - } else { + @ViewBuilder + private var amountSummary: some View { + if let amountStatus = receipt?.statusText ?? amountStatus ?? + (request.direction == .outgoing && request.paymentProofKind == .usdt ? t("wallet__payment_request_status_pending") : nil) + { + VStack(alignment: .trailing, spacing: 2) { PaykitAmountText(amount: request.amount, prefix: amountPrefix) + CaptionText(amountStatus, textColor: .white64) } + } else { + PaykitAmountText(amount: request.amount, prefix: amountPrefix) } - .padding(16) - .contentShape(Rectangle()) } @ViewBuilder diff --git a/changelog.d/next/983.fixed.md b/changelog.d/next/983.fixed.md index 343c626ff..b14410bdf 100644 --- a/changelog.d/next/983.fixed.md +++ b/changelog.d/next/983.fixed.md @@ -1 +1 @@ -Improved USDT deposit readability and recipient scrolling with accessibility text sizes. +Improved USDT deposit and payment request readability and recipient scrolling with enlarged text. diff --git a/journeys/payment-requests/request-summary.xml b/journeys/payment-requests/request-summary.xml index a643128bc..f78edff4f 100644 --- a/journeys/payment-requests/request-summary.xml +++ b/journeys/payment-requests/request-summary.xml @@ -15,6 +15,6 @@ On the requester, send the payer contact a Payment Request for 5,000 sats with no note On the payer, verify the Payment Request confirmation screen (id "PaymentRequestConfirm") appears automatically with 5,000 sats Verify From (id "PaymentRequestFrom") shows the requester's contact name and For (id "PaymentRequestFor") shows "Not specified" - Open the prepared USD-denominated request in payment history. With enlarged text and a long translated verification status, verify the contact name stays readable and the complete status wraps within the amount column. Verify the receipt status shows exactly 1.234567 USDT, using the selected locale's decimal separator. Repeat for the prepared BTC-denominated request paid in USDT. + Open the prepared USD-denominated request in payment history. On a 320-point/dp-wide screen with enlarged text and a long translated verification status, verify a short contact name such as Maria and every status word remain complete. The amount and receipt may move below the contact when needed. Repeat at the default text size and verify short amounts and statuses remain beside the contact when they fit. Verify the receipt status shows exactly 1.234567 USDT, using the selected locale's decimal separator. Repeat for the prepared BTC-denominated request paid in USDT. From d864a4e3ad95360252cf1057b9cd593515b1bfbe Mon Sep 17 00:00:00 2001 From: benk10 Date: Mon, 19 Oct 2026 11:39:44 -0600 Subject: [PATCH 6/7] docs: make USDT receipt journeys reproducible --- journeys/payment-requests/README.md | 13 ++++++++----- journeys/payment-requests/request-summary.xml | 4 ++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/journeys/payment-requests/README.md b/journeys/payment-requests/README.md index 6f614d22e..e72996cd6 100644 --- a/journeys/payment-requests/README.md +++ b/journeys/payment-requests/README.md @@ -131,11 +131,14 @@ correctly prevents it from entering the presentation queue. ## Request summary `request-summary.xml` is ported alongside Android's matching journey. -The USDT history step in `request-summary.xml` requires both instances opted in, a payer funded -with local test USDT, and two completed requests (one USD-denominated and one BTC-denominated), -each with a verified receipt for exactly 1.234567 USDT. Establish these using the local USDT -fixture before the journey. `contact-request-or-pay.xml` also needs a third linked contact that -publishes no payment endpoints, so the failure and retry steps are reproducible. +The USDT history step in `request-summary.xml` requires both instances opted in, a payer funded with +local test USDT, and two completed requests (one USD-denominated and one BTC-denominated), each paid +in USDT with a verified receipt. Establish these using the local USDT fixture before the journey and +record each payment's exact USDT amount from the payer's activity. Compare the receipt with that +amount, preserving all its decimals; the two payments need not have equal amounts. The iOS layout +check uses a supported 375-point-wide device with enlarged Dynamic Type, while Android uses 320 dp. +`contact-request-or-pay.xml` also needs a third linked contact that publishes no payment endpoints, +so the failure and retry steps are reproducible. `success-dismissal.xml` matches Android's journey: after paying a Lightning request, closing success must leave no error toast or duplicate payment; closing a later unpaid request must not send it. diff --git a/journeys/payment-requests/request-summary.xml b/journeys/payment-requests/request-summary.xml index f78edff4f..a20fbb780 100644 --- a/journeys/payment-requests/request-summary.xml +++ b/journeys/payment-requests/request-summary.xml @@ -1,6 +1,6 @@ - Verifies that the collapsed Payment Request confirmation shows who the request is from and what it is for, keeps the note in the details, and shows "Not specified" in For when the request has no note. Requires two Bitkit instances saved as each other's contacts and linked as identities, with the payer funded to cover 21,000 sats plus fees and local test USDT payments. Before starting, use the local USDT fixture to establish two completed requests in the requester's history: one USD-denominated and one BTC-denominated, each with a verified receipt for exactly 1.234567 USDT. Both instances must have USDT enabled. See README.md. + Verifies that the collapsed Payment Request confirmation shows who the request is from and what it is for, keeps the note in the details, and shows "Not specified" in For when the request has no note. Requires two Bitkit instances saved as each other's contacts and linked as identities, with the payer funded to cover 21,000 sats plus fees and local test USDT payments. Before starting, use the local USDT fixture to establish two completed requests in the requester's history: one USD-denominated and one BTC-denominated, each paid in USDT with a verified receipt. Record each payment's exact USDT amount from the payer's activity for comparison; the amounts need not be equal. Both instances must have USDT enabled. The iOS layout check uses a supported 375-point-wide device with enlarged Dynamic Type; Android uses 320 dp. See README.md. On the requester, send the payer contact a Payment Request for 21,000 sats with the note "Lunch last week" @@ -15,6 +15,6 @@ On the requester, send the payer contact a Payment Request for 5,000 sats with no note On the payer, verify the Payment Request confirmation screen (id "PaymentRequestConfirm") appears automatically with 5,000 sats Verify From (id "PaymentRequestFrom") shows the requester's contact name and For (id "PaymentRequestFor") shows "Not specified" - Open the prepared USD-denominated request in payment history. On a 320-point/dp-wide screen with enlarged text and a long translated verification status, verify a short contact name such as Maria and every status word remain complete. The amount and receipt may move below the contact when needed. Repeat at the default text size and verify short amounts and statuses remain beside the contact when they fit. Verify the receipt status shows exactly 1.234567 USDT, using the selected locale's decimal separator. Repeat for the prepared BTC-denominated request paid in USDT. + Open the prepared USD-denominated request in payment history. On a 375-point-wide iOS screen with enlarged text and a long translated verification status, verify a short contact name such as Maria and every status word remain complete. The amount and receipt may move below the contact when needed. Repeat at the default text size and verify short amounts and statuses remain beside the contact when they fit. Verify the receipt status shows the paid USDT amount with all its decimals, matching the payer's activity and using the selected locale's decimal separator. Repeat for the prepared BTC-denominated request paid in USDT. From edd752ac12836d2408ce6b072f5fb840fbb161a0 Mon Sep 17 00:00:00 2001 From: benk10 Date: Mon, 19 Oct 2026 12:45:07 -0600 Subject: [PATCH 7/7] docs: compare receipts with rounded payment values --- journeys/payment-requests/README.md | 6 ++++-- journeys/payment-requests/request-summary.xml | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/journeys/payment-requests/README.md b/journeys/payment-requests/README.md index e72996cd6..def29ce75 100644 --- a/journeys/payment-requests/README.md +++ b/journeys/payment-requests/README.md @@ -134,8 +134,10 @@ correctly prevents it from entering the presentation queue. The USDT history step in `request-summary.xml` requires both instances opted in, a payer funded with local test USDT, and two completed requests (one USD-denominated and one BTC-denominated), each paid in USDT with a verified receipt. Establish these using the local USDT fixture before the journey and -record each payment's exact USDT amount from the payer's activity. Compare the receipt with that -amount, preserving all its decimals; the two payments need not have equal amounts. The iOS layout +record the USD request amount and each payer activity detail's rounded Payment value. The USD +receipt matches the request amount. The BTC request's receipt retains up to six decimals and rounds +to that Payment value (shown as less than 0.01 below one cent), not the fee-inclusive activity total. +The two payments need not have equal amounts. The iOS layout check uses a supported 375-point-wide device with enlarged Dynamic Type, while Android uses 320 dp. `contact-request-or-pay.xml` also needs a third linked contact that publishes no payment endpoints, so the failure and retry steps are reproducible. diff --git a/journeys/payment-requests/request-summary.xml b/journeys/payment-requests/request-summary.xml index a20fbb780..29faddb0e 100644 --- a/journeys/payment-requests/request-summary.xml +++ b/journeys/payment-requests/request-summary.xml @@ -1,6 +1,6 @@ - Verifies that the collapsed Payment Request confirmation shows who the request is from and what it is for, keeps the note in the details, and shows "Not specified" in For when the request has no note. Requires two Bitkit instances saved as each other's contacts and linked as identities, with the payer funded to cover 21,000 sats plus fees and local test USDT payments. Before starting, use the local USDT fixture to establish two completed requests in the requester's history: one USD-denominated and one BTC-denominated, each paid in USDT with a verified receipt. Record each payment's exact USDT amount from the payer's activity for comparison; the amounts need not be equal. Both instances must have USDT enabled. The iOS layout check uses a supported 375-point-wide device with enlarged Dynamic Type; Android uses 320 dp. See README.md. + Verifies that the collapsed Payment Request confirmation shows who the request is from and what it is for, keeps the note in the details, and shows "Not specified" in For when the request has no note. Requires two Bitkit instances saved as each other's contacts and linked as identities, with the payer funded to cover 21,000 sats plus fees and local test USDT payments. Before starting, use the local USDT fixture to establish two completed requests in the requester's history: one USD-denominated and one BTC-denominated, each paid in USDT with a verified receipt. Record the USD request amount and each payer activity detail's rounded Payment value for comparison; the amounts need not be equal. Both instances must have USDT enabled. The iOS layout check uses a supported 375-point-wide device with enlarged Dynamic Type; Android uses 320 dp. See README.md. On the requester, send the payer contact a Payment Request for 21,000 sats with the note "Lunch last week" @@ -15,6 +15,6 @@ On the requester, send the payer contact a Payment Request for 5,000 sats with no note On the payer, verify the Payment Request confirmation screen (id "PaymentRequestConfirm") appears automatically with 5,000 sats Verify From (id "PaymentRequestFrom") shows the requester's contact name and For (id "PaymentRequestFor") shows "Not specified" - Open the prepared USD-denominated request in payment history. On a 375-point-wide iOS screen with enlarged text and a long translated verification status, verify a short contact name such as Maria and every status word remain complete. The amount and receipt may move below the contact when needed. Repeat at the default text size and verify short amounts and statuses remain beside the contact when they fit. Verify the receipt status shows the paid USDT amount with all its decimals, matching the payer's activity and using the selected locale's decimal separator. Repeat for the prepared BTC-denominated request paid in USDT. + Open the prepared USD-denominated request in payment history. On a 375-point-wide iOS screen with enlarged text and a long translated verification status, verify a short contact name such as Maria and every status word remain complete. The amount and receipt may move below the contact when needed. Repeat at the default text size and verify short amounts and statuses remain beside the contact when they fit. For the USD request, verify the receipt equals the request amount. Repeat the layout checks for the prepared BTC-denominated request paid in USDT. Verify its receipt shows the USDT amount with up to six decimals in the selected locale's separator and rounds to the Payment value in the payer's activity detail (shown as less than 0.01 below one cent). Do not compare with the fee-inclusive activity total.