refactor(settings/contact): consume createSupportTicket capability#594
Merged
TaprootFreak merged 6 commits intoMay 28, 2026
Merged
Conversation
Companion app PR to DFXswiss/api#3772 (merged). Closes V9 in docs/api-authority-audit.md. Per the eight consumer rules in CONTRIBUTING.md "Consuming API capabilities — eight rules", the Support tile reads the new `user.capabilities.createSupportTicket` field for its tap decision. No more local `mail != null` reconstruction; the rule is on the backend, the app maps a typed enum value to a UI step. ## What changes - `UserCapabilitiesDto` gains an optional `createSupportTicket` field (`CreateSupportTicketCapabilityDto { available, missingPrerequisite? }`). `MissingPrerequisite` is an open enum with members `email` + `unknown` — additive backend values degrade to `unknown` so a future prerequisite type never breaks /v2/user parsing for unrelated callers. - `SettingsContactPage` keeps the post-#588 tile layout unchanged (tiles unconditional). The Support tile's `onTap` now dispatches through `SettingsContactCubit`, which hydrates the capability: 1. `capability == null` (legacy backend pre-#3772) → direct push 2. `capability.available == true` → direct push 3. `capability.available == false, missingPrerequisite == email` → push email capture; on pop(true), re-init the cubit and push Support if the refreshed capability is now available (or `null` — symmetric to branch 1). 4. `missingPrerequisite == unknown` or `null` → defensive direct push, let the API render the error. - New standalone `SupportEmailCapturePage` (not KYC-coupled) calls `RealUnitRegistrationService.registerEmail`. `mergeRequested` status surfaces a dedicated i18n string telling the user to pick a different address — the multi-step verification flow is deliberately not dragged into this minimal capture page. - New route `SupportRoutes.emailCapture` under `/support/email`. - 4 new i18n keys (EN + DE). ## Notes on the eight consumer rules - Rule 1 (read shape, don't reconstruct): the cubit reads `user.capabilities.createSupportTicket` verbatim; no `mail` reads. - Rule 2 (unconditional visibility): tile rendered in every state. - Rule 3 (type → UI dispatch): the switch on `MissingPrerequisite` is pure routing, no business rule comments. - Rule 4 (legacy backend): null capability → direct push, tested. - Rule 5 (no reactive 400): tap branches pre-emptively on capability; no try/catch redirect path. - Rule 6 (pair-PR): replaces the deleted `emailSet` cubit field from PR #588 in the same flow. - Rule 7 (tests pin contract): cubit + page tests assert on capability state shape, not on `mail != null`. - Rule 8 (no over-engineering): 1 DTO + 1 closed-ish enum, no endpoint hierarchy. ## Tests - `user_dto_test.dart`: 14 new cases for createSupportTicket parsing, including `unknown` degradation and JSON-null handling - `settings_contact_cubit_test.dart`: 6 cases (init paths, capability shapes, failure) - `settings_contact_state_test.dart`: 8 cases (Equatable props) - `settings_contact_page_test.dart`: 15 widget tests covering tile visibility (4 states) + 11 routing branches incl. `unknown` → defensive direct push and pop(null|false) variants - `support_email_capture_cubit_test.dart`: 5 cases (success, mergeRequested, ApiException, generic throw) - `support_email_capture_state_test.dart`: 7 Equatable props cases - `support_email_capture_page_test.dart`: 9 widget tests (render, validation, submit states, snackbar variants) - 2 new goldens for the email capture page (default + submitting); settings_contact_page_default.png re-baselined for BlocProvider wrap (visual surface unchanged) ## Local verification - `dart format` clean on touched files - `dart analyze lib/ test/` no issues - `flutter test test/screens/settings_contact/ test/screens/support/ test/packages/service/dfx/models/user/` → 140/140 green
Two new keys (supportEmailCaptureDescription, supportEmailMergeRequiresVerification)
duzed the user ("Deine", "wähle", "kontaktiere") while the rest of
strings_de.arb uniformly siezt ("Verwenden Sie", "Klicken Sie", "Bitte
überweisen Sie"). Align the new strings with the file-wide convention.
Restore Dart 3.10 dot-shorthand for EdgeInsets / CrossAxisAlignment / FilledButtonState in the two touched files. Matches `home_page.dart`, `sell_page.dart`, `connect_bitbox_view.dart` etc. — all other widgets in lib/screens drop the type when it is inferable from the parameter position. `TextInputType` is intentionally kept long-form to match the nearby KYC pages (kyc_registration_address_step etc.).
45480e0
into
integration/realunit-registration
5 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Companion app PR to DFXswiss/api#3772 (merged 2026-05-26). Closes V9 in `docs/api-authority-audit.md`.
The Support tile now reads the new `user.capabilities.createSupportTicket` field for its tap decision. No more local `mail != null` reconstruction; the rule lives on the backend, the app maps a typed enum value to a UI step.
Architecture
Per the eight consumer rules in `CONTRIBUTING.md` → "Consuming API capabilities — eight rules" (documented in #593):
`MissingPrerequisite` is an open enum with `email` + `unknown`. Additive backend values degrade to `unknown` so a future prerequisite type never breaks `/v2/user` parsing for unrelated callers (KYC, settings, etc.).
What changes
`lib/`
`test/`
Local verification
Review history
Implemented + reviewed via internal subagent loop, three iterations:
Targeting `chore/post-580-followups`
Per request — this is a post-#580 follow-up that consumes a new API capability rather than introducing one in isolation. PR base set accordingly.