[LWDM] feat(pay-card): add the login intro sheet (LIVE-36793) - #21428
[LWDM] feat(pay-card): add the login intro sheet (LIVE-36793)#21428liviuciulinaru wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
🟢 Approval recommended
The new intro flow and persistence wiring are consistent with existing Pay feature patterns and are backed by focused unit/regression tests across the flow, apps, and devtools.
Pull request overview
Adds a first-login “Ledger Card” intro sheet to the Pay Card login flow (mobile bottom sheet + desktop dialog), and persists a “seen” flag so the intro is only shown until a user completes an authentication, while remaining resettable via the Pay Card devtool.
Changes:
- Added a new persisted Redux slice
payCardLoginIntro(hasSeenLoginIntro) and integrated it into the sharedpayCardpersistence blob on both mobile and desktop. - Updated
CardLoginto show an intro sheet on first login press, and to mark the intro as “seen” only after a login started in the current mount completes. - Extended Pay Card devtools + bindings to display/reset the new flag, and added targeted tests for restore/persistence/reset behavior.
File summaries
| File | Description |
|---|---|
| pnpm-lock.yaml | Lockfile updates for new workspace deps used by pay-card-auth/devtools. |
| features/flow/pay-card-auth/src/state/types.ts | Introduces PayCardLoginIntroState type. |
| features/flow/pay-card-auth/src/state/store.ts | Re-exports login-intro slice + selectors via @features/flow-pay-card-auth/state. |
| features/flow/pay-card-auth/src/state/loginIntroSlice.ts | New payCardLoginIntro slice with mark/reset/restore reducers. |
| features/flow/pay-card-auth/src/state/loginIntroSelectors.ts | Selector + persisted “lens” for composing the shared payCard blob. |
| features/flow/pay-card-auth/src/state/tests/loginIntroSlice.native.test.ts | Unit tests for the new slice + selectors + restore behavior. |
| features/flow/pay-card-auth/src/components/CardLogin/useCardLoginViewModel.ts | Adds intro-sheet orchestration + “mark seen after completed login (this mount)” logic. |
| features/flow/pay-card-auth/src/components/CardLogin/types.ts | Extends view contracts to include intro view props + row/action typing. |
| features/flow/pay-card-auth/src/components/CardLogin/CardLoginView.web.tsx | Renders the new web intro dialog alongside existing login button. |
| features/flow/pay-card-auth/src/components/CardLogin/CardLoginView.native.tsx | Renders the new native intro bottom sheet alongside existing login button. |
| features/flow/pay-card-auth/src/components/CardLogin/CardLoginIntroView.web.tsx | New Lumen-based web dialog implementation for the intro sheet. |
| features/flow/pay-card-auth/src/components/CardLogin/CardLoginIntroView.native.tsx | New Lumen + queued-bottom-sheet native implementation for the intro sheet. |
| features/flow/pay-card-auth/src/components/CardLogin/tests/useCardLoginViewModel.web.test.tsx | Expanded VM tests to cover intro open/close, “seen” semantics, and devtool reset regression. |
| features/flow/pay-card-auth/src/components/CardLogin/tests/fixtures.ts | Test i18n resources for the intro copy keys. |
| features/flow/pay-card-auth/src/components/CardLogin/tests/CardLoginView.web.test.tsx | Updates view tests to provide intro props. |
| features/flow/pay-card-auth/src/components/CardLogin/tests/CardLoginView.native.test.tsx | Updates view tests (and mocks queued sheet) to provide intro props. |
| features/flow/pay-card-auth/src/components/CardLogin/tests/CardLoginIntroView.web.test.tsx | New web intro view tests (rows/actions/close reporting). |
| features/flow/pay-card-auth/src/components/CardLogin/tests/CardLoginIntroView.native.test.tsx | New native intro view tests (rows/actions reporting, closed content behavior). |
| features/flow/pay-card-auth/src/assets.d.ts | Adds *.webp module typing for this package. |
| features/flow/pay-card-auth/README.md | Documents the new slice, persistence, and file layout additions. |
| features/flow/pay-card-auth/package.json | Adds @shared/i18n and @shared/ui-queued-bottom-sheet; adds optional react-native-svg peer. |
| devtools/pay-card/src/usePayCardViewModel.test.ts | Extends devtool VM test props to include intro flag + reset handler. |
| devtools/pay-card/src/usePayCardViewModel.native.test.ts | Extends devtool VM native test props to include intro flag + reset handler. |
| devtools/pay-card/src/types.ts | Adds new devtool props for intro seen/reset. |
| devtools/pay-card/src/pay-card/PayCard.web.tsx | Adds UI section to show/reset intro flag in web devtool. |
| devtools/pay-card/src/pay-card/PayCard.web.test.tsx | Tests new reset button wiring for web devtool. |
| devtools/pay-card/src/pay-card/PayCard.native.tsx | Adds UI section to show/reset intro flag in native devtool. |
| devtools/pay-card/src/pay-card/PayCard.native.test.tsx | Tests new reset button wiring for native devtool. |
| devtools/bindings/src/usePayCardToolProps.ts | Wires selector + reset dispatch for intro slice into devtool bindings. |
| devtools/bindings/src/usePayCardToolProps.test.tsx | Adds coverage for exposing/resetting intro flag and keeping reset actions distinct. |
| devtools/bindings/package.json | Adds dependency on @features/flow-pay-card-auth for new binding selectors/actions. |
| apps/ledger-live-mobile/src/reducers/types.ts | Adds payCardLoginIntro to the mobile root state type. |
| apps/ledger-live-mobile/src/reducers/index.ts | Registers payCardLoginIntroSlice.reducer in mobile root reducer. |
| apps/ledger-live-mobile/src/reducers/tests/payCardRestore.test.ts | Ensures one payCard blob restores into the 3 slices without clobbering neighbors. |
| apps/ledger-live-mobile/src/locales/en/common.json | Adds payTab.cardLoginIntro.* English strings for mobile. |
| apps/ledger-live-mobile/src/db.ts | Expands the persisted payCard blob type to include login intro state. |
| apps/ledger-live-mobile/src/context/LedgerStore.tsx | Dispatches restorePayCardLoginIntro during store hydration. |
| apps/ledger-live-mobile/src/components/DBSave.ts | Includes login-intro lens in persisted selector and in the “save trigger” selector. |
| apps/ledger-live-mobile/src/components/DBSave.test.ts | Updates payCard persistence lens test + adds save-trigger identity tests. |
| apps/ledger-live-mobile/tests/test-renderer.tsx | Adds payCardLoginIntro initial state to the app test renderer store. |
| apps/ledger-live-desktop/static/i18n/en/app.json | Adds payTab.cardLoginIntro.* English strings for desktop. |
| apps/ledger-live-desktop/src/renderer/storage.ts | Expands the persisted payCard blob type to include login intro state. |
| apps/ledger-live-desktop/src/renderer/reducers/index.ts | Registers payCardLoginIntroSlice.reducer in desktop root reducer + types. |
| apps/ledger-live-desktop/src/renderer/reducers/tests/payCardRestore.test.ts | Desktop equivalent restore-contract tests for the shared payCard blob. |
| apps/ledger-live-desktop/src/renderer/middlewares/db.ts | Persists the composed payCard blob on payCardLoginIntro/* actions too. |
| apps/ledger-live-desktop/src/renderer/middlewares/tests/db.test.ts | Extends middleware tests to assert persistence on login-intro actions. |
| apps/ledger-live-desktop/src/renderer/init.tsx | Dispatches restorePayCardLoginIntro during desktop init hydration. |
| .changeset/pay-card-login-intro-sheet.md | Changeset documenting the user-facing feature + persistence/devtool behavior. |
Review details
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
- Files reviewed: 46/49 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Web Tools Build Status
|
Rsdoctor Bundle Diff Analysis
Found 7 projects in monorepo, 2 projects with changes. 📊 Quick Summary
📋 Detailed Reports (Click to expand)📁 desktop-rendererPath:
📦 Download Diff Report: desktop-renderer Bundle Diff 📁 mobilePath:
📦 Download Diff Report: mobile Bundle Diff Generated by Rsdoctor GitHub Action |
There was a problem hiding this comment.
🟢 Approval recommended
The intro UX, persistence wiring (mobile + desktop), DevTools reset, and targeted regression tests are all present and consistent with established patterns in the codebase.
Review details
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
- Files reviewed: 46/50 changed files
- Comments generated: 0 new
- Review effort level: Lite
76ba5e9 to
c3d6d05
Compare
There was a problem hiding this comment.
🟢 Approval recommended
The change is well-contained, follows established patterns in neighboring Pay flows, and includes thorough unit/regression coverage across view model, persistence, restore, and devtools reset behavior.
Review details
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
- Files reviewed: 46/50 changed files
- Comments generated: 0 new
- Review effort level: Lite
c3d6d05 to
24e58bb
Compare
There was a problem hiding this comment.
🟡 Changes recommended
There is a likely Tailwind class typo (h-192) in the new web intro hero image that may break the intended layout sizing.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
- Files reviewed: 46/50 changed files
- Comments generated: 1
- Review effort level: Lite
There was a problem hiding this comment.
🟢 Approval recommended
The intro sheet, persistence wiring (mobile + desktop), and devtool reset behavior are implemented consistently with existing patterns and are backed by focused unit/regression tests.
Review details
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
- Files reviewed: 46/50 changed files
- Comments generated: 0 new
- Review effort level: Lite
Pressing Login on the Card block opened the Baanx hosted login at once. Show an intro sheet first, so the user reads what the Card offers before the app sends them out. The sheet carries a hero image, a title, three benefit rows, a provider disclaimer and two buttons. Both buttons run the same existing OAuth2 flow, so a later cut to one button is a change of one list. A new persisted payCardLoginIntro slice holds the seen flag. The flag goes up only after a login that this mount started reaches the ready state, so a hydrated session and a devtool reset both leave it down. The flag joins the shared payCard blob in both apps, and the Pay Card devtool resets it.
The auth leaf now imports a `*.webp` hero. `@features/flow-pay-card` compiles that source across the package boundary, and an ambient declaration does not cross a workspace boundary, so `tsc` could not resolve the import. Re-declare the module in this package, as it already does for `*.svg`. Both apps carry their own declaration, so only this flow package was exposed.
Co-authored-by: Cursor <cursoragent@cursor.com>
62edc95 to
bf53700
Compare
There was a problem hiding this comment.
🟢 Approval recommended
The intro flow, persistence wiring (mobile + desktop), devtool reset, translations, and regression coverage are all implemented coherently with targeted tests validating the intended “seen only after complete auth” semantics.
Review details
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
- Files reviewed: 46/50 changed files
- Comments generated: 0 new
- Review effort level: Lite
|



📝 Description
Pressing
Loginon the Card block opened the Baanx hosted login in the secure browser at once, with no explanation of what the Ledger Card offers.This pull request adds an intermediate step. The first
Loginpress now opens an intro sheet with a hero image, a title, three benefit rows, a provider disclaimer and two buttons,Create an accountandLog in to Baanx. Both buttons run the same existing OAuth2 PKCE flow, so the authentication itself is unchanged.A new persisted
payCardLoginIntroslice holds the seen flag. Once the user completes a whole authentication, the intro never appears again, and the flag survives a restart inside the sharedpayCardblob. The Pay Card devtool resets it.🔗 Context
🧠 Key decisions
CardLogin, not a rival componentpayCardLoginIntro, rather than a field on an existing onepayCardAuthis runtime state and its own comment forbids persisting it. A separate slice is also what makes the desktop persistence work:middlewares/db.tskeys off thepayCardLoginIntro/action prefix, which exists only because the slice has its own name.readystate and listed the flag as a dependency, so a devtool reset was undone on the next commit. It also raised the flag for a card holder who merely hydrated a stored session. Both are fixed, and both have a regression test.useState, notuseRef"categories": { "correctness": "error" }, soreact(refs)is a hard error and it follows the closure from the handler into the returned view model. No rule was relaxed.isIntroOpenis derived, not storedreact(set-state-in-effect)is a hard error here, so the guard effect became a derived value. The sheet still closes the moment the machine leavesidleorerror, in one render fewer.CardLoginProps, thepay-cardflow and both app view models. The dead plumbing was removed rather than left reaching nothing. See Known issues.✅ Verification
Every command ran in a clean worktree on this branch, with
--skip-nx-cache.pnpm nx run @features/flow-pay-card-auth:lintpnpm nx run @features/flow-pay-card-auth:typechecktsconfig.web.jsonandtsconfig.native.json.pnpm nx run @features/flow-pay-card-auth:testpnpm nx run @devtools/pay-card:lint/:typecheck/:testpnpm nx run @devtools/bindings:testpnpm nx run live-mobile:typecheckAll Good!, with the app's own pre-existing baseline of 1551 filtered errors.pnpm nx run ledger-live-desktop:typecheckAll Good!, with the app's own baseline of 857 filtered errors.apps/ledger-live-mobile/src/components/DBSave.test.tsapps/ledger-live-desktop/src/renderer/middlewares/__tests__/db.test.tspnpm nx run live-mobile:lint:i18noxfmt,gitleaks(no leaks found) andcommitlint.No native build ran. This environment builds no iOS application and no Android application.
The review pass proved several results by mutation rather than by inspection:
payCardcases.📋 Acceptance criteria
🖼️ Screenshots
The three row icons are placeholders. Figma ships placeholder glyphs for the three benefit rows, so no exact match exists. This pull request uses
CoinsAddPlus,CreditCardandNano, all of which exist in both Lumen symbol packages. A designer must confirm them.The provider disclaimer has no icon. Figma puts a 16 px icon beside "Card provided by Baanx", but the exported asset is Figma's own crossed-box placeholder, not a Baanx logo. The text renders alone.
The hero image is the Figma placeholder photograph, cropped to the design's framing and encoded at 1028×576, matching the existing
payTabTour.webp. A designer should replace it with the final asset.The disclaimer uses
body3, not the design'sbody4.BankTransferIntroView, the closest neighbour, usesbody3for the same line. Worth one word from the design system owner.On desktop the intro will keep reappearing. This is not caused by this pull request.
openHostedLogin.web.tsalways returnsdismissedand reading the desktop redirect is LIVE-34740, so no desktop login ever completes and the flag never goes up. Mobile behaves fully.A pre-existing persistence race, now one field wider. The db middleware is live during
init, so each of the threepayCardrestore dispatches writes the whole blob and only the third is correct. A renderer crash in that few-millisecond window could persisthasSeenLoginIntro: false. The existingdisable()helper was considered and rejected: it is a global, time-based kill switch that would also suppress writes tosettings,accounts,walletand a dozen other keys. The clean fix is onerestorePayCardaction answered by all three reducers, which touches two other packages and belongs in its own ticket.No mobile integration test.
PayTab.integration.test.tsxmocks the whole Card block away, so the realCardLoginnever mounts and noLoginbutton exists to press. Equivalent coverage sits in the package instead, in a suite that builds a real store, runs the real xstate machine and resolves the real app translation keys. Reaching the component from the app harness would mean supplying the Card API, the keychain and the secure browser, which is separate work.Open question: does the intro need analytics? Both neighbour flows fire a page event and a
button_clickedevent through a prop their app injects.CardLoginPropsaccepts no tracking prop, so this pull request adds none, and the dead plumbing was removed rather than left in place. Say the word and it is a small follow-up.