Fix wallet view hanging on loading placeholder indefinitely

Resolves a race condition in wallet data fetching that caused views to
hang on loading placeholders. The issue occurred due to:

1. Multiple state updates triggering view re-renders mid-fetch
2. Refreshable tasks getting cancelled before completion

Changes:
- Remove premature state reset in refreshWalletInformation()
- Atomically update balance and transactions together after fetching
- Replace onAppear + manual task cancellation with SwiftUI .task modifier
- Simplify refresh flow to use proper async/await without explicit task management

This ensures the wallet view completes data loading in a single atomic
operation, preventing intermediate loading states from persisting.

Closes: https://github.com/damus-io/damus/issues/2999
Changelog-Fixed: Wallet view no longer hangs on loading placeholder
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit is contained in:
Daniel D’Aquino
2026-01-28 18:16:45 -08:00
parent fa4b7a7518
commit c88d881801
2 changed files with 19 additions and 29 deletions

View File

@@ -122,25 +122,20 @@ class WalletModel: ObservableObject {
func refreshWalletInformation() async throws { func refreshWalletInformation() async throws {
await self.resetWalletStateInformation() // Implementation note: Do not reset wallet information here
// This is important to avoid re-rendering the view twice (waste),
// and to avoid refreshable tasks to be cancelled before updating everything
try await loadWalletInformation() try await loadWalletInformation()
} }
func loadWalletInformation() async throws { func loadWalletInformation() async throws {
try await loadBalance() // Implementation note: Get all needed info first, then atomically set the new state.
try await loadTransactionList() // This is important to avoid re-rendering the view twice (waste),
} // and to avoid refreshable tasks to be cancelled before updating everything
func loadBalance() async throws {
let balance = try await fetchBalance() let balance = try await fetchBalance()
DispatchQueue.main.async {
self.balance = balance
}
}
func loadTransactionList() async throws {
let transactions = try await fetchTransactions(from: nil, until: nil, limit: 50, offset: 0, unpaid: false, type: "") let transactions = try await fetchTransactions(from: nil, until: nil, limit: 50, offset: 0, unpaid: false, type: "")
DispatchQueue.main.async { DispatchQueue.main.async {
self.balance = balance
self.transactions = transactions self.transactions = transactions
} }
} }

View File

@@ -16,7 +16,6 @@ struct WalletView: View {
@ObservedObject var model: WalletModel @ObservedObject var model: WalletModel
@ObservedObject var settings: UserSettingsStore @ObservedObject var settings: UserSettingsStore
@State private var showBalance: Bool = false @State private var showBalance: Bool = false
@State private var walletRefreshTask: Task<Void, Never>? = nil
init(damus_state: DamusState, model: WalletModel? = nil) { init(damus_state: DamusState, model: WalletModel? = nil) {
self.damus_state = damus_state self.damus_state = damus_state
@@ -104,11 +103,11 @@ struct WalletView: View {
) )
} }
} }
.onAppear() { .task {
self.refreshWalletInformation() await self.refreshWalletInformation()
} }
.refreshable { .refreshable {
self.refreshWalletInformation() await self.refreshWalletInformation()
} }
.sheet(isPresented: $show_settings, onDismiss: { self.show_settings = false }) { .sheet(isPresented: $show_settings, onDismiss: { self.show_settings = false }) {
ScrollView { ScrollView {
@@ -126,10 +125,7 @@ struct WalletView: View {
} }
} }
@MainActor func refreshWalletInformation() async {
func refreshWalletInformation() {
walletRefreshTask?.cancel()
walletRefreshTask = Task {
do { do {
try await self.model.refreshWalletInformation() try await self.model.refreshWalletInformation()
} }
@@ -142,7 +138,6 @@ struct WalletView: View {
} }
} }
} }
}
@MainActor @MainActor
let test_wallet_connect_url = WalletConnectURL(pubkey: test_pubkey, relay: .init("wss://relay.damus.io")!, keypair: test_damus_state.keypair.to_full()!, lud16: "jb55@sendsats.com") let test_wallet_connect_url = WalletConnectURL(pubkey: test_pubkey, relay: .init("wss://relay.damus.io")!, keypair: test_damus_state.keypair.to_full()!, lud16: "jb55@sendsats.com")