feat(wallet): show the lifecycle overlay from the first moment of Add Wallet - #1064
Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughThe wallet lifecycle now tracks wallet creation and import operations. The overlay displays phase-specific progress text. Wallet provisioning handles transition cleanup and direct progression to wallet switching. Tests cover the new admission rules. ChangesWallet Addition Lifecycle
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant WalletsViewModel
participant LifecycleState
participant LifecycleOverlay
participant WalletProvisioning
User->>WalletsViewModel: addWallet(isImport)
WalletsViewModel->>LifecycleState: begin addingWallet
WalletsViewModel->>LifecycleOverlay: show progress card
WalletsViewModel->>WalletProvisioning: provision wallet
WalletProvisioning-->>WalletsViewModel: result
alt Provisioning succeeds
WalletsViewModel->>LifecycleState: begin switchingWallet
else Provisioning fails or duplicates
WalletsViewModel->>LifecycleState: finish addingWallet
end
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
romchornyi
left a comment
There was a problem hiding this comment.
Reviewed and built this. Small, careful change — no blockers. Two non-blocking notes below.
Build
feat/add-wallet-overlay against platform v4.2-dev @ 1e26927c61, the revision the description names. Only local modification is the SwiftDashSDK package path; no source patches.
0 compile errors
0 linker errors
no warnings in the changed files
Ld .../dashpay.app/dashpay.debug.dylib
Ld .../dashpay.app/dashpay
** BUILD SUCCEEDED **
Verified
The gate does not leak. Every exit after tryBegin(.addingWallet) either releases the phase or hands it to an owner:
| Exit | Release |
|---|---|
addWallet throws |
state.finish() |
.alreadyExists |
state.finish() |
.added, switch succeeds |
finish() inside gatedSwitchWallet |
.added, admission rejected |
if case .addingWallet { finish() } |
.added, real switch failure |
.failedWalletSwitch, the card owns recovery |
try? await Task.sleep introduces no early return — a cancellation is swallowed and the flow continues into the do/catch.
The previousId claim holds. I checked SwiftDashSDKHost.addWallet (639-698): it only walks networksToCreate calling createAndPersist, never assigns self.wallet, and never touches the active-wallet registry. So gatedSwitchWallet captures the pre-add wallet and Switch Back points where it should.
The un-scoped composite is safe here. Every interactive entry into .switchingWallet — the row tap, the Home "Switch Wallet" shortcut, the overlay's Retry / Switch Back — is either covered by the blocking window or requires a .failed* phase, so in practice only the add flow can reach (.addingWallet, .switchingWallet). The reasoning in the comment matches what the code allows.
The tests were not weakened. The exception in testBusyPhasesRejectEveryBegin is scoped to exactly the addingWallet → switchingWallet pair rather than skipping the row wholesale, and testAddFlowCompositeAdvancesToSwitch pins the continuation separately.
Notes
The rejected-admission alert cannot be seen. In
guard state.tryBegin(.addingWallet(isImport: isImported)) else {
errorMessage = SwiftDashSDKWalletRuntime.SwitchError.switchInProgress.localizedDescription
return nil
}a rejection means another operation already holds the gate — which means the blocking overlay window is already up at .alert + 1. errorMessage surfaces from the sheet in the main window at .normal, underneath it. This is the same class of issue as the silent Obj-C rejections fixed in #1062, reappearing at a new entry point. Either render it on the overlay card or drop the message and just return.
The scope note is honest, and the ticket should stay open. The MainActor still blocks for ~5 s under the overlay, so the SwiftUI spinner freezes while the card and copy stay up. That trades "frozen app" for "frozen card with an explanation" — a real improvement, but not the fix. Worth making sure the follow-up (async off-main createWallet mirroring the #4469 destroyQueue pattern) does not get closed out by this PR.
Merge order
This is stacked on #1062 and must land after it. #1062's branch has already been force-pushed once during a rebase; another one will require rebasing this branch too.
… Wallet Wallets -> Add Wallet blocked the MainActor for ~5s (wallet-creation FFI on the current network, then the other network's SDK build + creation) before the post-add switch finally raised the overlay - the app looked frozen and was dead to touch with no indicator. - new phase .addingWallet(isImport:) with a Creating/Importing wallet… card; admitted from idle, and the composite continuation (.addingWallet -> .switchingWallet) lets the existing gatedSwitchWallet advance the window Creating -> Switching without dropping through idle (the pair is not owner-scoped on purpose - the blocking overlay window covers every other interactive entry point for the phase's lifetime) - WalletsViewModel.addWallet begins the phase before provisioning and sleeps 100 ms so UIKit commits the overlay window before the blocking work starts (same trick as the Obj-C wipe's dispatch_after); every exit pairs the begin: provisioning error and .alreadyExists finish(), .added hands the phase to the gated switch, and a rejected post-add admission defensively finishes only if still .addingWallet - WALLETOP add begin/done/FAILED telemetry with duration - admission-matrix table test gains the .addingWallet rows and the composite-continuation case This is the cosmetic stage: the MainActor still blocks under the overlay. The real fix (async off-main createWallet in the Swift SDK + lifecycle-queue serialization) follows separately. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fed937c to
4d34115
Compare
Issue being fixed or feature implemented
Stacked on #1062 (review it first). Wallets → Add Wallet → Create/Import blocked the MainActor for ~5 s before any indicator appeared: ~2.2 s of wallet-creation FFI on the current network, then ~2.9 s for the other network's mirror (SDK build ~1.6 s + creation + temp-manager shutdown) — all before the post-add switch finally raised the overlay. Simulator logs: tap at 14:48:20.206, overlay only at 14:48:25.331.
What was done?
.addingWallet(isImport:)with a "Creating wallet…" / "Importing wallet…" card ("Preparing your wallet. This may take a few seconds."). Admitted from idle; the composite pair.addingWallet → .switchingWalletlets the existing gated switch advance the window Creating → Switching without dropping through idle. The pair is deliberately not owner-scoped — the blocking overlay window covers every other interactive entry point for the phase's lifetime (noted in the matrix).WalletsViewModel.addWalletbegins the phase before provisioning and sleeps 100 ms so UIKit commits the overlay window before the blocking work starts (the same trick as the Obj-C wipe'sdispatch_after; the ordering is provable — the phase-apply Task is enqueued synchronously by the@Publishedsink before the sleep's continuation exists). Every exit pairs the begin: provisioning error and.alreadyExists→finish();.addedhands the phase togatedSwitchWallet(whosepreviousIdcorrectly captures the pre-add wallet —host.addWalletis additive and never publishes); a rejected post-add admission defensively finishes only when the phase is still.addingWallet.🔁 WALLETOP add begin/done/FAILEDtelemetry with duration..addingWalletrows and the composite-continuation case.Scope note: this is the cosmetic stage — the MainActor still blocks under the overlay (the SwiftUI spinner may pause; the card and copy stay visible). The real fix (an async off-main
createWalletvariant in the Swift SDK mirroring the #4469destroyQueuepattern, plus lifecycle-queue serialization of the add) follows as a separate platform + app PR pair.How Has This Been Tested?
v4.2-dev@1e26927c61): BUILD SUCCEEDED.Breaking Changes
None.
Checklist:
For repository code-owners and collaborators only
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes