Skip to content

chore: post-#580 follow-ups (rolling collection)#587

Merged
TaprootFreak merged 8 commits into
developfrom
chore/post-580-followups
May 27, 2026
Merged

chore: post-#580 follow-ups (rolling collection)#587
TaprootFreak merged 8 commits into
developfrom
chore/post-580-followups

Conversation

@TaprootFreak
Copy link
Copy Markdown
Contributor

Rolling collection branch for follow-ups after #580 (goldens max coverage + handbook 52 slots) merged into develop via #571. Same pattern as the previous chore/post-541-followups collection.

Mechanics

  • Feature PRs target this branch as their base. Each lands as a separate PR for review history.
  • This collection PR aggregates them; when stable, it's merged into develop in one go.
  • Initial commit is intentionally empty — actual changes land on top.

Known follow-up topics

Tracked from the #580 audit + day-2 fixes:

  • BitBox-Wallet-Variante des Settings-Goldens (settings_page_bitbox) — die HTML-Erklärung im Handbook erwähnt das Hiding der Wallet-Sicherung-Tile bei BitBox, kein Golden pinnt es derzeit.
  • BitBox-Connection-State-Goldens — aktuell nur BitboxNotConnected ist als Golden. Weitere 8 States (Found, Connecting, CheckHash, Pairing, CapturingSignature, SignatureFailed, Connected, FinishSetup) sind theoretisch render­bar via Mock-Wallet.
  • Eventuelle Locale-en-Goldens für die Pages, wo lange deutsche Strings Layout-relevant sind (Buy/Sell Banner).

TaprootFreak and others added 2 commits May 26, 2026 16:54
Empty seed commit so a draft PR can exist before the actual follow-ups
land. Real changes (further goldens state-coverage, BitBox-aware variant
pinning, additional handbook slots, etc.) get pushed on top of this
branch and are visible incrementally in the PR diff.
…586)

## Summary

Both `TransactionIssue` and `LimitRequest` options in the in-app support
ticket type picker silently fail with HTTP 400 on submit because the app
never sends the API-required nested DTOs (`transaction` /
`limitRequest`). The reproduction in the field is `RealUnitApiException:
limitRequest should not be empty (statusCode: 400)` for LimitRequest;
the TransactionIssue equivalent has not surfaced because realunit-app
users typically file transaction issues via services.dfx.swiss instead.

This PR hides both options from the picker until the proper forms are
implemented. The enum values, i18n keys (`supportTransactionIssue`,
`supportLimitRequest`), icons, and the cubit's `_getTicketName` mapping
are intentionally left in place so re-enabling is a one-tuple revert per
type once the structured input UI (dropdowns, transaction picker,
document upload) lands.

## Why not implement the forms now

- `LimitRequest` needs three dropdowns (`limit` / `investmentDate` /
`fundOrigin`), an optional `fundOriginText` field mapped from message, a
KYC ≥ 50 gate, and a mandatory document upload (PNG / JPEG / PDF).
Requires a new `file_picker` dependency for PDF support.
- `TransactionIssue` needs a transaction picker (list of the user's
recent transactions via `GET /v1/transactions`) plus a reason dropdown
filtered to `[FundsNotReceived, TransactionMissing, Other]`.
- Both are independent feature PRs of meaningful scope; hiding now
removes the user-facing failure immediately.

## Scope

- `lib/screens/support/subpages/support_create_ticket_page.dart` —
remove two tuples from the `items:` array of
`TagSelection<SupportIssueType>`.
- No API, model, cubit, service, or i18n changes.

## Test plan

- [x] `flutter analyze` → `No issues found! (ran in 4.7s)`
- [x] `flutter test test/screens/support/` → 62 / 62 passed
- [ ] Manual smoke: open `Neues Ticket erstellen`, confirm only
`Allgemeines Anliegen`, `Fehlerbericht`, `KYC-Problem` are selectable.
- [ ] Manual smoke: submit each of the three remaining types with a
short message and verify a ticket is created (no 400).
@TaprootFreak TaprootFreak marked this pull request as ready for review May 26, 2026 15:00
TaprootFreak and others added 6 commits May 26, 2026 17:19
Re-opens [#584](#584)
(closed because its base \`chore/post-541-followups\` was deleted after
the collection-merge
[#571](#571)).
Cherry-picked Jana's original commit (\`517170a\`) onto the new
collection branch \`chore/post-580-followups\`.

## Summary

The "Support kontaktieren" tile under Settings → Kontakt must always be
visible — including pre-signin onboarding flows where the user has not
yet registered an email. Render it unconditionally and drop the
surrounding cubit/state machinery that only existed to gate this single
tile.

## Pair PR

Pairs with
[DFXswiss/api#3761](DFXswiss/api#3761) (drops
\`UserCapabilitiesDto.supportAvailable\`).

**Merge order is unconstrained — both PRs are independent-safe:**

| Order | Backend sends | App reads | Crash? |
|---|---|---|---|
| App-PR alone merged | \`supportAvailable: true/false\` still in JSON |
Field no longer read in \`fromJson\` — Dart \`Map<String,dynamic>\`
silently ignores unknown keys | No |
| API-PR alone merged | Field gone from JSON | Old app code reads
\`json['supportAvailable'] as bool? ?? false\` → \`false\` → tile stays
hidden (existing bug persists) | No |
| Both merged | Backend stops sending, app stops reading | — | No |

The DTO uses \`as bool? ?? false\` (nullable cast with default fallback)
for every capability flag, so neither side is brittle to the other's
deploy timing. Earlier "Merge order: API first" claim was inaccurate.

## Review history

Audited via subagent during the #584 cycle (clean, no MAJORs). Main-repo
mirror was needed for Visual Regression on the dfx01 self-hosted runner
(same Fork-PR pattern as
[#585](#585)).

Credit: code-diff by Jana Rüttimann (\`Blume1977\`).

---------

Co-authored-by: Blume1977 <jana.ruettimann@dfx.swiss>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…set (#589)

## Summary

Mirror the API-side `@IsSwissPaymentText()` validator
(DFXswiss/api#3766) in the four user flows that write into `user_data`:

- KYC registration — personal step (`firstName`, `lastName`)
- KYC registration — address step (`street`, `houseNumber`,
`postalCode`, `city`)
- Settings → edit name (`firstName`, `lastName`)
- Settings → edit address (`street`, `houseNumber`, `postalCode`,
`city`)

Users now get immediate field-level feedback in the form if they enter a
character outside the SIX SIG IG QR-Bill v2.3 permitted character set,
instead of submitting and waiting for a generic 400 from the backend.

## Why

DFX business decision: only characters valid in Swiss payment systems
are supported end-to-end. Pairs with the umlaut-preservation fix in
DFXswiss/api#3766 — now that diacritics survive storage and PDF
rendering, we also lock the input surface so CJK / Cyrillic / Arabic /
emoji never enter the system.

## Character set

```
[\x20-\x7E] (printable ASCII)
+ À Á Â Ä Ç È É Ê Ë Ì Í Î Ï Ñ Ò Ó Ô Ö Ù Ú Û Ü Ý ß
+ à á â ä ç è é ê ë ì í î ï ñ ò ó ô ö ù ú û ü ý
+ \n (line feed)
```

Covers German, French, Italian, Romansh. Regex is byte-for-byte aligned
with the API regex (`Config.formats.swissPaymentText`) so any input the
client accepts the backend also accepts.

## Changes

- `lib/packages/utils/swiss_payment_text.dart`: new
`isSwissPaymentText(String?)` helper (empty/null = valid).
- Four screen files: add the helper call to existing `validator:`
callbacks; missing validator on house-number in settings edit-address
page added.
- `assets/languages/strings_en.arb`, `strings_de.arb`: new
`swissPaymentTextInvalid` translation key, inserted in alphabetical
order.
- Generated `lib/generated/i18n.dart` is gitignored — built by `dart run
tool/generate_localization.dart` after pulling.

## Test plan

- [ ] `dart run tool/generate_localization.dart` runs without errors
after checkout
- [ ] `flutter analyze` — passes (verified locally, no new issues)
- [ ] Type `Rüttimann` / `Münchwilen` / `Genève` / `Saint-Légier` in any
of the four flows → no error, form accepts
- [ ] Type `王小明` / `Иван` / `日本語` / an emoji → field shows the
Swiss-payment-text error message
- [ ] Submit a valid form → API call succeeds (i.e. client and server
regex match)
- [ ] German UI shows German error message; English UI shows English
message
- [ ] `flutter test` — pre-existing golden failures unchanged (verified
locally — same 88 failures on clean develop)
Add the missing test coverage for `lib/packages/utils/swiss_payment_text.dart`
(introduced in #589) so the regex character set is locked in against
regressions when the API-side `Config.formats.swissPaymentText` drifts.

33 cases across 8 groups:

- empty / null → valid (matches the docstring contract: callers chain a
  non-empty check)
- printable ASCII (0x20–0x7E): digits, punctuation, full 95-char band
- uppercase + lowercase Latin diacritics for CH / DE / FR / IT
- real Swiss names + addresses (Rüttimann, Münchwilen, Genève,
  Saint-Légier, D'Hauterive, François)
- non-Latin scripts rejected (CJK, Cyrillic, Arabic, Hebrew, emoji)
- mixed-script attacks (Latin word with a Cyrillic homoglyph е inside)
- whitespace: newline accepted (multi-line memos), tab + CR rejected
- uncommon Latin diacritics rejected (Polish ąć, Portuguese ã,
  Norwegian øå, French ligature œ, French Ÿ) — explicitly documents
  what is NOT in the Swiss payment set so the regex is hand-verifiable

Run: `flutter test --no-pub test/packages/utils/swiss_payment_text_test.dart`
→ 33/33 pass.
## Summary
- Handbook (`docs/handbook/de/index.html:1088-1090`) behauptet, die
Wallet-Sicherung-Kachel ist bei BitBox-Wallet ausgeblendet
- Bestehender Golden deckt nur die Software-Wallet-Variante ab —
Invertierung der Bedingung in `settings_page.dart:100` würde nicht
auffallen
- Neuer `goldenTest` „bitbox wallet open hides the Wallet-Sicherung
tile" sichert die hidden-tile-Logik ab
- `MockBitboxWallet` in `test/helper/golden_mocks.dart` ergänzt (analog
zu `MockSoftwareWallet`)

Schließt Reviewer-Finding #2 aus dem #582-Review.

## Test plan
- [ ] CI grün (Visual Regression, Analyze & Test, Coverage Floor Gate,
Handbook Build Check)
- [ ] Goldens via `golden-regenerate.yaml` auf dfx01 generiert
(erwartet: 1 neuer PNG `settings_page_bitbox.png`)
- [ ] Manuell verifiziert: PNG zeigt Settings-Page ohne
Wallet-Sicherung-Tile

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…592)

Follow-up to the BitBox-Goldens deep-dive done after
[#580](#580). Of the 8
candidate BitBox connection sub-states, 3 are pixel-distinct UI patterns
worth pinning; the other 5 are either identical to the baseline,
near-variants of the top-3, or have no UI at all.

## Added (top-3 by ROI)

| File | State | Why pinned |
|---|---|---|
| `connect_bitbox_page_connecting.png` | `BitboxConnecting` | Only
spinner-only state with neither confirm nor cancel — distinct
\`ConnectContent\` branch |
| `connect_bitbox_page_check_hash.png` | `BitboxCheckHash` | Highest
pixel-drift risk: alpha-blended pairing-code pill with custom
BoxDecoration |
| `connect_bitbox_page_signature_failed.png` | `BitboxSignatureFailed` |
Only state with two override-labelled buttons (Retry + Continue anyway)
|

## Out of scope (deliberately)

| State | Reason |
|---|---|
| `BitboxFound` | Renders identical to \`BitboxNotConnected\` — view's
switch has no case, falls to \`_ =>\` default |
| `BitboxPairing` | Spinner subtitle variant of \`BitboxConnecting\` —
near-duplicate |
| `BitboxCapturingSignature` | Title-only variant of \`BitboxPairing\` |
| `BitboxConnected` | Button + title variant of the connected SVG branch
— close to \`SignatureFailed\` |
| `BitboxFinishSetup` | Emits no UI, only a BlocListener
\`onFinish(wallet)\` callback |

## Coverage gap closed

The view file
\`lib/screens/hardware_connect_bitbox/connect_bitbox_view.dart\` has
three orthogonal branches in its state-switch (spinner-only, hash-pill,
two-button-with-override-labels). Before this PR, only the default \"no
UI ceremony\" branch (\`BitboxNotConnected\`) was pixel-locked.
Cubit-tests (16 in \`connect_bitbox_cubit_test.dart\`) cover the
state-machine logic, view-tests cover button wiring — but pixel-layout
regression of these three branches was structurally unguarded.

## Mock setup

Lifted verbatim from
\`test/screens/hardware_connect_bitbox/connect_bitbox_view_test.dart\`:
\`_FakeBitboxDevice extends Fake\`, \`_MockBitboxWallet extends Mock\`.
No new helper file, no new pubspec entries.

## Pending CI dance

- Bot golden-regen workflow will commit the three new baselines on the
dfx01 self-hosted runner.
- Empty re-trigger commit needed afterwards per the GITHUB_TOKEN gotcha
documented in \`docs/visual-regression-tests.md\`.

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@TaprootFreak TaprootFreak merged commit 94176b2 into develop May 27, 2026
6 checks passed
@TaprootFreak TaprootFreak deleted the chore/post-580-followups branch May 27, 2026 08:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants