diff --git a/damus/Components/ZapButton.swift b/damus/Components/ZapButton.swift index 7cda07ca..6c2428b5 100644 --- a/damus/Components/ZapButton.swift +++ b/damus/Components/ZapButton.swift @@ -54,13 +54,14 @@ struct ZapButton: View { } var zap_color: Color { - switch our_zap { - case .none: + guard let our_zap else { return Color.gray - case .pending: - return Color.yellow - case .zap: + } + + if our_zap.is_paid { return Color.orange + } else { + return Color.yellow } } @@ -260,7 +261,9 @@ func send_zap(damus_state: DamusState, event: NostrEvent, lnurl: String, is_cust return } - pzap_state.state = .postbox_pending(nwc_req) + if pzap_state.update_state(state: .postbox_pending(nwc_req)) { + // we don't need to trigger a ZapsDataModel update here + } case .external(let pending_ext): pending_ext.state = .done let ev = ZappingEvent(is_custom: is_custom, type: .got_zap_invoice(inv), event: event) @@ -285,7 +288,9 @@ func cancel_zap(zap: PendingZap, box: PostBox, zapcache: Zaps, evcache: EventCac switch nwc_state.state { case .fetching_invoice: - nwc_state.state = .cancel_fetching_invoice + if nwc_state.update_state(state: .cancel_fetching_invoice) { + // we don't need to update the ZapsDataModel here + } // let the code that retrieves the invoice remove the zap, because // it still needs access to this pending zap to know to cancel diff --git a/damus/Models/HomeModel.swift b/damus/Models/HomeModel.swift index 6acb7c14..8b13e129 100644 --- a/damus/Models/HomeModel.swift +++ b/damus/Models/HomeModel.swift @@ -143,7 +143,7 @@ class HomeModel: ObservableObject { } if resp.response.error == nil { - nwc_success(zapcache: self.damus_state.zaps, resp: resp) + nwc_success(zapcache: self.damus_state.zaps, evcache: self.damus_state.events, resp: resp) return } diff --git a/damus/Util/EventCache.swift b/damus/Util/EventCache.swift index 24b917cd..4f5e01ab 100644 --- a/damus/Util/EventCache.swift +++ b/damus/Util/EventCache.swift @@ -61,17 +61,21 @@ class ZapsDataModel: ObservableObject { self.zaps = zaps } - func update_state(reqid: String, state: PendingZapState) { + func confirm_nwc(reqid: String) { guard let zap = zaps.first(where: { z in z.request.id == reqid }), - case .pending(let pzap) = zap, - pzap.state != state + case .pending(let pzap) = zap else { return } - pzap.state = state - - self.objectWillChange.send() + switch pzap.state { + case .external: + break + case .nwc(let nwc_state): + if nwc_state.update_state(state: .confirmed) { + self.objectWillChange.send() + } + } } var zap_total: Int64 { diff --git a/damus/Util/WalletConnect.swift b/damus/Util/WalletConnect.swift index 3f6e75ee..67ad920e 100644 --- a/damus/Util/WalletConnect.swift +++ b/damus/Util/WalletConnect.swift @@ -188,7 +188,7 @@ func nwc_pay(url: WalletConnectURL, pool: RelayPool, post: PostBox, invoice: Str } -func nwc_success(zapcache: Zaps, resp: FullWalletResponse) { +func nwc_success(zapcache: Zaps, evcache: EventCache, resp: FullWalletResponse) { // find the pending zap and mark it as pending-confirmed for kv in zapcache.our_zaps { let zaps = kv.value @@ -202,7 +202,10 @@ func nwc_success(zapcache: Zaps, resp: FullWalletResponse) { continue } - nwc_state.state = .confirmed + if nwc_state.update_state(state: .confirmed) { + // notify the zaps model of an update so it can mark them as paid + evcache.get_cache_data(pzap.target.id).zaps_model.objectWillChange.send() + } return } } diff --git a/damus/Util/Zap.swift b/damus/Util/Zap.swift index 29c669d2..dcde92e3 100644 --- a/damus/Util/Zap.swift +++ b/damus/Util/Zap.swift @@ -76,7 +76,7 @@ enum NWCStateType: Equatable { } class NWCPendingZapState: Equatable { - var state: NWCStateType + private(set) var state: NWCStateType let url: WalletConnectURL init(state: NWCStateType, url: WalletConnectURL) { @@ -84,6 +84,15 @@ class NWCPendingZapState: Equatable { self.url = url } + //@discardableResult -- not discardable, the ZapsDataModel may need to send objectWillChange but we don't force it + func update_state(state: NWCStateType) -> Bool { + guard state != self.state else { + return false + } + self.state = state + return true + } + static func == (lhs: NWCPendingZapState, rhs: NWCPendingZapState) -> Bool { return lhs.state == rhs.state && lhs.url == rhs.url } @@ -94,7 +103,7 @@ class PendingZap { let target: ZapTarget let request: ZapRequest let type: ZapType - var state: PendingZapState + private(set) var state: PendingZapState init(amount_msat: Int64, target: ZapTarget, request: ZapRequest, type: ZapType, state: PendingZapState) { self.amount_msat = amount_msat @@ -103,6 +112,17 @@ class PendingZap { self.type = type self.state = state } + + @discardableResult + func update_state(model: ZapsDataModel, state: PendingZapState) -> Bool { + guard self.state != state else { + return false + } + + self.state = state + model.objectWillChange.send() + return true + } } @@ -119,6 +139,23 @@ enum Zapping { } } + var is_paid: Bool { + switch self { + case .zap: + // we have a zap so this is proof of payment + return true + case .pending(let pzap): + switch pzap.state { + case .external: + // It could be but we don't know. We have to wait for a zap to know. + return false + case .nwc(let nwc_state): + // nwc confirmed that we have a payment, but we might not have zap yet + return nwc_state.state == .confirmed + } + } + } + var is_private: Bool { switch self { case .zap(let zap): diff --git a/damus/Views/Events/ZapEvent.swift b/damus/Views/Events/ZapEvent.swift index f92de39c..84b4608e 100644 --- a/damus/Views/Events/ZapEvent.swift +++ b/damus/Views/Events/ZapEvent.swift @@ -26,7 +26,7 @@ struct ZapEvent: View { if zap.is_pending { Image(systemName: "clock.arrow.circlepath") - .foregroundColor(DamusColors.yellow) + .foregroundColor(zap.is_paid ? Color.orange : DamusColors.yellow) .help(NSLocalizedString("Only you can see this message and who sent it.", comment: "Help text on green lock icon that explains that only the current user can see the message of a zap event and who sent the zap.")) } }