damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

commit 5c62a06618aa415297292eb8c6cb1a4087037880
parent 17f87d5438220d59da5209791ae5d1c56d647ec2
Author: William Casarin <jb55@jb55.com>
Date:   Thu,  4 Aug 2022 20:42:18 -0700

Load profiles everywhere

Fixes: #11
Changelog-Fixed: Missing profiles are now loaded everywhere
Signed-off-by: William Casarin <jb55@jb55.com>

Diffstat:
Mdamus/ContentView.swift | 2+-
Mdamus/Models/FollowersModel.swift | 33+++++++++++++++++++++++++++++----
Mdamus/Models/SearchHomeModel.swift | 64+++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Mdamus/Nostr/NostrResponse.swift | 5+++--
Mdamus/Nostr/RelayPool.swift | 7++++++-
Mdamus/Views/SearchHomeView.swift | 2+-
6 files changed, 97 insertions(+), 16 deletions(-)

diff --git a/damus/ContentView.swift b/damus/ContentView.swift @@ -113,7 +113,7 @@ struct ContentView: View { } switch selected_timeline { case .search: - SearchHomeView(damus_state: damus_state!, model: SearchHomeModel(pool: damus_state!.pool) ) + SearchHomeView(damus_state: damus_state!, model: SearchHomeModel(pool: damus_state!.pool, profiles: damus_state!.profiles)) case .home: PostingTimelineView diff --git a/damus/Models/FollowersModel.swift b/damus/Models/FollowersModel.swift @@ -16,6 +16,7 @@ class FollowersModel: ObservableObject { var has_contact: Set<String> = Set() let sub_id: String = UUID().description + let profiles_id: String = UUID().description init(damus_state: DamusState, target: String) { self.damus_state = damus_state @@ -52,20 +53,44 @@ class FollowersModel: ObservableObject { has_contact.insert(ev.pubkey) } + func load_profiles(relay_id: String) { + var filter = NostrFilter.filter_profiles + let authors = find_profiles_to_fetch_pk(profiles: damus_state.profiles, event_pubkeys: contacts) + if authors.isEmpty { + return + } + + filter.authors = authors + + damus_state.pool.subscribe_to(sub_id: profiles_id, filters: [filter], to: [relay_id], handler: handle_event) + } + func handle_event(relay_id: String, ev: NostrConnectionEvent) { switch ev { case .ws_event: break case .nostr_event(let nev): switch nev { - case .event(_, let ev): - if ev.kind == 3 { + case .event(let sub_id, let ev): + guard sub_id == self.sub_id || sub_id == self.profiles_id else { + return + } + + if ev.known_kind == .contacts { handle_contact_event(ev) + } else if ev.known_kind == .metadata { + process_metadata_event(profiles: self.damus_state.profiles, ev: ev) } + case .notice(let msg): print("followingmodel notice: \(msg)") - case .eose: - break + + case .eose(let sub_id): + if sub_id == self.sub_id { + load_profiles(relay_id: relay_id) + } else if sub_id == self.profiles_id { + damus_state.pool.unsubscribe(sub_id: profiles_id, to: [relay_id]) + } } } } diff --git a/damus/Models/SearchHomeModel.swift b/damus/Models/SearchHomeModel.swift @@ -14,16 +14,19 @@ class SearchHomeModel: ObservableObject { @Published var loading: Bool = false var seen_pubkey: Set<String> = Set() + let profiles: Profiles let pool: RelayPool - let sub_id = UUID().description + let base_subid = UUID().description + let profiles_subid = UUID().description let limit: UInt32 = 250 - init(pool: RelayPool) { + init(pool: RelayPool, profiles: Profiles) { self.pool = pool + self.profiles = profiles } func get_base_filter() -> NostrFilter { - var filter = NostrFilter.filter_text + var filter = NostrFilter.filter_kinds([1, 42]) filter.limit = self.limit filter.until = Int64(Date.now.timeIntervalSince1970) return filter @@ -31,12 +34,22 @@ class SearchHomeModel: ObservableObject { func subscribe() { loading = true - pool.subscribe(sub_id: sub_id, filters: [get_base_filter()], handler: handle_event) + pool.subscribe(sub_id: base_subid, filters: [get_base_filter()], handler: handle_event) } func unsubscribe() { loading = false - pool.unsubscribe(sub_id: sub_id) + pool.unsubscribe(sub_id: base_subid) + } + + func load_profiles(relay_id: String) { + var filter = NostrFilter.filter_profiles + let authors = find_profiles_to_fetch(profiles: profiles, events: events) + filter.authors = authors + + if !authors.isEmpty { + pool.subscribe(sub_id: profiles_subid, filters: [filter], handler: handle_event) + } } func handle_event(relay_id: String, conn_ev: NostrConnectionEvent) { @@ -46,7 +59,7 @@ class SearchHomeModel: ObservableObject { case .nostr_event(let event): switch event { case .event(let sub_id, let ev): - guard sub_id == self.sub_id else { + guard sub_id == self.base_subid || sub_id == self.profiles_subid else { return } if ev.kind == NostrKind.text.rawValue { @@ -57,13 +70,50 @@ class SearchHomeModel: ObservableObject { let _ = insert_uniq_sorted_event(events: &events, new_ev: ev) { $0.created_at > $1.created_at } + } else if ev.known_kind == .metadata { + process_metadata_event(profiles: self.profiles, ev: ev) } case .notice(let msg): print("search home notice: \(msg)") - case .eose: + case .eose(let sub_id): loading = false + + if sub_id == self.base_subid { + load_profiles(relay_id: relay_id) + } else if sub_id == self.profiles_subid { + pool.unsubscribe(sub_id: self.profiles_subid) + } + break } } } } + +func find_profiles_to_fetch_pk(profiles: Profiles, event_pubkeys: [String]) -> [String] { + var pubkeys = Set<String>() + + for pk in event_pubkeys { + if profiles.lookup(id: pk) != nil { + continue + } + + pubkeys.insert(pk) + } + + return Array(pubkeys) +} + +func find_profiles_to_fetch(profiles: Profiles, events: [NostrEvent]) -> [String] { + var pubkeys = Set<String>() + + for ev in events { + if profiles.lookup(id: ev.pubkey) != nil { + continue + } + + pubkeys.insert(ev.pubkey) + } + + return Array(pubkeys) +} diff --git a/damus/Nostr/NostrResponse.swift b/damus/Nostr/NostrResponse.swift @@ -10,7 +10,7 @@ import Foundation enum NostrResponse: Decodable { case event(String, NostrEvent) case notice(String) - case eose + case eose(String) init(from decoder: Decoder) throws { var container = try decoder.unkeyedContainer() @@ -34,7 +34,8 @@ enum NostrResponse: Decodable { self = .notice(msg) return } else if typ == "EOSE" { - self = .eose + let sub_id = try container.decode(String.self) + self = .eose(sub_id) return } diff --git a/damus/Nostr/RelayPool.swift b/damus/Nostr/RelayPool.swift @@ -107,7 +107,7 @@ class RelayPool { } } - func unsubscribe(sub_id: String) { + func unsubscribe(sub_id: String, to: [String]? = nil) { self.remove_handler(sub_id: sub_id) self.send(.unsubscribe(sub_id)) } @@ -117,6 +117,11 @@ class RelayPool { send(.subscribe(.init(filters: filters, sub_id: sub_id))) } + func subscribe_to(sub_id: String, filters: [NostrFilter], to: [String]?, handler: @escaping (String, NostrConnectionEvent) -> ()) { + register_handler(sub_id: sub_id, handler: handler) + send(.subscribe(.init(filters: filters, sub_id: sub_id)), to: to) + } + func send(_ req: NostrRequest, to: [String]? = nil) { let relays = to.map{ get_relays($0) } ?? self.relays diff --git a/damus/Views/SearchHomeView.swift b/damus/Views/SearchHomeView.swift @@ -70,7 +70,7 @@ struct SearchHomeView_Previews: PreviewProvider { let state = test_damus_state() SearchHomeView( damus_state: state, - model: SearchHomeModel(pool: state.pool) + model: SearchHomeModel(pool: state.pool, profiles: state.profiles) ) } }