Files
damus/damus/Models/ProfileModel.swift
kernelkind 7cc2825d89 nip19: fix shared nevents that are too long
There comes a point when the sharing a Nip19 mention becomes unwieldy
when there is too much TLV data. This patch features a naive approach to
making sure the relay portion of the TLV data doesn't contribute too
much data to the mention.

It adds a maximum number of relays that should be shared in the mention,
right now it is set to four.

Changelog-Fixed: Fix shared nevents that are too long
Lightning-address: kernelkind@getalby.com
Signed-off-by: kernelkind <kernelkind@gmail.com>
Reviewed-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2024-01-30 10:27:12 -08:00

183 lines
5.5 KiB
Swift

//
// ProfileModel.swift
// damus
//
// Created by William Casarin on 2022-04-27.
//
import Foundation
class ProfileModel: ObservableObject, Equatable {
@Published var contacts: NostrEvent? = nil
@Published var following: Int = 0
@Published var relays: [String: RelayInfo]? = nil
@Published var progress: Int = 0
private let MAX_SHARE_RELAYS = 4
var events: EventHolder
let pubkey: Pubkey
let damus: DamusState
var seen_event: Set<NoteId> = Set()
var sub_id = UUID().description
var prof_subid = UUID().description
var findRelay_subid = UUID().description
init(pubkey: Pubkey, damus: DamusState) {
self.pubkey = pubkey
self.damus = damus
self.events = EventHolder(on_queue: { ev in
preload_events(state: damus, events: [ev])
})
}
func follows(pubkey: Pubkey) -> Bool {
guard let contacts = self.contacts else {
return false
}
return contacts.referenced_pubkeys.contains(pubkey)
}
func get_follow_target() -> FollowTarget {
if let contacts = contacts {
return .contact(contacts)
}
return .pubkey(pubkey)
}
static func == (lhs: ProfileModel, rhs: ProfileModel) -> Bool {
return lhs.pubkey == rhs.pubkey
}
func hash(into hasher: inout Hasher) {
hasher.combine(pubkey)
}
func unsubscribe() {
print("unsubscribing from profile \(pubkey) with sub_id \(sub_id)")
damus.pool.unsubscribe(sub_id: sub_id)
damus.pool.unsubscribe(sub_id: prof_subid)
}
func subscribe() {
var text_filter = NostrFilter(kinds: [.text, .longform])
var profile_filter = NostrFilter(kinds: [.contacts, .metadata, .boost])
profile_filter.authors = [pubkey]
text_filter.authors = [pubkey]
text_filter.limit = 500
print("subscribing to profile \(pubkey) with sub_id \(sub_id)")
//print_filters(relay_id: "profile", filters: [[text_filter], [profile_filter]])
damus.pool.subscribe(sub_id: sub_id, filters: [text_filter], handler: handle_event)
damus.pool.subscribe(sub_id: prof_subid, filters: [profile_filter], handler: handle_event)
}
func handle_profile_contact_event(_ ev: NostrEvent) {
process_contact_event(state: damus, ev: ev)
// only use new stuff
if let current_ev = self.contacts {
guard ev.created_at > current_ev.created_at else {
return
}
}
self.contacts = ev
self.following = count_pubkeys(ev.tags)
self.relays = decode_json_relays(ev.content)
}
func add_event(_ ev: NostrEvent) {
guard ev.should_show_event else {
return
}
if seen_event.contains(ev.id) {
return
}
if ev.is_textlike || ev.known_kind == .boost {
if self.events.insert(ev) {
self.objectWillChange.send()
}
} else if ev.known_kind == .contacts {
handle_profile_contact_event(ev)
}
seen_event.insert(ev.id)
}
private func handle_event(relay_id: String, ev: NostrConnectionEvent) {
switch ev {
case .ws_event:
return
case .nostr_event(let resp):
guard resp.subid == self.sub_id || resp.subid == self.prof_subid else {
return
}
switch resp {
case .ok:
break
case .event(_, let ev):
// Ensure the event public key matches this profiles public key
// This is done to protect against a relay not properly filtering events by the pubkey
// See https://github.com/damus-io/damus/issues/1846 for more information
guard self.pubkey == ev.pubkey else { break }
add_event(ev)
case .notice:
break
//notify(.notice, notice)
case .eose:
guard let txn = NdbTxn(ndb: damus.ndb) else { return }
if resp.subid == sub_id {
load_profiles(context: "profile", profiles_subid: prof_subid, relay_id: relay_id, load: .from_events(events.events), damus_state: damus, txn: txn)
}
progress += 1
break
case .auth:
break
}
}
}
private func findRelaysHandler(relay_id: String, ev: NostrConnectionEvent) {
if case .nostr_event(let resp) = ev, case .event(_, let event) = resp, case .contacts = event.known_kind {
self.relays = decode_json_relays(event.content)
}
}
func subscribeToFindRelays() {
var profile_filter = NostrFilter(kinds: [.contacts])
profile_filter.authors = [pubkey]
damus.pool.subscribe(sub_id: findRelay_subid, filters: [profile_filter], handler: findRelaysHandler)
}
func unsubscribeFindRelays() {
damus.pool.unsubscribe(sub_id: findRelay_subid)
}
func getRelayStrings() -> [String] {
return relays?.keys.map {$0} ?? []
}
func getCappedRelayStrings() -> [String] {
return relays?.keys.prefix(MAX_SHARE_RELAYS).map { $0 } ?? []
}
}
func count_pubkeys(_ tags: Tags) -> Int {
var c: Int = 0
for tag in tags {
if tag.count >= 2 && tag[0].matches_char("p") {
c += 1
}
}
return c
}