Files
damus/damus/Util/Zaps.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

102 lines
2.8 KiB
Swift

//
// Zaps.swift
// damus
//
// Created by William Casarin on 2023-01-16.
//
import Foundation
class Zaps {
private(set) var zaps: [NoteId: Zapping]
let our_pubkey: Pubkey
var our_zaps: [NoteId: [Zapping]]
private(set) var event_counts: [NoteId: Int]
private(set) var event_totals: [NoteId: Int64]
init(our_pubkey: Pubkey) {
self.zaps = [:]
self.our_pubkey = our_pubkey
self.our_zaps = [:]
self.event_counts = [:]
self.event_totals = [:]
}
func remove_zap(reqid: NoteId) -> Zapping? {
var res: Zapping? = nil
for kv in our_zaps {
let ours = kv.value
guard let zap = ours.first(where: { z in z.request.ev.id == reqid }) else {
continue
}
res = zap
our_zaps[kv.key] = ours.filter { z in z.request.ev.id != reqid }
// counts for note zaps
if let note_id = zap.target.note_id {
if let count = event_counts[note_id] {
event_counts[note_id] = count - 1
}
if let total = event_totals[note_id] {
event_totals[note_id] = total - zap.amount
}
}
// we found the request id, we can stop looking
break
}
self.zaps.removeValue(forKey: reqid)
return res
}
func add_zap(zap: Zapping) {
if zaps[zap.request.ev.id] != nil {
return
}
self.zaps[zap.request.ev.id] = zap
if let zap_id = zap.event?.id {
self.zaps[zap_id] = zap
}
// record our zaps for an event
if zap.request.ev.pubkey == our_pubkey {
switch zap.target {
case .note(let note_zap):
let note_id = note_zap.note_id
if our_zaps[note_id] == nil {
our_zaps[note_id] = [zap]
} else {
insert_uniq_sorted_zap_by_amount(zaps: &(our_zaps[note_id]!), new_zap: zap)
}
case .profile:
break
}
}
// don't count tips to self. lame.
guard zap.request.ev.pubkey != zap.target.pubkey else {
return
}
if let note_id = zap.target.note_id {
if event_counts[note_id] == nil {
event_counts[note_id] = 0
}
if event_totals[note_id] == nil {
event_totals[note_id] = 0
}
event_counts[note_id] = event_counts[note_id]! + 1
event_totals[note_id] = event_totals[note_id]! + zap.amount
notify(.update_stats(note_id: note_id))
}
}
}