From 4ce94fcffc10a440501fda09f8bcee22be443c5e Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Wed, 15 Jul 2026 13:36:46 +0200 Subject: [PATCH 1/5] ci: dedupe staging-lane runs via shared concurrency group (#857) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem Every push to `staging` currently runs the "RealUnit Build" workflow twice on the same SHA: 1. once from the `push: staging` trigger, and 2. once from the permanently-open auto-PR "Promote: staging → develop" (#841) via `pull_request`. Those two runs use different concurrency groups (PR number vs. `github.ref`), so `cancel-in-progress` never cancels one against the other. Both compete for the single self-hosted runner slot used by Visual Regression / `golden-tests`. ## Fix Map that same-repo staging-head PR (`pull_request` + `head_ref == 'staging'` + head repo matches this repository) into the same constant concurrency group as `push: staging` (`refs/heads/staging`). `cancel-in-progress` then cancels the duplicate, and exactly one real run survives per staging-lane commit. ## Fail-closed (not skip-as-success) This is deliberately **not** the skip-as-success approach previously proposed and rejected in #850. A step/job that is merely skipped and treated as green can mask a red `push: staging` result. Here there is no `if:` skip and no success-without-running path: both triggers still schedule real work; the concurrency group collapses the pair so one run is cancelled. A red stays red. Worst case is a plain `cancelled` check — fail-closed and fixable by re-running — never a masked green. ## Fork-identity guard The group remapping requires `github.event.pull_request.head.repo.full_name == github.repository`, so a fork branch that happens to be named `staging` cannot cancel the real staging lane. ## Feature PRs Unaffected. They keep the existing PR-number concurrency group (with `github.ref` fallback for push / workflow_dispatch). --- .github/workflows/pull-request.yaml | 44 ++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/.github/workflows/pull-request.yaml b/.github/workflows/pull-request.yaml index bffcb8cb..443bff86 100644 --- a/.github/workflows/pull-request.yaml +++ b/.github/workflows/pull-request.yaml @@ -23,6 +23,11 @@ name: RealUnit Build # auto-opened staging → develop PR (the gate must be authoritative # at the moment the commit lands on staging). Other branches do not # need a post-push check — the PR-event already covers them. +# `push: staging` and the auto-opened #841 PR now share one +# concurrency group, so exactly one real run executes per staging +# commit (the survivor is the authoritative gate — in practice +# often the PR run against the merge commit, a stronger promotion +# gate). # - `workflow_dispatch` is kept as a manual override and is NOT # draft-gated (see the job `if:` guard below). on: @@ -33,17 +38,36 @@ on: branches-ignore: [main] types: [opened, synchronize, reopened, ready_for_review] -# Group by PR number so a new push to the same PR cancels the -# in-flight run for the outdated commit. macOS runners are scarce — -# letting an obsolete run finish wastes ~10 minutes per push. Grouping -# by SHA (the previous approach) put every commit in its own group, -# which meant no cancellation ever happened and back-to-back pushes -# queued sequentially. -# Falls back to `github.ref` for `push` and `workflow_dispatch` events -# (where there is no `pull_request.number`): `refs/heads/develop` for -# post-merge runs, the dispatched ref for manual triggers. +# Base case: group by PR number so a new push to the same PR cancels +# the in-flight run for the outdated commit. Falls back to +# `github.ref` for `push` and `workflow_dispatch` (no pull_request +# number): `refs/heads/develop` for post-merge runs, the dispatched +# ref for manual triggers. macOS runners are scarce — letting an +# obsolete run finish wastes ~10 minutes per push. +# Staging-lane special case: the permanently-open, same-repo auto-PR +# "staging → develop" (#841) would otherwise re-verify every staging +# commit a second time on the identical SHA (its PR-number group +# differs from the push group). Exactly that non-draft PR — +# pull_request event, head_ref == 'staging', same repo (the +# repo-identity check stops a fork branch named `staging` from +# cancelling the real staging lane), draft == false — is placed into +# the constant group `refs/heads/staging` shared with `push: staging`. +# A staging PR that is (manually) set to draft falls back to its own +# PR-number group so the `push: staging` run survives independently +# and the commit remains genuinely verified. cancel-in-progress then +# cancels the duplicate; exactly one real run always survives (no +# skip, no skipped=success masking — a red stays red). Feature PRs +# keep their own PR-number group, unaffected. concurrency: - group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + group: >- + ci-${{ github.workflow }}-${{ + (github.event_name == 'pull_request' + && github.head_ref == 'staging' + && github.event.pull_request.head.repo.full_name == github.repository + && github.event.pull_request.draft == false) + && 'refs/heads/staging' + || (github.event.pull_request.number || github.ref) + }} cancel-in-progress: true permissions: From 29c3b5d3fd6ebd0d4b2c766bf237eb89102e6eec Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Wed, 15 Jul 2026 14:36:12 +0200 Subject: [PATCH 2/5] fix(responsive): keep bottom-sheet CTAs tappable at large text scale (#858) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Follow-up to #854 (which fixed 25 sticky-CTA surfaces). An **empirical** audit of the surfaces the original grep missed — they use `Column(mainAxisSize: .min)`, not `Spacer()`, so no text search finds them — proved **4 more bottom sheets** clip their CTA out of the hit-test region at large system text scale (dead button, no red stripe in release): | Sheet | breaks at | impact | |---|---|---| | `sell_confirm_sheet` | text scale 2.0 | cannot confirm a sale | | `sell_executed_sheet` | **~1.2** | success sheet not closable — it was shown **without** `isScrollControlled`, so clamped to 9/16 of screen height | | `forgot_pin_bottom_sheet` | 2.0 | cannot reset the PIN | | `enable_biometric_bottom_sheet` | 2.0 | cannot enable/skip biometrics | ## What changed - New `shrinkWrap` mode on `ScrollableActionsLayout`: sizes to content up to the available height, scrolls the body past it, keeps the actions pinned — so a short sheet is not forced to full height but never clips its CTA. With `shrinkWrap:false` (default) the 25 screens from #854 are **byte-identical** (their whole test suite re-run unchanged). - The 4 sheets migrated to `ScrollableActionsLayout(shrinkWrap: true)` bounded at 0.9× screen; `sell_executed`'s call site fixed to `isScrollControlled: true`; `sell_confirm`'s info-row horizontal overflow fixed (both label and value shrink-safe). - Each sheet gated by a matrix test pumped through a **real** `showModalBottomSheet` (so the screen-height bound is faithful); every test verified to fail against the pre-fix code. - Catalog now lists all **29** surfaces; `welcome_page` audited → safe (scrolls). Docs state honestly that no further sticky-CTA surface of this shape is known. ## Test plan - [x] `flutter analyze` clean - [x] `flutter test` — **4432 passed** incl. goldens (rebased on current staging with #855/#856) - [x] 3 sell-sheet goldens regenerated & visually inspected; pin sheets render pixel-identical - [ ] CI on this PR --- docs/responsive-layout.md | 26 +- docs/testing.md | 2 +- .../enable_biometric_bottom_sheet.dart | 92 +++--- .../pin/widgets/forgot_pin_bottom_sheet.dart | 44 +-- lib/screens/sell/widgets/sell_button.dart | 1 + .../sell/widgets/sell_confirm_sheet.dart | 199 +++++++------ .../sell/widgets/sell_executed_sheet.dart | 83 +++--- lib/widgets/scrollable_actions_layout.dart | 52 +++- .../sell/goldens/macos/sell_confirm_sheet.png | Bin 26716 -> 26863 bytes .../macos/sell_confirm_sheet_loading.png | Bin 26683 -> 26833 bytes .../goldens/macos/sell_executed_sheet.png | Bin 20142 -> 20140 bytes test/helper/responsive_surface_catalog.dart | 35 ++- .../pin_sheets_responsive_matrix_test.dart | 193 +++++++++++++ .../sell_sheets_responsive_matrix_test.dart | 267 ++++++++++++++++++ .../scrollable_actions_layout_test.dart | 234 +++++++++++++++ 15 files changed, 997 insertions(+), 231 deletions(-) create mode 100644 test/screens/pin/pin_sheets_responsive_matrix_test.dart create mode 100644 test/screens/sell/sell_sheets_responsive_matrix_test.dart diff --git a/docs/responsive-layout.md b/docs/responsive-layout.md index a2c60034..16ff8aea 100644 --- a/docs/responsive-layout.md +++ b/docs/responsive-layout.md @@ -24,6 +24,7 @@ Source: [`lib/widgets/scrollable_actions_layout.dart`](../lib/widgets/scrollable - **Bounded height required.** The host must give `ScrollableActionsLayout` a bounded height (bottom sheet, `Expanded`, or fixed-size `SizedBox`). Unbounded height throws a `FlutterError` in every build mode (debug and release) — fail-loud, not a silent broken layout. - **`centerBody: true`** vertically centers `body` while it fits the viewport; once content outgrows the viewport, the body scrolls normally. Use this for screens that previously centered with `Spacer()` above and below. +- **`shrinkWrap: true`** sizes to content up to the incoming max height/cap instead of expanding to fill it; the body still scrolls past that cap and actions stay pinned below. Use for bottom sheets/dialogs that should not be forced to full height. - **`Spacer()` is illegal inside the body.** The body lives in a `SingleChildScrollView` (unbounded main axis); a `Spacer` there throws a RenderFlex "unbounded" exception. Prefer `centerBody: true` instead. | Do | Don't | @@ -32,6 +33,7 @@ Source: [`lib/widgets/scrollable_actions_layout.dart`](../lib/widgets/scrollable | Sticky actions under the scroll view | Buttons as last children of an overflowing `Column` | | Host with bounded height (sheet / `Expanded` / fixed `SizedBox`) | Unbounded height host (e.g. bare `Column(mainAxisSize: min)`) | | `centerBody: true` for vertically centered content | `Spacer()` inside the scrollable body | +| `shrinkWrap: true` for content-sized sheets/dialogs | Force full-height layout on short bottom sheets | | Fix/cap sheet height (e.g. a fraction of screen height) + scroll inside | Fixed height + non-scroll content that can exceed it | | Cap large illustrations (`maxHeight`) | Always paint 200×200 art above multi-paragraph copy | @@ -66,13 +68,15 @@ Helpers: ## Rollout -The migration is repo-wide. **25 surfaces** are on `ScrollableActionsLayout` and +The migration is repo-wide. **29 surfaces** are on `ScrollableActionsLayout` and registered in [`kResponsiveSurfaceCatalog`](../test/helper/responsive_surface_catalog.dart) — the BitBox connect sheet plus dashboard, create wallet, verify pin, the three KYC status pages, KYC account-merge / merge-processing / link-wallet, KYC financial-data questions, onboarding completed, support create-ticket, five settings_user_data edit/status subpages, verify seed phrase, restore wallet, buy, sell, setup PIN, and the two CTA-less KYC static -pages (failure / signature-unsupported). +pages (failure / signature-unsupported). Four bottom sheets were also migrated with +`shrinkWrap: true` and are now catalogued: `sell_confirm_sheet`, `sell_executed_sheet`, +`forgot_pin_bottom_sheet`, and `enable_biometric_bottom_sheet`. A follow-up sweep of this branch found and fixed 3 surfaces the original grep (`Spacer()` + `AppFilledButton`/`FilledButton`) had missed, because each either used a @@ -85,22 +89,20 @@ migrated `verify_seed_page`, `restore_wallet_view`, `buy_page`, and `sell_page` The catalog is a living list reviewed manually, not a proof that every sticky-CTA (or overflow-prone) surface in the app is covered — the 3-surface miss above is exactly why. -As of this branch, `grep -rl "Spacer()" lib/` finds hits only in +As of this update, `grep -rl "Spacer()" lib/` still finds hits only in `lib/widgets/scrollable_actions_layout.dart` (the widget's own doc comment, not a usage) and `lib/screens/dashboard/widgets/transaction_row.dart` (a horizontal `Row` use — legitimate, not this bug class, left unchanged). No sticky-CTA surface currently matches this grep signal. That grep is not exhaustive either — it is the same limited signal that missed the 3 -surfaces above in the first place. At least three more surfaces are still plausible, -un-re-audited candidates because they use a bounded, non-scrolling -`Column(mainAxisSize: .min, ...)` with no `Spacer()` and no scroll view: the sell -confirm/executed sheets (`sell_confirm_sheet.dart`, `sell_executed_sheet.dart`) and the two -PIN bottom sheets (`forgot_pin_bottom_sheet.dart`, `enable_biometric_bottom_sheet.dart`). -`welcome_page.dart` looks structurally safe (its entire body, including all interactive -cards, already lives inside one `SingleChildScrollView` with no separate sticky CTA) but -was not verified with a matrix test. None of these are confirmed either way — that is an -open item for the next review pass, not a claim of completeness. +surfaces above in the first place. The previously-open candidates from that sweep — the +sell confirm/executed sheets and the two PIN bottom sheets — are now migrated +(`shrinkWrap: true`) and catalogued. `welcome_page.dart` was reviewed and found safe +(scrolls end-to-end with no separate sticky CTA); it is not a migration candidate. As of +this update, no further known un-catalogued candidates remain from the prior sweep. The +catalog and the `Spacer()` grep are still not a completeness proof — every new sticky-CTA +surface remains a review responsibility going forward. ## Manual smoke (optional, not the gate) diff --git a/docs/testing.md b/docs/testing.md index fd03fc45..37350061 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -140,7 +140,7 @@ Rules for PRs: - Interactive widget tests must prefer `tester.tap` / `expectFullyTappable` over calling `onPressed` on the widget. - Worst-case content: longest locale you ship (`de`), longest dynamic strings (e.g. real channel-hash shape), not golden-friendly short stubs alone. -Reference implementation: `test/screens/hardware_connect_bitbox/connect_bitbox_responsive_matrix_test.dart` (7 devices × 5 text scales × 5 button states + focused regressions). 25 surfaces are catalogued in [`responsive_surface_catalog.dart`](../test/helper/responsive_surface_catalog.dart) (living list — not a completeness proof; see above). +Reference implementation: `test/screens/hardware_connect_bitbox/connect_bitbox_responsive_matrix_test.dart` (7 devices × 5 text scales × 5 button states + focused regressions). 29 surfaces are catalogued in [`responsive_surface_catalog.dart`](../test/helper/responsive_surface_catalog.dart) (living list — not a completeness proof; see above). ### Service + HTTP diff --git a/lib/screens/pin/widgets/enable_biometric_bottom_sheet.dart b/lib/screens/pin/widgets/enable_biometric_bottom_sheet.dart index b6e106df..b37c582e 100644 --- a/lib/screens/pin/widgets/enable_biometric_bottom_sheet.dart +++ b/lib/screens/pin/widgets/enable_biometric_bottom_sheet.dart @@ -3,6 +3,7 @@ import 'package:realunit_wallet/generated/i18n.dart'; import 'package:realunit_wallet/styles/colors.dart'; import 'package:realunit_wallet/widgets/buttons/app_filled_button.dart'; import 'package:realunit_wallet/widgets/buttons/app_text_button.dart'; +import 'package:realunit_wallet/widgets/scrollable_actions_layout.dart'; class EnableBiometricBottomSheet extends StatelessWidget { const EnableBiometricBottomSheet({super.key}); @@ -10,51 +11,58 @@ class EnableBiometricBottomSheet extends StatelessWidget { @override Widget build(BuildContext context) { return SafeArea( - child: Padding( - padding: const EdgeInsets.all(20.0), - child: Column( - spacing: 32.0, - mainAxisSize: MainAxisSize.min, - children: [ - const Icon( - Icons.fingerprint, - size: 64, - color: RealUnitColors.realUnitBlue, - ), - Column( - spacing: 8.0, - children: [ - Text( - S.of(context).biometricAuthenticationActivate, - style: const TextStyle( - fontSize: 20, - fontWeight: FontWeight.w700, - color: RealUnitColors.realUnitBlack, + child: ConstrainedBox( + constraints: BoxConstraints( + maxHeight: MediaQuery.sizeOf(context).height * 0.9, + ), + child: ScrollableActionsLayout( + shrinkWrap: true, + padding: const EdgeInsets.all(20.0), + actionsSpacing: 8.0, + body: Column( + spacing: 32.0, + mainAxisSize: MainAxisSize.min, + children: [ + const Icon( + Icons.fingerprint, + size: 64, + color: RealUnitColors.realUnitBlue, + ), + Column( + spacing: 8.0, + children: [ + Text( + S.of(context).biometricAuthenticationActivate, + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.w700, + color: RealUnitColors.realUnitBlack, + ), + textAlign: TextAlign.center, ), - textAlign: TextAlign.center, - ), - Text( - S.of(context).biometricAuthenticationActivateDescription, - style: const TextStyle( - fontSize: 14, - color: RealUnitColors.neutral500, + Text( + S.of(context).biometricAuthenticationActivateDescription, + style: const TextStyle( + fontSize: 14, + color: RealUnitColors.neutral500, + ), + textAlign: TextAlign.center, ), - textAlign: TextAlign.center, - ), - ], + ], + ), + ], + ), + actions: [ + Padding( + padding: const EdgeInsets.only(top: 32.0), + child: AppFilledButton( + onPressed: () => Navigator.of(context).pop(true), + label: S.of(context).enable, + ), ), - Column( - spacing: 8.0, - children: [ - AppFilledButton( - onPressed: () => Navigator.of(context).pop(true), - label: S.of(context).enable, - ), - AppTextButton( - onPressed: () => Navigator.of(context).pop(false), - label: S.of(context).skip, - ), - ], + AppTextButton( + onPressed: () => Navigator.of(context).pop(false), + label: S.of(context).skip, ), ], ), diff --git a/lib/screens/pin/widgets/forgot_pin_bottom_sheet.dart b/lib/screens/pin/widgets/forgot_pin_bottom_sheet.dart index 389ad979..af85d791 100644 --- a/lib/screens/pin/widgets/forgot_pin_bottom_sheet.dart +++ b/lib/screens/pin/widgets/forgot_pin_bottom_sheet.dart @@ -4,6 +4,7 @@ import 'package:realunit_wallet/generated/i18n.dart'; import 'package:realunit_wallet/styles/colors.dart'; import 'package:realunit_wallet/widgets/buttons/app_filled_button.dart'; import 'package:realunit_wallet/widgets/handlebars.dart'; +import 'package:realunit_wallet/widgets/scrollable_actions_layout.dart'; class ForgotPinBottomSheet extends StatelessWidget { const ForgotPinBottomSheet({super.key}); @@ -11,19 +12,22 @@ class ForgotPinBottomSheet extends StatelessWidget { @override Widget build(BuildContext context) { return SafeArea( - child: SizedBox( - width: double.infinity, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Handlebars.horizontal( - context, - margin: const EdgeInsets.only(top: 5), - width: 36, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Handlebars.horizontal( + context, + margin: const EdgeInsets.only(top: 5), + width: 36, + ), + ConstrainedBox( + constraints: BoxConstraints( + maxHeight: MediaQuery.sizeOf(context).height * 0.9, ), - Padding( + child: ScrollableActionsLayout( + shrinkWrap: true, padding: const EdgeInsets.symmetric(vertical: 40.0, horizontal: 30.0), - child: Column( + body: Column( mainAxisSize: MainAxisSize.min, children: [ Text( @@ -47,13 +51,17 @@ class ForgotPinBottomSheet extends StatelessWidget { letterSpacing: 0.0, ), ), - const SizedBox(height: 28), - Row( + ], + ), + actions: [ + Padding( + padding: const EdgeInsets.only(top: 28.0), + child: Row( spacing: 12.0, children: [ Expanded( child: AppFilledButton( - variant: .secondary, + variant: FilledButtonVariant.secondary, onPressed: () => context.pop(false), label: S.of(context).close, ), @@ -66,11 +74,11 @@ class ForgotPinBottomSheet extends StatelessWidget { ), ], ), - ], - ), + ), + ], ), - ], - ), + ), + ], ), ); } diff --git a/lib/screens/sell/widgets/sell_button.dart b/lib/screens/sell/widgets/sell_button.dart index aedefcfd..f4227d75 100644 --- a/lib/screens/sell/widgets/sell_button.dart +++ b/lib/screens/sell/widgets/sell_button.dart @@ -79,6 +79,7 @@ class SellButton extends StatelessWidget { if (confirmedSuccess ?? false) { if (context.mounted) { await showModalBottomSheet( + isScrollControlled: true, context: context, builder: (_) => const SellExecutedSheet(), ); diff --git a/lib/screens/sell/widgets/sell_confirm_sheet.dart b/lib/screens/sell/widgets/sell_confirm_sheet.dart index dc59266d..178d8854 100644 --- a/lib/screens/sell/widgets/sell_confirm_sheet.dart +++ b/lib/screens/sell/widgets/sell_confirm_sheet.dart @@ -10,6 +10,7 @@ import 'package:realunit_wallet/setup/di.dart'; import 'package:realunit_wallet/styles/colors.dart'; import 'package:realunit_wallet/widgets/buttons/app_filled_button.dart'; import 'package:realunit_wallet/widgets/handlebars.dart'; +import 'package:realunit_wallet/widgets/scrollable_actions_layout.dart'; class SellConfirmSheet extends StatelessWidget { final SellPaymentInfo paymentInfo; @@ -59,121 +60,72 @@ class SellConfirmSheetView extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ Handlebars.horizontal(context, margin: const EdgeInsets.only(top: 5), width: 36), - Padding( - padding: const EdgeInsets.symmetric(vertical: 24.0), - child: Text( - S.of(context).sellReviewAndConfirm, - style: const TextStyle( - fontSize: 26, - fontWeight: FontWeight.bold, - ), - textAlign: TextAlign.center, - ), - ), - Container( - decoration: BoxDecoration( - border: Border.all(color: RealUnitColors.neutral200), - borderRadius: BorderRadius.circular(16.0), + // Handlebar stays OUTSIDE the scrollable body (sibling above it) — + // it must never scroll away with the content. + ConstrainedBox( + constraints: BoxConstraints( + maxHeight: MediaQuery.sizeOf(context).height * 0.9, ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: _withDividers( + child: ScrollableActionsLayout( + shrinkWrap: true, + body: Column( + mainAxisSize: MainAxisSize.min, children: [ Padding( - padding: const EdgeInsets.symmetric( - vertical: 12.0, - horizontal: 20.0, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - realUnitAsset.symbol, - style: const TextStyle( - color: RealUnitColors.neutral500, - height: 18 / 14, - ), - ), - Text( - '${paymentInfo.amount}', - style: const TextStyle( - fontWeight: FontWeight.w600, - height: 18 / 14, - ), - ), - ], + padding: const EdgeInsets.symmetric(vertical: 24.0), + child: Text( + S.of(context).sellReviewAndConfirm, + style: const TextStyle( + fontSize: 26, + fontWeight: FontWeight.bold, + ), + textAlign: TextAlign.center, ), ), - Padding( - padding: const EdgeInsets.symmetric( - vertical: 12.0, - horizontal: 20.0, + Container( + decoration: BoxDecoration( + border: Border.all(color: RealUnitColors.neutral200), + borderRadius: BorderRadius.circular(16.0), ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - '${S.of(context).amountIn} ${paymentInfo.currency.code}', - style: const TextStyle( - color: RealUnitColors.neutral500, - height: 18 / 14, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: _withDividers( + children: [ + _infoRow( + label: realUnitAsset.symbol, + value: '${paymentInfo.amount}', ), - ), - Text( - '${paymentInfo.estimatedAmount}', - style: const TextStyle( - fontWeight: FontWeight.w600, - height: 18 / 14, + _infoRow( + label: + '${S.of(context).amountIn} ${paymentInfo.currency.code}', + value: '${paymentInfo.estimatedAmount}', ), - ), - ], - ), - ), - Padding( - padding: const EdgeInsets.symmetric( - vertical: 12.0, - horizontal: 20.0, - ), - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - S.of(context).receiver, - style: const TextStyle( - color: RealUnitColors.neutral500, - height: 18 / 14, - ), - ), - Text( - paymentInfo.beneficiary.iban, - style: const TextStyle( - fontWeight: FontWeight.w600, - height: 18 / 14, - ), - ), - ], - ), - ], + _infoRow( + label: S.of(context).receiver, + value: paymentInfo.beneficiary.iban, + ), + ], + ), ), ), ], ), - ), - ), - Padding( - padding: const EdgeInsets.all(16.0), - child: BlocBuilder( - builder: (context, state) { - final isLoading = state is SellConfirmLoading; - return AppFilledButton( - label: S.of(context).confirm, - onPressed: () => - context.read().confirmPayment(paymentInfo), - state: isLoading ? .loading : .idle, - ); - }, + actions: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: BlocBuilder( + builder: (context, state) { + final isLoading = state is SellConfirmLoading; + return AppFilledButton( + label: S.of(context).confirm, + onPressed: () => + context.read().confirmPayment(paymentInfo), + state: isLoading ? .loading : .idle, + ); + }, + ), + ), + ], ), ), ], @@ -184,6 +136,47 @@ class SellConfirmSheetView extends StatelessWidget { ); } + /// Label/value row: both sides are Flexible and WRAP (not ellipsize) so a long + /// value (e.g. a beneficiary IBAN) always stays fully readable — the sheet + /// scrolls, so extra height here is absorbed, never data loss via truncation. + /// Wrapping also prevents horizontal RenderFlex overflow for either side. + Widget _infoRow({required String label, required String value}) { + return Padding( + padding: const EdgeInsets.symmetric( + vertical: 12.0, + horizontal: 20.0, + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Text( + label, + softWrap: true, + style: const TextStyle( + color: RealUnitColors.neutral500, + height: 18 / 14, + ), + ), + ), + const SizedBox(width: 12), + Flexible( + child: Text( + value, + textAlign: TextAlign.end, + softWrap: true, + style: const TextStyle( + fontWeight: FontWeight.w600, + height: 18 / 14, + ), + ), + ), + ], + ), + ); + } + List _withDividers({required List children}) { final result = []; for (var i = 0; i < children.length; i++) { diff --git a/lib/screens/sell/widgets/sell_executed_sheet.dart b/lib/screens/sell/widgets/sell_executed_sheet.dart index 8f65fbad..3a5d70a0 100644 --- a/lib/screens/sell/widgets/sell_executed_sheet.dart +++ b/lib/screens/sell/widgets/sell_executed_sheet.dart @@ -4,6 +4,7 @@ import 'package:realunit_wallet/generated/i18n.dart'; import 'package:realunit_wallet/styles/colors.dart'; import 'package:realunit_wallet/widgets/buttons/app_filled_button.dart'; import 'package:realunit_wallet/widgets/handlebars.dart'; +import 'package:realunit_wallet/widgets/scrollable_actions_layout.dart'; class SellExecutedSheet extends StatelessWidget { const SellExecutedSheet({super.key}); @@ -16,44 +17,56 @@ class SellExecutedSheet extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ Handlebars.horizontal(context, margin: const EdgeInsets.only(top: 5), width: 36), - Padding( - padding: const EdgeInsets.symmetric(vertical: 50.0, horizontal: 30.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon( - Icons.check_circle_rounded, - color: RealUnitColors.realUnitBlue, - size: 64, - ), - const SizedBox(height: 28), - Text( - S.of(context).sellSuccess, - style: const TextStyle( - fontSize: 26, - fontWeight: FontWeight.w700, - height: 30 / 26, - letterSpacing: 26 * -0.02, - ), + ConstrainedBox( + constraints: BoxConstraints( + maxHeight: MediaQuery.sizeOf(context).height * 0.9, + ), + child: ScrollableActionsLayout( + shrinkWrap: true, + body: Padding( + padding: const EdgeInsets.symmetric(vertical: 50.0, horizontal: 30.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon( + Icons.check_circle_rounded, + color: RealUnitColors.realUnitBlue, + size: 64, + ), + const SizedBox(height: 28), + Text( + S.of(context).sellSuccess, + style: const TextStyle( + fontSize: 26, + fontWeight: FontWeight.w700, + height: 30 / 26, + letterSpacing: 26 * -0.02, + ), + ), + const SizedBox(height: 8), + Text( + S.of(context).sellSuccessDescription, + textAlign: TextAlign.center, + style: const TextStyle( + color: RealUnitColors.neutral500, + fontSize: 14, + height: 18 / 14, + letterSpacing: 0.0, + ), + ), + ], ), - const SizedBox(height: 8), - Text( - S.of(context).sellSuccessDescription, - textAlign: TextAlign.center, - style: const TextStyle( - color: RealUnitColors.neutral500, - fontSize: 14, - height: 18 / 14, - letterSpacing: 0.0, + ), + actions: [ + Padding( + padding: const EdgeInsets.only(bottom: 24), + child: AppFilledButton( + variant: .secondary, + fullWidth: false, + onPressed: () => context.pop(), + label: S.of(context).close, ), ), - const SizedBox(height: 28), - AppFilledButton( - variant: .secondary, - fullWidth: false, - onPressed: () => context.pop(), - label: S.of(context).close, - ), ], ), ), diff --git a/lib/widgets/scrollable_actions_layout.dart b/lib/widgets/scrollable_actions_layout.dart index 4ac11411..3c4063cf 100644 --- a/lib/widgets/scrollable_actions_layout.dart +++ b/lib/widgets/scrollable_actions_layout.dart @@ -36,6 +36,7 @@ class ScrollableActionsLayout extends StatelessWidget { this.actionsSpacing = 12, this.scrollPhysics, this.centerBody = false, + this.shrinkWrap = false, }); /// Scrollable main content (illustration, titles, forms, hints). @@ -57,6 +58,14 @@ class ScrollableActionsLayout extends StatelessWidget { /// `Spacer()` above and below — without this they would top-align. final bool centerBody; + /// Size to content (up to the incoming max height) instead of expanding to + /// fill it. For bottom sheets / dialogs that should be only as tall as their + /// content but still cap-and-scroll when content grows past the available + /// height. In this mode the body is NOT floored to the viewport height (so it + /// genuinely shrink-wraps), the actions stay pinned below it, and the whole + /// layout scrolls its body once content exceeds the cap. + final bool shrinkWrap; + @override Widget build(BuildContext context) { return Padding( @@ -103,27 +112,42 @@ class ScrollableActionsLayout extends StatelessWidget { return Column( crossAxisAlignment: .stretch, + mainAxisSize: shrinkWrap ? MainAxisSize.min : MainAxisSize.max, children: [ - Expanded( - child: LayoutBuilder( - builder: (context, viewport) => SingleChildScrollView( + if (shrinkWrap) + Flexible( + child: SingleChildScrollView( key: const Key('scrollable_actions_layout.body_scroll_view'), physics: scrollPhysics, - child: ConstrainedBox( - // Always at least the leftover viewport height, so bodies may use - // mainAxisAlignment / centering. Once the body outgrows the viewport - // minHeight stops binding and the body simply scrolls. - constraints: BoxConstraints(minHeight: viewport.maxHeight), - child: SizedBox( - // Center() below loosens the width constraint; without this the - // crossAxisAlignment: .stretch chain breaks for width-dependent bodies. - width: double.infinity, - child: centerBody ? Center(child: body) : body, + child: SizedBox( + // Center() below loosens the width constraint; without this the + // crossAxisAlignment: .stretch chain breaks for width-dependent bodies. + width: double.infinity, + child: centerBody ? Center(child: body) : body, + ), + ), + ) + else + Expanded( + child: LayoutBuilder( + builder: (context, viewport) => SingleChildScrollView( + key: const Key('scrollable_actions_layout.body_scroll_view'), + physics: scrollPhysics, + child: ConstrainedBox( + // Always at least the leftover viewport height, so bodies may use + // mainAxisAlignment / centering. Once the body outgrows the viewport + // minHeight stops binding and the body simply scrolls. + constraints: BoxConstraints(minHeight: viewport.maxHeight), + child: SizedBox( + // Center() below loosens the width constraint; without this the + // crossAxisAlignment: .stretch chain breaks for width-dependent bodies. + width: double.infinity, + child: centerBody ? Center(child: body) : body, + ), ), ), ), ), - ), actionBlock, ], ); diff --git a/test/goldens/screens/sell/goldens/macos/sell_confirm_sheet.png b/test/goldens/screens/sell/goldens/macos/sell_confirm_sheet.png index b59a55fc52143faa96b6c7a7f487a0b4b3376674..bdd7498d96bd5d79659ef0dc2f4873cca4152184 100644 GIT binary patch literal 26863 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 QScIh@ry^S-{qDn+a literal 26716 zcmeFYXHb*v-z^$NQITQ+lp;JTNE1-$T|q!VIsxelO79R#D8T|KNC#1>^iHIPj=}?o zG^r6nl@daL03iet%DH&=c|V*pbIzx|_lG@yXB>t*;mVz>tlwJex9$&mI_eB`>~s(a zghAuUV*?1}R5%1e^YlC|xbn<}jtl%aKu7KYeW)7Bm{h-MvMQbafe(wE5TJOumYN9#1m!)5rH8o^g2`r}wz>aN2|yq^oBae9 z2*l#qc^b&~f9N5QxA!0r`gRE9-hVFqXFmS39sY9~{sRX8!NUL4hX2z_W5+DU@6u%} zi)q<9qcP1TX5*Zq_Krw_=EwKLQus?YuXHjq8~wzTLLg@~y}U{oxueJCbGyZk19^Q2PY(kTJx%)i|*&vlxju;2r$*h4016Xk9TE zV#Rl?#Ol8LI32{pSkh%C+Y=d7CUfyq7O`qTfIb>OHd^0JE%j_k#Wvr9gnFY~#^BMx zjya#o&5n^NTgdiHa|ER4Z3@0Z(ej9NgC>BNS3Vz2xwud!UH^$7T`u5k(QKjCW7P+pY@!{GNW*&k20;&Na zsoa~Q5@qHp#MVGdOSwAPQp`R}OwrLxH?gy{tixilOur2~2*%kE$wbfj&3I;xl)Rdq zj|WyP8U}jLZzj_VrOuX~s%I8t{<@KY)O+?J$E?|B(~8!T_2Csj7ZKWF$2azF7DPFR_O+S>MQZ%NTGI zI|K1}K?$KCSC2P|G6RVV_ruT8TIe!E=9KcfCCW+PL-F3}e3*L{VXcsXh})E(?Da z@>9Jdd?!9DYg+A|3fj-Pv}ikjkPJTs+3tBIA%v&6x%Nw&Tt7{75jXS$wb*^~!qD$W zQbPAOM^TQA?Y{{H11`dl@2W*MVGmaQ4DHe1gyqq{4$vWfoz-h7QmqV->qBMC&j@s9v@h7Lmny$G0!25`&WBV@Flwa77}QJ<{0%{*%oGbbR7cuqN#d`7V_QkLC5zB9?16#`I?{NbTu_gs`au% z53H_%H~%9cp{=M>IPzsy=rp9>!~eiZ7xKL`>i?6a>@}}PJyTN)Pe?Sh=r}qGp`)ko zL&JVu)G0I^I>#g^BBH|^nWFdMO6L+5n=)Gpo8jT(b0*9L)A2KZO}KoSj*)SI098KK z!P=qfLiuP~VoZF1)7dm}E5y1)H7aGL6Wc{}?}|FF!c||PE_F4W zfY{UMh+rT|yPKJ%77TuN?1B+|=UW|En0rmi?saIVijUuBnh~|Owk|hsvx;Vy=tIGJ zwR>5G#U17#!u!F+lc*-MXm-gso@oCy{r=A~TG1R5quNq0+azj24rx)Ix*tO+-Sy#q8t?;w!3$hzp;fZ{-9bS&_ZUleTB8MVqmATD|o%b z>`^1=hYwX~m*CV6^)*}f(QmJ58CM9Dmao#jYoA$#t!%oY5%98KDMCmSt6*)!VaudM z$UyV{yB{aB!f)7EV@(LraaJ8FDbAp<5eG+;wi#1*YE1nh9^-zV+!|M`fPzQ1v2b0kNXbMPCx{0Q$RAe2O7Xkp zW|DrGvki9rW{19$QLn~Ipre(+a%c~Wpys2az~}~T3ybEN5OT0a0umWZI~AM~6s((J z?^Q8a4nZJDZB!k!*O66=8o-4FWGu481X(bUAzNbHujO?R7+B>>WfYs**}hYO-JHK2 zK|co$4-A|{wH z)6y+5nA541E^KC=9yv>fDIGKC&4D3!a_> z9PX_WXllql0=Yyv7RAs0Z6Y8%7!g4=-AlEuvpok4ItmQ~ASx!c5hYGhC$~71o zh(0(x$M|4mCJ^xk^MEgCr}7InritLGP2He!PaTD1h8P$cTB3SNvE!>_&gnGgrv1i` z+q}Kj{@kzrNUNPHGcr7!;pm8SJI+%2vV$`f<+hI*ffAoYFs}D)rb;;1`Vkbx+{UsH zUo5$@vv0aJ$?UF8lzu8l4s-W@vJbSre?J07R(M;piJtYcD74QC)F&(LyvOY2s75VQ z{1Xxqd@wisoDajJ?Sk9(x5cUl8!Z~DmZyV!kI0i8sg#b`h5Abqhkvia3x2VU3dOEctTO z>4HjIHQYc5NzO)*XYBzhcSF>Dzn%-UDshwwpSzz14GIp9$t+ zBqg2{fEcD;Rg}ed@3edS-cSNtT&D6is5e*nnvpi0kr|{a&t>VZxCr>pirvh_(=qS?8=6&sCA!riMv`+`A@o$N5h2IL5%>BD+ zNytmVe|kC6xN)ls4{dRR%B07*V2LDR;s|c^_lph*?_EwY*I#)bCb)hEtqrMOF6J)4ixJ(3Y5+`B5?7Z(heQAps5BUP*oPw=)W#%QTbBqiS5_QlOx8P~U zzJYR{`op)lu#npk5nF_Twe*hUUnzqlcmc``JnH2C3>>SV zpl~FE-WxAB!<9=}Nf6-+4Pzy*E^4PrKl<_Ghb+Kx3KORbBbWPR^L`n(A%HkgL|oCN zi-o27EscstD>P`??ruJZj;?6COj;5iOX)h-U>kLl3Fct$$0uyo{4Z>58I;Ld#PKO~ z4cI5O4l{jyStN~+S83xV*P)L9&NHenUxbenw@|Kw0a63ge!C+{J=~@CWVaGaY859E z$$y2t^%;*xi_IpZNG!qj?&(Cd^l^T!AhA)VDM%Ex(O4-9@2nUlWwXHIDw{SFhpZRa_kht}#=A`O>B5UF&};N9aYL?Unw5_U&5|#>+8Y z>55;EE$t6v9-Z)_{kFUJ7Q&f0O7L(yL}$59^Fy8e1Fdp1QlX^lO!`6y;mtP7!sW~J zZuaUDE_$={V7~bk6@R!&jcA-qLT#^vmF1w=QZ&~15Dx4AR_LtC8AGqppmc0tVBj-N zlKNt3P|Yspn-1zE%sHzs)~yHraUp!B<>+b_33l@z`hc=Pw8$NLG5bM%f|UY^DBh&h zh_Brcy^ir)^WaEH8_^~82|lR$D3~OQ81I#z#uXaEw~x+ouYM@ZR4k${n71oH@se_c|v*Sg@a{ci3bv-B|ba)di9luKW#A=+L-4KW`7q(rHlIZI-oGLS~us zYBCQdl4KpNJ_(NrLIv^3DVZkW9oav~3SYBA6iHpHTl%{7o%I@tU2f(+dt|_AgpA8R z%fcMo8D~Pox2}o}Mx)8Q{-Jll+_7y;mln?WlUPJ6%9{WljWsV`@xHY6BZ3?HmO|<) zewVN_E=OpX4#DMEvv6_U$@pU*RQKXfBP?w~H9j>pbzRZkeX{L1G$ApOg_(Jg9p#L6 zL+uajDE|%jy;Jxz=Qq6sbAW~2EO=3RA-c>xx=ZWBRD=9U8P{Z?8?}#nRbQPV271Hy$X4x6V%+AMrqjy z+?=-{RKsC`(LV|e714h@7it&D`KjCcDUHtGTP9}4F|<>1&k~gmpSFn}stn3gn=un+ zrpJhk@{z%BLm$eMPLUbM53E@omdhprD2Lbo>wAq)Kg86znv@D5$H9OSOGHu9=I<91 zR`>kwN{++qShi`K5}Lm{#NPrWaKH^4t!04BRH&Y&XSyIzwxt_8wve0Y5pCY~BDay` zGuwu|iiHJg<%TneyR3A;=kpDlI25UWwQ604`e1QDwx&`5#j3!``K=oqs%>=ZczFeY ztw@NR4L+u6p&p&X9$epU|91Iyb%x|-X49bKL^OM2+oL@k7H5~DVbu_qX`Z>W0n3=l z+RUC%tw9{W7K3v=O_f(o5D%I96SbN~Y5pKgE84R`Joc6g-ipDvkHkHqX{j-`$F}}> zdxE`UZkvZp4LY`Kewgye_dkyy&W|70<4?D(?X=+;p~VI=-2oeo3%k{3gyy*EFKT>1 z4Xnu^x*vfmBF{CdH0>HpO&XK3#tScF z@A-cyoYpwm3wmu<6{s6BfA4D_E^CpR%H8X@g{O*D0;4&%KC@x*&P=WQ%i9|QsQtsk z!<}X4jdT2j7Q!jqzpC1^A*2SgT9+3E5dadV$6O`sm&F;l?UYJ;2GKL>dU|mMA;f&j zlvg6=&;k^a*5h3v1sS`VfNaIYLrR0f?v%Lmq>=U0FN?nMRzQeoe-R%d6=xL^iNyW~ z=~P+yDVP1 z@i4T#U6cRr-KVFHu8muLx9yGr!ev@eKj$3~S!CqEB@HdDkqw2wUGAClpnze9&Q zYN8%aZQA?%xkFP+yI+#Ndbw|`E1H8>12q+t2;^yf{{oTeAWAz-|U1a zW{{$*JNO%LN)~V>!DlSl71ZFLmt%2X?6c4VmQADJq>xr-gqgU6J&rEZGD~NL?CzWP z&wdS22;Lq#BUa%wCI~02OL@1f|K-)P(Eq887C{#|r#J(wo!?3u4}J6I-b2|fXf)8y zz_Sp0nBAA2I$freeo{4(tH!XW5Y^={kfVv=Z1W z{)a@KoVo)h*?V~atz?dJ8k}i~lwr=b>5LL6Q9di-^ylF^#&Uh@ZVosYxPH{E#De1S zUuR*ngC^&_We%iYa480B9g?4`9@|g=!+VwgN)g4@O}*+haTxl`J6Y?D%3a3 zXvB{k&e#014?G?5xedAgMj1{Bg&|$trC0H6tCu1^o??<4U=x~CMyok<8{kfx^nXn$rz%6O?mX=ZP;lofi zC7Z?T=*l;1J`RXl#eIi2jNFPj>Jds6Y-09H(g?74D~EnFUJSG3xem%QrdVdCK#jDT zx_N{tu_{k>o&rKJPo0}1dvEU@r@I_t;OQ7BzdXgJy^_nZ>!q{TfpgY8+uP9&e7Jz=8SMA+Oq*_GMn??04jGW~G7HKNzEK_!A=P!fFf z^LZqkM4Ixeo^Ok~_x=AClx9G^+Z}-OvP0a#*-b497}kVzsEKV%I?LkouVs#89J+}u zSO+c$ixK1qAz`n~>kNFMGq<&K=rm>6Mz)Pe=Ch%&Nco)W$0F~Nc2_&$r-|7m* z@z2-RFN!ZWTPkMdfZ-D>zZ2T^5pE%zDkVk)Fw@!WfO435dLnhHRB^}QKU3C~IjYmk zTQ3hO3}!X{AK1uS>f9??(fFO+G-N~nF<#WWW_ggMqP&c+TVY|hp3u19+j8*N+pQ^t zi(R(wu?Qa++7MJ_%pQ6+C=Gv4#D>#0wAa#as>0JYD=s!RmXTBXt8*i1^yu)M#fTCx zBH%uFK@Cl9?Xf+q)jbq~+K(P3)`)p>G8Rq<0`B10SYxb>d+ea&w?Jwc1NN##2XHoO zZI(2l#TFU~K91$7l$E!Bq^-Oj_WNERom*M^({Q;=Yo4Ja&y#!OQP>@GV&Qu2Yc-m;dgZgVR%ttk;32|$=X30uGLwSC2M z`Emt!VMyt(WI;h;VQtaook+}9QZSVakRz-*q>O*>k4?o9q^LotjH=O)JwU}{G}-1<#u1(R$F}?5#h8x2^;=!twQI9++xpy z(+Gvmgc%4K77mV5;D6zM-4#s-gY+a)i9GoD=CX5(rN-qp;ZkmsifPI=Ky(I zKniD@2S*B>M+$`9%moXH%K_Jcv3#$i?e4P`io)&N9rRiT6F;Az339$` z;v=kg51>ne#{FAIb(y=aKAAO6L+$h?NMmhqXzuK?af#6W+xhz5!vyAl$%{i8g#@?^ zZZ2D8BK)RQA%WaV9S8w1n{o%3Z8CSp}W=Kn-^m@-7fsY<1QdHkmxq_)u*y+;F~Ex)smpA^hANPdfzn@2TF! z#g}K#o^9xIC4bA3y(Lei|5PS=qI1j|{j)I#4UWV0@vTQTk)M#yiczxxgX@VpZO2=M z2$pbQ0<9Egt(zJ#O-VfijRY72H&~J8ut$6Y&8y@2Q76(Mc0|r^wJ4iOX+`!S!rHxJrjIH82%hF`IVXhiXO!&^SM~rBdDS6g@ z8Zn*cd0YEnGx(73DzIRr&J~lK;I$ffJ1BU~(04Gig^M66OZkeaPjhelQ8xIOx2>r^ zF>Pf<#XdMKamF8i!-V{;@VKA}89 zCWpnIgYu<~4B(@Oaw`JxQf?(TndI|iys`L)J~in&g}~5FPEA;?vkEC(uan*1y2@8R z`%RxaNLt?+T`?khk=5xJ_KRpUuhu6YbKqpR@e3+8P3wg-a{c(D3d4IjvuU|*%KKZ>YeC#kyVTg>1^=q;mude>P zaCNlMT}jxw(!kNtQ4$t!KM=NVRYuQ_c1s)#og;wmM*83-*96W#{OHjmcGBJ^CuoL{ zGq{ixh3U{Hf^0`^|MYDp1rIgiA&KmCQuoWv5vz^ya&B$fDzprJ)v!}u%GX5}LbQyd%X zzxRVWtvK`7hvwW4`b|T%LMsR2I0H3GsB7hNN7w7Cpb20UbKMxv+0!S*Ig|UiCKGN` zL)n#q)z%`js7eth<8=)x`O9DA2iDE6BpDX1w!7T5>)w-Farub5hS-Yf>VTTe;Ll4& zGx4{%Mn>IRjoL7j56n=0axp;g846cOlLFKL(y%{p_?jx^T+;p{EIB>B4E(vnBp8kSi>OnZRn`deG7k5K zv5!&j&zk^%h35Es-gqD5+eFI)YMHma_8^JQ{yWEcnz>g%`oTBj)x8hzG$bmksO)t3 z&H6Y0VUXrNPvgOrS7`6h)n}S2<6PGYOpjQiIN)QzsSi|}tz_`O`T{agos`6UG6OhA zM>;w>gPw%Tpf9(=mhs_iI@7yTkmnx8SN<2KYde&b2=W5F+8=rxHMRVH&inkD_xOqH z8%BMo;1Q(GI zL|dOyBoY}EG=QCiJUsH2&8G+Md4ypxj!jcT+r-H%{0$9_k+`^CD|SC@t2z9p$bHqV z55^LS_`) zf0n-RkWPjKdJCCr;}^LP-TELPc!PCyOhMGDH^y){+x7w-BmF;gkhpV^%p!Sn z-yxlxT5(c-B!6Lg&kF+Eq%HleTWgA^Lz@knpTwQqR7%|6TI#0eb-+IVUNL00D*GnI z$^^MqyJ-`~lJ)skM28%P!RmsFOHh^a+l!vNmm&VILGx4j%|!vpn-g-qgZQ(u89JSO z`GPeb&#gq+As)&O^ACA>^5IJ(x3=Z!dx~*_%yWE7b>M3|9|Q$6&k+h2M{DG*eKR9& z3h*jeD=pAKL|A{kKBcLnF-Tkj+-P`MjQRRp3Oed6^|0j*Q=Oe*0W0!pIR z$>$((V4xN+JA=%&H*ya+Ijtd~#P5TH>TFi$RWcFWZ;AifM`3H<{rj@Bi}wki&X}Mv zkI|1uyOFE8m7bcKIuZ*(L3uw7=ItyrolLc7*-L5X(b=rz5^k$mJg4zwc%zkjioO#R^7;Ah#|p4_qFG!C7wIH_zx*}R(~9| zz)Yhx?IuURMAf*W6&3b9+Pp)`AloK3HaTk1Y^9(xsHxBh8tTPk7im_J5^Tzq<+N1(>zn^k~U+~2DS zVEB;Wt&X6Utsy_S?_&g7p25k)n^z-NK^9mJb-!uI493x`V-mO#0Zno{+v6F%xpt36CA5ov z{O@+VSxdEW6#)UqimV!+z!*YvXQ@#sTy?C-;L(fDs8y7bF}5R>re06M|2UZP>O}LK zjx6jb1d>lWJeo=TAV9~&bfOqR@9D0_WRsM)pKpJvgayhG#5(|~@mXAnCvIOKfWDqg zPWqglgCid4St#ciyZMz+mJ-?(f2=)X{1v5~`pnCn;=C|tnLg7_@`ylm$d*d~-V&2Uv3tiQ1LdPkXA^ug~~BX+v80 zsi7vCl+=bIVpg+HwMh6nD4}%;BCb2wNntm%lK+)w{10EhK1H!zq7_qAxUJ$WwXRpI z`Kr1_IP^Fiv}Oeq6(6OcT;h3L|I}-2ZCxla5Fj$|PS%Ph@(7#PDJLX>I2Z`yK*8EO zm;O~Y`C4~f$E4Q4bgZeYq~K{rX?U$O|K`6UxqEv&>ysS8lj(|)f+m_oR;0lP8&#tQ zLWU14emn{rt$^MJI^QTuJEM?QTV$m2 zkAJ__=lwGd%R|CV=yYU;=o;D|6{I!VbD8|9hoyA2$WHW;2dbHz zx3+6nXR`A#58k|`D}J=@M0wR?U2!iD?9KzcVPDW&DoO9Kx1<MxK#iyV62&k zcsFlu?s+hI?Aof5$G1#4ycq~03DELw#D&l7Z{0g$;kM_p)@_lKHD=Mg*4wLU^)05G zx5UHaE2j1epOWx0~RDu58dxCqHlvyd(ix6q1KQ_R?x0S|_PckFV@IxDxk3V#rmX+UZ?nBK*&c(QIV)^7PZ_j?)W!? z?t;KtzWQer-P7wnx6T}M$UQsJCf;*#A)gzbWg(QTYekE53@&$iw;ayrR;KPP(M!1d zIMyAjwB)WftT!T=}A_*OZj;j|f4 zJot#)Y>zrC>nm^dN;AAzHL-WJQu`_ze$ePs8*;edzCLyJB!>seokoJ1(jODv4%Z^% zikUKQ?A`H^j*SE!?Ju$>swyfzE1UPr&6-TZfbUoHNEqfiYZ3PLt-Rt!&+LHy{?GFY z{(kQJ6uZ_?Ds{iutz7V!;*P@-N+HLm_r)PzOFpusqj#~RMUQAmD?{2$3J>~RGs#MO zdMV>2#W&y5Y9^@BBaHl}Bpxbhrxl!tZtGxT1`iHbm?6r~-W2k1XA*nBsX6%bH`o=FfDHm2D3qE@ zw|OcjEN0wkBQTax6bRy3UY5&KGZ5icV zFwvp<7k(wa{l5Tn9?B)p6utjujO3%5izF^T&*}XtoSnC8e&MX*-{~BW=IRwbZ zYJ{nlyrNq~M>YVGBGV2nOZ}fX_oRdbyAS{jgY!s6=BJ8^ij>7%rX5^f>Py@qDY7A$#1hTN1s_|+j#qoqiL+}<;S%qQr?GFl@^ZuRM%wV z@|m1cq(f1s(D*OVfW3iiwop_wz}BVU2W!niVfQp{DpsS%<$hN_m5>KZt8>Ow1ao0A z*D?a;q)RMt7AgCHM6f?ttrcrL-j6bNJY3GGnD51-jX*)nn4hG%)@su^HCvS7^N*}q z0<#&V$tgI0518JN6?el@EII#Co+c1@kZXv-#hvK1f7g9}3``Ay&HnE$=e za?ab*_wD4^bl1AVrQFV5w8QM{4nX7#!aF=|P{~FWPv3DeM(o~ruixz_LBw#;DfNRd)~jT7Mc+o?=CDpYDJkSemY)L+uX^_VCc zKYt#|$)-cv7&bc!v2Q*8_VU@rxjtPwA*;u&t*w)_Qf4V*MM(?I+1Woz^u}9-WPJ8M zcL!iBhn|$Dj>;^MAV*raZauKpwXxC3_!YePWSI@YBAArIqpGInMBQDlz=59bInezq zH>;ic#0NOft5z9#_)ZaMP(LYj4SxKoCphX@S?_CmTyn>J99Ww9+%MP}^-px@5()Fs z0@Fjz!6S{qpFc^V_?mz(-J%<9r67MC?6cF+u~@g(Kx_0fSR9C>SN2&@Q~a zEcC9mnb@+ZVUrNM=lAPBX*wfWPEy{&W>t?JaX7k7GJ06)D2_)J`cr-c`REXCR&7^) z()I&cRp*W_ZE&pgk~Irwz5-|LC*j|;_Q_hY!EN(8moO(IWJOJl_(^IWl#lA^U-iG4 zi{1`8vf)mI^R8;Q16ULIVMkx}Sy{_~2A*QeQZ>-rq0Cih)jGS^T)C21>$~8)K9kZQ zRP58!_f}oYq{0NqDmJzl7ec9*QQnnALm%)<<+*eO6Sjzp3y_uL1B)wQ=~W6fY?ua z+S&*29$J*|1t$9Z{fne-tQFHG{7RxXS=SPD9+s+>z@<&VU(PYmiDm}qJu#hGL%Esh zo?z1af>In004XTU847b{S&NF%4{d8Ru9zr`Hun>dk%5BcbHY@;cH8i*rT%JieY#g4 zP4vv_IWMaJG?uYZ-_ZWc2^dLI=6f{1d9v;j0!~cKCcUf-DRrg;ynbFLgoMnVoiZaN3j~nX7 zngC@(yVIs6HT|0^uk;r&*!`s&?L>%)frbq?5Jt##_3s|b+%Sm${T#)o49u3{ksE?$ zJmk_#V0EEIdZ9ADb3U@L*%kCym8a|UpYn2N)EMwV9GBlL!2=K8RzyKJ9{hI}pfswb z6PFZoBi1lG6cWZPay_l^d)QqNfY#{I5WK>^>e)DgGXf8-bdOjuJytIT83AX%VU5bl zN}|1nKtR^h4t{ z&BpKV!q1dyw50C0utVYs{R1!l&wBuO^Os3rR~KKPZ6)x`LWMzEXg*mu+$Q_c%(J^k zvopaF>iXqOoW~n>QfV(oE(*N61Xot4|N75YBNn4e@1poEQg5+ddfalfwn7pVymiau z6bnl)%cID%dX>NP(eJJ##GTF~mlLVBiM%SO&uILlof#x920UMFs;a+0YgZLYo!%a!J*FQJocPsmyBxv;|RQ07p zCoX}gJCh9%h%^(v$C?BGOlW9`1|``Vr5@Vx2t?64PTRn0B7l8 zDb!l*OTV2C4{v;8q88QHb!%s*j5%t|$reOy&d}0cnU$f_pZ87dhB*PlF*euK;sjn2&x zEH~GC_Qb~KmT!|XhzfaldmHwfFoyw%H5eIwf1ZgC&@Wh7qM^aJiM0|K2&!#v?N<2G zUz6p&%X*Q|a73{DD%-@R79DjsoHqZCVRAR`EqVI4KlGO5Q*uVXEqy~?qxW5-vjS)L~^pTlwePBVbTS$ z8NKa`kxmkf*%-kl1|2bg_q>L`@k%Kxb7!devOr_oc@O5 zu#78spii@~-fh5DJk~MSWvXUE1_7FpCBg4h)r1o7=;}vC4uoKjsmtd10Z%!w2iYC! zf?;9ySb~y=9u97+i#@2jAt$6B%Vo%{sy|+S+%UY`d#rX~TjR|!F){IL<^F;Z5kWoF zRn=$a6_7Dy3m_bp1mt#qG)zcHEM-w?$jQ0&YI5g`=ChYI+jAjCkC{&-;Jw$A8$)YM5`Al>frxew+cEk z^A8DA2wL+E(?~452Ry3~D=LjEre?XN!M*e^ z2=hrrDN^1^x|-4GSy=RaZQ5Ag+j^XtD)SbkMEfjIp;p_eKh%Z>Khfb4vCsQEVjr`1AQ5E34~)Z^=aic6 zl}X;1_I72fpna%an?yuNuzPlJlkeTbZD+}YUAb7rgG+uh_dlNuGjnkEAgqbNK0H{R!<^_6HZVvi;iYma#RJd|lrrzPv- z$i!XC%FKlx+_SrqW+;cg%^xiv&NFzqq0yM1U&<_eH*~*SA`6`hpV5+=PKoL=K^VEU zmJ1m$GN0qlg#MTw;*{JTFt6XDNG>k3Y~!k6oU#e;Qc_-Tc)GGZ*t~Kk5M0e*C(qMbHfJg-hmlySajtr%!^A(?<=P{ zch7C@5VE(wf8XU_eo42 zv5=1q2yuvW>v8--_tgOR4NUT-R@vXFw0Em8M7 z&OyGS@#pC_7v?Kf{9jI8G7x)U+7ePN_z9qULb`7xHGJ3pAwhclxbiI_Wb#btbj7{n z1p?TK&C<4a{TSACG>Sho(e8fL>b?$O$NlbJg5j5mkJRq+snL9e+2iKq+P1@`DRnzv zu8W__{({SaTaOLGlt^RWtO)%790*NC zoWI~%Q_3yv@mr?fsWv_%qeHaK?KxN=$dqsCLeLXoXZM&7jyQ`}A}j+W!*9v)rS0ta zKqfy~9@5k}kI;r*<YEpd9b#7J@I#I;u#k*6j)+c^ zEFCX>_9qz?>*qj_7A?8jF{;izkA^8-;katJRJj&l=(oF0iE7%o{&CEyR2;D`tFE0x z5SAhi@oD)V<(uB6`ks}y;e>1{}WLv8s5B(^q>TXIu(f8a-;VI5Z! z)&DLR!T=+ z7dKziuT6)sD7od;=P_ptMAKa0lg{|kdvTE2DLGux^xhq+9kcLc`GBVW3^m!_k+0uZ+5{oN=iy#yn(=G5ueU9}^w|3WI_C4`-1@1xok! zU)Xy?!Sm{S+r;WwYnP`&%grQOLvF;IZhPsveSkBLSJoZQ0%CY}uDceWjsaief%^t0 z?G8S0=z58_B6Etglkn16#a9yXqA4Ob8$E5ub`1?j_UtCS;M1dT$19H2H5in@CK1?{kB2YXAKT=V% z$`qtr&)|0m$Q-Zg7Qaal#aBi0ZMjKpLsM{K;+=>-Lz;E5{~nlX%t?JU@MtNrf&A}8 z?)kGs>ioAL+JtIOqgMb7*#pwdC2j}!13^q)dqDC`M3VH1h8?|6;vtHJkkC7LjEpRpgud0jSt|MA?Ck8FvdKZihaZcho%@WR{NzOuRcqHd)#&xEdgpuR zr}p$M$3f(>pdlw6ooQ%-?g2-iQv{!3cawpC`Ay#L9UY+L0p^?1kAaR@08mT7Fzq-HXe2$reSKA5E0jXo@7$vaC~Q?c@gO_6FL_ z8C2dZjeTrzFhC_RK;A6n&DS6k2nmZCkdHniN@HbZN|rX?Hy_MS$(8HaxS$0R@`Iy! zhcdrYf*K5Ikq<>! zusj_QhQmRDAwiOP?;9xvo8Q=jDKf&%d-4#tdo9_7g@1ut9x|$`;%}}mKts8%e8o`c znOmoOU&^H@T@)U`eY6Ipv4Cei8`ly(UrV}7+cbJZ;;-#lIUedAoG}U`BqB01zV+)f z+Pf2SpV3Ve-?!360wQ>8@cA0@#&zCS(8u~`=~Mc5imW=A)#Q<*>9vU(n7=jv;IcTHyWQK-{ad84bz4W@$)y%SXukk_tc1|KP%F5}snS25Q z7OnWqlne~Zjq>^>il`c!`5W4 z*G=uZi{8LENSH$PF|K=*K)6 zmwkey*A^JyuBl-qo|P7XAtA9zI;10DhRrrHMB#UbtH*Q7Wa_yHmTMPLiVS&?Z+4-|SsBikANUMzgd>_S zzGT$Sy};&1b&JQT`N@}8*8kO`ard=k@^Ya3x2w%}YMoDqP|mE6bmNOC*AFd zRcJu?W1S8j1E;G+A8(1J+;$ej{*Dk30SoN5ITyj=J}d9y0;Qyalr5s(gGhLIxd00v zQCk~JYl&8nH(>$tZa9U7BPL6wrgGU>+v-JeCPhwp&+2V>M$6czunA>uKU|oV2ajN{*Rt}881PhHWM*obJEjFwf2aQ zLz~Caw~N>rNe57CtNhkfb%;4O5qIb?mkD%Kx3BD{kBZJict-Dt&9yMoi+u+QqKY02 zz@@$c`TZ2*^};zERLC*$A}FEyZ7(EGhnZ2HJ{TZWdNb#2FMSh6)yk^ zQ~e;JkRc+sEpTfFXJ*zlJ3Gzf_<}!)Kbq+S>DOz|l^#M5ue!qyMYgj)m7|R)k9ucx z>$RN&2j0t5wDs_HzPdY28BQ%P1hGH9J1m;i5BQL4i4mivA0~7X;0nk^IqfU zfOy7x9@0M7b?$T?yunJ$+to^WOGVLnLUO>TDQs2>Z(iNQV?qharG0Z*H}u}lGtOGc&(DwVdW(t)AvB`<$8DRk zOUiyw897Rci20*`GIRe<1}WW8tvz!eO$1Ltwe@%cd?b$o5PFc| z2=@9lpz<(&c zH8#$H7IP5+EN{Zb4F~5+us%T9bpRhZstlNZFL#+H*&n;MhOetNR>wS|G8)m9O~(oQ zPwI?*9neLs+OyyLeAUM4gGQ&mme+W6qw^GGXt+T&-2TU}r`*805gp~>eS1y6j~9^f z%j3n`uT7UxFY&)Kj_`r^qhOW94b#iIq~y>AQ-L*}$&r!6Od*&^@?rE+_V6cxP4N3Q zpP|1qscI^&1vR}>bPy6dJyKEB*MVX`_F&$>>8)r_-lAZM~oqMWrREA(>bzM0JZk+-FwH zt(yoe{T7Rf&G7IX3(%kqA{S^kEB~(j_Dw(wb{DR#5ycstdPR98Db|S2K361QfoaK? zKScH`e}?pnl8v3ss*(jnex$XlYkVYTE?pq8iK3mD6Fy^0+0mB%w|nc>^0*e5~c zi+aX26I?*Qu$n(>+h2wcd!slUm8ft(1IP4&A{siT%2{_H0Zi^`^j>K+=z;BUGewp_ z2Xrh#juP_%DA9KO=21(BP!Flb~+Y6H;8#r}L_wy8FM)|AqV)yp{#ZfX@f7NfZcJ-aQrq;{R`BJ01aN z0!owt|L+aMqpqOpRh%R4sUWOm@zEfJGceyWa>p+4vW{wX zTfOd^RBRL8BCBs*Myw#-?5Ml1?`UH_C;xn3;TfOkmLL7hHug}+;WWF6e-6-%u?d$B}Q6_FADXTj%m1`Gqs^+s#CE^4Jno}95(@EasEK> zHg{&8yQ-u;Kf!DGj{9W~PPzd?=fB32t&0~mX?*#cPYOF%s1)^@;K10*CpMKjl?hl1 z&B}QrjhG;MP4z&}T#4!=_c-XG35*=biT{ng!yL;~^Wx zL<)7kozgB%ANK1A_K>)h4Duc+)dd~m*BbA#Ii7)GZd(`Q_#)OjkAR6)c~5H zE&6zvXCh+y<2^?eEq@#t*T^06-pgrN3Gw`_t-{`rcGF3Z*yJZ^?})?O{`o0AEBJm| zJHgR!3dWyID*kFdG-`7*JZWq%q8vM)LMQ|37Aqq7w^Vt}EcIB7wq(>6k4nHUdZWZ@ zZN4+-HXeNa0n)=(&!|&u`6ki_Qv7qp6XBz^gt#B?QtoVe4BC}XVhY=J`=*n7E!$1^ z`BaF)Mo>6U{6Es!U&Xd93s9)?cwc9#BQsjemJ*jF`mX+p19EjcjuMJnY`X&<;|P?3 ze&wo}uo8Bca<3&cIcdpd805|wT$Fj6@R`GqS$z}FaJ`C-PxRism#B$66l8zvf@-~* zPPPuoj!rp)4p|9JZ(aGZej~(ErB;8F<8))4$cMHa++Cd?{`yEzhMh*L8PUBmdCd8$ zKXD#2+Wi14ooM|=(%|tDs{ya{v7WL!N#{@J)L@m1T~Xa- z>N#SH0(mrgKVFgyq1f3}hc$%>E73I0%zk3{BX1L`aXc_<8j)riF?&ul0c?Gs7b?!a zA~wMu)Aa|FR^G}-_lxK=eU{HKj6p2X)-4G5ZCeFU-s1`^u>zX0hpQvsa_8r26`8Qp zx~fpf0Ckr8$e*3u6&*M3W_C$Q&7w<7e7q`xj-Mk=x!qq;DG5#eI64vy#li#RSxiO& z(WS~Y91|+vCShGFrRK&F&zl%1yp@`$99Qa1;cHIMf`yez&RatAC2FgQSkJ@k)_IP$ z&QFXvyoDK-Owm)NggbV6;rwo)@U3Q2QR)vFl11 zTRO0=A6Hy~b#(e^rL?;7z<-%7HxLa=om*=ljp5|(Cso_Xl zZ=Dg3FNT|^W7+W>kF}_u=x$F`(OKGNK(zenfaiLIDaMC1)@hS%8-JA&f|~HU3N{MH zGsj_P{6gP$H2kXUjF(G$(mcIKUdzsFq4QP-sdd+>clu+-T15!=9u*c5V*j}PeXxEI z{qDi8(noBA_K$JN3o)|53X?=mm;3Glmf8#k=Jufn?4I)v*N%$0&w=sXHEyjD!MG49 zAszX6vj!u0TURI*Lx2hH~%)#92#+XQPa|+#mfK|C8z_ z5wyLAf(RF>5vbwH=s5dJ&CH63JlQCpdT?|2@+oNeCffIJbuy0}q-@yW_*O1%K(;H6 zX9*1zfQb+EK+VZhR_B?QaT>5Ps=Zvn4Y!5xKjP?!sL4?NMcfy! zGf*=Eo@E@Ayz8;i%?%(>nEBLcmkuMMKd;@CV9zD>*9zMqtQW?U$B^`8t*P;on-LTFd2iwuo%k?A$p5!u?)#TWIQw&+XWGpimOKIr0AK;RuMn zw|;q_bA889%RS`#Op9ZCitg5~#l7EFX{TTilb-9{&!;unPLOGG5=s7-%)7l`w3L!; ziTJ-tOTg_j0qf=?$(JxeE_gqko3;73@>*~-J#aB&SR{@x4QjEDSttXS(@FKpM><6C zBt6qtM@vuaNyXw^%?o^wcb=U!Qs|fg3Ryrwv4cyJKgVI~^$zYiE4}uJ&1$+6Im(cN zt%Z8uyzr#vXHmfPY-HerVX2y#xYB2is@~rZRkLZ6LQhT~wP?LO+UgzD2(=F3P4E;L zH>h?FbT# zC8h&w^If~p&#HHt9^%x{{S_~ibU#x!CM!21-j~38QVy@{E|j0XO=&l`b$XUmj*L-; z?sC7U47`@~22HbL@e+222Bi>>_)-$v1gMa+G^(m?U*p{+`8b|7-=RqOqF$a!Q;#yr zy%y{78mAYp&YB+fxb!!sN1CdJC=Usk(Ij#RpJjk9dBN=+u$3BcD^YGW*xafLC#F$HUnnnDIt(7N1m1n3~3?hg&@m=oH|9JS_}Z znYa7i7Hi)O8JemWNXz?1ji9W=_dYSq?L3oW=v#5B&wXqZKtG#xmAc)$IAx*zwz@~# ztY&ghsx(ziVmG*KiS~pUh6O zpeaR(iE%_r!~Kjm0`A}L7(b5ynN@i&Y}kY=0K0j%^OobBgT4umks~DyI?3KQ71Pll zang-zwb&@q(vnaD2kY-JDb|bKsxI_Lac=$(pFMccb>s9*Ko<&10@KQe63d&Q;i8NiFo z|AFYG=!u{IK(zjeMDXD`Z}n#(?EN?`(_+joplf#p^sKIUHhUVI&xT~YismjQ?(;Y4 zVf)ynN&k(urDXuj8(7V@bg#8T369;+Ce?zuh8%dH2sM-8XqUN3ea~()0LJqAF-bE0 z9g7JY4;i1%-StWV_pbcV`=6(og0m>P60>YXhNxpZHrqY(t?&9V2}sCVRgYeFycGaQ z%f^4CMS|T3Q%?(^+yHfzTIHrAyT!pYA^E`b-^~_Th5FCJGFanA2Ug>KY7l&X%$mZE zaD*QQMLQlx^!=Fc;RT4isf#;X-*lP-bC*$e>6f}!%yyrakzO*}ql<;A|7hDy6H;&# j;N5k6|DTe%4sOMV<8#qWA;rM!fk3a7)L)b+7=QggVvbTP diff --git a/test/goldens/screens/sell/goldens/macos/sell_confirm_sheet_loading.png b/test/goldens/screens/sell/goldens/macos/sell_confirm_sheet_loading.png index f918f88895e3678a988fdafccf5631f10d147526..64aed46f23c865682ca7bb7298a98e2d69471224 100644 GIT binary patch literal 26833 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 F{TJOjJtqJF literal 26683 zcmeFYXH-+|_bnRrRTL2{6zQ)5(xfOL(p3bcO9`DIp!5!*1VXR?3ep9nOOXzthZ+j}?pJ*Ci1inHpzI+e-ea6>N;|Zv8fC~=- z{R`53`q(%ybA1{bG7`?)vrUO|cN=+N_rvL*ud0U6`IDJ-p5A-(kK5Du=TEDTCO+i- z+h8|%OXy!i5$++moA<_`6q>!)*2MIKcy9Y0m@-drqvsR_EgKg+joJ*kE#A~E3(XQ0dn7E@o!qpSUs*=)9JXPbnjB%; z&XTX9*tJOZ_8_tAK^zgB!B!Q@ZSuuINs~eK&p?kDLv7F|N$uGsNW`H4CM8YKc6Fqo zga@(eKUxa8=Q&0X@-lhgHk0d(3@MkpoRUMT84zZOA&ibR_Rz|_+tOEBZ-c^oQEsEK z7`Ss@N`=J#3ozaj<8fHa3nM>C+rd+^kk*OQMLHn#&F@A6(1f z$Ti==oEl$nE;3eFIS0k{gq}{g=A}2wrAq@?GN`83mc&SIA*NXch%IqMFxhbDdSnfF zNc{uyvZaPnk!vSgB)Ng4g?kZn7p(M|L31hvJu(&KZ(#)AOhKHa)o5uh4`R#Tvz!g| zFR#+^+(vUVbiu7Cm_c>J0=zq4#<%~G546v;LnaT>B$$k{w98=Go6d_kj7r|^-0-$G z4^UW$VcoO$+3feeEl8szcUv3kT}F%jl*cZE;^Wd~ODbF^Wdb-oJLXna$_$vu3OR)H zpr0C@5xWUFIn(Nrs@MS6vf`bu7z*qRXy?}}8Bqe&!@d8Z>CLm}F5?HkqZWHkUl{!J zSXNYWYXs%o-0_E4IN&A*`leQF7k+;&z{n9>CCZ?LbWXA%`~&*wSS0(+G*K&+!KkxY zX2&-R1Ue|To#qfR648-oj~FnOO^Rb;t3C6r>vvC&M%LtmB)y35!$IYsu;|`jtoy8& z#L8223Ck2J2()9~TL=XUeX~JtOiBaWcyb8g1wB$0Wu8Nt^soIw!ItO`+QdO&m!B@c zWR@&TUw$}*FoC|E&olmYeMgMt@l6mYO-t?S1<*I=`<>q^`9a?JcavfsK9ZKkdsk(gv;)p>jzO3%R1 zhlT#WtXE_-NXH~1F0LmKmHPa{^{%CrmDJfX=nTK0peu0(PA|m#CGpxddPc?pBKXOf zPS#E}H|j_8QWMgBtUi*{12`%#$SLbSMwTTTOcaZ_{Ao>qh1ma5LjybxVy{HMx|!BjMc=5m?FQL zm_jNF#VpFSVzftVbS*2WBqV9;*$%`4)*Qbh%S^c z@@ww_31R+aP+xh8gYnX(!E$Gnoi^Oci-R4OHUDYygdWpfqN1`EB46~dQnA1tKL+kS4@Voy-BR? z{(MnRy*thy>lNo={h>n+wsLeyPLDWKuN^x~G8)WN^T}E1U}h zF7(4A+Co=`saH6mE7KIGl}c-ekdTl!DTiod?NFOiwdmB2k=9 z#d0fv(IIlkN(2OExBd_%^ipQ2R>F@~0XHtv+aNVS*f=}l>Exm7U#{`>G)4q$PJY$R zkR>E?$V@FC!SVOIdIUdvyIa_JL&iw$cke7$v4<{xr&%Mgq89aiMfLJhK%{ag`MksJ z(;^v$j#Mu!!DyWt>-L@_Rj)5Ft`ezjUmp6ee`Xbf*mcJsVCBD4MUkcuxGv(TZBiz5 zp!MM0_tRAoGHR}~koGJTXqSM-WJm>|dnEhPm2eD{&C%fnb3VpM)yu5MrBH&b(?Ykd zmbSi8SFsSd%mkdJ05u-Gz1hOs;4xij?z^m&_~z|fzn%@0X5t=EhBT=ONxh*;DWo0MB6Hz9*JEW#th%WU6LoA=W7M64RC zaQ8!x+^febEd@18P3lYK>)I{ktAk$-Oa)O1$aWTT4)(zufpO% z0PcK~L%+q5|77&5(Nge8HCzGfWfjtTd>kCpq-$l>IulBPYbGL*aTliGsUdLvEJvS8 zOa%ymAh*->us+9-Hgy0O5|MGpQd4B%K$d)|Nx!zwVQ_GbJB?9lYIo;O<;vDPUnCjJ z*J(8z%d-?#XDXKsywT{F+oO$UsfwEOz3 zkKe2PctJN^Zg^-Y%h?(4agw8wwu?8H;B|~029useGHrOUY12N0E|JVtX6Y1TZs zxwkx8?oJf}-^Yx3<9Rn*1bh=yF%^thzfp?H z@u!X5leWExQ1hy-k67KrC>GH|j?uD~2nvuu*Yec~&72N@^n$yjG-Ys9&e|cj*&;R*zbsE~U0Xyhg8QCip?XQcBVC>yK z+{3gV{&u%=a(aJw!Y8=*M2cXp@i(=U&f9uaVPF(;BJrdfKE<*Nd1y0F?_ z9X}9?5|)d?O%tE*}yssrzVht z5JR*Z%JPJs-40)WZWSQJ<*NCBdUHdt6=~NMl|`=cUVhje9|_xCb(opZ$xt>r@*s$2 zjWeBWj1c<|r<)E$Ss=Iw3*xXlGDml^A}LL!rrhVdhRY&&Iq_qe3oe;0h1;Q1M{KBzIWvdlT!=*{O# zZ(CcaD>FCqy>hez znw5`N&tc_zdIX(1yJH-(FOUf<)NZ;a`{-LtP$$O#K{1QgAJEZdpiI^yPR?NKfHkjg1_T!B4V*^AxrOGtP%QlM#X%6$F$b?D+x18a-2LR>3dUcRr@?B!qQC`{f; zggrVXS>Bo+R#hJQ z%c8mdrYIRU&i<2EuVIumd;=%GIa`VO>eUzB8{^f(3=(GhWxv6F2iC-~3Y<@-@|P29 z$3wZtr@ZK({jTIf1QTZ|0p@_{s?ckFq<3(rU132kdf+~jxe!Wxv%|7*?b^JDqlS#z z^H~O9y@iyOzq`v0Yo0DbeXk6}8Y8t7vtn`tgZ5VyxvFu+GH5oboERD!`c0E%(~Jyj zIi&nEfjS9w&FPEt_yzm85HZtsd?SYpz4b3cP;fo4@KF^MYNx~E*9ygsYK0}1vHIk zpM(rL+^4>)M%G;E9Cb&^uaKW`B480ha%#3|L}%`Iiqe;yP-SxW+O~myV^^bQQn!bt-#!K4G@>TupXH%Wo{Tf0 z(%UzrFfmxlUSQZ=VC~p9r^||F0?8~Al@%=j9*wpxUiZDa{XLQw{FX}YD|wf=JElNv znhwS1*|6~N+{qeu3~8_(Z-!<}s3oMQr*9}bdQP^Vge4{=u`n|)a-dwX9;kzX-6!9r zs)I->9t#gVE>H(61)p+{Ob+a5#b?ONAAl{+P zN{w&qcD2zc8S?;YZ$!P(vqB|gY)R58VfdO?4iAf%B1sK$z8i1^Ela7a+|Kw@Q{KMY zt7#c>?~~w}lh>=Oiou5mM^nFbOAP%9i>R(>YfGmy$Hy>247NrI;(HSRxHRYkt%zZ6 zmX84vKg_NO1iedUKV36)ZT$Ob*ca>1hFYNQz77oyeagU%KE%jFY6)T5jKbzfuCk8lm{re&hYG2nzUOC$;M)*ssmh-ARs2V+nN zmYoYNiLGCp5^e(|aKK|FM%xgXtyDYBz;sEtd|N+mbRj?6E5@?jHouwdH`|W9u>uX& z&X2ev?Y7zpoBwLm!l_LAt6lFl*awXVWNSJVpjef7g@6siBlXQ5Js+PSAQXvFv+$F1 zZM0*$mBX7m9aY!(YO@|}Ww&6QCt^67+aK@aSMUz0nvkaWY|HH3O=#9k&Q|V(S{>r# zwG@m;CtXo3Q95*LJbEpI+WJB4g2b;)(uuDEd^;BBIUN7^Tw9%q;|luw+f(e7^4L0J zYSOb`55QGKz5jU(a(()=k#M$seYc&!2re;{>j~OyUf8R(AhyO&r>P49YG7Rs(eoIn zB8ogCD%0+8TIMi3bMpzz;}y8cP_2!$OmI|0jIHz){4(18ncs<7Yrga{PBQRA(X{4i zEIejG{P;!$+y_;=eBmfE1qwX?}%hFeP9aPGGVX!kA&!5K^hLXNgr+kudM^-=~ zL7(i2D#GR2xKof_=7E3->^FTYO=d8-ska?}-J3%e06=-a9~KQIG>yHMO;eH`Mp;Lt6Lc+CQKF=bvJ&C9R+ay&wV7`C)~}4}W2? zra)Di(i0^&Ts~+5mz&*FzAKSaC3*?;0;1mHS^tuC?a>tFtx7cTe1qot<4vPnitw0OV�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 diff --git a/test/goldens/screens/sell/goldens/macos/sell_executed_sheet.png b/test/goldens/screens/sell/goldens/macos/sell_executed_sheet.png index dc1a85543e4e951f9559600e005175122862495e..213037a281451bc9741d4600f55b75bd65ad5450 100644 GIT binary patch literal 20140 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 literal 20142 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 KelF{r5}E*g1f)^` diff --git a/test/helper/responsive_surface_catalog.dart b/test/helper/responsive_surface_catalog.dart index 3394fbc0..a81eb843 100644 --- a/test/helper/responsive_surface_catalog.dart +++ b/test/helper/responsive_surface_catalog.dart @@ -204,10 +204,33 @@ const kResponsiveSurfaceCatalog = [ matrixTestPath: 'test/screens/kyc/kyc_static_pages_responsive_matrix_test.dart', productionPath: 'lib/screens/kyc/steps/signature_unsupported/kyc_signature_unsupported_page.dart', ), - // Migration covers 25 surfaces total (bitbox_connect_sheet + 24 above). Known - // candidates NOT yet catalogued or confirmed either way (bounded, non-scrolling - // Column with no Spacer() and no scroll view — a Spacer() grep would not catch - // them): welcome_page, sell_confirm_sheet / sell_executed_sheet, - // forgot_pin_bottom_sheet / enable_biometric_bottom_sheet. Not exhaustive — review - // responsibility for every new sticky-CTA surface. + ResponsiveSurface( + id: 'sell_confirm_sheet', + description: 'Sell confirmation bottom sheet (shrinkWrap mode)', + matrixTestPath: 'test/screens/sell/sell_sheets_responsive_matrix_test.dart', + productionPath: 'lib/screens/sell/widgets/sell_confirm_sheet.dart', + ), + ResponsiveSurface( + id: 'sell_executed_sheet', + description: 'Sell executed/receipt bottom sheet (shrinkWrap mode)', + matrixTestPath: 'test/screens/sell/sell_sheets_responsive_matrix_test.dart', + productionPath: 'lib/screens/sell/widgets/sell_executed_sheet.dart', + ), + ResponsiveSurface( + id: 'forgot_pin_bottom_sheet', + description: 'Forgot-PIN bottom sheet (shrinkWrap mode)', + matrixTestPath: 'test/screens/pin/pin_sheets_responsive_matrix_test.dart', + productionPath: 'lib/screens/pin/widgets/forgot_pin_bottom_sheet.dart', + ), + ResponsiveSurface( + id: 'enable_biometric_bottom_sheet', + description: 'Enable-biometric bottom sheet (shrinkWrap mode)', + matrixTestPath: 'test/screens/pin/pin_sheets_responsive_matrix_test.dart', + productionPath: 'lib/screens/pin/widgets/enable_biometric_bottom_sheet.dart', + ), + // Migration covers 29 surfaces total (bitbox_connect_sheet + 28 above). No + // further known candidates remain from the prior sweep. welcome_page was + // reviewed and found safe (scrolls end-to-end, no separate sticky CTA) — not + // a migration candidate. Not exhaustive — review responsibility for every + // new sticky-CTA surface. ]; diff --git a/test/screens/pin/pin_sheets_responsive_matrix_test.dart b/test/screens/pin/pin_sheets_responsive_matrix_test.dart new file mode 100644 index 00000000..e5960c1d --- /dev/null +++ b/test/screens/pin/pin_sheets_responsive_matrix_test.dart @@ -0,0 +1,193 @@ +// Responsive matrix gate for ForgotPin + EnableBiometric bottom sheets. +// +// Proves CTAs stay fully tappable across the full device × text-scale matrix +// (see test/helper/responsive_matrix.dart) when presented via a real +// showModalBottomSheet(isScrollControlled: true). Regression lock for the +// pre-fix Column(mainAxisSize: .min) host that pushed CTAs outside the +// hit-test region at large accessibility text scales. +import 'package:flutter/material.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:go_router/go_router.dart'; +import 'package:realunit_wallet/generated/i18n.dart'; +import 'package:realunit_wallet/screens/pin/widgets/enable_biometric_bottom_sheet.dart'; +import 'package:realunit_wallet/screens/pin/widgets/forgot_pin_bottom_sheet.dart'; +import 'package:realunit_wallet/widgets/buttons/app_filled_button.dart'; + +import '../../helper/helper.dart'; + +const _openSheetKey = Key('pin_sheets_matrix.open'); + +void main() { + Future pumpAndOpenSheet( + WidgetTester tester, + MatrixCell cell, { + required Widget sheet, + required bool useGoRouter, + }) async { + await tester.binding.setSurfaceSize(cell.mediaQuery.size); + addTearDown(() async { + await tester.binding.setSurfaceSize(null); + }); + + Widget openButton(BuildContext context) => Center( + child: ElevatedButton( + key: _openSheetKey, + onPressed: () { + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (_) => sheet, + ); + }, + child: const Text('open'), + ), + ); + + if (useGoRouter) { + // ForgotPinBottomSheet calls context.pop via go_router; a plain + // MaterialApp Navigator is not enough for that extension to resolve. + final router = GoRouter( + routes: [ + GoRoute( + path: '/', + builder: (context, state) => Scaffold( + body: openButton(context), + ), + ), + ], + ); + + await tester.pumpWidget( + MediaQuery( + data: cell.mediaQuery, + child: MaterialApp.router( + locale: const Locale('de'), + localizationsDelegates: const [ + S.delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ], + supportedLocales: S.delegate.supportedLocales, + routerConfig: router, + ), + ), + ); + } else { + await tester.pumpWidget( + MediaQuery( + data: cell.mediaQuery, + child: MaterialApp( + locale: const Locale('de'), + localizationsDelegates: const [ + S.delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ], + supportedLocales: S.delegate.supportedLocales, + home: Scaffold( + body: Builder(builder: openButton), + ), + ), + ), + ); + } + + await tester.pump(); + await tester.tap(find.byKey(_openSheetKey)); + // Modal bottom sheet enter transition is ~250ms; budget 300ms to settle. + await tester.pump(); + await tester.pump(const Duration(milliseconds: 300)); + } + + group('ForgotPinBottomSheet responsive matrix', () { + for (final cell in kFullResponsiveMatrix) { + testWidgets('forgotPin · ${cell.id}', (tester) async { + await withTargetPlatform(cell.device.platform, () async { + await expectNoLayoutOverflow( + tester, + () async { + await pumpAndOpenSheet( + tester, + cell, + sheet: const ForgotPinBottomSheet(), + useGoRouter: true, + ); + }, + reason: 'ForgotPinBottomSheet overflow / ${cell.label}', + ); + + expect( + find.byType(AppFilledButton), + findsNWidgets(2), + reason: 'ForgotPinBottomSheet / ${cell.label}: expected 2 CTAs', + ); + + await expectFullyTappable( + tester, + find.byType(AppFilledButton).first, + within: find.byType(ForgotPinBottomSheet), + reason: + 'ForgotPinBottomSheet / ${cell.label}: Close CTA not tappable', + ); + + // expectFullyTappable taps for real and dismisses the sheet — reopen + // fresh before asserting the second button. + await expectNoLayoutOverflow( + tester, + () async { + await pumpAndOpenSheet( + tester, + cell, + sheet: const ForgotPinBottomSheet(), + useGoRouter: true, + ); + }, + reason: + 'ForgotPinBottomSheet re-open overflow / ${cell.label}', + ); + + await expectFullyTappable( + tester, + find.byType(AppFilledButton).last, + within: find.byType(ForgotPinBottomSheet), + reason: + 'ForgotPinBottomSheet / ${cell.label}: Reset CTA not tappable', + ); + }); + }); + } + }); + + group('EnableBiometricBottomSheet responsive matrix', () { + for (final cell in kFullResponsiveMatrix) { + testWidgets('enableBiometric · ${cell.id}', (tester) async { + await withTargetPlatform(cell.device.platform, () async { + await expectNoLayoutOverflow( + tester, + () async { + await pumpAndOpenSheet( + tester, + cell, + sheet: const EnableBiometricBottomSheet(), + useGoRouter: false, + ); + }, + reason: 'EnableBiometricBottomSheet overflow / ${cell.label}', + ); + + await expectFullyTappable( + tester, + find.byType(AppFilledButton), + within: find.byType(EnableBiometricBottomSheet), + reason: + 'EnableBiometricBottomSheet / ${cell.label}: ' + 'Enable CTA not tappable', + ); + }); + }); + } + }); +} diff --git a/test/screens/sell/sell_sheets_responsive_matrix_test.dart b/test/screens/sell/sell_sheets_responsive_matrix_test.dart new file mode 100644 index 00000000..75274ea7 --- /dev/null +++ b/test/screens/sell/sell_sheets_responsive_matrix_test.dart @@ -0,0 +1,267 @@ +// Responsive matrix gate for SellConfirmSheetView and SellExecutedSheet CTAs. +// +// Proves both sell bottom sheets stay fully tappable and overflow-free across +// the full device × text-scale matrix (see test/helper/responsive_matrix.dart). +// Regression lock for: +// - CTA clipped out of the hit-test region at large system text scale (no red +// overflow stripe in release builds). +// - Horizontal RenderFlex overflow of the IBAN row in the confirm info card. +// - SellExecutedSheet shown without isScrollControlled (modal height clamped +// to 9/16 of the screen) — the dedicated test below documents/demonstrates +// why sell_button.dart needs isScrollControlled: true by reproducing the +// underlying Flutter clamp behavior in isolation. It does NOT exercise the +// real SellButton widget, so it cannot detect a regression at that call site +// itself — only the behavior the call site depends on. The matrix loops +// always pump with isScrollControlled: true and do not cover this case. +// +// Matrix sheets are pumped via a real showModalBottomSheet(isScrollControlled: +// true) so the screen-height bound production imposes is reproduced. +// pumpClippedSheet is intentionally not used — it does not reproduce that +// modal constraint. +import 'package:bloc_test/bloc_test.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:go_router/go_router.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:realunit_wallet/generated/i18n.dart'; +import 'package:realunit_wallet/packages/service/dfx/models/payment/sell/dto/eip7702/eip7702_data_dto.dart'; +import 'package:realunit_wallet/packages/service/dfx/models/payment/sell/dto/real_unit_sell_payment_info_dto.dart'; +import 'package:realunit_wallet/packages/service/dfx/models/payment/sell/sell_payment_info.dart'; +import 'package:realunit_wallet/screens/sell/cubits/sell_confirm/sell_confirm_cubit.dart'; +import 'package:realunit_wallet/screens/sell/widgets/sell_confirm_sheet.dart'; +import 'package:realunit_wallet/screens/sell/widgets/sell_executed_sheet.dart'; +import 'package:realunit_wallet/styles/currency.dart'; +import 'package:realunit_wallet/styles/themes.dart'; +import 'package:realunit_wallet/widgets/buttons/app_filled_button.dart'; + +import '../../helper/helper.dart'; + +class _MockSellConfirmCubit extends MockCubit implements SellConfirmCubit {} + +class _FakeSellPaymentInfo extends Fake implements SellPaymentInfo {} + +/// Worst-case-but-realistic confirm payload: long IBAN forces the unfixed +/// spaceBetween Row to overflow on every matrix device width; amounts stay +/// plausible so they do not gratuitously blow the other info rows. +SellPaymentInfo _worstCasePaymentInfo() => const SellPaymentInfo( + id: 42, + eip7702: Eip7702Data( + relayerAddress: '0x1', + delegationManagerAddress: '0x2', + delegatorAddress: '0x3', + userNonce: 0, + domain: Eip7702Domain( + name: 'RealUnit', + version: '1', + chainId: 1, + verifyingContract: '0x4', + ), + types: Eip7702Types(delegation: [], caveat: []), + message: Eip7702Message( + delegate: '0x5', + delegator: '0x6', + authority: '0x7', + caveats: [], + salt: 0, + ), + tokenAddress: '0x8', + amountWei: '0', + depositAddress: '0x9', + ), + amount: 12345, + exchangeRate: 1.0, + rate: 1.0, + // Two standard Swiss IBANs concatenated — long enough to overflow the + // unfixed receiver Row on every matrix width, realistic character set. + beneficiary: BeneficiaryDto( + iban: 'CH93 ACCT-000017 CH93 ACCT-000017', + ), + estimatedAmount: 12345.67, + currency: Currency.chf, + depositAddress: '0xA', + tokenAddress: '0xB', + chainId: 1, + ethBalance: 0.01, + requiredGasEth: 0.001, +); + +void main() { + late _MockSellConfirmCubit confirmCubit; + + setUpAll(() { + registerFallbackValue(_FakeSellPaymentInfo()); + }); + + setUp(() { + confirmCubit = _MockSellConfirmCubit(); + when(() => confirmCubit.state).thenReturn(SellConfirmInitial()); + whenListen( + confirmCubit, + const Stream.empty(), + initialState: SellConfirmInitial(), + ); + when(() => confirmCubit.confirmPayment(any())).thenAnswer((_) async {}); + }); + + /// Pumps a sheet the way production shows it: real [showModalBottomSheet] + /// with [isScrollControlled] matching the fixed call site (true for both). + Future pumpModalSheet( + WidgetTester tester, + MatrixCell cell, { + required bool isScrollControlled, + required WidgetBuilder sheetBuilder, + }) async { + await tester.binding.setSurfaceSize(cell.device.size); + addTearDown(() async => await tester.binding.setSurfaceSize(null)); + + final router = GoRouter( + routes: [ + GoRoute( + path: '/', + builder: (context, _) => Scaffold( + body: Center( + child: ElevatedButton( + onPressed: () { + showModalBottomSheet( + isScrollControlled: isScrollControlled, + context: context, + builder: sheetBuilder, + ); + }, + child: const Text('open-sheet'), + ), + ), + ), + ), + ], + ); + addTearDown(router.dispose); + + await tester.pumpWidget( + MediaQuery( + data: cell.mediaQuery, + child: MaterialApp.router( + theme: realUnitTheme, + locale: const Locale('de'), + localizationsDelegates: const [ + S.delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ], + supportedLocales: S.delegate.supportedLocales, + routerConfig: router, + ), + ), + ); + await tester.pump(); + + await tester.tap(find.text('open-sheet')); + await tester.pumpAndSettle(); + } + + group('SellConfirmSheetView responsive matrix (full device × textScale)', () { + for (final cell in kFullResponsiveMatrix) { + testWidgets(cell.id, (tester) async { + await withTargetPlatform(cell.device.platform, () async { + await expectNoLayoutOverflow( + tester, + () async { + await pumpModalSheet( + tester, + cell, + isScrollControlled: true, + sheetBuilder: (_) => BlocProvider.value( + value: confirmCubit, + child: SellConfirmSheetView(paymentInfo: _worstCasePaymentInfo()), + ), + ); + }, + reason: 'overflow on SellConfirmSheetView / ${cell.label}', + ); + + await expectFullyTappable( + tester, + find.byType(AppFilledButton), + within: find.byType(SellConfirmSheetView), + reason: 'SellConfirmSheetView / ${cell.label}: CTA not tappable', + ); + }); + }); + } + }); + + group('SellExecutedSheet responsive matrix (full device × textScale)', () { + for (final cell in kFullResponsiveMatrix) { + testWidgets(cell.id, (tester) async { + await withTargetPlatform(cell.device.platform, () async { + await expectNoLayoutOverflow( + tester, + () async { + await pumpModalSheet( + tester, + cell, + isScrollControlled: true, + sheetBuilder: (_) => const SellExecutedSheet(), + ); + }, + reason: 'overflow on SellExecutedSheet / ${cell.label}', + ); + + await expectFullyTappable( + tester, + find.byType(AppFilledButton), + within: find.byType(SellExecutedSheet), + reason: 'SellExecutedSheet / ${cell.label}: CTA not tappable', + ); + }); + }); + } + + // Documents why sell_button.dart needs isScrollControlled: true when showing + // SellExecutedSheet: pumps with false (Flutter's default) and asserts the + // 9/16 clamp. Does not instantiate SellButton, so it cannot catch a call-site + // regression there — only the underlying Flutter behavior the flag avoids. + testWidgets( + 'SellExecutedSheet without isScrollControlled=true reproduces the 9/16 clamp bug (iphone_se_3@2.0x, de)', + (tester) async { + final cell = MatrixCell( + kIosDeviceProfiles.firstWhere((d) => d.id == 'iphone_se_3'), + 2.0, + ); + + await withTargetPlatform(cell.device.platform, () async { + await pumpModalSheet( + tester, + cell, + isScrollControlled: false, + sheetBuilder: (_) => const SellExecutedSheet(), + ); + + // Without isScrollControlled: true, Flutter clamps the modal to 9/16 of the + // available height regardless of content, so SellExecutedSheet's content + // genuinely overflows at this small-device / high-text-scale cell. This is + // the exact regression the call-site fix in sell_button.dart guards against. + final exception = tester.takeException(); + expect( + exception, + isNotNull, + reason: 'Expected SellExecutedSheet to overflow / fail to lay out under the ' + '9/16 modal clamp (isScrollControlled: false) at ${cell.label}. If this ' + 'no longer fails, either the clamp reproduction stopped working or the ' + 'widget silently degrades instead of failing loudly — investigate before ' + 'weakening this assertion.', + ); + expect( + exception.toString(), + contains('overflowed'), + reason: 'Expected the captured exception to be the RenderFlex overflow caused ' + 'by the 9/16 clamp, not some unrelated failure.', + ); + }); + }, + ); + }); +} diff --git a/test/widgets/scrollable_actions_layout_test.dart b/test/widgets/scrollable_actions_layout_test.dart index 0104c8cb..f3e9ad7e 100644 --- a/test/widgets/scrollable_actions_layout_test.dart +++ b/test/widgets/scrollable_actions_layout_test.dart @@ -558,5 +558,239 @@ void main() { expect(taps, 1); }, ); + + testWidgets( + 'shrinkWrap: true sizes to short content under a generous cap', + (tester) async { + // Cap is a loose maxHeight (bottom-sheet style). Align loosens the + // SizedBox's tight height so the layout can genuinely shrink-wrap. + const hostSize = Size(375, 600); + const cap = 600.0; + const bodyHeight = 80.0; + const actionHeight = 48.0; + + await tester.binding.setSurfaceSize(hostSize); + addTearDown(() => tester.binding.setSurfaceSize(null)); + + await expectNoLayoutOverflow(tester, () async { + await tester.pumpWidget( + MaterialApp( + home: MediaQuery( + data: const MediaQueryData(size: hostSize), + child: Scaffold( + body: SizedBox( + height: cap, + child: Align( + alignment: Alignment.topCenter, + child: ConstrainedBox( + constraints: const BoxConstraints(maxHeight: cap), + child: ScrollableActionsLayout( + shrinkWrap: true, + body: const SizedBox( + height: bodyHeight, + child: Text('shrink wrap short body'), + ), + actions: [ + SizedBox( + height: actionHeight, + width: double.infinity, + child: FilledButton( + onPressed: () {}, + child: const Text('Shrink wrap CTA'), + ), + ), + ], + ), + ), + ), + ), + ), + ), + ), + ); + await tester.pump(); + }); + + final layoutBox = tester.renderObject( + find.byType(ScrollableActionsLayout), + ); + // Content-sized: body + actions, not expanded to the 600px cap. + // Tolerance covers spacing/theme chrome only — not a near-full fill. + expect( + layoutBox.size.height, + closeTo(bodyHeight + actionHeight, 20), + reason: + 'shrinkWrap short layout must size to content ' + '(~${bodyHeight + actionHeight}), not fill $cap; ' + 'got ${layoutBox.size.height}', + ); + expect( + layoutBox.size.height, + lessThan(cap / 2), + reason: 'must not expand toward the generous cap', + ); + + await expectFullyTappable( + tester, + find.text('Shrink wrap CTA'), + within: find.byType(ScrollableActionsLayout), + ); + }, + ); + + testWidgets( + 'shrinkWrap: true caps tall body, scrolls, and pins actions below', + (tester) async { + const hostSize = Size(375, 500); + const cap = 500.0; + const actionHeight = 48.0; + var taps = 0; + + await tester.binding.setSurfaceSize(hostSize); + addTearDown(() => tester.binding.setSurfaceSize(null)); + + await expectNoLayoutOverflow(tester, () async { + await tester.pumpWidget( + MaterialApp( + home: MediaQuery( + data: const MediaQueryData(size: hostSize), + child: Scaffold( + body: SizedBox( + height: cap, + child: Align( + alignment: Alignment.topCenter, + child: ConstrainedBox( + constraints: const BoxConstraints(maxHeight: cap), + child: ScrollableActionsLayout( + shrinkWrap: true, + body: Column( + children: List.generate( + 40, + (i) => SizedBox(height: 40, child: Text('row $i')), + ), + ), + actions: [ + SizedBox( + height: actionHeight, + width: double.infinity, + child: FilledButton( + onPressed: () => taps++, + child: const Text('Shrink wrap tall CTA'), + ), + ), + ], + ), + ), + ), + ), + ), + ), + ), + ); + await tester.pump(); + }); + + final layoutBox = tester.renderObject( + find.byType(ScrollableActionsLayout), + ); + // Tall content must hit the cap, not overflow past it. + expect( + layoutBox.size.height, + closeTo(cap, 1), + reason: + 'shrinkWrap tall layout must equal the cap ($cap), ' + 'got ${layoutBox.size.height}', + ); + + final bodyScrollKey = const Key('scrollable_actions_layout.body_scroll_view'); + final scrollable = tester.state( + find.descendant( + of: find.byKey(bodyScrollKey), + matching: find.byType(Scrollable), + ), + ); + expect( + scrollable.position.maxScrollExtent, + greaterThan(0), + reason: 'body must be scrollable when taller than the remaining cap', + ); + + final bodyScrollRect = _globalRect(tester, find.byKey(bodyScrollKey)); + final actionRect = _globalRect(tester, find.text('Shrink wrap tall CTA')); + expect( + actionRect.top, + greaterThanOrEqualTo(bodyScrollRect.bottom - 0.5), + reason: + 'actions must stay pinned below the body scroll view ' + '(action.top=${actionRect.top}, body.bottom=${bodyScrollRect.bottom})', + ); + + await expectFullyTappable( + tester, + find.text('Shrink wrap tall CTA'), + within: find.byType(ScrollableActionsLayout), + ); + expect(taps, 1); + }, + ); + + testWidgets( + 'shrinkWrap: false (default) still expands short body to fill host', + (tester) async { + const hostSize = Size(375, 600); + const hostHeight = 600.0; + const bodyHeight = 80.0; + const actionHeight = 48.0; + + await tester.binding.setSurfaceSize(hostSize); + addTearDown(() => tester.binding.setSurfaceSize(null)); + + await tester.pumpWidget( + MaterialApp( + home: MediaQuery( + data: const MediaQueryData(size: hostSize), + child: Scaffold( + body: SizedBox( + height: hostHeight, + child: ConstrainedBox( + constraints: const BoxConstraints(maxHeight: hostHeight), + child: ScrollableActionsLayout( + // shrinkWrap intentionally omitted — default false. + body: const SizedBox( + height: bodyHeight, + child: Text('default short body'), + ), + actions: [ + SizedBox( + height: actionHeight, + width: double.infinity, + child: FilledButton( + onPressed: () {}, + child: const Text('Default expand CTA'), + ), + ), + ], + ), + ), + ), + ), + ), + ), + ); + await tester.pump(); + + final layoutBox = tester.renderObject( + find.byType(ScrollableActionsLayout), + ); + // Existing screens rely on Expanded body filling the bounded host. + expect( + layoutBox.size.height, + closeTo(hostHeight, 1), + reason: + 'default shrinkWrap=false must expand to full host height ' + '($hostHeight), got ${layoutBox.size.height}', + ); + }, + ); }); } From 389a8669782e68287d2dd50491ee290677b6c0e3 Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Wed, 15 Jul 2026 15:49:36 +0200 Subject: [PATCH 3/5] fix(buy): typed handling for primary-email confirm errors and unconfirmed-email quote gate (#860) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem A buy confirm that the share register rejects with the `PrimaryEmailRequired` error code (HTTP 400) currently falls into `BuyConfirmError.unknown` and shows the generic technical-problem snackbar — the buyer gets no hint that an email confirmation is outstanding. This is the error path a real buyer hit repeatedly last week. ## Changes - **Confirm path:** `BuyConfirmError.primaryEmailRequired` — mapped from the API error code (never from message text), 503 precedence unchanged and pinned by a test. The snackbar reuses the existing `buyPaymentConfirmFailedAktionariat` copy, which describes exactly the required action (check your inbox for the pending confirmation); no new ARB keys. - **Quote path (forward-compatible):** handles the upcoming `PrimaryEmailNotConfirmed` quote error (`isValid:false`). The action button routes to the KYC page — which auto-resolves to its confirm-email step — instead of the email-capture flow, then re-fetches the quote. Unreachable until the API starts emitting the code, so this PR is fully backward-compatible. - Tests: cubit mappings (incl. 503-precedence pin), state props, snackbar copy, action-button routing (pushes KYC, not email capture), plus golden coverage for the new gate state and snackbar. ## Merge order This app change must be released **before** the API starts emitting `PrimaryEmailNotConfirmed` — older app versions would otherwise degrade to the generic retry dead-end on the quote screen. The confirm-path fix is effective immediately with the current API. --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../models/payment/payment_info_error.dart | 1 + .../cubits/buy_confirm/buy_confirm_cubit.dart | 7 +++ .../cubits/buy_confirm/buy_confirm_state.dart | 2 +- .../buy_payment_info_cubit.dart | 9 +++ .../buy/widgets/buy_confirm_button.dart | 1 + .../buy/widgets/payment_action_button.dart | 21 +++++++ test/goldens/screens/buy/buy_golden_test.dart | 19 ++++++ .../screens/buy/buy_states_golden_test.dart | 6 +- ..._confirm_failed_primary_email_required.png | Bin 0 -> 23999 bytes .../macos/buy_primary_email_not_confirmed.png | Bin 0 -> 16671 bytes .../models/payment/buy_sell_dtos_test.dart | 5 +- .../buy_confirm/buy_confirm_state_test.dart | 11 ++++ .../buy/cubits/buy_confirm_cubit_test.dart | 42 +++++++++++++ .../cubits/buy_payment_info_cubit_test.dart | 19 ++++++ .../buy/widgets/buy_confirm_button_test.dart | 17 +++++ .../widgets/payment_action_button_test.dart | 58 ++++++++++++++++++ ...uy_sell_converter_confirm_states_test.dart | 3 +- 17 files changed, 215 insertions(+), 6 deletions(-) create mode 100644 test/goldens/screens/buy/goldens/macos/buy_confirm_failed_primary_email_required.png create mode 100644 test/goldens/screens/buy/goldens/macos/buy_primary_email_not_confirmed.png diff --git a/lib/packages/service/dfx/models/payment/payment_info_error.dart b/lib/packages/service/dfx/models/payment/payment_info_error.dart index 810b513b..42409bbe 100644 --- a/lib/packages/service/dfx/models/payment/payment_info_error.dart +++ b/lib/packages/service/dfx/models/payment/payment_info_error.dart @@ -2,6 +2,7 @@ enum PaymentInfoError { registrationRequired, kycRequired, primaryEmailRequired, + primaryEmailNotConfirmed, minAmountNotMet, bitboxDisconnected, priceSourceUnavailable, diff --git a/lib/screens/buy/cubits/buy_confirm/buy_confirm_cubit.dart b/lib/screens/buy/cubits/buy_confirm/buy_confirm_cubit.dart index 2e49b4c7..8f1c51d1 100644 --- a/lib/screens/buy/cubits/buy_confirm/buy_confirm_cubit.dart +++ b/lib/screens/buy/cubits/buy_confirm/buy_confirm_cubit.dart @@ -12,6 +12,11 @@ part 'buy_confirm_state.dart'; // message text — the message is Aktionariat's and may change. const String _errorCodeAmountTooLow = 'AmountTooLow'; +// Backend error code for a confirm rejected upstream because the buyer's +// primary email is missing or not yet confirmed at the share register +// (HTTP 400). Dispatch on the code, not the message text. +const String _errorCodePrimaryEmailRequired = 'PrimaryEmailRequired'; + class BuyConfirmCubit extends Cubit { final RealUnitBuyPaymentInfoService _buyPaymentInfoService; @@ -36,6 +41,8 @@ class BuyConfirmCubit extends Cubit { ? BuyConfirmError.aktionariat : e.code == _errorCodeAmountTooLow ? BuyConfirmError.amountTooLow + : e.code == _errorCodePrimaryEmailRequired + ? BuyConfirmError.primaryEmailRequired : BuyConfirmError.unknown; emit(BuyConfirmFailure(error)); } catch (e) { diff --git a/lib/screens/buy/cubits/buy_confirm/buy_confirm_state.dart b/lib/screens/buy/cubits/buy_confirm/buy_confirm_state.dart index daa893ca..1c36f33b 100644 --- a/lib/screens/buy/cubits/buy_confirm/buy_confirm_state.dart +++ b/lib/screens/buy/cubits/buy_confirm/buy_confirm_state.dart @@ -1,6 +1,6 @@ part of 'buy_confirm_cubit.dart'; -enum BuyConfirmError { aktionariat, amountTooLow, unknown } +enum BuyConfirmError { aktionariat, amountTooLow, primaryEmailRequired, unknown } abstract class BuyConfirmState extends Equatable { const BuyConfirmState(); diff --git a/lib/screens/buy/cubits/buy_payment_info/buy_payment_info_cubit.dart b/lib/screens/buy/cubits/buy_payment_info/buy_payment_info_cubit.dart index 4ed99705..0eb8891f 100644 --- a/lib/screens/buy/cubits/buy_payment_info/buy_payment_info_cubit.dart +++ b/lib/screens/buy/cubits/buy_payment_info/buy_payment_info_cubit.dart @@ -27,6 +27,12 @@ const String _quoteErrorAmountTooLow = 'AmountTooLow'; // instead of reacting to a post-submit 400 and losing the flow. const String _quoteErrorPrimaryEmailRequired = 'PrimaryEmailRequired'; +// Backend QuoteError code for the "buyer has a primary email on record but +// has not yet confirmed it" case. This is distinct from having no email at +// all, so it routes to the existing email-confirmation flow (via the KYC +// page), not to the email-capture flow. +const String _quoteErrorPrimaryEmailNotConfirmed = 'PrimaryEmailNotConfirmed'; + class BuyPaymentInfoCubit extends Cubit { final RealUnitBuyPaymentInfoService _buyPaymentInfoService; CancelableOperation? _completer; @@ -72,6 +78,9 @@ class BuyPaymentInfoCubit extends Cubit { if (paymentInfo.error == _quoteErrorPrimaryEmailRequired) { return const BuyPaymentInfoFailure(PaymentInfoError.primaryEmailRequired); } + if (paymentInfo.error == _quoteErrorPrimaryEmailNotConfirmed) { + return const BuyPaymentInfoFailure(PaymentInfoError.primaryEmailNotConfirmed); + } return const BuyPaymentInfoFailure(PaymentInfoError.unknown); } return BuyPaymentInfoSuccess(paymentInfo); diff --git a/lib/screens/buy/widgets/buy_confirm_button.dart b/lib/screens/buy/widgets/buy_confirm_button.dart index c4481b65..f3702ff1 100644 --- a/lib/screens/buy/widgets/buy_confirm_button.dart +++ b/lib/screens/buy/widgets/buy_confirm_button.dart @@ -65,6 +65,7 @@ class BuyConfirmButtonView extends StatelessWidget { final text = switch (state.error) { BuyConfirmError.aktionariat => S.of(context).buyPaymentConfirmFailedAktionariat, BuyConfirmError.amountTooLow => S.of(context).buyPaymentConfirmFailedAmountTooLow, + BuyConfirmError.primaryEmailRequired => S.of(context).buyPaymentConfirmFailedAktionariat, BuyConfirmError.unknown => S.of(context).buyPaymentConfirmFailed, }; ScaffoldMessenger.of(context).showSnackBar( diff --git a/lib/screens/buy/widgets/payment_action_button.dart b/lib/screens/buy/widgets/payment_action_button.dart index 34d7c6b9..f334f9cf 100644 --- a/lib/screens/buy/widgets/payment_action_button.dart +++ b/lib/screens/buy/widgets/payment_action_button.dart @@ -119,6 +119,27 @@ class PaymentActionButton extends StatelessWidget { ), ); } + if (paymentState.error == PaymentInfoError.primaryEmailNotConfirmed) { + // The buyer has a primary email on record but has not yet confirmed it. + // The KYC page auto-routes to its confirm-email step in this case, so + // route there (no `extra`) instead of to email capture, then re-fetch + // the quote so a now-confirmed email surfaces the binding-buy CTA. + return Padding( + padding: const EdgeInsets.symmetric(vertical: 20), + child: AppFilledButton( + onPressed: () async { + await context.pushNamed(AppRoutes.kyc); + if (context.mounted) { + context.read().getPaymentInfo( + amount: amountController.text, + currency: context.read().state.currency, + ); + } + }, + label: S.of(context).next, + ), + ); + } if (paymentState.error == PaymentInfoError.bitboxDisconnected) { return Padding( padding: const EdgeInsets.symmetric(vertical: 20), diff --git a/test/goldens/screens/buy/buy_golden_test.dart b/test/goldens/screens/buy/buy_golden_test.dart index 2df467e4..f56940b7 100644 --- a/test/goldens/screens/buy/buy_golden_test.dart +++ b/test/goldens/screens/buy/buy_golden_test.dart @@ -207,6 +207,25 @@ void main() { }, ); + goldenTest( + 'primary email not confirmed failure', + fileName: 'buy_primary_email_not_confirmed', + constraints: const BoxConstraints.tightFor(width: 390, height: 844), + builder: () { + when(() => paymentInfoCubit.state).thenReturn( + const BuyPaymentInfoFailure(PaymentInfoError.primaryEmailNotConfirmed), + ); + when(() => converterCubit.state).thenReturn( + const BuyConverterState( + fiatText: '100', + sharesText: '1.00', + currency: Currency.chf, + ), + ); + return wrapForGolden(buildSubject()); + }, + ); + goldenTest( 'min amount not met failure', fileName: 'buy_min_amount_not_met', diff --git a/test/goldens/screens/buy/buy_states_golden_test.dart b/test/goldens/screens/buy/buy_states_golden_test.dart index cca3835b..dcb51c03 100644 --- a/test/goldens/screens/buy/buy_states_golden_test.dart +++ b/test/goldens/screens/buy/buy_states_golden_test.dart @@ -127,13 +127,15 @@ void main() { ); // Emitting `BuyConfirmFailure` drives the BlocConsumer listener - // (`buy_confirm_button.dart:64-73`) to show the failure SnackBar. The three - // error codes map to three distinct user-facing texts, so each is captured. + // (`buy_confirm_button.dart:64-73`) to show the failure SnackBar. Each + // error code's user-facing text is captured (aktionariat and + // primaryEmailRequired intentionally share the same copy). // The `.state` getter follows the emission (BuyConfirmFailure) — the builder // still renders the idle button, which is the real post-failure UI. for (final (error, name) in const [ (BuyConfirmError.aktionariat, 'buy_confirm_failed_aktionariat'), (BuyConfirmError.amountTooLow, 'buy_confirm_failed_amount_too_low'), + (BuyConfirmError.primaryEmailRequired, 'buy_confirm_failed_primary_email_required'), (BuyConfirmError.unknown, 'buy_confirm_failed_unknown'), ]) { goldenTest( diff --git a/test/goldens/screens/buy/goldens/macos/buy_confirm_failed_primary_email_required.png b/test/goldens/screens/buy/goldens/macos/buy_confirm_failed_primary_email_required.png new file mode 100644 index 0000000000000000000000000000000000000000..5684fa0730f520973ddb940492e35e6f5c782a7c GIT binary patch literal 23999 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

{K9SQjQE_o{s)x>YJ-qpX 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 D{R7y8 literal 0 HcmV?d00001 diff --git a/test/goldens/screens/buy/goldens/macos/buy_primary_email_not_confirmed.png b/test/goldens/screens/buy/goldens/macos/buy_primary_email_not_confirmed.png new file mode 100644 index 0000000000000000000000000000000000000000..bb9a2e41e870099400184e4719d363d604a514f4 GIT binary patch literal 16671 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 service.confirmPayment(any())).thenAnswer( + (_) async => throw const ApiException( + statusCode: 400, + code: 'PrimaryEmailRequired', + message: 'Primary email is required', + ), + ); + + final cubit = BuyConfirmCubit(service); + final done = cubit.stream.firstWhere((s) => s is BuyConfirmFailure); + await cubit.confirmPayment(7); + await done; + + expect( + (cubit.state as BuyConfirmFailure).error, + BuyConfirmError.primaryEmailRequired, + ); + }); + + test('confirmPayment prefers aktionariat over primaryEmailRequired when a ' + '503 also carries code PrimaryEmailRequired', () async { + // 503 keeps precedence over the PrimaryEmailRequired code — pins the + // branch order so a future refactor can't surface an email-required + // message for a genuine service outage. + when(() => service.confirmPayment(any())).thenAnswer( + (_) async => throw const ApiException( + statusCode: 503, + code: 'PrimaryEmailRequired', + message: 'Aktionariat down', + ), + ); + + final cubit = BuyConfirmCubit(service); + final done = cubit.stream.firstWhere((s) => s is BuyConfirmFailure); + await cubit.confirmPayment(7); + await done; + + expect((cubit.state as BuyConfirmFailure).error, BuyConfirmError.aktionariat); + }); + test('confirmPayment emits Failure(unknown) on other ApiException', () async { when(() => service.confirmPayment(any())).thenAnswer( (_) async => throw const ApiException( diff --git a/test/screens/buy/cubits/buy_payment_info_cubit_test.dart b/test/screens/buy/cubits/buy_payment_info_cubit_test.dart index 5abf60d5..9b9b8845 100644 --- a/test/screens/buy/cubits/buy_payment_info_cubit_test.dart +++ b/test/screens/buy/cubits/buy_payment_info_cubit_test.dart @@ -113,6 +113,25 @@ void main() { expect((cubit.state as BuyPaymentInfoFailure).error, PaymentInfoError.primaryEmailRequired); }); + test('API isValid=false with error=PrimaryEmailNotConfirmed → ' + 'Failure(primaryEmailNotConfirmed)', () async { + // A registered-but-unconfirmed email routes to the confirmation flow + // (via KYC) instead of a post-submit failure or the email-capture path. + when(() => service.getPaymentInfo(any(), currency: any(named: 'currency'))) + .thenAnswer( + (_) async => _info(isValid: false, error: 'PrimaryEmailNotConfirmed'), + ); + + final cubit = build(); + await cubit.getPaymentInfo(amount: '300'); + + expect(cubit.state, isA()); + expect( + (cubit.state as BuyPaymentInfoFailure).error, + PaymentInfoError.primaryEmailNotConfirmed, + ); + }); + test('API isValid=false with unknown error → generic Failure', () async { when(() => service.getPaymentInfo(any(), currency: any(named: 'currency'))) .thenAnswer((_) async => _info(isValid: false, error: 'AmountTooHigh', minVolume: 100)); diff --git a/test/screens/buy/widgets/buy_confirm_button_test.dart b/test/screens/buy/widgets/buy_confirm_button_test.dart index 6127600d..f97ff8b2 100644 --- a/test/screens/buy/widgets/buy_confirm_button_test.dart +++ b/test/screens/buy/widgets/buy_confirm_button_test.dart @@ -160,6 +160,23 @@ void main() { expect(find.text(S.current.buyPaymentConfirmFailedAmountTooLow), findsOneWidget); }); + testWidgets( + 'shows the aktionariat-specific error on a primary-email-required failure', + (tester) async { + whenListen( + cubit, + Stream.fromIterable([ + const BuyConfirmFailure(BuyConfirmError.primaryEmailRequired), + ]), + initialState: const BuyConfirmInitial(), + ); + + await tester.pumpWidget(host()); + await tester.pump(); + + expect(find.text(S.current.buyPaymentConfirmFailedAktionariat), findsOneWidget); + }); + GoRouter detailsRouter({BuyPaymentInfo info = _info}) => GoRouter( initialLocation: '/buy', routes: [ diff --git a/test/screens/buy/widgets/payment_action_button_test.dart b/test/screens/buy/widgets/payment_action_button_test.dart index 4d25af7e..5bb775a3 100644 --- a/test/screens/buy/widgets/payment_action_button_test.dart +++ b/test/screens/buy/widgets/payment_action_button_test.dart @@ -11,6 +11,7 @@ import 'package:realunit_wallet/screens/buy/cubits/buy_converter/buy_converter_c import 'package:realunit_wallet/screens/buy/cubits/buy_payment_info/buy_payment_info_cubit.dart'; import 'package:realunit_wallet/screens/buy/widgets/buy_confirm_button.dart'; import 'package:realunit_wallet/screens/buy/widgets/payment_action_button.dart'; +import 'package:realunit_wallet/setup/routing/routes/app_routes.dart'; import 'package:realunit_wallet/setup/routing/routes/support_routes.dart'; import 'package:realunit_wallet/styles/currency.dart'; @@ -84,6 +85,20 @@ void main() { ); }, ), + GoRoute( + name: AppRoutes.kyc, + path: '/kyc', + builder: (_, _) { + pushedRoutes.add(AppRoutes.kyc); + return _EmailCaptureStub( + onReady: (popContext) { + WidgetsBinding.instance.addPostFrameCallback((_) { + if (popContext.mounted) popContext.pop(); + }); + }, + ); + }, + ), ], ); } @@ -146,6 +161,49 @@ void main() { }, ); }); + + group('$PaymentActionButton primaryEmailNotConfirmed gate', () { + testWidgets( + 'renders the Weiter CTA, not the binding-buy button', + (tester) async { + when(() => paymentInfoCubit.state).thenReturn( + const BuyPaymentInfoFailure(PaymentInfoError.primaryEmailNotConfirmed), + ); + + await pumpButton(tester); + + expect(find.text(S.current.next), findsOne); + // Pre-tap gate must NOT surface the confirm affordance. + expect(find.byType(BuyConfirmButton), findsNothing); + expect(find.text(S.current.buyPaymentConfirm), findsNothing); + }, + ); + + testWidgets( + 'tap pushes kyc (never email capture) and re-fetches the quote', + (tester) async { + when(() => paymentInfoCubit.state).thenReturn( + const BuyPaymentInfoFailure(PaymentInfoError.primaryEmailNotConfirmed), + ); + + await pumpButton(tester); + + await tester.tap(find.text(S.current.next)); + await tester.pumpAndSettle(); + + // Routed to KYC confirm-email, not to email capture. + expect(pushedRoutes, [AppRoutes.kyc]); + // After the KYC flow returns, the quote is re-fetched with the + // current amount + currency so a now-confirmed email surfaces the CTA. + verify( + () => paymentInfoCubit.getPaymentInfo( + amount: '250', + currency: Currency.eur, + ), + ).called(1); + }, + ); + }); } /// Minimal capture-page stub that pops with a caller-controlled value on diff --git a/test/screens/buy_sell_converter_confirm_states_test.dart b/test/screens/buy_sell_converter_confirm_states_test.dart index 476082b7..74414480 100644 --- a/test/screens/buy_sell_converter_confirm_states_test.dart +++ b/test/screens/buy_sell_converter_confirm_states_test.dart @@ -73,11 +73,12 @@ void main() { expect(a, isNot(c)); }); - test('BuyConfirmError enum has exactly aktionariat + amountTooLow + unknown', () { + test('BuyConfirmError enum has exactly aktionariat + amountTooLow + primaryEmailRequired + unknown', () { // Pin the variants — the listener in BuyButton switches on these. expect(BuyConfirmError.values.toSet(), { BuyConfirmError.aktionariat, BuyConfirmError.amountTooLow, + BuyConfirmError.primaryEmailRequired, BuyConfirmError.unknown, }); }); From 8e2ab79d620a085f86a4d72b0c6f3b765146a0e6 Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Wed, 15 Jul 2026 16:00:33 +0200 Subject: [PATCH 4/5] fix(ci): route staging checks without cancelled required checks (#861) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - isolate staging-push routing from canonical required checks - let the ready `staging → develop` PR own normal CI - dispatch the full `RealUnit Build` as a fail-safe when the promotion PR is missing, draft, stale, or cannot be queried - preserve per-lane cancellation for obsolete SHAs without any cross-event cancellation - upgrade first-party Actions to Node.js 24-compatible majors and disable the inapplicable Go module cache - document trigger ownership and fallback guarantees ## Root cause `push: staging` and the promotion PR previously shared one concurrency group. When the PR event arrived after the push event, `cancel-in-progress: true` cancelled the push run on the same head SHA. GitHub attached the cancelled `Analyze & Test`, `Coverage Floor Gate`, `Visual Regression`, and `BitBox quirks audit` checks to the current promotion PR, leaving it visibly red even though the replacement PR run succeeded. ## Design Canonical jobs remain in `pull-request.yaml` with their stable required-check names. That workflow now handles PRs, `push: develop`, and manual dispatches only. The new `staging-ci-fallback.yaml` workflow handles `push: staging`: 1. Query open `staging → develop` PRs using an exact base, head owner/branch, repository, draft-state, and head-SHA match. 2. If a ready same-repository PR already points at the pushed SHA, do nothing: its `synchronize` event owns canonical CI. 3. If the PR is missing, draft, stale, or the API lookup fails, dispatch `pull-request.yaml` on `staging` via `workflow_dispatch`. 4. Keep router concurrency isolated from `RealUnit Build`, so routing can never cancel required PR checks. This removes duplicate heavy macOS/self-hosted work in the normal promotion path while failing safe to a full build whenever PR ownership cannot be proven. ## Required-check safety The initial same-workflow approach was deliberately replaced after live verification showed that GitHub renders expressions in skipped job names literally. Keeping routing in a separate workflow means: - canonical required-check names are stable - the router never emits skipped checks with canonical names - a skipped job cannot accidentally satisfy a required check - no raw expression-based check names appear in the staging-push path ## Additional CI cleanup - `actions/checkout@v6` - `actions/setup-go@v6` with `cache: false` because this repository has no `go.mod`/`go.sum` - `actions/upload-artifact@v6` - `actions/download-artifact@v7` These versions use the Node.js 24 runtime. The self-hosted golden runner reported version `2.335.1`, above the Actions minimum of `2.327.1`. ## Validation - `actionlint -ignore 'label ".+" is unknown' .github/workflows/pull-request.yaml .github/workflows/staging-ci-fallback.yaml` - Ruby YAML parse for both workflows - `git diff --check` - exact embedded router script exercised with mocked GitHub responses: - ready PR at current SHA → no fallback dispatch - draft PR → full fallback dispatch - stale PR SHA → full fallback dispatch - GitHub API failure → full fallback dispatch - manual invocation → full dispatch The ignored `actionlint` diagnostics are the repository's existing custom self-hosted labels (`m3-ultra`, `realunit-app`). ### End-to-end result [Manual `RealUnit Build` run 29418489339](https://github.com/RealUnitCH/app/actions/runs/29418489339) completed successfully on commit `69e86893`: - `Analyze & Test` — success - `Coverage Floor Gate` — success, including artifact upload/download on the upgraded Actions - `Visual Regression` — success on the self-hosted runner - `BitBox quirks audit` — success without the previous `go.sum` cache warning - no Node.js 20 deprecation annotation --- .github/workflows/pull-request.yaml | 78 +++++++--------------- .github/workflows/staging-ci-fallback.yaml | 77 +++++++++++++++++++++ README.md | 3 +- docs/testing.md | 5 +- 4 files changed, 108 insertions(+), 55 deletions(-) create mode 100644 .github/workflows/staging-ci-fallback.yaml diff --git a/.github/workflows/pull-request.yaml b/.github/workflows/pull-request.yaml index 443bff86..d596f6ce 100644 --- a/.github/workflows/pull-request.yaml +++ b/.github/workflows/pull-request.yaml @@ -16,58 +16,27 @@ name: RealUnit Build # below (saves macOS-runner minutes while work is still in progress). # - `labeled` / `unlabeled` are intentionally omitted: realunit-app has # no label-gated heavy job here (Tier 3 has its own workflow). -# - `push: develop` and `push: staging` keep post-merge verification -# as an authoritative source of truth, independent of any PR state. -# `staging` participates because feature → staging merges land an -# integration commit whose verification cannot be deferred to the -# auto-opened staging → develop PR (the gate must be authoritative -# at the moment the commit lands on staging). Other branches do not -# need a post-push check — the PR-event already covers them. -# `push: staging` and the auto-opened #841 PR now share one -# concurrency group, so exactly one real run executes per staging -# commit (the survivor is the authoritative gate — in practice -# often the PR run against the merge commit, a stronger promotion -# gate). +# - `push: develop` keeps post-merge verification as an authoritative +# source of truth. Staging fallback routing lives in the separate +# `staging-ci-fallback.yaml` workflow: it dispatches this workflow +# only when no ready staging → develop PR owns CI for the pushed SHA. # - `workflow_dispatch` is kept as a manual override and is NOT # draft-gated (see the job `if:` guard below). on: workflow_dispatch: push: - branches: [develop, staging] + branches: [develop] pull_request: branches-ignore: [main] types: [opened, synchronize, reopened, ready_for_review] -# Base case: group by PR number so a new push to the same PR cancels -# the in-flight run for the outdated commit. Falls back to -# `github.ref` for `push` and `workflow_dispatch` (no pull_request -# number): `refs/heads/develop` for post-merge runs, the dispatched -# ref for manual triggers. macOS runners are scarce — letting an -# obsolete run finish wastes ~10 minutes per push. -# Staging-lane special case: the permanently-open, same-repo auto-PR -# "staging → develop" (#841) would otherwise re-verify every staging -# commit a second time on the identical SHA (its PR-number group -# differs from the push group). Exactly that non-draft PR — -# pull_request event, head_ref == 'staging', same repo (the -# repo-identity check stops a fork branch named `staging` from -# cancelling the real staging lane), draft == false — is placed into -# the constant group `refs/heads/staging` shared with `push: staging`. -# A staging PR that is (manually) set to draft falls back to its own -# PR-number group so the `push: staging` run survives independently -# and the commit remains genuinely verified. cancel-in-progress then -# cancels the duplicate; exactly one real run always survives (no -# skip, no skipped=success masking — a red stays red). Feature PRs -# keep their own PR-number group, unaffected. +# Group PR runs by PR number, and push/dispatch runs by ref. A new SHA +# cancels work for the outdated SHA in the same lane. Staging push +# routing is isolated in `staging-ci-fallback.yaml`; it either relies on +# this PR workflow or dispatches this workflow as a fallback, but never +# shares a concurrency group with the PR run. concurrency: - group: >- - ci-${{ github.workflow }}-${{ - (github.event_name == 'pull_request' - && github.head_ref == 'staging' - && github.event.pull_request.head.repo.full_name == github.repository - && github.event.pull_request.draft == false) - && 'refs/heads/staging' - || (github.event.pull_request.number || github.ref) - }} + group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true permissions: @@ -84,7 +53,7 @@ jobs: runs-on: macos-latest timeout-minutes: 30 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: subosito/flutter-action@v2 with: flutter-version: "3.41.6" @@ -175,7 +144,7 @@ jobs: - name: Upload coverage report if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: coverage-lcov path: coverage/lcov.info @@ -192,7 +161,7 @@ jobs: # fail-open (silent `exit 0`). - name: Upload coverage summary if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: coverage-summary path: coverage/lcov.summary @@ -252,16 +221,16 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Download coverage summary - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v7 with: name: coverage-summary path: coverage - name: Download scoped coverage tracefile - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v7 with: name: coverage-lcov path: coverage @@ -348,7 +317,7 @@ jobs: # the slot back twice as fast as 30. timeout-minutes: 15 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: subosito/flutter-action@v2 with: flutter-version: "3.41.6" @@ -368,7 +337,7 @@ jobs: run: flutter test test/goldens - name: Upload golden diff on failure if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: golden-diffs path: test/goldens/**/failures/** @@ -385,10 +354,13 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 + - uses: actions/checkout@v6 + - uses: actions/setup-go@v6 with: go-version: "1.25.5" + # This repository has no Go module; caching would only emit a + # misleading "go.sum not found" warning for this audit-only job. + cache: false - name: Install bitbox-audit continue-on-error: true @@ -416,7 +388,7 @@ jobs: - name: Upload audit report if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: bitbox-audit-report path: bitbox-audit-report.md diff --git a/.github/workflows/staging-ci-fallback.yaml b/.github/workflows/staging-ci-fallback.yaml new file mode 100644 index 00000000..cacf7286 --- /dev/null +++ b/.github/workflows/staging-ci-fallback.yaml @@ -0,0 +1,77 @@ +name: Staging CI Router + +# A staging push normally produces a `synchronize` event on the open +# staging → develop promotion PR. That PR run owns the canonical required +# checks. If the PR is missing, draft, stale, or cannot be queried, this +# workflow dispatches the canonical RealUnit Build as a fail-safe. +on: + push: + branches: [staging] + workflow_dispatch: + +permissions: + actions: write + contents: read + pull-requests: read + +# Only the newest staging SHA needs routing. This group is deliberately +# isolated from RealUnit Build, so it can never cancel required PR checks. +concurrency: + group: staging-ci-router-${{ github.ref }} + cancel-in-progress: true + +jobs: + route: + name: Route staging CI + runs-on: ubuntu-latest + timeout-minutes: 2 + steps: + - name: Use PR CI or dispatch fail-safe CI + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + + SHOULD_DISPATCH=true + REASON="manual routing request" + + if [ "${GITHUB_EVENT_NAME}" = "push" ]; then + REASON="promotion PR lookup failed" + if PULLS_JSON="$( + gh api --method GET "/repos/${GITHUB_REPOSITORY}/pulls" \ + -f state=open \ + -f base=develop \ + -f head="${GITHUB_REPOSITORY_OWNER}:staging" \ + -f per_page=100 + )"; then + if jq -e \ + --arg repo "${GITHUB_REPOSITORY}" \ + --arg sha "${GITHUB_SHA}" \ + 'any(.[]; .draft == false and .head.repo.full_name == $repo and .head.sha == $sha)' \ + <<<"${PULLS_JSON}" >/dev/null; then + SHOULD_DISPATCH=false + REASON="ready promotion PR at this SHA owns canonical CI" + else + REASON="no ready promotion PR at this SHA" + fi + else + echo "::warning::Could not query the staging promotion PR; dispatching fail-safe CI" + fi + fi + + if [ "${SHOULD_DISPATCH}" = "true" ]; then + gh workflow run pull-request.yaml \ + --repo "${GITHUB_REPOSITORY}" \ + --ref staging + echo "::notice::Dispatched RealUnit Build on staging (${REASON})" + else + echo "::notice::No fallback dispatch needed (${REASON})" + fi + + { + echo "### Staging CI routing" + echo + echo "- Fallback dispatched: \`${SHOULD_DISPATCH}\`" + echo "- Reason: ${REASON}" + echo "- SHA: \`${GITHUB_SHA}\`" + } >> "${GITHUB_STEP_SUMMARY}" diff --git a/README.md b/README.md index 47255642..2575e765 100644 --- a/README.md +++ b/README.md @@ -163,7 +163,8 @@ Tier 1 specs live under `test/integration/**` and run inside the same `flutter t | Workflow | Trigger | Action | | ---------------------------- | ------------------------------------------------------------- | --------------------------------------------------------------------------------------- | -| `pull-request.yaml` | Any PR except PRs to `main` · push `develop` · manual | `flutter analyze` + `flutter test --coverage --exclude-tags golden`, scope lcov to the activated surface, fail below the committed floor, upload lcov artifact. In parallel, the `Visual Regression` job runs `flutter test test/goldens` on the self-hosted runner against the committed pixel baselines under `test/goldens/**/goldens/macos/` and uploads diff PNGs on failure. Jobs: `Analyze & Test`, `Coverage Floor Gate`, `Visual Regression`, `BitBox quirks audit`. | +| `pull-request.yaml` | Any PR except PRs to `main` · push `develop` · manual | Runs `flutter analyze` + `flutter test --coverage --exclude-tags golden`, enforces scoped coverage, validates Goldens on the self-hosted runner, and uploads diagnostics. Jobs: `Analyze & Test`, `Coverage Floor Gate`, `Visual Regression`, `BitBox quirks audit`. | +| `staging-ci-fallback.yaml` | Push `staging` · manual | Routes staging CI without cross-event cancellation: a ready `staging → develop` PR owns canonical CI; otherwise it dispatches `pull-request.yaml` on `staging` as a fail-safe. | | `tier3-handbook.yaml` | Any PR except PRs to `main`, with label `tier3:full` · push `develop` · manual | Tier-3 navigation/tap-routing smoke: runs every `.maestro/handbook/*.yaml` flow on a fresh `iPhone 17` simulator and uploads diagnostic captures (`build/handbook-captures/`) as a build artifact. Pixel drift on the page renders is owned by `Visual Regression` in `pull-request.yaml`, not this job. | | `bitbox-simulator.yml` | Any PR except PRs to `main` touching `lib/packages/hardware_wallet/**`, `lib/packages/wallet/**`, `lib/screens/hardware_connect_bitbox/**`, their test mirrors, `pubspec.yaml`, or the workflow itself · manual | Runs the BitBox02 firmware simulator with `bitbox-testkit` baselines (Tier 2) | | `bitbox-simulator-slash.yml` | `/bitbox-simulator` comment on any PR | Same engine as above, on-demand per PR (variants: default / `ref=main`) | diff --git a/docs/testing.md b/docs/testing.md index 37350061..cbc93123 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -416,12 +416,15 @@ flutter analyze flutter test --coverage ``` -The workflow runs three jobs: +The workflow runs four CI jobs: - **`Analyze & Test`** — the block above, plus a `lcov --extract` step that narrows `coverage/lcov.info` to the activated surface (`lib/packages/**`, `lib/screens/**/cubit(s)/**`, `lib/screens/**/bloc/**`), followed by `lcov --remove '*.g.dart'` to strip generator output (Drift schema mirror) before summarising. The filtered tracefile (`coverage-lcov`) and a one-line summary (`coverage-summary`) are uploaded as artifacts. - **`Coverage Floor Gate`** — downloads `coverage-summary` and fails the build when scoped line/function coverage drops below the integers committed to `.coverage-floor-lines` and `.coverage-floor-functions`. Required status check on `develop` + `main` (ruleset `PRs` / id `11317379`) alongside `Analyze & Test` and `Visual Regression` — a coverage regression blocks the merge. Ratchet protocol is documented in `README.md`. The same job also runs `scripts/check-coverage-visibility.sh`, which fails the build when an in-scope file produced no coverage at all: a file that no test loads is absent from `lcov.info` and therefore invisible to the scoped %, so the floor number alone cannot catch it. Files with genuinely no coverable lines (interfaces/ports, barrels) are ratcheted in `.coverage-visibility-allowlist`. +- **`Visual Regression`** — validates committed golden baselines on the deterministic self-hosted macOS runner and uploads render diffs on failure. - **`BitBox quirks audit`** — runs `bitbox-audit` against the diff and inlines its report into the workflow run summary; uploaded as `bitbox-audit-report`. +Staging deduplication lives in the separate `Staging CI Router` workflow (`staging-ci-fallback.yaml`). On each staging push it verifies that an open, non-draft, same-repository `staging → develop` PR already points at the pushed SHA. That PR's `synchronize` run owns the canonical required checks. If the PR is missing, draft, stale, or cannot be queried, the router dispatches `RealUnit Build` on `staging` as a fail-safe. The router has its own concurrency group and never cancels a PR run, so cancelled checks cannot be attached to the current promotion-PR head. + Tier 3 runs separately under `tier3-handbook.yaml` (push to `develop`, manual, or any PR labelled `tier3:full` except PRs targeting `main`). Its only artifact is `handbook-captures`, the per-flow diagnostic recordings — coverage data is owned by `Analyze & Test` instead. The Tier 0/1 coverage artifacts (`coverage-lcov`, `coverage-summary`) are emitted by the `Analyze & Test` job in `pull-request.yaml` (see [#323](https://github.com/RealUnitCH/app/pull/323)) and consumed by the `Coverage Floor Gate`. The repo holds a [100 % coverage rule](https://github.com/RealUnitCH/app/pull/322) for new code on the activated surface; the committed floor lives in `.coverage-floor-lines` and `.coverage-floor-functions` and is ratcheted upward per PR — drop the threshold only with reviewer sign-off and a written reason (`coverage:lower-floor` label). From 3d8ff8cbdc1d01596423f496b4219bbb4b8171b5 Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Wed, 15 Jul 2026 16:00:44 +0200 Subject: [PATCH 5/5] =?UTF-8?q?docs(handbook):=20stage=20the=20four=20no-r?= =?UTF-8?q?egistration=20web=20baselines=20(18=20=E2=86=92=2022)=20(#862)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Why RealUnitCH/web#23 adds a dedicated confirm-page end state (`no-registration`) with four new Playwright baselines (18 → 22 PNGs). The handbook deploy stages those baselines behind the `EXPECTED_WEB_BASELINE_COUNT` guard, which requires the count bump and the matching gallery cards in the same change. ## Changes - `handbook.yaml`: `EXPECTED_WEB_BASELINE_COUNT` 18 → 22. - `docs/handbook/de/index.html` (`#spec-web`): four cards for the new views (desktop de/en, tablet, mobile), mirroring the confirm-invalid card group. ## Coupling Merge together with RealUnitCH/web#23 — the guard fails the next handbook deploy if either side lands alone. The PR-level `Handbook Build Check` does not stage web baselines, so this PR's CI is unaffected either way. --- .github/workflows/handbook.yaml | 4 +- docs/handbook/de/index.html | 70 +++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/.github/workflows/handbook.yaml b/.github/workflows/handbook.yaml index f3fc345b..578c5a87 100644 --- a/.github/workflows/handbook.yaml +++ b/.github/workflows/handbook.yaml @@ -317,13 +317,13 @@ jobs: - name: Stage web e2e baselines from web repo env: WEB_DIR: _web-checkout - # RealUnitCH/web commits 18 Playwright visual baselines under + # RealUnitCH/web commits 22 Playwright visual baselines under # tests/__screenshots__/{desktop-chromium,tablet-chromium,mobile-safari}/. # `-ne` below fails the build on ANY drift (add OR remove) so a change # to the committed set upstream requires an explicit bump here AND a # matching card edit in docs/handbook/de/index.html (#spec-web) in the # SAME change — same guard rationale as the api steps above. - EXPECTED_WEB_BASELINE_COUNT: 18 + EXPECTED_WEB_BASELINE_COUNT: 22 run: | set -euo pipefail diff --git a/docs/handbook/de/index.html b/docs/handbook/de/index.html index eaaf6831..b9aaa3a2 100644 --- a/docs/handbook/de/index.html +++ b/docs/handbook/de/index.html @@ -8183,6 +8183,76 @@

Adressbestätigung — ungültiger Link

+
+
+
+ + + desktop-chromium/confirm-no-registration.png +
+
+ Keine Wallet-Registrierung — Desktop (DE) +
+
+ Zustand, wenn die E-Mail-Adresse bestätigt ist, aber keine + Wallet-Registrierung zum Link gefunden wurde — permanenter + Zustand ohne Retry, Desktop, Deutsch. +
+
+
+
+ + + desktop-chromium/confirm-no-registration-en.png +
+
+ No wallet registration found — Desktop (EN) +
+
+ Derselbe Zustand auf Englisch. +
+
+
+
+ + + tablet-chromium/confirm-no-registration.png +
+
+ Keine Wallet-Registrierung — Tablet +
+
+ Zustand im Tablet-Layout. +
+
+
+
+ + + mobile-safari/confirm-no-registration.png +
+
+ Keine Wallet-Registrierung — Mobile +
+
+ Zustand in der Mobile-Ansicht. +
+
+
+

Adressbestätigung — Dienst nicht verfügbar