Skip to content

feat(bridge): Bridge banking in settings#653

Merged
islandbitcoin merged 9 commits into
feat/fygarofrom
feat/bridge-account-routing-settings
Jul 2, 2026
Merged

feat(bridge): Bridge banking in settings#653
islandbitcoin merged 9 commits into
feat/fygarofrom
feat/bridge-account-routing-settings

Conversation

@forge0x

@forge0x forge0x commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Establishes a single Bank accounts hub in Settings that unifies everything
bank-transfer related: money-in (your Flash USD receiving account) and
money-out (withdrawal banks). The user sees one place to receive by bank
transfer, add withdrawal accounts, and choose a default withdrawal account.

This supersedes and removes the original standalone "Account and routing number"
screen/row. The receiving account now lives inside the hub as the Receive card.

What's in this PR

1. Receive money

  • Flash USD receiving account: bank name, account number, routing number
  • Bridge KYC-gated locked state with an Upgrade your account CTA that opens the
    same AccountType upgrade flow as Home screen QuickStart
  • reveal/copy account number, per-field copy actions, and copy-all
  • Contact Support button opens the existing WhatsApp support URL

2. Withdraw to bank accounts

  • BankAccountVM + useBankAccounts() normalize:
    • bridgeVirtualAccount -> single receive account
    • bridgeExternalAccounts (USD) -> Bridge withdrawal accounts
    • me.bankAccounts (local) -> local withdrawal accounts
  • Withdraw accounts are grouped by currency with a per-currency default
  • The default chosen in the hub is now used by:
    • Bridge cash-out account selection
    • local cash-out Withdraw to confirmation selector
  • Add Bank Account supports a settings return path, so adding from the hub returns
    to the hub instead of dropping the user into the cash-out flow

3. PR cleanup / review pass

  • deleted the orphaned standalone routing screen and settings row
  • extracted the new Bank Accounts / Add Bank Account copy to i18n
  • regenerated i18n source/types
  • kept generated GraphQL unchanged

Interim vs. backend dependency

Bridge external accounts have no isDefault and no set-default mutation yet, so
the chosen default is persisted client-side in AsyncStorage for now. The storage
helpers are isolated in use-bank-accounts.ts so the frontend can swap to server
defaults in one place once the backend PR lands.

Backend PR for full best-case: lnflash/flash#423

  • BridgeExternalAccount.isDefault: Boolean!
  • bridgeSetDefaultExternalAccount(input:{ externalAccountId }): ...
  • bridgeDeleteExternalAccount(input:{ externalAccountId }): ...
  • later: bankAccountSetDefault / bankAccountCreate for local-rail parity

Until the backend mutations land, remove is still shown as "coming soon" and defaults
are client-side.

File-by-file

File Change
app/screens/settings-screen/bank-accounts/types.ts new normalized BankAccountVM / WithdrawGroup
app/screens/settings-screen/bank-accounts/use-bank-accounts.ts normalized hook + interim default store/helpers
app/screens/settings-screen/bank-accounts/bank-accounts-screen.tsx hub screen: Receive card + Withdraw list
app/screens/settings-screen/settings/bank-accounts.tsx Settings row entry
app/screens/settings-screen/settings-screen.tsx Bank accounts row replaces standalone routing row
app/components/topup-cashout-flow/CashoutWithdrawTo.tsx reads hub-selected default for the confirmation selector
app/screens/topup-cashout-flow/CashoutDetails.tsx uses hub-selected default for actual local/Bridge cash-out selection
app/screens/topup-cashout-flow/BridgeAddExternalAccount.tsx cancel button, i18n, settings return path
app/navigation/* add BankAccounts, remove orphaned BridgeAccountRouting
app/i18n/* new Bank Accounts / Add Bank Account strings

Verification

  • yarn tsc:check
  • targeted eslint on touched TS/TSX files ✅
  • yarn check:translations
  • yarn check:codegen
  • yarn graphql-check
  • git diff --check
  • yarn test ⚠️ previously showed an existing unrelated failure in
    __tests__/screens/send-confirmation.spec.tsx

@islandbitcoin islandbitcoin self-assigned this Jun 28, 2026
@islandbitcoin islandbitcoin requested a review from Nodirbek75 June 28, 2026 09:19
Introduce a single "Bank accounts" settings surface that unifies the Bridge
virtual (receive) account and the user's withdrawal banks behind one
rail-agnostic view model, replacing the standalone routing-number row.

- add normalized BankAccountVM + useBankAccounts() hook merging
  bridgeVirtualAccount (receive), bridgeExternalAccounts (USD withdraw) and
  me.bankAccounts (local withdraw), with per-currency default resolution
- add BankAccountsScreen: Receive card (reveal/copy, KYC-gated) + currency
  grouped Withdraw list with default selection, status pills and add flow
- interim client-side default store (AsyncStorage) until backend adds
  isDefault + bridgeSetDefaultExternalAccount (kept isolated from the
  persistentState migration chain for a one-file swap later)
- wire BankAccounts route + settings row; hub handles KYC state internally

Scaffolding only: set-default persists client-side; remove is gated on a
future bridgeDeleteExternalAccount mutation. tsc + eslint clean.
@forge0x forge0x changed the title feat(bridge): show virtual account details in settings feat(bridge): Bridge banking in settings Jul 2, 2026
patoo0x added 3 commits July 2, 2026 14:10
- Add Cancel button in the Add Bank Account header (navigation.goBack)
- Add Contact Support button at the bottom of the Bank accounts hub
  that opens WhatsApp support (wa.flashapp.me) via openWhatsAppUrl
 KYC-not-approved state now shows an Upgrade your account button that
 navigates to the AccountType upgrade flow — same destination as the
 QuickStart upgrade card on the Home screen.
patoo0x and others added 3 commits July 2, 2026 15:21
Conflict in BridgeAddExternalAccount.tsx: #652 switched the post-link
navigation to navigation.replace (back must not return to the form) and
added an already-linked path (BRIDGE_API_ERROR treated as success);
this branch added the returnTo=BankAccounts entry point and i18n.

Resolved by extracting a single navigateAfterLink helper used by both
the success and already-linked alerts: returnTo=BankAccounts navigates
back to the existing hub instance (drops the form from the stack),
otherwise replace to CashoutDetails per #652. The already-linked path
now also respects returnTo, so adding a duplicate bank from Settings
returns to Settings instead of dropping into the cash-out flow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- extract the #652 already-linked alert strings to i18n
  (BridgeAddExternalAccount.alreadyLinkedTitle/Message)
- toast confirmation when a default withdrawal account is set in the
  hub (no feedback before beyond the radio moving)
- CashoutWithdrawTo: reload the stored default on screen focus instead
  of mount only, so a default changed in Settings is reflected when
  returning to an already-mounted cash-out screen
- promote serverDefault to a proper optional field on BankAccountVM
  instead of smuggling it through type casts

Verified: tsc:check clean, eslint clean on touched files, jest
36 suites / 159 tests green on the merged tree.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@islandbitcoin

Copy link
Copy Markdown
Contributor

Pushed two commits to unblock this PR:

dfd006fa — merge feat/fygaro (conflict resolution). While this PR was open, #652 rewrote the same success-handler region of BridgeAddExternalAccount.tsx: it switched post-link navigation to navigation.replace (back-stack trap fix) and added an already-linked path that treats a duplicate submission (BRIDGE_API_ERROR) as success. A "take mine" resolution would have silently reverted that fix. Resolved by extracting a single navigateAfterLink helper used by both the success and already-linked alerts: returnTo: "BankAccounts" navigates back to the existing hub instance (form dropped from the stack), otherwise replace to CashoutDetails per #652. The already-linked path now also respects returnTo — adding a duplicate bank from Settings returns to Settings instead of dumping the user into the cash-out flow.

cb690e58 — small polish on top:

  • extracted fix: bridge manual external account fallback #652's hardcoded already-linked alert strings to i18n (they predated this PR's i18n pass)
  • toast confirmation when a default withdrawal account is set in the hub (there was no feedback beyond the radio moving)
  • CashoutWithdrawTo reloads the stored default on screen focus instead of mount only, so a default changed in Settings shows up when returning to an already-mounted cash-out screen
  • serverDefault is now a proper optional field on BankAccountVM instead of being smuggled through type casts

Verification on the merged tree (this repo's PR CI runs no typecheck or tests): yarn tsc:check clean, eslint clean on all touched files, yarn test green — 36 suites / 159 tests.

Deliberately NOT done here: wiring the backend #423 API (isDefault, bridgeSetDefaultExternalAccount, bridgeDeleteExternalAccount + GraphQL regen). That backend PR merged today, so the interim AsyncStorage default and the "Remove (coming soon)" toast can be replaced in a focused follow-up — the swap point is isolated in use-bank-accounts.ts as designed.

🤖 Generated with Claude Code

The translation-drift check (added in #604) requires every locale to
contain all en.json keys. Backfill the 72 new Bank accounts / add-bank
keys into all 23 locale files with English placeholder values — same
interim convention as existing untranslated keys (e.g. Cashout section).
Proper translations can follow the ENG-242 machine-translate process.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@islandbitcoin islandbitcoin merged commit cd98305 into feat/fygaro Jul 2, 2026
4 checks passed
islandbitcoin pushed a commit that referenced this pull request Jul 2, 2026
…switch

The Bank accounts hub (#653) landed after the ENG-465 remote-config gate
was folded into feat/fygaro and missed it: the settings row rendered for
everyone and the hub fired Bridge KYC/virtual-account/external-account
queries with the kill switch off — violating the gate's contract (hide
Bridge entry points, skip Bridge queries) and dead-ending users at an
Upgrade CTA whose Bridge path is hidden.

- hide BankAccountsSetting when !bridgeTopupEnabled (same contract as
  AccountType / TopupCashout)
- skip useBridgeKycStatusQuery in useBankAccounts when the flag is off;
  the virtual/external account queries already skip on !kycApproved and
  are now transitively gated
- split the AsyncStorage default-account helpers into
  default-account-store.ts: CashoutWithdrawTo/CashoutDetails only need
  the stored id, and importing the hook module would have pulled
  feature-flags-context (and Firebase Remote Config) into their module
  graph — breaking home.spec and coupling cash-out to settings

Verified: tsc:check clean, eslint clean, jest 36 suites / 159 tests
green (home.spec included).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

3 participants