Files
damus/damus/Util/WalletConnect+.swift
Daniel D’Aquino 003482c971 Implement zap notification support for push notifications
The code paths for generating zap notifications were very different from
the paths used by most other notifications. In this commit, I include
the logic and data structures necessary for formatting zap notifications
in the same fashion as local notifications.

A good amount of refactoring and moving functions/structures around was
necessary to reuse zap local notification logic. I also attempted to
make the notification generation process more consistent between zaps
and other notifications, without changing too much of existing logic to
avoid even more regression risk.

General push notifications + local notifications test
-----------------------------------------------------

PASS

Device: iPhone 15 Pro simulator
iOS: 17.0.1
Damus: This commit
Setup:
- Two phones running Damus on different accounts
- Local relay with strfry-push-notify test setup
- Apple push notification test tool

Coverage:
1. Mention notifications
2. DM notifications
3. Reaction notifications
4. Repost notifications

Steps for each notification type:
1. Trigger a notification (local and then push)
2. Ensure that the notification is received on the other device
3. Ensure that the notification is formatted correctly
4. Ensure that DMs are decrypted correctly
5. Ensure that profile names are unfurled correctly
6. Click on the notification and ensure that the app opens to the correct screen

Result: PASS (all notifications received and formatted correctly)

Notes:
- For some reason my relay is not receiving zap events, so I could not
  test zap notifications yet.

- Reply notifications do not seem to be implemented yet

- These apply to the tests below as well

Changelog-Added: Zap notification support for push notifications
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Signed-off-by: William Casarin <jb55@jb55.com>
2024-01-10 11:06:32 -08:00

119 lines
4.2 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// WalletConnect+.swift
// damus
//
// Created by Daniel DAquino on 2023-11-27.
//
import Foundation
func make_wallet_pay_invoice_request(invoice: String) -> WalletRequest<PayInvoiceRequest> {
let data = PayInvoiceRequest(invoice: invoice)
return WalletRequest(method: "pay_invoice", params: data)
}
func make_wallet_balance_request() -> WalletRequest<EmptyRequest> {
return WalletRequest(method: "get_balance", params: nil)
}
struct EmptyRequest: Codable {
}
struct PayInvoiceRequest: Codable {
let invoice: String
}
func make_wallet_connect_request<T>(req: WalletRequest<T>, to_pk: Pubkey, keypair: FullKeypair) -> NostrEvent? {
let tags = [to_pk.tag]
let created_at = UInt32(Date().timeIntervalSince1970)
guard let content = encode_json(req) else {
return nil
}
return create_encrypted_event(content, to_pk: to_pk, tags: tags, keypair: keypair, created_at: created_at, kind: 23194)
}
func subscribe_to_nwc(url: WalletConnectURL, pool: RelayPool) {
var filter = NostrFilter(kinds: [.nwc_response])
filter.authors = [url.pubkey]
filter.limit = 0
let sub = NostrSubscribe(filters: [filter], sub_id: "nwc")
pool.send(.subscribe(sub), to: [url.relay.id], skip_ephemeral: false)
}
@discardableResult
func nwc_pay(url: WalletConnectURL, pool: RelayPool, post: PostBox, invoice: String, delay: TimeInterval? = 5.0, on_flush: OnFlush? = nil) -> NostrEvent? {
let req = make_wallet_pay_invoice_request(invoice: invoice)
guard let ev = make_wallet_connect_request(req: req, to_pk: url.pubkey, keypair: url.keypair) else {
return nil
}
try? pool.add_relay(.nwc(url: url.relay))
subscribe_to_nwc(url: url, pool: pool)
post.send(ev, to: [url.relay.id], skip_ephemeral: false, delay: delay, on_flush: on_flush)
return ev
}
func nwc_success(state: DamusState, resp: FullWalletResponse) {
// find the pending zap and mark it as pending-confirmed
for kv in state.zaps.our_zaps {
let zaps = kv.value
for zap in zaps {
guard case .pending(let pzap) = zap,
case .nwc(let nwc_state) = pzap.state,
case .postbox_pending(let nwc_req) = nwc_state.state,
nwc_req.id == resp.req_id
else {
continue
}
if nwc_state.update_state(state: .confirmed) {
// notify the zaps model of an update so it can mark them as paid
state.events.get_cache_data(NoteId(pzap.target.id)).zaps_model.objectWillChange.send()
print("NWC success confirmed")
}
return
}
}
}
func send_donation_zap(pool: RelayPool, postbox: PostBox, nwc: WalletConnectURL, percent: Int, base_msats: Int64) async {
let percent_f = Double(percent) / 100.0
let donations_msats = Int64(percent_f * Double(base_msats))
let payreq = LNUrlPayRequest(allowsNostr: true, commentAllowed: nil, nostrPubkey: "", callback: "https://sendsats.lol/@damus")
guard let invoice = await fetch_zap_invoice(payreq, zapreq: nil, msats: donations_msats, zap_type: .non_zap, comment: nil) else {
// we failed... oh well. no donation for us.
print("damus-donation failed to fetch invoice")
return
}
print("damus-donation donating...")
nwc_pay(url: nwc, pool: pool, post: postbox, invoice: invoice, delay: nil)
}
func nwc_error(zapcache: Zaps, evcache: EventCache, resp: FullWalletResponse) {
// find a pending zap with the nwc request id associated with this response and remove it
for kv in zapcache.our_zaps {
let zaps = kv.value
for zap in zaps {
guard case .pending(let pzap) = zap,
case .nwc(let nwc_state) = pzap.state,
case .postbox_pending(let req) = nwc_state.state,
req.id == resp.req_id
else {
continue
}
// remove the pending zap if there was an error
let reqid = ZapRequestId(from_pending: pzap)
remove_zap(reqid: reqid, zapcache: zapcache, evcache: evcache)
return
}
}
}