Skip to content

Render transaction details with partial data while full details load #798

Description

@praveenperera

GitHub Issue Draft: Render Transaction Details With Partial Data While Full Details Load

Problem

Opening transaction details currently shows a full-page loading state until transactionDetails(txId) returns. That blocks the whole screen even though the selected wallet screen already has enough transaction summary data to show the main details layout immediately: sent/received state, pending/confirmed state, amount, fiat snapshot, label, and confirmation date or last-seen metadata.

The result is that a tap from the transaction list feels slower than it needs to. The user should land on the normal transaction details screen immediately, with inline loading indicators only for fields that require the full TransactionDetails object.

Current Flow

Android:

  • SelectedWalletScreen renders transactions from manager.loadState.
  • A transaction row pushes Route.TransactionDetails(walletId, txId).
  • TransactionDetailsContainer resolves the wallet manager, checks manager.transactionDetailsCache[txId], and shows FullPageLoadingView() until full details exist.
  • TransactionDetailsScreen requires a full TransactionDetails.

iOS:

  • SelectedWalletScreen renders transactions from manager.loadState.
  • A transaction row navigates to Route.transactionDetails(id: walletId, txId: txId).
  • TransactionsDetailScreen.TransactionDetailsLoader checks manager.transactionDetails[txId] and shows FullPageLoadingView until full details exist.
  • TransactionDetailsView requires a full TransactionDetails.

Rust:

  • Summary transactions come from WalletLoadState.scanning(txns) or WalletLoadState.loaded(txns).
  • Full details are built by RustWalletManager.transactionDetails(txId), which does BDK tx lookup, label DB lookup, fee/address/index/change/RBF derivation, and then returns TransactionDetails.

Data Available Without Loading Full Details

From the selected wallet screen and wallet manager:

  • walletId
  • txId
  • walletMetadata
    • wallet name
    • selected unit
    • fiat/BTC primary-secondary preference
    • sensitive visibility
    • detailsExpanded
    • wallet type
    • network/origin metadata if exposed through WalletMetadata
  • balancePresentation, app theme state, and normal screen chrome inputs
  • loadState
    • loading
    • scanning([Transaction])
    • loaded([Transaction])
  • scanStatus and ledgerState
  • unsignedTransactions
  • cached full details, if already loaded
    • Android: manager.transactionDetailsCache[txId]
    • iOS: manager.transactionDetails[txId]

From a summary Transaction.Confirmed:

  • txid
  • block_height
  • confirmed_at
  • sent_and_received
    • direction: incoming/outgoing
    • amount
  • optional current fiat amount snapshot
  • labels

From a summary Transaction.Unconfirmed:

  • txid
  • last_seen
  • sent_and_received
    • direction: incoming/outgoing
    • amount
  • optional current fiat amount snapshot
  • labels

Data Not Available Until Full Details Load

These should render as inline loading, hidden disabled controls, or placeholders until full details are available:

  • destination/receive address
  • copy-address action
  • fee
  • fee rate, if surfaced
  • sent amount excluding fee
  • input indexes
  • output indexes
  • change address
  • RBF signaling flag
  • full transaction URL if it depends on TransactionDetails.transactionUrl()
  • label edit/save/delete if the label manager still requires full TransactionDetails
  • historical fiat value
  • full current fiat calculation if the summary fiat snapshot is absent
  • confirmation count, because it requires current block height

Proposed UX

Render the normal transaction details screen immediately when a summary transaction is available.

Immediate content:

  • back button and normal details background
  • header icon/rings using summary direction and confirmed/pending state
  • title:
    • confirmed outgoing: Transaction Sent
    • confirmed incoming: Transaction Received
    • unconfirmed: Transaction Pending
  • label display from summary labels
  • confirmed status copy and date from confirmed_at
  • pending status copy from current UI
  • primary amount using sent_and_received.amount() and wallet metadata
  • secondary fiat amount from summary fiat snapshot when available
  • sent/received/sending/receiving capsule
  • show/hide details button

Inline loading content:

  • show small spinner or skeleton text where address would appear
  • show spinner for confirmations until numberOfConfirmations returns
  • show spinner for block number only if summary is unavailable; confirmed summary already has block_height
  • show spinner for fee, recipient receives, total spent fiat, historical fiat
  • disable copy-address until address is loaded
  • disable label editing until full details is loaded, or allow display-only summary label first
  • disable View in Explorer until the URL is available, unless we add a summary-safe URL builder from txid + network

Fallback behavior:

  • If no summary transaction is available and no full details are cached, keep the current full-page loading state.
  • If full details loading fails after the partial screen is shown, keep the summary screen visible and show an inline/banner error with retry instead of replacing everything with an error page.

Data Model Plan

Add a platform-level view model for transaction details data instead of making the UI depend directly on TransactionDetails.

Suggested shape:

TransactionDetailsPresentation
- txId
- direction
- state
- primaryAmount
- secondaryFiatAmount?
- label?
- confirmedAt?
- blockNumber?
- lastSeen?
- fullDetails?
- fullDetailsLoadState
- confirmations?
- confirmationsLoadState
- fiatLoadStates

Build it from:

  1. cached full details, if present
  2. otherwise matching summary transaction from manager.loadState
  3. otherwise no partial data, current full-page loading fallback

Do not initially change the UniFFI Route.TransactionDetails(id, txId) shape unless needed. The least invasive approach is:

  • In the details container, after resolving WalletManager, search manager.loadState and manager.unsignedTransactions for txId.
  • If found, render partial presentation immediately.
  • Continue loading full details in the background.

If this is not reliable enough across route resets or future deep links, add a transient manager cache instead of expanding the Rust route:

  • selectedTransactionSummary: Map<TxId, Transaction> or equivalent
  • populate it on transaction row tap
  • clear/replace opportunistically when transaction lists update

Changing the Rust route to carry a summary object should be a later option because it touches generated bindings and every route call site.

Implementation Steps

  1. Add helper to find summary transaction by txId.

    • Android: helper on WalletManager or local helper near TransactionDetailsContainer.
    • iOS: helper on WalletManager or local helper in TransactionsDetailScreen.
  2. Introduce a details presentation state.

    • Supports summaryOnly, fullDetailsLoaded, loadingFullDetails, and loadFailed.
    • Keeps full details optional.
    • Exposes common fields without requiring UI code to branch everywhere.
  3. Refactor transaction details screen inputs.

    • Current: screen requires TransactionDetails.
    • New: screen accepts presentation state and optional full details.
    • Header/main amount/status use summary fields.
    • Expanded rows read from full details when available.
  4. Replace full-page loading on summary hit.

    • If summary exists, render normal screen immediately.
    • Start manager.transactionDetails(txId) in the background.
    • When it completes, update existing details cache and presentation state.
  5. Keep current full-page loading fallback.

    • If details are not cached and summary is not found, preserve current behavior.
  6. Adjust label behavior.

    • Display summary label immediately.
    • Gate add/edit/delete behind full details unless LabelManager gets an API that can update only the transaction label by txId.
  7. Adjust explorer button behavior.

    • Prefer adding a summary-safe URL builder from txid + wallet network if the network is already available.
    • Otherwise keep the button disabled/loading until full details loads.
  8. Preserve refresh and polling behavior.

    • Pull-to-refresh should request full details and update presentation state.
    • Confirmation polling should only run once there is enough confirmed transaction data.
    • For confirmed summaries, use summary block_height to fetch confirmations even before full details arrives.
  9. Add focused tests.

    • Summary transaction maps to sent/received/pending title and amount correctly.
    • Details container renders partial state when cache miss plus summary hit.
    • Details container falls back to full-page loading when cache miss plus summary miss.
    • Full details replaces partial placeholders when loaded.
    • Failed full-details load keeps partial content and exposes retry/error state.

Acceptance Criteria

  • Tapping a transaction with an available summary shows the normal details screen immediately.
  • Sent/received/pending title, amount, label display, and status capsule are visible before full details load.
  • Fields that require full details show inline loading placeholders and fill in when data arrives.
  • No full-page loading screen is shown for transaction-list taps when the summary is available.
  • Current behavior is preserved for direct/cold details routes where no summary or cached full details exists.
  • Label editing does not operate on incomplete data.
  • Android and iOS follow the same state model and visual behavior.

Open Questions

  • Should View in Explorer be enabled from summary data by adding a txid + network URL helper, or wait for full details?
  • Should label editing get a lighter API for transaction-label-only updates, or remain disabled until full details loads?
  • Should the summary be looked up from loadState only, or should transaction row taps write a transient selected-summary cache for more reliable navigation transitions?
  • Should unsignedTransactions participate in this partial-details flow, or should this issue only cover broadcast transactions?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions