Fix issue with wallet loading
Changelog-Changed: Increased transaction list limit to 50 transactions Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit is contained in:
@@ -85,8 +85,8 @@ class NostrNetworkManager {
|
|||||||
self.pool.send_raw_to_local_ndb(.typical(.event(event)))
|
self.pool.send_raw_to_local_ndb(.typical(.event(event)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func send(event: NostrEvent) {
|
func send(event: NostrEvent, to targetRelays: [RelayURL]? = nil, skipEphemeralRelays: Bool = true) {
|
||||||
self.pool.send(.event(event))
|
self.pool.send(.event(event), to: targetRelays, skip_ephemeral: skipEphemeralRelays)
|
||||||
}
|
}
|
||||||
|
|
||||||
func query(filters: [NostrFilter], to: [RelayURL]? = nil) async -> [NostrEvent] {
|
func query(filters: [NostrFilter], to: [RelayURL]? = nil) async -> [NostrEvent] {
|
||||||
@@ -208,14 +208,6 @@ class NostrNetworkManager {
|
|||||||
WalletConnect.pay(url: url, pool: self.pool, post: post, invoice: invoice, zap_request: nil)
|
WalletConnect.pay(url: url, pool: self.pool, post: post, invoice: invoice, zap_request: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func requestTransactionList(url: WalletConnectURL, delay: TimeInterval? = 0.0, on_flush: OnFlush? = nil) {
|
|
||||||
WalletConnect.request_transaction_list(url: url, pool: self.pool, post: self.postbox, delay: delay, on_flush: on_flush)
|
|
||||||
}
|
|
||||||
|
|
||||||
func requestBalanceInformation(url: WalletConnectURL, delay: TimeInterval? = 0.0, on_flush: OnFlush? = nil) {
|
|
||||||
WalletConnect.request_balance_information(url: url, pool: self.pool, post: self.postbox, delay: delay, on_flush: on_flush)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Send a donation zap to the Damus team
|
/// Send a donation zap to the Damus team
|
||||||
func send_donation_zap(nwc: WalletConnectURL, percent: Int, base_msats: Int64) async {
|
func send_donation_zap(nwc: WalletConnectURL, percent: Int, base_msats: Int64) async {
|
||||||
let percent_f = Double(percent) / 100.0
|
let percent_f = Double(percent) / 100.0
|
||||||
|
|||||||
@@ -25,6 +25,28 @@ extension NostrNetworkManager {
|
|||||||
|
|
||||||
// MARK: - Reading data from Nostr
|
// MARK: - Reading data from Nostr
|
||||||
|
|
||||||
|
/// Subscribes to data from user's relays, for a maximum period of time — after which the stream will end.
|
||||||
|
///
|
||||||
|
/// This is useful when waiting for some specific data from Nostr, but not indefinitely.
|
||||||
|
func subscribe(filters: [NostrFilter], to desiredRelays: [RelayURL]? = nil, timeout: Duration) -> AsyncStream<StreamItem> {
|
||||||
|
return AsyncStream<StreamItem> { continuation in
|
||||||
|
let streamingTask = Task {
|
||||||
|
for await item in self.subscribe(filters: filters, to: desiredRelays) {
|
||||||
|
try Task.checkCancellation()
|
||||||
|
continuation.yield(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let timeoutTask = Task {
|
||||||
|
try await Task.sleep(for: timeout)
|
||||||
|
continuation.finish() // End the stream due to timeout.
|
||||||
|
}
|
||||||
|
continuation.onTermination = { @Sendable _ in
|
||||||
|
timeoutTask.cancel()
|
||||||
|
streamingTask.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Subscribes to data from the user's relays
|
/// Subscribes to data from the user's relays
|
||||||
///
|
///
|
||||||
/// ## Implementation notes
|
/// ## Implementation notes
|
||||||
@@ -112,10 +134,16 @@ extension NostrNetworkManager {
|
|||||||
}
|
}
|
||||||
let streamTask = Task {
|
let streamTask = Task {
|
||||||
do {
|
do {
|
||||||
for await _ in self.pool.subscribe(filters: filters, to: desiredRelays) {
|
for await item in self.pool.subscribe(filters: filters, to: desiredRelays) {
|
||||||
// NO-OP. Notes will be automatically ingested by NostrDB
|
// NO-OP. Notes will be automatically ingested by NostrDB
|
||||||
// TODO: Improve efficiency of subscriptions?
|
// TODO: Improve efficiency of subscriptions?
|
||||||
try Task.checkCancellation()
|
try Task.checkCancellation()
|
||||||
|
switch item {
|
||||||
|
case .event(let event):
|
||||||
|
Log.debug("Session subscribe: Received kind %d event with id %s from the network", for: .subscription_manager, event.kind, event.id.hex())
|
||||||
|
case .eose:
|
||||||
|
Log.debug("Session subscribe: Received EOSE from the network", for: .subscription_manager)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
|
|||||||
@@ -207,7 +207,10 @@ class RelayPool {
|
|||||||
func subscribe(sub_id: String, filters: [NostrFilter], handler: @escaping (RelayURL, NostrConnectionEvent) -> (), to: [RelayURL]? = nil) {
|
func subscribe(sub_id: String, filters: [NostrFilter], handler: @escaping (RelayURL, NostrConnectionEvent) -> (), to: [RelayURL]? = nil) {
|
||||||
Task {
|
Task {
|
||||||
await register_handler(sub_id: sub_id, handler: handler)
|
await register_handler(sub_id: sub_id, handler: handler)
|
||||||
send(.subscribe(.init(filters: filters, sub_id: sub_id)), to: to)
|
// When the caller specifies no relays, it is implied that the user wants to use the ones in the user relay list. Skip ephemeral relays in that case.
|
||||||
|
// When the caller specifies specific relays, do not skip ephemeral relays to respect the exact list given by the caller.
|
||||||
|
let shouldSkipEphemeralRelays = to == nil ? true : false
|
||||||
|
send(.subscribe(.init(filters: filters, sub_id: sub_id)), to: to, skip_ephemeral: shouldSkipEphemeralRelays)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,9 @@ class DamusState: HeadlessDamusState {
|
|||||||
self.favicon_cache = FaviconCache()
|
self.favicon_cache = FaviconCache()
|
||||||
|
|
||||||
let networkManagerDelegate = NostrNetworkManagerDelegate(settings: settings, contacts: contacts, ndb: ndb, keypair: keypair, relayModelCache: relay_model_cache, relayFilters: relay_filters)
|
let networkManagerDelegate = NostrNetworkManagerDelegate(settings: settings, contacts: contacts, ndb: ndb, keypair: keypair, relayModelCache: relay_model_cache, relayFilters: relay_filters)
|
||||||
self.nostrNetwork = NostrNetworkManager(delegate: networkManagerDelegate)
|
let nostrNetwork = NostrNetworkManager(delegate: networkManagerDelegate)
|
||||||
|
self.nostrNetwork = nostrNetwork
|
||||||
|
self.wallet.nostrNetwork = nostrNetwork
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
@@ -122,7 +124,7 @@ class DamusState: HeadlessDamusState {
|
|||||||
events: EventCache(ndb: ndb),
|
events: EventCache(ndb: ndb),
|
||||||
bookmarks: BookmarksManager(pubkey: pubkey),
|
bookmarks: BookmarksManager(pubkey: pubkey),
|
||||||
replies: ReplyCounter(our_pubkey: pubkey),
|
replies: ReplyCounter(our_pubkey: pubkey),
|
||||||
wallet: WalletModel(settings: settings),
|
wallet: WalletModel(settings: settings), // nostrNetwork is connected after initialization
|
||||||
nav: navigationCoordinator,
|
nav: navigationCoordinator,
|
||||||
music: MusicController(onChange: { _ in }),
|
music: MusicController(onChange: { _ in }),
|
||||||
video: DamusVideoCoordinator(),
|
video: DamusVideoCoordinator(),
|
||||||
|
|||||||
@@ -54,80 +54,6 @@ extension WalletConnect {
|
|||||||
return ev
|
return ev
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends out a wallet balance request to the NWC relay, and ensures that:
|
|
||||||
/// 1. the NWC relay is connected and we are listening to NWC events
|
|
||||||
/// 2. the NWC relay is connected and we are listening to NWC
|
|
||||||
///
|
|
||||||
/// Note: This does not return the actual balance information. The actual balance is handled elsewhere around `HomeModel` and `WalletModel`
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - url: The NWC wallet connection URL
|
|
||||||
/// - pool: The relay pool to connect to
|
|
||||||
/// - post: The postbox to send events in
|
|
||||||
/// - delay: The delay before actually sending the request to the network
|
|
||||||
/// - on_flush: A callback to call after the event has been flushed to the network
|
|
||||||
/// - Returns: The Nostr Event that was sent to the network, representing the request that was made
|
|
||||||
@discardableResult
|
|
||||||
static func request_balance_information(url: WalletConnectURL, pool: RelayPool, post: PostBox, delay: TimeInterval? = 0.0, on_flush: OnFlush? = nil) -> NostrEvent? {
|
|
||||||
let req = WalletConnect.Request.getBalance
|
|
||||||
guard let ev = req.to_nostr_event(to_pk: url.pubkey, keypair: url.keypair) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
try? pool.add_relay(.nwc(url: url.relay)) // Ensure the NWC relay is connected
|
|
||||||
WalletConnect.subscribe(url: url, pool: pool) // Ensure we are listening to NWC updates from the relay
|
|
||||||
post.send(ev, to: [url.relay], skip_ephemeral: false, delay: delay, on_flush: on_flush)
|
|
||||||
return ev
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends out a wallet transaction list request to the NWC relay, and ensures that:
|
|
||||||
/// 1. the NWC relay is connected and we are listening to NWC events
|
|
||||||
/// 2. the NWC relay is connected and we are listening to NWC
|
|
||||||
///
|
|
||||||
/// Note: This does not return the actual transaction list. The actual transaction list is handled elsewhere around `HomeModel` and `WalletModel`
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - url: The NWC wallet connection URL
|
|
||||||
/// - pool: The relay pool to connect to
|
|
||||||
/// - post: The postbox to send events in
|
|
||||||
/// - delay: The delay before actually sending the request to the network
|
|
||||||
/// - on_flush: A callback to call after the event has been flushed to the network
|
|
||||||
/// - Returns: The Nostr Event that was sent to the network, representing the request that was made
|
|
||||||
@discardableResult
|
|
||||||
static func request_transaction_list(url: WalletConnectURL, pool: RelayPool, post: PostBox, delay: TimeInterval? = 0.0, on_flush: OnFlush? = nil) -> NostrEvent? {
|
|
||||||
let req = WalletConnect.Request.getTransactionList(from: nil, until: nil, limit: 10, offset: 0, unpaid: false, type: "")
|
|
||||||
guard let ev = req.to_nostr_event(to_pk: url.pubkey, keypair: url.keypair) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
try? pool.add_relay(.nwc(url: url.relay)) // Ensure the NWC relay is connected
|
|
||||||
WalletConnect.subscribe(url: url, pool: pool) // Ensure we are listening to NWC updates from the relay
|
|
||||||
post.send(ev, to: [url.relay], skip_ephemeral: false, delay: delay, on_flush: on_flush)
|
|
||||||
return ev
|
|
||||||
}
|
|
||||||
|
|
||||||
@MainActor
|
|
||||||
static func refresh_wallet_information(damus_state: DamusState) async {
|
|
||||||
damus_state.wallet.resetWalletStateInformation()
|
|
||||||
await Self.update_wallet_information(damus_state: damus_state)
|
|
||||||
}
|
|
||||||
|
|
||||||
@MainActor
|
|
||||||
static func update_wallet_information(damus_state: DamusState) async {
|
|
||||||
guard let url = damus_state.settings.nostr_wallet_connect,
|
|
||||||
let nwc = WalletConnectURL(str: url) else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let flusher: OnFlush? = nil
|
|
||||||
|
|
||||||
let delay = 0.0 // We don't need a delay when fetching a transaction list or balance
|
|
||||||
|
|
||||||
damus_state.nostrNetwork.requestTransactionList(url: nwc, delay: delay, on_flush: flusher)
|
|
||||||
damus_state.nostrNetwork.requestBalanceInformation(url: nwc, delay: delay, on_flush: flusher)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
static func handle_zap_success(state: DamusState, resp: WalletConnect.FullWalletResponse) {
|
static func handle_zap_success(state: DamusState, resp: WalletConnect.FullWalletResponse) {
|
||||||
// find the pending zap and mark it as pending-confirmed
|
// find the pending zap and mark it as pending-confirmed
|
||||||
for kv in state.zaps.our_zaps {
|
for kv in state.zaps.our_zaps {
|
||||||
|
|||||||
@@ -11,11 +11,24 @@ enum WalletConnectState {
|
|||||||
case new(WalletConnectURL)
|
case new(WalletConnectURL)
|
||||||
case existing(WalletConnectURL)
|
case existing(WalletConnectURL)
|
||||||
case none
|
case none
|
||||||
|
|
||||||
|
/// Gets the currently connected NWC URL
|
||||||
|
func currentNwcUrl() -> WalletConnectURL? {
|
||||||
|
switch self {
|
||||||
|
case .new:
|
||||||
|
return nil // User has not confirmed they want to use this yet, so we cannot call it "current"
|
||||||
|
case .existing(let nwcUrl):
|
||||||
|
return nwcUrl
|
||||||
|
case .none:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Models and manages the user's NWC wallet based on the app's settings
|
/// Models and manages the user's NWC wallet based on the app's settings
|
||||||
class WalletModel: ObservableObject {
|
class WalletModel: ObservableObject {
|
||||||
var settings: UserSettingsStore
|
var settings: UserSettingsStore
|
||||||
|
var nostrNetwork: NostrNetworkManager? = nil
|
||||||
private(set) var previous_state: WalletConnectState
|
private(set) var previous_state: WalletConnectState
|
||||||
var initial_percent: Int
|
var initial_percent: Int
|
||||||
/// The wallet's balance, in sats.
|
/// The wallet's balance, in sats.
|
||||||
@@ -37,6 +50,7 @@ class WalletModel: ObservableObject {
|
|||||||
self.previous_state = .none
|
self.previous_state = .none
|
||||||
self.settings = settings
|
self.settings = settings
|
||||||
self.initial_percent = settings.donation_percent
|
self.initial_percent = settings.donation_percent
|
||||||
|
self.nostrNetwork = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
init(settings: UserSettingsStore) {
|
init(settings: UserSettingsStore) {
|
||||||
@@ -50,6 +64,7 @@ class WalletModel: ObservableObject {
|
|||||||
self.connect_state = .none
|
self.connect_state = .none
|
||||||
}
|
}
|
||||||
self.initial_percent = settings.donation_percent
|
self.initial_percent = settings.donation_percent
|
||||||
|
self.nostrNetwork = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func cancel() {
|
func cancel() {
|
||||||
@@ -96,12 +111,114 @@ class WalletModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - Wallet internal state lifecycle functions
|
||||||
|
|
||||||
|
@MainActor
|
||||||
func resetWalletStateInformation() {
|
func resetWalletStateInformation() {
|
||||||
self.transactions = nil
|
self.transactions = nil
|
||||||
self.balance = nil
|
self.balance = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func refreshWalletInformation() async throws {
|
||||||
|
await self.resetWalletStateInformation()
|
||||||
|
try await loadWalletInformation()
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadWalletInformation() async throws {
|
||||||
|
try await loadBalance()
|
||||||
|
try await loadTransactionList()
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadBalance() async throws {
|
||||||
|
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: "")
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.transactions = transactions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Easy wallet info fetching interface
|
||||||
|
|
||||||
|
func fetchTransactions(from: UInt64?, until: UInt64?, limit: Int?, offset: Int?, unpaid: Bool?, type: String?) async throws -> [WalletConnect.Transaction] {
|
||||||
|
let response = try await self.request(.getTransactionList(from: from, until: until, limit: limit, offset: offset, unpaid: unpaid, type: type))
|
||||||
|
guard case .list_transactions(let transactionResponse) = response else { throw FetchError.responseMismatch }
|
||||||
|
return transactionResponse.transactions
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Fetches the balance amount from the network and returns the amount in sats
|
||||||
|
func fetchBalance() async throws -> Int64 {
|
||||||
|
let response = try await self.request(.getBalance)
|
||||||
|
guard case .get_balance(let balanceResponse) = response else { throw FetchError.responseMismatch }
|
||||||
|
return balanceResponse.balance / 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
enum FetchError: Error {
|
||||||
|
case responseMismatch
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Easy request/response interface
|
||||||
|
|
||||||
|
func request(_ request: WalletConnect.Request, timeout: Duration = .seconds(10)) async throws(WalletRequestError) -> WalletConnect.Response.Result {
|
||||||
|
guard let nostrNetwork else { throw .notConnectedToTheNostrNetwork }
|
||||||
|
guard let currentNwcUrl = self.connect_state.currentNwcUrl() else { throw .noConnectedWallet }
|
||||||
|
guard let requestEvent = request.to_nostr_event(to_pk: currentNwcUrl.pubkey, keypair: currentNwcUrl.keypair) else { throw .errorFormattingRequest }
|
||||||
|
|
||||||
|
let responseFilters = [
|
||||||
|
NostrFilter(
|
||||||
|
kinds: [.nwc_response],
|
||||||
|
referenced_ids: [requestEvent.id],
|
||||||
|
pubkeys: [currentNwcUrl.keypair.pubkey],
|
||||||
|
authors: [currentNwcUrl.pubkey]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
nostrNetwork.send(event: requestEvent, to: [currentNwcUrl.relay], skipEphemeralRelays: false)
|
||||||
|
for await item in nostrNetwork.reader.subscribe(filters: responseFilters, to: [currentNwcUrl.relay], timeout: timeout) {
|
||||||
|
switch item {
|
||||||
|
case .event(borrow: let borrow):
|
||||||
|
var responseEvent: NostrEvent? = nil
|
||||||
|
try? borrow { ev in responseEvent = ev.toOwned() }
|
||||||
|
guard let responseEvent else { throw .internalError }
|
||||||
|
|
||||||
|
let fullWalletResponse: WalletConnect.FullWalletResponse
|
||||||
|
do { fullWalletResponse = try WalletConnect.FullWalletResponse(from: responseEvent, nwc: currentNwcUrl) }
|
||||||
|
catch { throw WalletRequestError.walletResponseDecodingError(error) }
|
||||||
|
|
||||||
|
guard fullWalletResponse.req_id == requestEvent.id else { continue } // Our filters may match other responses
|
||||||
|
if let responseError = fullWalletResponse.response.error { throw .walletResponseError(responseError) }
|
||||||
|
|
||||||
|
guard let result = fullWalletResponse.response.result else { throw .walletEmptyResponse }
|
||||||
|
return result
|
||||||
|
case .eose:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
do { try Task.checkCancellation() } catch { throw .cancelled }
|
||||||
|
throw .responseTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
enum WalletRequestError: Error {
|
||||||
|
case notConnectedToTheNostrNetwork
|
||||||
|
case noConnectedWallet
|
||||||
|
case errorFormattingRequest
|
||||||
|
case internalError
|
||||||
|
case walletResponseDecodingError(WalletConnect.FullWalletResponse.InitializationError)
|
||||||
|
case walletResponseMismatch
|
||||||
|
case walletResponseError(WalletConnect.WalletResponseErr)
|
||||||
|
case walletEmptyResponse
|
||||||
|
case responseTimeout
|
||||||
|
case cancelled
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Async wallet response waiting mechanism
|
// MARK: - Async wallet response waiting mechanism
|
||||||
|
|
||||||
func waitForResponse(for requestId: NoteId, timeout: Duration = .seconds(10)) async throws -> WalletConnect.Response.Result {
|
func waitForResponse(for requestId: NoteId, timeout: Duration = .seconds(10)) async throws -> WalletConnect.Response.Result {
|
||||||
|
|||||||
@@ -45,11 +45,11 @@ struct SendPaymentView: View {
|
|||||||
break
|
break
|
||||||
case .completed:
|
case .completed:
|
||||||
// Refresh wallet to reflect new balance after payment
|
// Refresh wallet to reflect new balance after payment
|
||||||
Task { await WalletConnect.refresh_wallet_information(damus_state: damus_state) }
|
Task { try await model.refreshWalletInformation() }
|
||||||
case .failed:
|
case .failed:
|
||||||
// Even when a wallet says it has failed, update balance just in case it is a false negative,
|
// Even when a wallet says it has failed, update balance just in case it is a false negative,
|
||||||
// This might prevent the user from accidentally sending a payment twice in case of a bug.
|
// This might prevent the user from accidentally sending a payment twice in case of a bug.
|
||||||
Task { await WalletConnect.refresh_wallet_information(damus_state: damus_state) }
|
Task { try await model.refreshWalletInformation() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ 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 +105,10 @@ struct WalletView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onAppear() {
|
.onAppear() {
|
||||||
Task { await self.updateWalletInformation() }
|
self.refreshWalletInformation()
|
||||||
}
|
}
|
||||||
.refreshable {
|
.refreshable {
|
||||||
model.resetWalletStateInformation()
|
self.refreshWalletInformation()
|
||||||
await self.updateWalletInformation()
|
|
||||||
}
|
}
|
||||||
.sheet(isPresented: $show_settings, onDismiss: { self.show_settings = false }) {
|
.sheet(isPresented: $show_settings, onDismiss: { self.show_settings = false }) {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
@@ -127,8 +127,20 @@ struct WalletView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
func updateWalletInformation() async {
|
func refreshWalletInformation() {
|
||||||
await WalletConnect.update_wallet_information(damus_state: damus_state)
|
walletRefreshTask?.cancel()
|
||||||
|
walletRefreshTask = Task {
|
||||||
|
do {
|
||||||
|
try await self.model.refreshWalletInformation()
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
guard let error = error as? ErrorView.UserPresentableErrorProtocol else {
|
||||||
|
Log.error("Error while refreshing wallet: %s", for: .nwc, error.localizedDescription)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
present_sheet(.error(error.userPresentableError))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -140,6 +140,11 @@ struct ErrorView: View {
|
|||||||
let technical_info: String?
|
let technical_info: String?
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An error that can be displayed to the user, and can be sent to the Developers as well.
|
||||||
|
protocol UserPresentableErrorProtocol: Error {
|
||||||
|
var userPresentableError: UserPresentableError { get }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user