damus

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

commit fe444228e6036391154f9fb9456e0a3d1a1497b9
parent 10596ddb09690cf2ab5b7c5917dff6b38075fec9
Author: William Casarin <jb55@jb55.com>
Date:   Thu,  9 Feb 2023 15:56:26 -0800

Cached relay metadata

Diffstat:
Mdamus.xcodeproj/project.pbxproj | 14+++++++++++++-
Mdamus/ContentView.swift | 26+++++++++++++++++---------
Mdamus/Models/DamusState.swift | 3++-
Mdamus/Models/FollowersModel.swift | 6+-----
Mdamus/Models/HomeModel.swift | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Mdamus/Models/ProfileModel.swift | 2+-
Rdamus/Util/RelayFilters.swift -> damus/Util/Relays/RelayFilters.swift | 0
Adamus/Util/Relays/RelayMetadatas.swift | 20++++++++++++++++++++
Mdamus/Views/Relays/RecommendedRelayView.swift | 2+-
Mdamus/Views/Relays/RelayConfigView.swift | 2+-
Mdamus/Views/Relays/RelayDetailView.swift | 40++++------------------------------------
Mdamus/Views/Relays/RelayView.swift | 12++++++++----
12 files changed, 127 insertions(+), 73 deletions(-)

diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj @@ -168,6 +168,7 @@ 4CE6DF1227F7A2B300C66700 /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = 4CE6DF1127F7A2B300C66700 /* Starscream */; }; 4CE6DF1627F8DEBF00C66700 /* RelayConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DF1527F8DEBF00C66700 /* RelayConnection.swift */; }; 4CE8794829941DA700F758CC /* RelayFilters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE8794729941DA700F758CC /* RelayFilters.swift */; }; + 4CE8794C2995B59E00F758CC /* RelayMetadatas.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE8794B2995B59E00F758CC /* RelayMetadatas.swift */; }; 4CEE2AED2805B22500AB5EEF /* NostrRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AEC2805B22500AB5EEF /* NostrRequest.swift */; }; 4CEE2AF1280B216B00AB5EEF /* EventDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF0280B216B00AB5EEF /* EventDetailView.swift */; }; 4CEE2AF3280B25C500AB5EEF /* ProfilePicView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF2280B25C500AB5EEF /* ProfilePicView.swift */; }; @@ -459,6 +460,7 @@ 4CE6DF0327F7A08200C66700 /* damusUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = damusUITestsLaunchTests.swift; sourceTree = "<group>"; }; 4CE6DF1527F8DEBF00C66700 /* RelayConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayConnection.swift; sourceTree = "<group>"; }; 4CE8794729941DA700F758CC /* RelayFilters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayFilters.swift; sourceTree = "<group>"; }; + 4CE8794B2995B59E00F758CC /* RelayMetadatas.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayMetadatas.swift; sourceTree = "<group>"; }; 4CEE2AE72804F57C00AB5EEF /* libsecp256k1.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libsecp256k1.a; sourceTree = "<group>"; }; 4CEE2AEC2805B22500AB5EEF /* NostrRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NostrRequest.swift; sourceTree = "<group>"; }; 4CEE2AF0280B216B00AB5EEF /* EventDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventDetailView.swift; sourceTree = "<group>"; }; @@ -732,6 +734,7 @@ 4C7FF7D628233637009601DB /* Util */ = { isa = PBXGroup; children = ( + 4CE879492995B58700F758CC /* Relays */, 4CF0ABEA29844B2F00D66079 /* AnyCodable */, 4CC7AAE6297EFA7B00430951 /* Zap.swift */, 4C3A1D322960DB0500558C0F /* Markdown.swift */, @@ -757,7 +760,6 @@ 4CB883A72975FC1800DC99E7 /* Zaps.swift */, 4CB883B5297730E400DC99E7 /* LNUrls.swift */, 3AB72AB8298ECF30004BB58C /* Translator.swift */, - 4CE8794729941DA700F758CC /* RelayFilters.swift */, ); path = Util; sourceTree = "<group>"; @@ -917,6 +919,15 @@ path = damusUITests; sourceTree = "<group>"; }; + 4CE879492995B58700F758CC /* Relays */ = { + isa = PBXGroup; + children = ( + 4CE8794729941DA700F758CC /* RelayFilters.swift */, + 4CE8794B2995B59E00F758CC /* RelayMetadatas.swift */, + ); + path = Relays; + sourceTree = "<group>"; + }; 4CEE2AE62804F57B00AB5EEF /* Frameworks */ = { isa = PBXGroup; children = ( @@ -1129,6 +1140,7 @@ 4C285C86283892E7008A31F1 /* CreateAccountModel.swift in Sources */, 4C64987C286D03E000EAE2B3 /* DirectMessagesView.swift in Sources */, 7C902AE32981D55B002AB16E /* ZoomableScrollView.swift in Sources */, + 4CE8794C2995B59E00F758CC /* RelayMetadatas.swift in Sources */, 4C363A8C28236B92006E126D /* PubkeyView.swift in Sources */, 4C5C7E68284ED36500A22DF5 /* SearchHomeModel.swift in Sources */, 4C75EFB728049D990006080F /* RelayPool.swift in Sources */, diff --git a/damus/ContentView.swift b/damus/ContentView.swift @@ -287,14 +287,17 @@ struct ContentView: View { } } - Button(action: { - //isFilterVisible.toggle() - self.active_sheet = .filter - }) { - // checklist, checklist.checked, lisdt.bullet, list.bullet.circle, line.3.horizontal.decrease..., line.3.horizontail.decrease - Label("Filter", systemImage: "line.3.horizontal.decrease") - .foregroundColor(.gray) - //.contentShape(Rectangle()) + // maybe expand this to other timelines in the future + if selected_timeline == .search { + Button(action: { + //isFilterVisible.toggle() + self.active_sheet = .filter + }) { + // checklist, checklist.checked, lisdt.bullet, list.bullet.circle, line.3.horizontal.decrease..., line.3.horizontail.decrease + Label("Filter", systemImage: "line.3.horizontal.decrease") + .foregroundColor(.gray) + //.contentShape(Rectangle()) + } } } } @@ -585,8 +588,12 @@ struct ContentView: View { func connect() { let pool = RelayPool() + let metadatas = RelayMetadatas() for relay in BOOTSTRAP_RELAYS { + if let url = URL(string: relay) { + add_new_relay(url: url, info: .rw, metadatas: metadatas, pool: pool) + } add_relay(pool, relay) } @@ -603,7 +610,8 @@ struct ContentView: View { zaps: Zaps(our_pubkey: pubkey), lnurls: LNUrls(), settings: UserSettingsStore(), - relay_filters: RelayFilters(our_pubkey: pubkey) + relay_filters: RelayFilters(our_pubkey: pubkey), + relay_metadata: metadatas ) home.damus_state = self.damus_state! diff --git a/damus/Models/DamusState.swift b/damus/Models/DamusState.swift @@ -22,6 +22,7 @@ struct DamusState { let lnurls: LNUrls let settings: UserSettingsStore let relay_filters: RelayFilters + let relay_metadata: RelayMetadatas var pubkey: String { return keypair.pubkey @@ -33,6 +34,6 @@ struct DamusState { static var empty: DamusState { - return DamusState.init(pool: RelayPool(), keypair: Keypair(pubkey: "", privkey: ""), likes: EventCounter(our_pubkey: ""), boosts: EventCounter(our_pubkey: ""), contacts: Contacts(our_pubkey: ""), tips: TipCounter(our_pubkey: ""), profiles: Profiles(), dms: DirectMessagesModel(our_pubkey: ""), previews: PreviewCache(), zaps: Zaps(our_pubkey: ""), lnurls: LNUrls(), settings: UserSettingsStore(), relay_filters: RelayFilters(our_pubkey: "")) + return DamusState.init(pool: RelayPool(), keypair: Keypair(pubkey: "", privkey: ""), likes: EventCounter(our_pubkey: ""), boosts: EventCounter(our_pubkey: ""), contacts: Contacts(our_pubkey: ""), tips: TipCounter(our_pubkey: ""), profiles: Profiles(), dms: DirectMessagesModel(our_pubkey: ""), previews: PreviewCache(), zaps: Zaps(our_pubkey: ""), lnurls: LNUrls(), settings: UserSettingsStore(), relay_filters: RelayFilters(our_pubkey: ""), relay_metadata: RelayMetadatas()) } } diff --git a/damus/Models/FollowersModel.swift b/damus/Models/FollowersModel.swift @@ -51,11 +51,7 @@ class FollowersModel: ObservableObject { if has_contact.contains(ev.pubkey) { return } - process_contact_event( - pool: damus_state.pool, - contacts: damus_state.contacts, - pubkey: damus_state.pubkey, ev: ev - ) + process_contact_event(state: damus_state, ev: ev) contacts?.append(ev.pubkey) has_contact.insert(ev.pubkey) } diff --git a/damus/Models/HomeModel.swift b/damus/Models/HomeModel.swift @@ -193,7 +193,7 @@ class HomeModel: ObservableObject { } func handle_contact_event(sub_id: String, relay_id: String, ev: NostrEvent) { - process_contact_event(pool: damus_state.pool, contacts: damus_state.contacts, pubkey: damus_state.pubkey, ev: ev) + process_contact_event(state: self.damus_state, ev: ev) if sub_id == init_subid { pool.send(.unsubscribe(init_subid), to: [relay_id]) @@ -643,31 +643,31 @@ func robohash(_ pk: String) -> String { return "https://robohash.org/" + pk } -func load_our_stuff(pool: RelayPool, contacts: Contacts, pubkey: String, ev: NostrEvent) { - guard ev.pubkey == pubkey else { +func load_our_stuff(state: DamusState, ev: NostrEvent) { + guard ev.pubkey == state.pubkey else { return } // only use new stuff - if let current_ev = contacts.event { + if let current_ev = state.contacts.event { guard ev.created_at > current_ev.created_at else { return } } - let m_old_ev = contacts.event - contacts.event = ev + let m_old_ev = state.contacts.event + state.contacts.event = ev - load_our_contacts(contacts: contacts, our_pubkey: pubkey, m_old_ev: m_old_ev, ev: ev) - load_our_relays(contacts: contacts, our_pubkey: pubkey, pool: pool, m_old_ev: m_old_ev, ev: ev) + load_our_contacts(contacts: state.contacts, our_pubkey: state.pubkey, m_old_ev: m_old_ev, ev: ev) + load_our_relays(state: state, m_old_ev: m_old_ev, ev: ev) } -func process_contact_event(pool: RelayPool, contacts: Contacts, pubkey: String, ev: NostrEvent) { - load_our_stuff(pool: pool, contacts: contacts, pubkey: pubkey, ev: ev) - add_contact_if_friend(contacts: contacts, ev: ev) +func process_contact_event(state: DamusState, ev: NostrEvent) { + load_our_stuff(state: state, ev: ev) + add_contact_if_friend(contacts: state.contacts, ev: ev) } -func load_our_relays(contacts: Contacts, our_pubkey: String, pool: RelayPool, m_old_ev: NostrEvent?, ev: NostrEvent) { +func load_our_relays(state: DamusState, m_old_ev: NostrEvent?, ev: NostrEvent) { let bootstrap_dict: [String: RelayInfo] = [:] let old_decoded = m_old_ev.flatMap { decode_json_relays($0.content) } ?? BOOTSTRAP_RELAYS.reduce(into: bootstrap_dict) { (d, r) in d[r] = .rw @@ -695,10 +695,10 @@ func load_our_relays(contacts: Contacts, our_pubkey: String, pool: RelayPool, m_ changed = true if new.contains(d) { if let url = URL(string: d) { - try? pool.add_relay(url, info: decoded[d] ?? .rw) + add_new_relay(url: url, info: decoded[d] ?? .rw, metadatas: state.relay_metadata, pool: state.pool) } } else { - pool.remove_relay(d) + state.pool.remove_relay(d) } } @@ -707,6 +707,51 @@ func load_our_relays(contacts: Contacts, our_pubkey: String, pool: RelayPool, m_ } } +func add_new_relay(url: URL, info: RelayInfo, metadatas: RelayMetadatas, pool: RelayPool) { + try? pool.add_relay(url, info: info) + + let relay_id = url.absoluteString + guard metadatas.lookup(relay_id: relay_id) == nil else { + return + } + + Task.detached(priority: .background) { + guard let meta = try? await fetch_relay_metadata(relay_id: relay_id) else { + return + } + + DispatchQueue.main.async { + metadatas.insert(relay_id: relay_id, metadata: meta) + } + } +} + +func fetch_relay_metadata(relay_id: String) async throws -> RelayMetadata? { + var urlString = relay_id.replacingOccurrences(of: "wss://", with: "https://") + urlString = urlString.replacingOccurrences(of: "ws://", with: "http://") + + guard let url = URL(string: urlString) else { + return nil + } + + var request = URLRequest(url: url) + request.setValue("application/nostr+json", forHTTPHeaderField: "Accept") + + var res: (Data, URLResponse)? = nil + + res = try await URLSession.shared.data(for: request) + + guard let data = res?.0 else { + return nil + } + + let nip11 = try JSONDecoder().decode(RelayMetadata.self, from: data) + return nip11 +} + +func process_relay_metadata() { +} + func handle_incoming_dm(contacts: Contacts, prev_events: NewEventsBits, dms: DirectMessagesModel, our_pubkey: String, ev: NostrEvent) -> NewEventsBits? { // hide blocked users guard should_show_event(contacts: contacts, ev: ev) else { diff --git a/damus/Models/ProfileModel.swift b/damus/Models/ProfileModel.swift @@ -88,7 +88,7 @@ class ProfileModel: ObservableObject, Equatable { } func handle_profile_contact_event(_ ev: NostrEvent) { - process_contact_event(pool: damus.pool, contacts: damus.contacts, pubkey: damus.pubkey, ev: ev) + process_contact_event(state: damus, ev: ev) // only use new stuff if let current_ev = self.contacts { diff --git a/damus/Util/RelayFilters.swift b/damus/Util/Relays/RelayFilters.swift diff --git a/damus/Util/Relays/RelayMetadatas.swift b/damus/Util/Relays/RelayMetadatas.swift @@ -0,0 +1,20 @@ +// +// RelayMetadatas.swift +// damus +// +// Created by William Casarin on 2023-02-09. +// + +import Foundation + +class RelayMetadatas { + private var metadata: [String: RelayMetadata] = [:] + + func lookup(relay_id: String) -> RelayMetadata? { + return metadata[relay_id] + } + + func insert(relay_id: String, metadata: RelayMetadata) { + self.metadata[relay_id] = metadata + } +} diff --git a/damus/Views/Relays/RecommendedRelayView.swift b/damus/Views/Relays/RecommendedRelayView.swift @@ -37,7 +37,7 @@ struct RecommendedRelayView: View { guard let ev_after_add = add_relay(ev: ev_before_add, privkey: privkey, current_relays: damus.pool.descriptors, relay: relay, info: .rw) else { return } - process_contact_event(pool: damus.pool, contacts: damus.contacts, pubkey: damus.pubkey, ev: ev_after_add) + process_contact_event(state: damus, ev: ev_after_add) damus.pool.send(.event(ev_after_add)) } } diff --git a/damus/Views/Relays/RelayConfigView.swift b/damus/Views/Relays/RelayConfigView.swift @@ -67,7 +67,7 @@ struct RelayConfigView: View { return } - process_contact_event(pool: state.pool, contacts: state.contacts, pubkey: state.pubkey, ev: ev) + process_contact_event(state: state, ev: ev) state.pool.send(.event(new_ev)) } diff --git a/damus/Views/Relays/RelayDetailView.swift b/damus/Views/Relays/RelayDetailView.swift @@ -10,10 +10,9 @@ import SwiftUI struct RelayDetailView: View { let state: DamusState let relay: String + let nip11: RelayMetadata @State private var errorString: String? - @State private var nip11: RelayMetadata? - @State var conn_color: Color @Environment(\.dismiss) var dismiss @@ -69,39 +68,7 @@ struct RelayDetailView: View { .onReceive(handle_notify(.switched_timeline)) { notif in dismiss() } - .navigationTitle(nip11?.name ?? "") - .task { - var urlString = relay.replacingOccurrences(of: "wss://", with: "https://") - urlString = urlString.replacingOccurrences(of: "ws://", with: "http://") - - guard let url = URL(string: urlString) else { - return - } - - var request = URLRequest(url: url) - request.setValue("application/nostr+json", forHTTPHeaderField: "Accept") - - var res: (Data, URLResponse)? = nil - - do { - res = try await URLSession.shared.data(for: request) - } catch { - errorString = error.localizedDescription - return - } - - guard let data = res?.0 else { - errorString = "Relay not responding to metadata request" - return - } - - do { - let nip11 = try JSONDecoder().decode(RelayMetadata.self, from: data) - self.nip11 = nip11 - } catch { - errorString = error.localizedDescription - } - } + .navigationTitle(nip11.name ?? "") } private func nipsList(nips: [Int]) -> AttributedString { @@ -124,6 +91,7 @@ struct RelayDetailView: View { struct RelayDetailView_Previews: PreviewProvider { static var previews: some View { - RelayDetailView(state: test_damus_state(), relay: "wss://nostr.klabo.blog", conn_color: .green) + let metadata = RelayMetadata(name: "name", description: "desc", pubkey: "pubkey", contact: "contact", supported_nips: [1,2,3], software: "software", version: "version") + RelayDetailView(state: test_damus_state(), relay: "relay", nip11: metadata, conn_color: .green) } } diff --git a/damus/Views/Relays/RelayView.swift b/damus/Views/Relays/RelayView.swift @@ -34,9 +34,13 @@ struct RelayView: View { Circle() .frame(width: 8.0, height: 8.0) .foregroundColor(conn_color) - NavigationLink { - RelayDetailView(state: state, relay: relay, conn_color: conn_color) - } label: { + if let meta = state.relay_metadata.lookup(relay_id: relay) { + NavigationLink { + RelayDetailView(state: state, relay: relay, nip11: meta, conn_color: conn_color) + } label: { + Text(relay) + } + } else { Text(relay) } } @@ -79,7 +83,7 @@ struct RelayView: View { return } - process_contact_event(pool: state.pool, contacts: state.contacts, pubkey: state.pubkey, ev: new_ev) + process_contact_event(state: state, ev: new_ev) state.pool.send(.event(new_ev)) } label: { Label(NSLocalizedString("Delete", comment: "Button to delete a relay server that the user connects to."), systemImage: "trash")