feat(realunit): pre-fill registration form from wallet status#600
Conversation
Pair with the API's wallet/status user_data fallback (DFXswiss/api#3780). When the user lands on the RealUnit registration screen with completed DFX KYC, the form now starts pre-populated from the values the backend already holds, instead of forcing the user to retype every field and hoping each one matches the stored record byte-for-byte (which `completeRegistration` enforces server-side via `isPersonalDataMatching`). `KycRegistrationView` fetches `getWalletStatus()` in initState, resolves the nationality and address Country objects by symbol, and initializes the controllers in one setState pass. The form area renders a CupertinoActivityIndicator while the fetch is in flight. Any failure falls through to the existing empty-form behaviour — degraded gracefully, no regression for unverified accounts. `CountryField` learns an optional `initialValue: Country?` that gets passed through to the underlying dropdown. Because `DropdownButtonFormField` does not invoke its `onChanged` callback for `initialValue`, the field also propagates the preselected country to the parent controller once via a post-frame callback so the submit step sees the same value the user sees. Tests gain the new `RealUnitWalletService` mock (golden + unit fixtures), two `CountryField.initialValue` cases (preselect + propagate, ignore-when-filtered), and the existing render tests now `pumpAndSettle` to clear the prefill loading state before asserting on the form widgets.
Replace the local `_registrationSignProduced` session flag with the new `state` field on `RealUnitWalletStatusDto` returned by `GET /v1/realunit/wallet/status` (paired API change DFXswiss/api#3782). The API is now the single source of truth for whether the user needs a full registration form, a one-tap "Add wallet" confirmation, can skip the registration step entirely, or is in an edge-case KYC-required state. KycCubit re-fetches wallet status after every successful registration round-trip and dispatches from whatever the backend now reports — no client-side flag to keep in sync. Four-state dispatch in `KycCubit._runCheckKyc` (after the disclaimer gate): - NewRegistration → KycSuccess(KycStep.registration) (full form) - AddWallet → KycSuccess(KycStep.linkWallet) (one-tap confirm) - AlreadyRegistered → fall through to processStatus dispatch - KycRequired → KycUnsupportedStepFailure(null) (edge case) New `KycLinkWalletPage` + `KycLinkWalletCubit` render the streamlined one-tap flow for the AddWallet branch: shows the existing account's name plus the current wallet address (hexEip55), submits via `RealUnitRegistrationService.registerWallet(userData)`, then triggers `KycCubit.checkKyc()` which sees AlreadyRegistered and routes forward. Drop the now-dead `markRegistrationSignProduced()` API and update its two callers in `KycRegistrationPage` and `KycEmailPage` to just call `checkKyc()`. Remove `isRegistered` from `RealUnitWalletStatusDto` (the field is no longer parsed or read anywhere in lib/; update all tests + fixtures to use the new `state` parameter). ARB keys added (DE + EN, alphabetically sorted): kycLinkWalletTitle, kycLinkWalletDescription, kycLinkWalletSubmit.
Update the stale `markRegistrationSignProduced()` example in `docs/testing.md` so it instead seeds `RealUnitWalletService.getWalletStatus()` with an `alreadyRegistered` fixture — mirroring the current routing path after the cubit moved off the local sign-produced flag. Add a failure-path test for `KycCubit._runCheckKyc` that mocks `getWalletStatus()` to throw and asserts the outer `catch` surfaces a `KycFailure` rather than wedging the state machine after the disclaimer gate passes. Eliminate the redundant `getWalletStatus()` round-trip in `KycRegistrationPage` and `KycLinkWalletPage`/`KycLinkWalletCubit`. The parent `KycCubit` now attaches the `RealUnitUserDataDto` to `KycSuccess` and `KycPageManager` forwards it via constructor: the registration page seeds its controllers synchronously in `initState` and the link-wallet cubit starts directly in `Ready(userData)` with the `Initial`/`Loading` states removed. Tests updated to drop their wallet-service mocks and drive the pages via the constructor instead. Replace the generic `KycUnsupportedStepFailure(null)` emission for the `KycRequired` branch with a dedicated `KycRequiredFailure` state. Add a matching `kycRequiredFailureMessage` i18n key (DE + EN, alphabetical) so the user sees "complete your identity verification first" instead of the "step (-) cannot be completed" fallback.
|
Self-reviewed via independent subagent loop before flipping to ready:
End state: bestehende Aktionäre überspringen das Form komplett ( |
## Summary Integration branch bundling the RealUnit registration UX improvements so they can land on `develop` as a single coordinated change. Individual feature PRs target this branch as their base; once they have all merged here, this PR is reviewed end-to-end and merged into `develop`. ## Member PRs - **[#599](#599) — feat(i18n): broaden purchase pre-flight message wording** Renames the buy-screen banner from "Registrierung erforderlich" → "Zusätzliche Angaben erforderlich" so KYC-completed users aren't told they need to register again. - **[#600](#600) — feat(realunit): pre-fill registration form from wallet status** Pre-populates the Aktionariat registration wizard from `GET /v1/realunit/wallet/status`, replacing the empty-controllers experience that today forces users to retype data the backend already holds. Pairs with [DFXswiss/api#3782](DFXswiss/api#3782). ## Why bundle Both PRs touch the same screen (RealUnit registration), share goldens, and only deliver their full UX value together — the new wording communicates the right expectation, and the pre-fill makes that expectation accurate. Reviewing them as one end-to-end change at the integration step prevents partial-merge regressions and keeps the goldens in sync. ## Workflow 1. Open each member PR against `integration/realunit-registration` 2. Merge member PRs into this branch as they're approved 3. Review this PR end-to-end against `develop` (full diff = combined member content) 4. Merge into `develop` Empty seed commit will disappear once the member PRs merge in and the branch diverges with real content. --------- Co-authored-by: Blume1977 <jana.ruettimann@dfx.swiss> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…equired flows by wallet mode Rename the wallet-status DTO and fold the standalone wallet service into `RealUnitRegistrationService`: - `RealUnitWalletStatusDto` -> `RealUnitRegistrationInfoDto` (wire shape unchanged), file renamed accordingly. - `RealUnitWalletService.getWalletStatus()` -> merged into `RealUnitRegistrationService.getRegistrationInfo()` calling the new canonical `GET /v1/realunit/registration` path. The deprecated `/wallet/status` mirror exists API-side for back-compat but the app no longer references it. - DI registration for the standalone wallet service removed; every consumer (`KycCubit`, `KycEmailVerificationCubit`, `SettingsUserDataCubit`, page factories, golden mocks) now injects `RealUnitRegistrationService`. Wallet-mode signing-capability gate: - New `KycSignatureUnsupportedFailure` terminal state, emitted by `KycCubit._runCheckKyc` when the API routes the user to `NewRegistration` / `AddWallet` (both require an EIP-712 signature) AND the current wallet is `WalletType.debug` (address+signature mode that cannot sign). `AlreadyRegistered` / `KycRequired` still pass through. - New `KycSignatureUnsupportedPage` rendered by `KycPageManager`. - `KycCubit` now takes `AppStore` (instead of `RealUnitWalletService`) so the cubit can inspect `wallet.walletType`. DI updated. - i18n keys `kycSignatureUnsupportedTitle` / `kycSignatureUnsupportedDescription` added to both ARB files alphabetically; generated `lib/generated/i18n.dart`. Docs: - New `docs/wallet-modes.md` documents the three wallet modes, their signing capability, the gate location, and the pattern future sign-required features should follow. - `CONTRIBUTING.md` "State Management" section links the new doc. - `docs/testing.md` example updated to the new service / DTO names. Tests: - New tests in `kyc_cubit_test.dart` covering the gate for debug+ `NewRegistration`, debug+`AddWallet`, and the pass-through for debug+`AlreadyRegistered`. - New widget test `kyc_signature_unsupported_page_test.dart` and golden `kyc_signature_unsupported_golden_test.dart`. - `getRegistrationInfo` HTTP-wiring tests moved into `real_unit_registration_service_test.dart` (Bearer JWT + path + ApiException), replacing the deleted standalone wallet-service test. - Aggregate DTO test, email verification cubit/page test, settings user-data cubit/page test, golden mocks and golden tests all updated to the new types.
|
Follow-up — two changes: (1) Consume canonical API endpoint. DTO (2) Wallet-mode signature gate. Three wallet modes exist: The new Docs: new reference
|
Summary
Pre-fills the RealUnit registration wizard with the values the user has already verified through DFX KYC, replacing the current empty-controllers UX. Pairs with DFXswiss/api#3780.
Why
Today every user reaching the buy screen with completed DFX KYC gets sent into the Aktionariat registration wizard with all fields blank. The backend's `completeRegistration` runs `isPersonalDataMatching` over every personal-data field after transliteration — name, phone, full address, country — so any guess-and-type discrepancy hard-fails the submit. The user is forced to reproduce their KYC submission byte-for-byte from memory, which is not realistic for street formatting, house-number layout, or city spelling. The companion API PR exposes the existing record on `GET /v1/realunit/wallet/status`; this PR consumes it.
What changed
`lib/screens/kyc/steps/registration/kyc_registration_page.dart`
`lib/widgets/form/country_field.dart`
`lib/screens/kyc/steps/registration/steps/kyc_registration_{personal,address}_step.dart`
Tests
Out of scope
The deeper redesign — driving every onboarding step purely from API-shaped definitions (`show`/`skip` per step, schema-driven forms, signature-only confirmation when everything is known) — is being scoped separately. This PR is the minimal change that converts the current re-typing trap into a one-tap confirmation flow for KYC-completed users.
Manual verification
Tested against PRD with userData 290795 (Cyrill, KYC 50, three wallets): all form fields land in the wizard pre-populated and the submit path becomes "continue → continue → sign" without any user edits required. Failure path verified by killing the wallet-status request — form opens empty as before.
Pair PR
DFXswiss/api#3780 — backend mapper that exposes the user_data fallback. App PR is safe to land first (no `userData` in the response = empty form, same as today), but the UX gain materialises only once the API change ships to dev.