nwc: turn pending zap orange when we have a NWC success

Orange means payment successful now, not just presence of zap

This introduces a paid pending state, which shows up as an orange timer
thing in the zaps view. This can be useful if the zap is never sent. We
don't want the user to think the payment didn't go through.
This commit is contained in:
William Casarin
2023-05-13 23:28:07 -07:00
parent 03691d0369
commit 69fc6694f1
6 changed files with 68 additions and 19 deletions

View File

@@ -54,13 +54,14 @@ struct ZapButton: View {
} }
var zap_color: Color { var zap_color: Color {
switch our_zap { guard let our_zap else {
case .none:
return Color.gray return Color.gray
case .pending: }
return Color.yellow
case .zap: if our_zap.is_paid {
return Color.orange return Color.orange
} else {
return Color.yellow
} }
} }
@@ -260,7 +261,9 @@ func send_zap(damus_state: DamusState, event: NostrEvent, lnurl: String, is_cust
return 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): case .external(let pending_ext):
pending_ext.state = .done pending_ext.state = .done
let ev = ZappingEvent(is_custom: is_custom, type: .got_zap_invoice(inv), event: event) 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 { switch nwc_state.state {
case .fetching_invoice: 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 // let the code that retrieves the invoice remove the zap, because
// it still needs access to this pending zap to know to cancel // it still needs access to this pending zap to know to cancel

View File

@@ -143,7 +143,7 @@ class HomeModel: ObservableObject {
} }
if resp.response.error == nil { 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 return
} }

View File

@@ -61,17 +61,21 @@ class ZapsDataModel: ObservableObject {
self.zaps = zaps 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 }), guard let zap = zaps.first(where: { z in z.request.id == reqid }),
case .pending(let pzap) = zap, case .pending(let pzap) = zap
pzap.state != state
else { else {
return return
} }
pzap.state = state switch pzap.state {
case .external:
self.objectWillChange.send() break
case .nwc(let nwc_state):
if nwc_state.update_state(state: .confirmed) {
self.objectWillChange.send()
}
}
} }
var zap_total: Int64 { var zap_total: Int64 {

View File

@@ -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 // find the pending zap and mark it as pending-confirmed
for kv in zapcache.our_zaps { for kv in zapcache.our_zaps {
let zaps = kv.value let zaps = kv.value
@@ -202,7 +202,10 @@ func nwc_success(zapcache: Zaps, resp: FullWalletResponse) {
continue 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 return
} }
} }

View File

@@ -76,7 +76,7 @@ enum NWCStateType: Equatable {
} }
class NWCPendingZapState: Equatable { class NWCPendingZapState: Equatable {
var state: NWCStateType private(set) var state: NWCStateType
let url: WalletConnectURL let url: WalletConnectURL
init(state: NWCStateType, url: WalletConnectURL) { init(state: NWCStateType, url: WalletConnectURL) {
@@ -84,6 +84,15 @@ class NWCPendingZapState: Equatable {
self.url = url 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 { static func == (lhs: NWCPendingZapState, rhs: NWCPendingZapState) -> Bool {
return lhs.state == rhs.state && lhs.url == rhs.url return lhs.state == rhs.state && lhs.url == rhs.url
} }
@@ -94,7 +103,7 @@ class PendingZap {
let target: ZapTarget let target: ZapTarget
let request: ZapRequest let request: ZapRequest
let type: ZapType let type: ZapType
var state: PendingZapState private(set) var state: PendingZapState
init(amount_msat: Int64, target: ZapTarget, request: ZapRequest, type: ZapType, state: PendingZapState) { init(amount_msat: Int64, target: ZapTarget, request: ZapRequest, type: ZapType, state: PendingZapState) {
self.amount_msat = amount_msat self.amount_msat = amount_msat
@@ -103,6 +112,17 @@ class PendingZap {
self.type = type self.type = type
self.state = state 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 { var is_private: Bool {
switch self { switch self {
case .zap(let zap): case .zap(let zap):

View File

@@ -26,7 +26,7 @@ struct ZapEvent: View {
if zap.is_pending { if zap.is_pending {
Image(systemName: "clock.arrow.circlepath") 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.")) .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."))
} }
} }