From b19bc27ead4bba9ce8a7a60bb76478a7e17ab6c8 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Wed, 28 Dec 2022 14:40:06 -0800 Subject: [PATCH 01/12] Adjust invoice height --- damus/Components/InvoicesView.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/damus/Components/InvoicesView.swift b/damus/Components/InvoicesView.swift index 8f116bb5..6964aafd 100644 --- a/damus/Components/InvoicesView.swift +++ b/damus/Components/InvoicesView.swift @@ -23,7 +23,7 @@ struct InvoicesView: View { .id(invoice.string) } } - .frame(height: 200) + .frame(height: 240) .tabViewStyle(PageTabViewStyle()) } } @@ -31,5 +31,6 @@ struct InvoicesView: View { struct InvoicesView_Previews: PreviewProvider { static var previews: some View { InvoicesView(invoices: [Invoice.init(description: "description", amount: 10000, string: "invstr", expiry: 100000, payment_hash: Data(), created_at: 1000000)]) + .frame(width: 300) } } From bfae6fcf121da71743db46cf316897823c9f0fa5 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Wed, 28 Dec 2022 22:53:39 -0800 Subject: [PATCH 02/12] v0.1.8-8 Mostly crash fixes and invoice tweaks --- damus.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj index 0a549b9e..35a84c3d 100644 --- a/damus.xcodeproj/project.pbxproj +++ b/damus.xcodeproj/project.pbxproj @@ -1039,7 +1039,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 7; + CURRENT_PROJECT_VERSION = 8; DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\""; DEVELOPMENT_TEAM = XK7H4JAB3D; ENABLE_PREVIEWS = YES; @@ -1078,7 +1078,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 7; + CURRENT_PROJECT_VERSION = 8; DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\""; DEVELOPMENT_TEAM = XK7H4JAB3D; ENABLE_PREVIEWS = YES; From 614b48eeaeaafa100ca76006550234bd290b5523 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Thu, 29 Dec 2022 09:21:37 -0800 Subject: [PATCH 03/12] remove extra whitespace on tabbar --- damus/ContentView.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/damus/ContentView.swift b/damus/ContentView.swift index a99006a2..5a567685 100644 --- a/damus/ContentView.swift +++ b/damus/ContentView.swift @@ -228,7 +228,6 @@ struct ContentView: View { } TabBar(new_events: $home.new_events, selected: $selected_timeline, action: switch_timeline) - .padding() } .onAppear() { self.connect() From 37189e3862de79ed4ac5e78a34685ecea2fff5bc Mon Sep 17 00:00:00 2001 From: William Casarin Date: Thu, 29 Dec 2022 09:27:29 -0800 Subject: [PATCH 04/12] Re-introduce a small amount of padding under the tab bar --- damus/ContentView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/damus/ContentView.swift b/damus/ContentView.swift index 5a567685..6c99b828 100644 --- a/damus/ContentView.swift +++ b/damus/ContentView.swift @@ -228,6 +228,7 @@ struct ContentView: View { } TabBar(new_events: $home.new_events, selected: $selected_timeline, action: switch_timeline) + .padding([.bottom], 8) } .onAppear() { self.connect() From 3370ac59d845c1e4f02b1c3d5cb9c10fd2d24be9 Mon Sep 17 00:00:00 2001 From: Thomas <31560900+0xtlt@users.noreply.github.com> Date: Thu, 29 Dec 2022 17:16:30 +0100 Subject: [PATCH 05/12] Don't show boosts in threads Changelog-Fixed: Don't show boosts in threads --- damus/Views/ThreadV2View.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/damus/Views/ThreadV2View.swift b/damus/Views/ThreadV2View.swift index 5738013e..8d55dfae 100644 --- a/damus/Views/ThreadV2View.swift +++ b/damus/Views/ThreadV2View.swift @@ -130,6 +130,7 @@ struct BuildThreadV2View: View { // Ask for children let childs_events = NostrFilter( + kinds: [1], referenced_ids: [self.event_id], limit: 50 ) From 4440f32814988b6196068cd48a938132f5313118 Mon Sep 17 00:00:00 2001 From: Joel Klabo Date: Wed, 28 Dec 2022 22:03:15 -0800 Subject: [PATCH 06/12] Tweak Boosted Label Layout for Better Alignment --- damus/Views/ProfileName.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/damus/Views/ProfileName.swift b/damus/Views/ProfileName.swift index 5e619214..1e5ce23f 100644 --- a/damus/Views/ProfileName.swift +++ b/damus/Views/ProfileName.swift @@ -76,10 +76,9 @@ struct ProfileName: View { Text(prefix + String(display_name ?? Profile.displayName(profile: profile, pubkey: pubkey))) .font(.body) .fontWeight(prefix == "@" ? .none : .bold) - if let frend = friend_icon { - Label("", systemImage: frend) + if let friend = friend_icon { + Image(systemName: friend) .foregroundColor(.gray) - .font(.footnote) } } .onReceive(handle_notify(.profile_updated)) { notif in From 3ebd5622176dd497fe35872b284edea0e5a93553 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Thu, 29 Dec 2022 13:24:32 -0800 Subject: [PATCH 07/12] add relays.nostr.bg to bootstrap relay list --- damus/ContentView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/damus/ContentView.swift b/damus/ContentView.swift index 6c99b828..989ccd12 100644 --- a/damus/ContentView.swift +++ b/damus/ContentView.swift @@ -13,6 +13,7 @@ var BOOTSTRAP_RELAYS = [ "wss://relay.damus.io", "wss://nostr-relay.wlvs.space", "wss://nostr.fmt.wiz.biz", + "wss://relay.nostr.bg", "wss://nostr.oxtr.dev", ] From 2867da61ca9c1414fd23345d8d6d4c572755a6ce Mon Sep 17 00:00:00 2001 From: William Casarin Date: Thu, 29 Dec 2022 13:25:07 -0800 Subject: [PATCH 08/12] Fixed issue where contact list would sometimes revert to an older version Changelog-Fixed: Fixed issue where contact list would sometimes revert to an older version --- damus/Models/HomeModel.swift | 55 ++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/damus/Models/HomeModel.swift b/damus/Models/HomeModel.swift index e7e18b88..e587b416 100644 --- a/damus/Models/HomeModel.swift +++ b/damus/Models/HomeModel.swift @@ -260,6 +260,9 @@ class HomeModel: ObservableObject { var contacts_filter = NostrFilter.filter_kinds([0]) contacts_filter.authors = friends + + var our_contacts_filter = NostrFilter.filter_kinds([3, 0]) + our_contacts_filter.authors = [damus_state.pubkey] var dms_filter = NostrFilter.filter_kinds([ NostrKind.dm.rawValue, @@ -297,7 +300,7 @@ class HomeModel: ObservableObject { var home_filters = [home_filter] var notifications_filters = [notifications_filter] - var contacts_filters = [contacts_filter] + var contacts_filters = [contacts_filter, our_contacts_filter] var dms_filters = [dms_filter, our_dms_filter] let last_of_kind = relay_id.flatMap { last_event_of_kind[$0] } ?? [:] @@ -447,14 +450,43 @@ func load_our_contacts(contacts: Contacts, our_pubkey: String, ev: NostrEvent) { guard ev.pubkey == our_pubkey else { return } - + + // only use new stuff + if let current_ev = contacts.event { + guard ev.created_at > current_ev.created_at else { + return + } + } + + let m_old_ev = contacts.event contacts.event = ev - + + var new_pks = Set() // our contacts for tag in ev.tags { - if tag.count > 1 && tag[0] == "p" { - // TODO: validate pubkey? - contacts.add_friend_pubkey(tag[1]) + if tag.count >= 2 && tag[0] == "p" { + new_pks.insert(tag[1]) + } + } + + var old_pks = Set() + // find removed contacts + if let old_ev = m_old_ev { + for tag in old_ev.tags { + if tag.count >= 2 && tag[0] == "p" { + old_pks.insert(tag[1]) + } + } + } + + let diff = new_pks.symmetricDifference(old_pks) + for pk in diff { + if new_pks.contains(pk) { + notify(.followed, pk) + contacts.add_friend_pubkey(pk) + } else { + notify(.unfollowed, pk) + contacts.remove_friend(pk) } } } @@ -542,14 +574,21 @@ func robohash(_ pk: String) -> String { func process_contact_event(pool: RelayPool, contacts: Contacts, pubkey: String, ev: NostrEvent) { load_our_contacts(contacts: contacts, our_pubkey: pubkey, ev: ev) - load_our_relays(our_pubkey: pubkey, pool: pool, ev: ev) + load_our_relays(contacts: contacts, our_pubkey: pubkey, pool: pool, ev: ev) add_contact_if_friend(contacts: contacts, ev: ev) } -func load_our_relays(our_pubkey: String, pool: RelayPool, ev: NostrEvent) { +func load_our_relays(contacts: Contacts, our_pubkey: String, pool: RelayPool, ev: NostrEvent) { guard ev.pubkey == our_pubkey else { return } + + // only load new stuff + if let old_contacts = contacts.event { + guard ev.created_at > old_contacts.created_at else { + return + } + } guard let decoded = decode_json_relays(ev.content) else { return From 3824e95f296719fd8cf8a266f89e163cfce15cc4 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Thu, 29 Dec 2022 15:23:34 -0800 Subject: [PATCH 09/12] relays: keep in sync with user relay list Still need to figure out how to ensure new bootstrap relays are added... Changelog-Changed: Ensure contact relay list is kept in sync with internal relay pool --- damus/ContentView.swift | 1 + damus/Models/Contacts.swift | 9 +--- damus/Models/HomeModel.swift | 79 +++++++++++++++++++++------------- damus/Nostr/Relay.swift | 2 + damus/Nostr/RelayPool.swift | 18 +++++++- damus/Util/Notifications.swift | 6 +++ damus/Views/ConfigView.swift | 11 ++--- damus/Views/RelayView.swift | 10 +++-- 8 files changed, 87 insertions(+), 49 deletions(-) diff --git a/damus/ContentView.swift b/damus/ContentView.swift index 989ccd12..d0416b39 100644 --- a/damus/ContentView.swift +++ b/damus/ContentView.swift @@ -15,6 +15,7 @@ var BOOTSTRAP_RELAYS = [ "wss://nostr.fmt.wiz.biz", "wss://relay.nostr.bg", "wss://nostr.oxtr.dev", + "wss://nostr.v0l.io", ] struct TimestampedProfile { diff --git a/damus/Models/Contacts.swift b/damus/Models/Contacts.swift index 9397d355..69159ee6 100644 --- a/damus/Models/Contacts.swift +++ b/damus/Models/Contacts.swift @@ -139,13 +139,8 @@ func decode_json_relays(_ content: String) -> [String: RelayInfo]? { return decode_json(content) } -func remove_relay(ev: NostrEvent, privkey: String, relay: String) -> NostrEvent? { - let damus_relay = RelayDescriptor(url: URL(string: "wss://relay.damus.io")!, info: .rw) - - var relays = ensure_relay_info(relays: [damus_relay], content: ev.content) - guard relays.index(forKey: relay) != nil else { - return nil - } +func remove_relay(ev: NostrEvent, current_relays: [RelayDescriptor], privkey: String, relay: String) -> NostrEvent? { + var relays = ensure_relay_info(relays: current_relays, content: ev.content) relays.removeValue(forKey: relay) diff --git a/damus/Models/HomeModel.swift b/damus/Models/HomeModel.swift index e587b416..2eb84a87 100644 --- a/damus/Models/HomeModel.swift +++ b/damus/Models/HomeModel.swift @@ -446,21 +446,7 @@ func add_contact_if_friend(contacts: Contacts, ev: NostrEvent) { contacts.add_friend_contact(ev) } -func load_our_contacts(contacts: Contacts, our_pubkey: String, ev: NostrEvent) { - guard ev.pubkey == our_pubkey else { - return - } - - // only use new stuff - if let current_ev = contacts.event { - guard ev.created_at > current_ev.created_at else { - return - } - } - - let m_old_ev = contacts.event - contacts.event = ev - +func load_our_contacts(contacts: Contacts, our_pubkey: String, m_old_ev: NostrEvent?, ev: NostrEvent) { var new_pks = Set() // our contacts for tag in ev.tags { @@ -572,35 +558,68 @@ func robohash(_ pk: String) -> String { return "https://robohash.org/" + pk } -func process_contact_event(pool: RelayPool, contacts: Contacts, pubkey: String, ev: NostrEvent) { - load_our_contacts(contacts: contacts, our_pubkey: pubkey, ev: ev) - load_our_relays(contacts: contacts, our_pubkey: pubkey, pool: pool, ev: ev) - add_contact_if_friend(contacts: contacts, ev: ev) -} - -func load_our_relays(contacts: Contacts, our_pubkey: String, pool: RelayPool, ev: NostrEvent) { - guard ev.pubkey == our_pubkey else { +func load_our_stuff(pool: RelayPool, contacts: Contacts, pubkey: String, ev: NostrEvent) { + guard ev.pubkey == pubkey else { return } - // only load new stuff - if let old_contacts = contacts.event { - guard ev.created_at > old_contacts.created_at else { + // only use new stuff + if let current_ev = contacts.event { + guard ev.created_at > current_ev.created_at else { return } } + + let m_old_ev = contacts.event + 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) +} + +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 load_our_relays(contacts: Contacts, our_pubkey: String, pool: RelayPool, 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 + } + guard let decoded = decode_json_relays(ev.content) else { return } - + + var changed = false + + var new = Set() for key in decoded.keys { - if let url = URL(string: key) { - if let _ = try? pool.add_relay(url, info: decoded[key]!) { - pool.connect(to: [key]) + new.insert(key) + } + + var old = Set() + for key in old_decoded.keys { + old.insert(key) + } + + let diff = old.symmetricDifference(new) + + for d in diff { + changed = true + if new.contains(d) { + if let url = URL(string: d) { + try? pool.add_relay(url, info: decoded[d] ?? .rw) } + } else { + pool.remove_relay(d) } } + + if changed { + notify(.relays_changed, ()) + } } diff --git a/damus/Nostr/Relay.swift b/damus/Nostr/Relay.swift index cfdbf231..20cf767d 100644 --- a/damus/Nostr/Relay.swift +++ b/damus/Nostr/Relay.swift @@ -28,12 +28,14 @@ class Relay: Identifiable { let descriptor: RelayDescriptor let connection: RelayConnection + var last_pong: UInt32 var flags: Int init(descriptor: RelayDescriptor, connection: RelayConnection) { self.flags = 0 self.descriptor = descriptor self.connection = connection + self.last_pong = 0 } func mark_broken() { diff --git a/damus/Nostr/RelayPool.swift b/damus/Nostr/RelayPool.swift index cdb204fa..59d32e73 100644 --- a/damus/Nostr/RelayPool.swift +++ b/damus/Nostr/RelayPool.swift @@ -179,8 +179,23 @@ class RelayPool { return nil } - + + func record_last_pong(relay_id: String, event: NostrConnectionEvent) { + if case .ws_event(let ws_event) = event { + if case .pong = ws_event { + for relay in relays { + if relay.id == relay_id { + relay.last_pong = UInt32(Date.now.timeIntervalSince1970) + return + } + } + } + } + } + func handle_event(relay_id: String, event: NostrConnectionEvent) { + record_last_pong(relay_id: relay_id, event: event) + // handle reconnect logic, etc? for handler in handlers { handler.callback(relay_id, event) @@ -193,3 +208,4 @@ func add_rw_relay(_ pool: RelayPool, _ url: String) { try? pool.add_relay(url_, info: RelayInfo.rw) } + diff --git a/damus/Util/Notifications.swift b/damus/Util/Notifications.swift index 8b494849..a3374834 100644 --- a/damus/Util/Notifications.swift +++ b/damus/Util/Notifications.swift @@ -13,6 +13,12 @@ extension Notification.Name { } } +extension Notification.Name { + static var relays_changed: Notification.Name { + return Notification.Name("relays_changed") + } +} + extension Notification.Name { static var select_event: Notification.Name { return Notification.Name("select_event") diff --git a/damus/Views/ConfigView.swift b/damus/Views/ConfigView.swift index 88798102..dab1bea1 100644 --- a/damus/Views/ConfigView.swift +++ b/damus/Views/ConfigView.swift @@ -41,13 +41,9 @@ struct ConfigView: View { var body: some View { ZStack(alignment: .leading) { Form { - if let ev = state.contacts.event { - Section("Relays") { - if let relays = decode_json_relays(ev.content) { - List(Array(relays.keys.sorted()), id: \.self) { relay in - RelayView(state: state, ev: ev, relay: relay) - } - } + Section("Relays") { + List(Array(state.pool.relays), id: \.descriptor.url) { relay in + RelayView(state: state, relay: relay.descriptor.url.absoluteString) } } @@ -113,7 +109,6 @@ struct ConfigView: View { } .sheet(isPresented: $show_add_relay) { AddRelayView(show_add_relay: $show_add_relay, relay: $new_relay) { m_relay in - guard let relay = m_relay else { return } diff --git a/damus/Views/RelayView.swift b/damus/Views/RelayView.swift index 47c5d2ec..e08bb0b4 100644 --- a/damus/Views/RelayView.swift +++ b/damus/Views/RelayView.swift @@ -9,7 +9,6 @@ import SwiftUI struct RelayView: View { let state: DamusState - let ev: NostrEvent let relay: String let timer = Timer.publish(every: 2, on: .main, in: .common).autoconnect() @@ -46,7 +45,12 @@ struct RelayView: View { .swipeActions { if let privkey = state.keypair.privkey { Button { - guard let new_ev = remove_relay( ev: ev, privkey: privkey, relay: relay) else { + guard let ev = state.contacts.event else { + return + } + + let descriptors = state.pool.descriptors + guard let new_ev = remove_relay( ev: ev, current_relays: descriptors, privkey: privkey, relay: relay) else { return } @@ -64,6 +68,6 @@ struct RelayView: View { struct RelayView_Previews: PreviewProvider { static var previews: some View { - RelayView(state: test_damus_state(), ev: NostrEvent(content: "content", pubkey: "pk"), relay: "wss://relay.damus.io", conn_color: .red) + RelayView(state: test_damus_state(), relay: "wss://relay.damus.io", conn_color: .red) } } From 14a3a352d6e31d6b0143efd3032391fc9c0e44d4 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Thu, 29 Dec 2022 16:08:00 -0800 Subject: [PATCH 10/12] Recommended relay view Changelog-Changed: Show recommended relays in config. Currently just a fixed set. --- damus.xcodeproj/project.pbxproj | 4 +++ damus/ContentView.swift | 1 + damus/Models/Contacts.swift | 6 ++-- damus/Models/HomeModel.swift | 23 ------------- damus/Views/ConfigView.swift | 31 +++++++++++++++--- damus/Views/RecommendedRelayView.swift | 37 +++++++++++++++++++++ damus/Views/RelayView.swift | 45 +++++++++++++++++--------- 7 files changed, 101 insertions(+), 46 deletions(-) create mode 100644 damus/Views/RecommendedRelayView.swift diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj index 35a84c3d..5c3ff703 100644 --- a/damus.xcodeproj/project.pbxproj +++ b/damus.xcodeproj/project.pbxproj @@ -109,6 +109,7 @@ 4CA2EFA0280E37AC0044ACD8 /* TimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */; }; 4CACA9D5280C31E100D9BBE8 /* ReplyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */; }; 4CACA9DC280C38C000D9BBE8 /* Profiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CACA9DB280C38C000D9BBE8 /* Profiles.swift */; }; + 4CB55EF3295E5D59007FD187 /* RecommendedRelayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB55EF2295E5D59007FD187 /* RecommendedRelayView.swift */; }; 4CD7641B28A1641400B6928F /* EndBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CD7641A28A1641400B6928F /* EndBlock.swift */; }; 4CE4F8CD281352B30009DFBB /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE4F8CC281352B30009DFBB /* Notifications.swift */; }; 4CE4F9DE2852768D00C00DD9 /* ConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE4F9DD2852768D00C00DD9 /* ConfigView.swift */; }; @@ -288,6 +289,7 @@ 4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineView.swift; sourceTree = ""; }; 4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyView.swift; sourceTree = ""; }; 4CACA9DB280C38C000D9BBE8 /* Profiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Profiles.swift; sourceTree = ""; }; + 4CB55EF2295E5D59007FD187 /* RecommendedRelayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendedRelayView.swift; sourceTree = ""; }; 4CD7641A28A1641400B6928F /* EndBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EndBlock.swift; sourceTree = ""; }; 4CE4F8CC281352B30009DFBB /* Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifications.swift; sourceTree = ""; }; 4CE4F9DD2852768D00C00DD9 /* ConfigView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigView.swift; sourceTree = ""; }; @@ -486,6 +488,7 @@ E990020E2955F837003BBC5A /* EditMetadataView.swift */, BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */, E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */, + 4CB55EF2295E5D59007FD187 /* RecommendedRelayView.swift */, ); path = Views; sourceTree = ""; @@ -860,6 +863,7 @@ 4C3BEFDA281DCA1400B3DE84 /* LikeCounter.swift in Sources */, 4C3AC79B28306D7B00E1F516 /* Contacts.swift in Sources */, 4C3EA63D28FF52D600C48A62 /* bolt11.c in Sources */, + 4CB55EF3295E5D59007FD187 /* RecommendedRelayView.swift in Sources */, 4C5F9118283D88E40052CD1C /* FollowingModel.swift in Sources */, 4C3EA67D28FFBBA300C48A62 /* InvoicesView.swift in Sources */, 4C363A8E28236FE4006E126D /* NoteContentView.swift in Sources */, diff --git a/damus/ContentView.swift b/damus/ContentView.swift index d0416b39..384a7612 100644 --- a/damus/ContentView.swift +++ b/damus/ContentView.swift @@ -16,6 +16,7 @@ var BOOTSTRAP_RELAYS = [ "wss://relay.nostr.bg", "wss://nostr.oxtr.dev", "wss://nostr.v0l.io", + "wss://nostr-2.zebedee.cloud", ] struct TimestampedProfile { diff --git a/damus/Models/Contacts.swift b/damus/Models/Contacts.swift index 69159ee6..8e719712 100644 --- a/damus/Models/Contacts.swift +++ b/damus/Models/Contacts.swift @@ -144,6 +144,7 @@ func remove_relay(ev: NostrEvent, current_relays: [RelayDescriptor], privkey: St relays.removeValue(forKey: relay) + print("remove_relay \(relays)") guard let content = encode_json(relays) else { return nil } @@ -154,10 +155,9 @@ func remove_relay(ev: NostrEvent, current_relays: [RelayDescriptor], privkey: St return new_ev } -func add_relay(ev: NostrEvent, privkey: String, relay: String, info: RelayInfo) -> NostrEvent? { - let damus_relay = RelayDescriptor(url: URL(string: "wss://relay.damus.io")!, info: .rw) +func add_relay(ev: NostrEvent, privkey: String, current_relays: [RelayDescriptor], relay: String, info: RelayInfo) -> NostrEvent? { + var relays = ensure_relay_info(relays: current_relays, content: ev.content) - var relays = ensure_relay_info(relays: [damus_relay], content: ev.content) guard relays.index(forKey: relay) == nil else { return nil } diff --git a/damus/Models/HomeModel.swift b/damus/Models/HomeModel.swift index 2eb84a87..87a732bf 100644 --- a/damus/Models/HomeModel.swift +++ b/damus/Models/HomeModel.swift @@ -623,26 +623,3 @@ func load_our_relays(contacts: Contacts, our_pubkey: String, pool: RelayPool, m_ } -func remove_bootstrap_nodes(_ damus_state: DamusState) { - guard let contacts = damus_state.contacts.event else { - return - } - - guard let relays = decode_json_relays(contacts.content) else { - return - } - - let descriptors = relays.reduce(into: []) { arr, kv in - guard let url = URL(string: kv.key) else { - return - } - arr.append(RelayDescriptor(url: url, info: kv.value)) - } - - for relay in BOOTSTRAP_RELAYS { - if !(descriptors.contains { ($0 as! RelayDescriptor).url.absoluteString == relay }) { - damus_state.pool.remove_relay(relay) - } - } -} - diff --git a/damus/Views/ConfigView.swift b/damus/Views/ConfigView.swift index dab1bea1..beeca588 100644 --- a/damus/Views/ConfigView.swift +++ b/damus/Views/ConfigView.swift @@ -18,11 +18,14 @@ struct ConfigView: View { @State var privkey_copied: Bool = false @State var pubkey_copied: Bool = false + @State var relays: [RelayDescriptor] + let generator = UIImpactFeedbackGenerator(style: .light) init(state: DamusState) { self.state = state _privkey = State(initialValue: self.state.keypair.privkey_bech32 ?? "") + _relays = State(initialValue: state.pool.descriptors) } // TODO: (jb55) could be more general but not gonna worry about it atm @@ -38,12 +41,28 @@ struct ConfigView: View { } } + var recommended: [RelayDescriptor] { + let rs: [RelayDescriptor] = [] + return BOOTSTRAP_RELAYS.reduce(into: rs) { (xs, x) in + if let _ = state.pool.get_relay(x) { + } else { + xs.append(RelayDescriptor(url: URL(string: x)!, info: .rw)) + } + } + } + var body: some View { ZStack(alignment: .leading) { Form { Section("Relays") { - List(Array(state.pool.relays), id: \.descriptor.url) { relay in - RelayView(state: state, relay: relay.descriptor.url.absoluteString) + List(Array(relays), id: \.url) { relay in + RelayView(state: state, relay: relay.url.absoluteString) + } + } + + Section("Recommended Relays") { + List(recommended, id: \.url) { r in + RecommendedRelayView(damus: state, relay: r.url.absoluteString) } } @@ -133,17 +152,21 @@ struct ConfigView: View { state.pool.connect(to: [new_relay]) - guard let new_ev = add_relay(ev: ev, privkey: privkey, relay: new_relay, info: info) else { + guard let new_ev = add_relay(ev: ev, privkey: privkey, current_relays: state.pool.descriptors, relay: new_relay, info: info) else { return } - state.contacts.event = new_ev + process_contact_event(pool: state.pool, contacts: state.contacts, pubkey: state.pubkey, ev: ev) + state.pool.send(.event(new_ev)) } } .onReceive(handle_notify(.switched_timeline)) { _ in dismiss() } + .onReceive(handle_notify(.relays_changed)) { _ in + self.relays = state.pool.descriptors + } } } diff --git a/damus/Views/RecommendedRelayView.swift b/damus/Views/RecommendedRelayView.swift new file mode 100644 index 00000000..d7b21eba --- /dev/null +++ b/damus/Views/RecommendedRelayView.swift @@ -0,0 +1,37 @@ +// +// RecommendedRelayView.swift +// damus +// +// Created by William Casarin on 2022-12-29. +// + +import SwiftUI + +struct RecommendedRelayView: View { + let damus: DamusState + let relay: String + + var body: some View { + HStack { + Text(relay) + Spacer() + if let ev = damus.contacts.event { + if let privkey = damus.keypair.privkey { + Button("Add") { + guard let ev = add_relay(ev: ev, 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) + damus.pool.send(.event(ev)) + } + } + } + } + } +} + +struct RecommendedRelayView_Previews: PreviewProvider { + static var previews: some View { + RecommendedRelayView(damus: test_damus_state(), relay: "wss://relay.damus.io") + } +} diff --git a/damus/Views/RelayView.swift b/damus/Views/RelayView.swift index e08bb0b4..80f8e03a 100644 --- a/damus/Views/RelayView.swift +++ b/damus/Views/RelayView.swift @@ -44,26 +44,39 @@ struct RelayView: View { } .swipeActions { if let privkey = state.keypair.privkey { - Button { - guard let ev = state.contacts.event else { - return - } - - let descriptors = state.pool.descriptors - guard let new_ev = remove_relay( ev: ev, current_relays: descriptors, privkey: privkey, relay: relay) else { - return - } - - state.contacts.event = new_ev - state.pool.send(.event(new_ev)) - } label: { - Label("Delete", systemImage: "trash") - } - .tint(.red) + RemoveAction(privkey: privkey) + } + } + .contextMenu { + if let privkey = state.keypair.privkey { + RemoveAction(privkey: privkey) } } } + func RemoveAction(privkey: String) -> some View { + Button { + guard let ev = state.contacts.event else { + return + } + + let descriptors = state.pool.descriptors + guard let new_ev = remove_relay( ev: ev, current_relays: descriptors, privkey: privkey, relay: relay) else { + return + } + + process_contact_event(pool: state.pool, contacts: state.contacts, pubkey: state.pubkey, ev: new_ev) + state.pool.send(.event(new_ev)) + } label: { + Label("Delete", systemImage: "trash") + } + .tint(.red) + } + +} + +fileprivate func remove_action() { + } struct RelayView_Previews: PreviewProvider { From 9e6e22fa2dc34299292253bda02282bf6581fa23 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Thu, 29 Dec 2022 16:18:07 -0800 Subject: [PATCH 11/12] v0.1.8-9 changelog --- CHANGELOG.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1060ec7..05d07754 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +## [0.1.8-9] - 2022-12-29 + +### Changed + +- Show recommended relays in config. Currently just a fixed set. (William Casarin) +- Ensure contact relay list is kept in sync with internal relay pool (William Casarin) + +### Fixed + +- Fixed issue where contact list would sometimes revert to an older version (William Casarin) +- Don't show boosts in threads (Thomas) + + +[0.1.8-9]: https://github.com/damus-io/damus/releases/tag/v0.1.8-9 + ## [0.1.8-6] - 2022-12-28 ### Added @@ -218,4 +233,3 @@ [0.1.2]: https://github.com/damus-io/damus/releases/tag/v0.1.2 - From 1b21a7a7b76239438a70c3827b03907becf9715b Mon Sep 17 00:00:00 2001 From: William Casarin Date: Thu, 29 Dec 2022 16:55:36 -0800 Subject: [PATCH 12/12] User relays view Changelog-Added: Added relay list to user profiles --- damus.xcodeproj/project.pbxproj | 8 +++-- damus/Models/ProfileModel.swift | 2 ++ damus/Views/ProfileView.swift | 11 ++++++ damus/Views/RecommendedRelayView.swift | 15 ++++++++- damus/Views/UserRelaysView.swift | 46 ++++++++++++++++++++++++++ 5 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 damus/Views/UserRelaysView.swift diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj index 5c3ff703..1582c30b 100644 --- a/damus.xcodeproj/project.pbxproj +++ b/damus.xcodeproj/project.pbxproj @@ -110,6 +110,7 @@ 4CACA9D5280C31E100D9BBE8 /* ReplyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */; }; 4CACA9DC280C38C000D9BBE8 /* Profiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CACA9DB280C38C000D9BBE8 /* Profiles.swift */; }; 4CB55EF3295E5D59007FD187 /* RecommendedRelayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB55EF2295E5D59007FD187 /* RecommendedRelayView.swift */; }; + 4CB55EF5295E679D007FD187 /* UserRelaysView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB55EF4295E679D007FD187 /* UserRelaysView.swift */; }; 4CD7641B28A1641400B6928F /* EndBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CD7641A28A1641400B6928F /* EndBlock.swift */; }; 4CE4F8CD281352B30009DFBB /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE4F8CC281352B30009DFBB /* Notifications.swift */; }; 4CE4F9DE2852768D00C00DD9 /* ConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE4F9DD2852768D00C00DD9 /* ConfigView.swift */; }; @@ -290,6 +291,7 @@ 4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyView.swift; sourceTree = ""; }; 4CACA9DB280C38C000D9BBE8 /* Profiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Profiles.swift; sourceTree = ""; }; 4CB55EF2295E5D59007FD187 /* RecommendedRelayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendedRelayView.swift; sourceTree = ""; }; + 4CB55EF4295E679D007FD187 /* UserRelaysView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserRelaysView.swift; sourceTree = ""; }; 4CD7641A28A1641400B6928F /* EndBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EndBlock.swift; sourceTree = ""; }; 4CE4F8CC281352B30009DFBB /* Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifications.swift; sourceTree = ""; }; 4CE4F9DD2852768D00C00DD9 /* ConfigView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigView.swift; sourceTree = ""; }; @@ -489,6 +491,7 @@ BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */, E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */, 4CB55EF2295E5D59007FD187 /* RecommendedRelayView.swift */, + 4CB55EF4295E679D007FD187 /* UserRelaysView.swift */, ); path = Views; sourceTree = ""; @@ -781,6 +784,7 @@ 4CE6DEE927F7A08100C66700 /* ContentView.swift in Sources */, 4CEE2AF5280B29E600AB5EEF /* TimeAgo.swift in Sources */, 4C75EFAD28049CFB0006080F /* PostButton.swift in Sources */, + 4CB55EF5295E679D007FD187 /* UserRelaysView.swift in Sources */, 4C363AA228296A7E006E126D /* SearchView.swift in Sources */, 4C285C8A2838B985008A31F1 /* ProfilePictureSelector.swift in Sources */, 4C75EFB92804A2740006080F /* EventView.swift in Sources */, @@ -1043,7 +1047,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 8; + CURRENT_PROJECT_VERSION = 9; DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\""; DEVELOPMENT_TEAM = XK7H4JAB3D; ENABLE_PREVIEWS = YES; @@ -1082,7 +1086,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 8; + CURRENT_PROJECT_VERSION = 9; DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\""; DEVELOPMENT_TEAM = XK7H4JAB3D; ENABLE_PREVIEWS = YES; diff --git a/damus/Models/ProfileModel.swift b/damus/Models/ProfileModel.swift index 1f89b0b7..1f63e12c 100644 --- a/damus/Models/ProfileModel.swift +++ b/damus/Models/ProfileModel.swift @@ -11,6 +11,7 @@ class ProfileModel: ObservableObject, Equatable { @Published var events: [NostrEvent] = [] @Published var contacts: NostrEvent? = nil @Published var following: Int = 0 + @Published var relays: [String: RelayInfo]? = nil let pubkey: String let damus: DamusState @@ -71,6 +72,7 @@ class ProfileModel: ObservableObject, Equatable { func handle_profile_contact_event(_ ev: NostrEvent) { self.contacts = ev self.following = count_pubkeys(ev.tags) + self.relays = decode_json_relays(ev.content) if damus.contacts.is_friend(ev.pubkey) { self.damus.contacts.add_friend_contact(ev) } diff --git a/damus/Views/ProfileView.swift b/damus/Views/ProfileView.swift index 0b820b75..420c785a 100644 --- a/damus/Views/ProfileView.swift +++ b/damus/Views/ProfileView.swift @@ -240,6 +240,17 @@ struct ProfileView: View { followers.subscribe() } } + + if let relays = profile.relays { + NavigationLink(destination: UserRelaysView(state: damus_state, pubkey: profile.pubkey, relays: Array(relays.keys).sorted())) { + Text("\(relays.keys.count)") + .font(.subheadline.weight(.medium)) + Text("Relays") + .font(.subheadline) + .foregroundColor(.gray) + } + .buttonStyle(PlainButtonStyle()) + } } } } diff --git a/damus/Views/RecommendedRelayView.swift b/damus/Views/RecommendedRelayView.swift index d7b21eba..297ec433 100644 --- a/damus/Views/RecommendedRelayView.swift +++ b/damus/Views/RecommendedRelayView.swift @@ -10,12 +10,25 @@ import SwiftUI struct RecommendedRelayView: View { let damus: DamusState let relay: String + let add_button: Bool + + init(damus: DamusState, relay: String) { + self.damus = damus + self.relay = relay + self.add_button = true + } + + init(damus: DamusState, relay: String, add_button: Bool) { + self.damus = damus + self.relay = relay + self.add_button = add_button + } var body: some View { HStack { Text(relay) Spacer() - if let ev = damus.contacts.event { + if let ev = damus.contacts.event, add_button { if let privkey = damus.keypair.privkey { Button("Add") { guard let ev = add_relay(ev: ev, privkey: privkey, current_relays: damus.pool.descriptors, relay: relay, info: .rw) else { diff --git a/damus/Views/UserRelaysView.swift b/damus/Views/UserRelaysView.swift new file mode 100644 index 00000000..e7897a31 --- /dev/null +++ b/damus/Views/UserRelaysView.swift @@ -0,0 +1,46 @@ +// +// UserRelaysView.swift +// damus +// +// Created by William Casarin on 2022-12-29. +// + +import SwiftUI + +struct UserRelaysView: View { + let state: DamusState + let pubkey: String + let relays: [String] + + @State var relay_state: [(String, Bool)] + + init (state: DamusState, pubkey: String, relays: [String]) { + self.state = state + self.pubkey = pubkey + self.relays = relays + let relay_state = UserRelaysView.make_relay_state(pool: state.pool, relays: relays) + self._relay_state = State(initialValue: relay_state) + } + + static func make_relay_state(pool: RelayPool, relays: [String]) -> [(String, Bool)] { + return relays.map({ r in + return (r, pool.get_relay(r) == nil) + }).sorted { (a, b) in a.0 < b.0 } + } + + var body: some View { + List(relay_state, id: \.0) { (r, add) in + RecommendedRelayView(damus: state, relay: r, add_button: add) + } + .onReceive(handle_notify(.relays_changed)) { _ in + self.relay_state = UserRelaysView.make_relay_state(pool: state.pool, relays: self.relays) + } + .navigationBarTitle("Relays") + } +} + +struct UserRelaysView_Previews: PreviewProvider { + static var previews: some View { + UserRelaysView(state: test_damus_state(), pubkey: "", relays: []) + } +}