Recover stranded payjoin stack (pj/01-06) - #2504
Merged
Merged
Conversation
Building a plain (local) DateTime from a UTC end date's wall-clock fields shifted the inclusive-day boundary by the device's UTC offset, so the same export included or excluded edge transactions depending on the machine's timezone. Round up in UTC when the input is UTC.
WatchPayjoinUsecase now emits both PayjoinReceiver and PayjoinSender (scoped by ids); the send flow needs sender completion events a receiver-only filter would swallow. ReceiveBloc keeps its receiver-only behaviour by filtering the concrete type at its own call site.
Adds a sealed WalletFailure family and a single-transaction lookup use-case returning Result<WalletTransaction?, WalletTransactionLookupFailure> (Ok(null) = synced-and-absent, Err = infra failure), per the #1895 Result migration. Consumed by the payjoin engine and transaction details.
PdkPayjoinDatasource gains stopPolling(id) and an idempotent dispose() so a resolved session's directory poll can be cut (no more stale expiry events) and tests get a clean teardown. The local datasource filters include isAborted and fetchByTxId matches originalTxId too, and sender poll logs/exceptions use a hashed logRef instead of the raw BIP21 URI.
The core of the fix. A unified backoff-poll engine arms two per-session watchers — one for the real payjoin tx, one for the original (fallback) tx — so a session always reaches a terminal state (completed or aborted) instead of hanging. tryBroadcastOriginalTransaction is split into a guarded public entry (refuses via canManuallyBroadcastOriginal, emits on the stream) and the internal mechanism; expiry/proposal processing re-fetch the fresh row and bail on terminal; resume moves out of the constructor to resumePayjoinsOnStartup, called from AppLocator once wallet/labels deps are registered. Wallet repos are injected as lazy closures; the directory receiveTimeout is raised to 35s (above the 30s long-poll hold). Adds the PAYJOIN --> LABELS edge to FEATURES.md.
…2246) SendCubit._watchPayjoin now owns resolving the flow: a completed/aborted sender session moves to success with the on-chain txid, syncs the wallet and stores the user label on the final txid; an expired session returns to confirm with a broadcast-failure error and clears the provisional txId/payjoinSender so a retry starts clean. Payjoin is attempted only when willAttemptPayjoin holds (global setting on, locally-signing wallet, non-self BIP21 with a pj= param), fail-closed by default.
The bitcoin confirm review shows a 'payjoin will be attempted' card; the success screen says when a send fell back to a plain transaction and navigates to the broadcast tx by txid (never the sender BIP21 URI); the coordinating screen shows a fallback countdown, gated on canManuallyBroadcastOriginal and only when the fallback is imminent (<=1h), and localizes its title. Adds the SEND --> TX_HISTORY edge.
Gates payjoin session creation on the global setting and carries payjoinMinAmountSat in state (with isPayjoinBelowMinimum); drops the per-address payjoin toggle in favour of that setting. Adds isPayjoinFlowOwningNavigation so the shell's generic tx-received listener defers to the payjoin screen, and lets the in-progress screen be entered on any post-started status. Guards the manual original-tx broadcast behind canManuallyBroadcastOriginal, adds isClosed guards to the watchers, splits the new-address error like the start path, and fixes the lightning in-progress navigation to use the route name. Adds the RECEIVE --> TX_HISTORY edge.
Rewrites the payjoin-in-progress screen to distinguish below-minimum, generic fallback, real payjoin and expired outcomes with their own copy, auto-navigates to transaction details on a real completion, and shows a fallback countdown (imminence-gated) plus a View Details / receive normally action driven by canManuallyBroadcastOriginal.
White in both light and dark mode, mirroring onError next to error. Lets a green success surface use theme-correct white text instead of reaching for a mismatched onSecondary/onError token.
Adds an optional action widget slot alongside the existing onAction/actionIcon plain-icon-button trailing, for a screen that needs something richer than a single icon (e.g. the receive screen's payjoin toggle chip). Positioned like the plain icon button — no extra offset stacked on top of its own padding — so it stays aligned with the title.
Adds a green/red toggle chip (ReceivePayjoinToggleButton) to the receive TopBar, wired to the global setting — only for a Bitcoin receive with a payjoin-capable wallet (funded, locally-signing), so it never shows on Liquid/Lightning or non-eligible wallets. Tapping flips the setting; long-pressing opens the payjoin settings screen. The BlocProvider now wraps the whole ReceiveScaffold (not just its body) so the TopBar can host it, gated behind a narrow selector so switching receive networks still does not rebuild the scaffold. ReceiveBloc now watches payjoinEnabledChangeStream live, creating or clearing the session the moment the setting changes anywhere in the app — no need to leave and re-enter the receive screen. Session creation (and the toggle itself) is also gated on the wallet having a confirmed balance (isPayjoinToggleable/_isPayjoinEligible): a payjoin proposal needs at least one UTXO, so there is nothing to toggle for an empty wallet, and isPayjoinLoading resolves immediately instead of waiting forever for a session that will never be created. Also suppresses the pj= endpoint from the QR (without tearing down the session) when the requested amount is below the configured anti-probing minimum, since the receiver would decline it anyway — with an explanatory hint, and it reappears the instant the amount is raised or cleared. Adds the RECEIVE --> SETTINGS edge (payjoin settings long-press).
Adds Transaction.displayPayjoinStatus: when the broadcast transaction is visible its txid is authoritative (== payjoin txid -> completed, == original txid -> aborted), else the session status. The status label and details table consume it, and the table's status row becomes an exhaustive switch (no more raw status.name leaking to the user).
TransactionDetailsCubit reloads on payjoin events (not just syncs), resolves a resolved session's broadcast txid (local then a forced Result-based lookup) and re-enters by that txid so the screen lands on the real transaction instead of a session placeholder. The manual original-tx broadcast and its button gate on canManuallyBroadcastOriginal, with tightened subscription hygiene (cancel-before-replace, isClosed guards).
Adds a paged carousel of dismissible announcements on the wallet home, shown between the balance header and the wallet list. Ships two compile-time announcements: a payjoin-privacy nudge (once the wallet has transaction history and payjoin is off) and an autoswap-active notice. Each card is tappable to its target (payjoin/autoswap settings) and dismissible via an explicit x with a Read/Dismiss confirmation; the Read option opens the same target as tapping. Dismissals persist in the dismissed_announcements table with a permanent/snooze policy per announcement. The carousel adapts its height to the text scale and collapses to nothing when no announcement is visible. Built on bull_ui (BullInfoCard + PageView) and wired via a thin cubit that re-evaluates on payjoin-setting changes and wallet-sync completion.
fix(send): resolve stuck sender flow on payjoin fallback (#2246)
… settings-fetch stuck-loading gap
feat(receive): payjoin toggle badge in TopBar with eligibility guard
feat(transactions): display payjoin status and land on broadcast tx
feat(announcements): dismissible home announcement carousel
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.
The Jul 21 stacked merges of #2478 #2479 #2480 #2481 landed in their parent branches (pj/00, pj/01) instead of payjoin-upgrade, so that work never reached the integration branch. This PR merges it in.
Verified: analyze clean, 765/765 tests pass, content containment of pj/00-05 proven via git.