damus

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

commit 6ac4214be791ecd550205333234eac32260dbab9
parent 0ab1494b1e77ac9c3f1b0444bcd5e922e88c0e0a
Author: William Casarin <jb55@jb55.com>
Date:   Mon,  9 May 2022 10:33:03 -0700

insert sort, profile updates revamp

Signed-off-by: William Casarin <jb55@jb55.com>

Diffstat:
Mdamus.xcodeproj/project.pbxproj | 12++++++++++++
Mdamus/ContentView.swift | 40+++++++++++++++++++++++++++-------------
Mdamus/Models/DamusState.swift | 1+
Mdamus/Models/EventRef.swift | 4++--
Adamus/Models/SearchModel.swift | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdamus/Models/ThreadModel.swift | 29++++++++---------------------
Mdamus/Nostr/NostrFilter.swift | 4++++
Mdamus/Nostr/NostrLink.swift | 4++--
Mdamus/Nostr/Profiles.swift | 5++---
Mdamus/Notifications.swift | 4++--
Adamus/Util/InsertSort.swift | 29+++++++++++++++++++++++++++++
Mdamus/Util/Parser.swift | 4++++
Mdamus/Views/ChatView.swift | 15++++++---------
Mdamus/Views/EventActionBar.swift | 2+-
Mdamus/Views/EventDetailView.swift | 2--
Mdamus/Views/EventView.swift | 14+++++---------
Mdamus/Views/MentionView.swift | 4+---
Mdamus/Views/NoteContentView.swift | 2+-
Mdamus/Views/ProfileName.swift | 23+++++++++++++++++++----
Mdamus/Views/ProfilePicView.swift | 16+++++++++++++---
Mdamus/Views/ProfileView.swift | 7++-----
Mdamus/Views/ReplyQuoteView.swift | 5++---
Mdamus/Views/ReplyView.swift | 4+---
Adamus/Views/SearchView.swift | 32++++++++++++++++++++++++++++++++
Mdamus/Views/ThreadView.swift | 3---
Mdamus/Views/TimelineView.swift | 6------
26 files changed, 250 insertions(+), 95 deletions(-)

diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj @@ -28,6 +28,9 @@ 4C363A9C282838B9006E126D /* EventRef.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363A9B282838B9006E126D /* EventRef.swift */; }; 4C363A9E2828A822006E126D /* ReplyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363A9D2828A822006E126D /* ReplyTests.swift */; }; 4C363AA02828A8DD006E126D /* LikeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363A9F2828A8DD006E126D /* LikeTests.swift */; }; + 4C363AA228296A7E006E126D /* SearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363AA128296A7E006E126D /* SearchView.swift */; }; + 4C363AA428296DEE006E126D /* SearchModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363AA328296DEE006E126D /* SearchModel.swift */; }; + 4C363AA828297703006E126D /* InsertSort.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363AA728297703006E126D /* InsertSort.swift */; }; 4C3BEFD22819DB9B00B3DE84 /* ProfileModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFD12819DB9B00B3DE84 /* ProfileModel.swift */; }; 4C3BEFD42819DE8F00B3DE84 /* NostrKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFD32819DE8F00B3DE84 /* NostrKind.swift */; }; 4C3BEFD6281D995700B3DE84 /* ActionBarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFD5281D995700B3DE84 /* ActionBarModel.swift */; }; @@ -108,6 +111,9 @@ 4C363A9B282838B9006E126D /* EventRef.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventRef.swift; sourceTree = "<group>"; }; 4C363A9D2828A822006E126D /* ReplyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyTests.swift; sourceTree = "<group>"; }; 4C363A9F2828A8DD006E126D /* LikeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LikeTests.swift; sourceTree = "<group>"; }; + 4C363AA128296A7E006E126D /* SearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchView.swift; sourceTree = "<group>"; }; + 4C363AA328296DEE006E126D /* SearchModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchModel.swift; sourceTree = "<group>"; }; + 4C363AA728297703006E126D /* InsertSort.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertSort.swift; sourceTree = "<group>"; }; 4C3BEFD12819DB9B00B3DE84 /* ProfileModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileModel.swift; sourceTree = "<group>"; }; 4C3BEFD32819DE8F00B3DE84 /* NostrKind.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NostrKind.swift; sourceTree = "<group>"; }; 4C3BEFD5281D995700B3DE84 /* ActionBarModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionBarModel.swift; sourceTree = "<group>"; }; @@ -195,6 +201,7 @@ 4C363A952827096D006E126D /* PostBlock.swift */, 4C363A9928283854006E126D /* Reply.swift */, 4C363A9B282838B9006E126D /* EventRef.swift */, + 4C363AA328296DEE006E126D /* SearchModel.swift */, ); path = Models; sourceTree = "<group>"; @@ -221,6 +228,7 @@ 4C363A8928236B57006E126D /* MentionView.swift */, 4C363A8B28236B92006E126D /* PubkeyView.swift */, 4C363A8D28236FE4006E126D /* NoteContentView.swift */, + 4C363AA128296A7E006E126D /* SearchView.swift */, ); path = Views; sourceTree = "<group>"; @@ -249,6 +257,7 @@ children = ( 4C363A8328233689006E126D /* Parser.swift */, 4C363A8528234FDE006E126D /* ImageCache.swift */, + 4C363AA728297703006E126D /* InsertSort.swift */, ); path = Util; sourceTree = "<group>"; @@ -468,12 +477,14 @@ files = ( 4C363A8A28236B57006E126D /* MentionView.swift in Sources */, 4CE4F8CD281352B30009DFBB /* Notifications.swift in Sources */, + 4C363AA828297703006E126D /* InsertSort.swift in Sources */, 4C363A8C28236B92006E126D /* PubkeyView.swift in Sources */, 4C363A8628234FDE006E126D /* ImageCache.swift in Sources */, 4C75EFB728049D990006080F /* RelayPool.swift in Sources */, 4CE6DEE927F7A08100C66700 /* ContentView.swift in Sources */, 4CEE2AF5280B29E600AB5EEF /* TimeAgo.swift in Sources */, 4C75EFAD28049CFB0006080F /* PostButton.swift in Sources */, + 4C363AA228296A7E006E126D /* SearchView.swift in Sources */, 4C75EFB92804A2740006080F /* EventView.swift in Sources */, 4C7FF7D52823313F009601DB /* Mentions.swift in Sources */, 4C363A9828283441006E126D /* TestingPrivate.swift in Sources */, @@ -501,6 +512,7 @@ 4C8682872814DE470026224F /* ProfileView.swift in Sources */, 4CE6DF1627F8DEBF00C66700 /* RelayConnection.swift in Sources */, 4C3BEFD6281D995700B3DE84 /* ActionBarModel.swift in Sources */, + 4C363AA428296DEE006E126D /* SearchModel.swift in Sources */, 4CEE2AF3280B25C500AB5EEF /* ProfilePicView.swift in Sources */, 4CEE2AF9280B2EAC00AB5EEF /* PowView.swift in Sources */, 4C3BEFD42819DE8F00B3DE84 /* NostrKind.swift in Sources */, diff --git a/damus/ContentView.swift b/damus/ContentView.swift @@ -43,7 +43,6 @@ enum Timeline: String, CustomStringConvertible { struct ContentView: View { @State var status: String = "Not connected" @State var active_sheet: Sheets? = nil - @State var profiles: Profiles = Profiles() @State var friends: [String: ()] = [:] @State var loading: Bool = true @State var damus: DamusState? = nil @@ -59,9 +58,11 @@ struct ContentView: View { @State var friend_events: [NostrEvent] = [] @State var notifications: [NostrEvent] = [] @State var active_profile: String? = nil + @State var active_search: NostrFilter? = nil @State var active_event_id: String? = nil @State var profile_open: Bool = false @State var thread_open: Bool = false + @State var search_open: Bool = false // connect retry timer let timer = Timer.publish(every: 60, on: .main, in: .common).autoconnect() @@ -142,7 +143,6 @@ struct ContentView: View { ZStack { if let damus = self.damus { TimelineView(events: $friend_events, damus: damus) - .environmentObject(profiles) } PostButtonContainer } @@ -157,6 +157,9 @@ struct ContentView: View { NavigationLink(destination: MaybeThreadView, isActive: $thread_open) { EmptyView() } + NavigationLink(destination: MaybeSearchView, isActive: $search_open) { + EmptyView() + } switch selected_timeline { case .home: PostingTimelineView @@ -166,13 +169,11 @@ struct ContentView: View { case .notifications: TimelineView(events: $notifications, damus: damus) - .environmentObject(profiles) .navigationTitle("Notifications") case .global: TimelineView(events: $events, damus: damus) - .environmentObject(profiles) .navigationTitle("Global") case .none: EmptyView() @@ -184,12 +185,21 @@ struct ContentView: View { .navigationViewStyle(.stack) } + var MaybeSearchView: some View { + Group { + if let search = self.active_search { + SearchView(appstate: damus!, search: SearchModel(pool: damus!.pool, search: search)) + } else { + EmptyView() + } + } + } + var MaybeThreadView: some View { Group { if let evid = self.active_event_id { let thread_model = ThreadModel(evid: evid, pool: damus!.pool) ThreadView(thread: thread_model, damus: damus!) - .environmentObject(profiles) } else { EmptyView() } @@ -201,7 +211,6 @@ struct ContentView: View { if let pk = self.active_profile { let profile_model = ProfileModel(pubkey: pk, damus: damus!) ProfileView(damus: damus!, profile: profile_model) - .environmentObject(profiles) } else { EmptyView() } @@ -230,7 +239,6 @@ struct ContentView: View { PostView(references: []) case .reply(let event): ReplyView(replying_to: event, damus: damus!) - .environmentObject(profiles) } } .onOpenURL { url in @@ -247,8 +255,9 @@ struct ContentView: View { active_event_id = ref.ref_id thread_open = true } - case .filter: - + case .filter(let filt): + active_search = filt + search_open = true break // TODO: handle filter searches? } @@ -340,10 +349,12 @@ struct ContentView: View { func add_relay(_ pool: RelayPool, _ relay: String) { //add_rw_relay(pool, "wss://nostr-pub.wellorder.net") add_rw_relay(pool, relay) + /* let profile = Profile(name: relay, about: nil, picture: nil) let ts = Int64(Date().timeIntervalSince1970) let tsprofile = TimestampedProfile(profile: profile, timestamp: ts) - self.profiles.add(id: relay, profile: tsprofile) + damus!.profiles.add(id: relay, profile: tsprofile) + */ } func connect() { @@ -361,7 +372,8 @@ struct ContentView: View { self.damus = DamusState(pool: pool, pubkey: pubkey, likes: EventCounter(our_pubkey: pubkey), boosts: EventCounter(our_pubkey: pubkey), - image_cache: ImageCache() + image_cache: ImageCache(), + profiles: Profiles() ) pool.connect() } @@ -404,7 +416,7 @@ struct ContentView: View { return } - if let mprof = self.profiles.lookup_with_timestamp(id: ev.pubkey) { + if let mprof = damus!.profiles.lookup_with_timestamp(id: ev.pubkey) { if mprof.timestamp > ev.created_at { // skip if we already have an newer profile return @@ -412,7 +424,9 @@ struct ContentView: View { } let tprof = TimestampedProfile(profile: profile, timestamp: ev.created_at) - self.profiles.add(id: ev.pubkey, profile: tprof) + damus!.profiles.add(id: ev.pubkey, profile: tprof) + + notify(.profile_updated, ProfileUpdate(pubkey: ev.pubkey, profile: profile)) } func get_last_event_of_kind(relay_id: String, kind: Int) -> NostrEvent? { diff --git a/damus/Models/DamusState.swift b/damus/Models/DamusState.swift @@ -13,4 +13,5 @@ struct DamusState { let likes: EventCounter let boosts: EventCounter let image_cache: ImageCache + let profiles: Profiles } diff --git a/damus/Models/EventRef.swift b/damus/Models/EventRef.swift @@ -35,11 +35,11 @@ enum EventRef { var is_thread_id: ReferencedId? { switch self { - case .mention(let mention): + case .mention: return nil case .thread_id(let referencedId): return referencedId - case .reply(let referencedId): + case .reply: return nil case .reply_to_root(let referencedId): return referencedId diff --git a/damus/Models/SearchModel.swift b/damus/Models/SearchModel.swift @@ -0,0 +1,74 @@ +// +// Timeline.swift +// damus +// +// Created by William Casarin on 2022-05-09. +// + +import Foundation + + +class SearchModel: ObservableObject { + @Published var events: [NostrEvent] = [] + let pool: RelayPool + let search: NostrFilter + let sub_id = UUID().description + + init(pool: RelayPool, search: NostrFilter) { + self.pool = pool + self.search = search + } + + func subscribe() { + // since 2 month + var filter = NostrFilter.copy(from: search) + filter.since = Int64(Date.now.timeIntervalSince1970) - 2629800 * 2 + + //likes_filter.ids = ref_events.referenced_ids! + + print("subscribing to search '\(filter)' with sub_id \(sub_id)") + pool.register_handler(sub_id: sub_id, handler: handle_event) + pool.send(.subscribe(.init(filters: [filter], sub_id: sub_id))) + } + + func unsubscribe() { + self.pool.unsubscribe(sub_id: sub_id) + print("unsubscribing from search '\(search)' with sub_id \(sub_id)") + } + + func add_event(_ ev: NostrEvent) { + if insert_uniq_sorted_event(events: &self.events, new_ev: ev) { + objectWillChange.send() + } + } + + func handle_event(relay_id: String, ev: NostrConnectionEvent) { + handle_subid_event(pool: pool, sub_id: sub_id, relay_id: relay_id, ev: ev) { ev in + if ev.known_kind == .text { + self.add_event(ev) + } + } + } +} + + +func handle_subid_event(pool: RelayPool, sub_id: String, relay_id: String, ev: NostrConnectionEvent, handle: (NostrEvent) -> ()) { + switch ev { + case .ws_event: + break + case .nostr_event(let res): + switch res { + case .event(let ev_subid, let ev): + if ev_subid == sub_id { + handle(ev) + } + + case .notice(let note): + if note.contains("Too many subscription filters") { + // TODO: resend filters? + pool.reconnect(to: [relay_id]) + } + break + } + } +} diff --git a/damus/Models/ThreadModel.swift b/damus/Models/ThreadModel.swift @@ -140,9 +140,11 @@ class ThreadModel: ObservableObject { self.replies.add(id: ev.id, reply_id: reply.ref_id) } - self.events.append(ev) - self.events = self.events.sorted { $0.created_at < $1.created_at } - //objectWillChange.send() + if insert_uniq_sorted_event(events: &self.events, new_ev: ev) { + objectWillChange.send() + } + //self.events.append(ev) + //self.events = self.events.sorted { $0.created_at < $1.created_at } var i: Int = 0 for ev in events { @@ -160,24 +162,9 @@ class ThreadModel: ObservableObject { } func handle_event(relay_id: String, ev: NostrConnectionEvent) { - switch ev { - case .ws_event: - break - case .nostr_event(let res): - switch res { - case .event(let sub_id, let ev): - if sub_id == self.sub_id { - if ev.known_kind == .text { - add_event(ev) - } - } - - case .notice(let note): - if note.contains("Too many subscription filters") { - // TODO: resend filters? - pool.reconnect(to: [relay_id]) - } - break + handle_subid_event(pool: pool, sub_id: sub_id, relay_id: relay_id, ev: ev) { ev in + if ev.known_kind == .text { + self.add_event(ev) } } } diff --git a/damus/Nostr/NostrFilter.swift b/damus/Nostr/NostrFilter.swift @@ -28,6 +28,10 @@ struct NostrFilter: Codable { case authors } + public static func copy(from: NostrFilter) -> NostrFilter { + return NostrFilter(ids: from.ids, kinds: from.kinds, referenced_ids: from.referenced_ids, pubkeys: from.pubkeys, since: from.since, until: from.until, authors: from.authors, hashtag: from.hashtag) + } + public static func filter_hashtag(_ htags: [String]) -> NostrFilter { return NostrFilter(ids: nil, kinds: nil, referenced_ids: nil, pubkeys: nil, since: nil, until: nil, authors: nil, hashtag: htags) } diff --git a/damus/Nostr/NostrLink.swift b/damus/Nostr/NostrLink.swift @@ -92,8 +92,8 @@ func decode_nostr_uri(_ s: String) -> NostrLink? { return } - if parts.count >= 3 && parts[1] == "hashtag" { - return .filter(NostrFilter.filter_hashtag([parts[2]])) + if parts.count >= 2 && parts[0] == "hashtag" { + return .filter(NostrFilter.filter_hashtag([parts[1]])) } return tag_to_refid(parts).map { .ref($0) } diff --git a/damus/Nostr/Profiles.swift b/damus/Nostr/Profiles.swift @@ -10,12 +10,11 @@ import UIKit import Combine -class Profiles: ObservableObject { - @Published var profiles: [String: TimestampedProfile] = [:] +class Profiles { + var profiles: [String: TimestampedProfile] = [:] func add(id: String, profile: TimestampedProfile) { profiles[id] = profile - objectWillChange.send() } func lookup(id: String) -> Profile? { diff --git a/damus/Notifications.swift b/damus/Notifications.swift @@ -32,8 +32,8 @@ extension Notification.Name { } extension Notification.Name { - static var profile_update: Notification.Name { - return Notification.Name("profile_update") + static var profile_updated: Notification.Name { + return Notification.Name("profile_updated") } } diff --git a/damus/Util/InsertSort.swift b/damus/Util/InsertSort.swift @@ -0,0 +1,29 @@ +// +// InsertSort.swift +// damus +// +// Created by William Casarin on 2022-05-09. +// + +import Foundation + + +func insert_uniq_sorted_event(events: inout [NostrEvent], new_ev: NostrEvent) -> Bool { + var i: Int = 0 + + for event in events { + // don't insert duplicate events + if new_ev.id == event.id { + return false + } + + if new_ev.created_at < event.created_at { + events.insert(new_ev, at: i) + return true + } + i += 1 + } + + events.append(new_ev) + return true +} diff --git a/damus/Util/Parser.swift b/damus/Util/Parser.swift @@ -51,6 +51,10 @@ func parse_str(_ p: Parser, _ s: String) -> Bool { } func parse_char(_ p: Parser, _ c: Character) -> Bool { + if p.pos >= p.str.count { + return false + } + let ind = p.str.index(p.str.startIndex, offsetBy: p.pos) if p.str[ind] == c { diff --git a/damus/Views/ChatView.swift b/damus/Views/ChatView.swift @@ -14,7 +14,6 @@ struct ChatView: View { let damus: DamusState - @EnvironmentObject var profiles: Profiles @EnvironmentObject var thread: ThreadModel var just_started: Bool { @@ -72,7 +71,7 @@ struct ChatView: View { } var ReplyDescription: some View { - Text("\(reply_desc(profiles: profiles, event: event))") + Text("\(reply_desc(profiles: damus.profiles, event: event))") .font(.footnote) .foregroundColor(.gray) .frame(alignment: .leading) @@ -84,8 +83,7 @@ struct ChatView: View { HStack { VStack { if is_active || just_started { - ProfilePicView(pubkey: event.pubkey, size: 32, highlight: is_active ? .main : .none, image_cache: damus.image_cache) - .environmentObject(profiles) + ProfilePicView(pubkey: event.pubkey, size: 32, highlight: is_active ? .main : .none, image_cache: damus.image_cache, profiles: damus.profiles) } Spacer() @@ -96,7 +94,7 @@ struct ChatView: View { VStack(alignment: .leading) { if just_started { HStack { - ProfileName(pubkey: event.pubkey, profile: profiles.lookup(id: event.pubkey)) + ProfileName(pubkey: event.pubkey, profile: damus.profiles.lookup(id: event.pubkey)) .foregroundColor(colorScheme == .dark ? id_to_color(event.pubkey) : Color.black) //.shadow(color: Color.black, radius: 2) Text("\(format_relative_time(event.created_at))") @@ -106,21 +104,20 @@ struct ChatView: View { if let ref_id = thread.replies.lookup(event.id) { if !is_reply_to_prev() { - ReplyQuoteView(quoter: event, event_id: ref_id, image_cache: damus.image_cache) + ReplyQuoteView(quoter: event, event_id: ref_id, image_cache: damus.image_cache, profiles: damus.profiles) .environmentObject(thread) - .environmentObject(profiles) ReplyDescription } } - NoteContentView(event: event, profiles: profiles, content: event.content) + NoteContentView(event: event, profiles: damus.profiles, content: event.content) if is_active || next_ev == nil || next_ev!.pubkey != event.pubkey { EventActionBar(event: event, our_pubkey: damus.pubkey, + profiles: damus.profiles, bar: make_actionbar_model(ev: event, counter: damus.likes) ) - .environmentObject(profiles) } //Spacer() diff --git a/damus/Views/EventActionBar.swift b/damus/Views/EventActionBar.swift @@ -21,7 +21,7 @@ struct EventActionBar: View { let event: NostrEvent let our_pubkey: String @State var sheet: ActionBarSheet? = nil - @EnvironmentObject var profiles: Profiles + let profiles: Profiles @StateObject var bar: ActionBarModel var body: some View { diff --git a/damus/Views/EventDetailView.swift b/damus/Views/EventDetailView.swift @@ -36,8 +36,6 @@ struct EventDetailView: View { @StateObject var thread: ThreadModel @State var collapsed: Bool = true - - @EnvironmentObject var profiles: Profiles func toggle_collapse_thread(scroller: ScrollViewProxy, id mid: String?, animate: Bool = true, anchor: UnitPoint = .center) { self.collapsed = !self.collapsed diff --git a/damus/Views/EventView.swift b/damus/Views/EventView.swift @@ -42,19 +42,16 @@ struct EventView: View { let has_action_bar: Bool let damus: DamusState - @EnvironmentObject var profiles: Profiles @EnvironmentObject var action_bar: ActionBarModel var body: some View { - let profile = profiles.lookup(id: event.pubkey) + let profile = damus.profiles.lookup(id: event.pubkey) HStack { VStack { let pv = ProfileView(damus: damus, profile: ProfileModel(pubkey: event.pubkey, damus: damus)) - .environmentObject(profiles) NavigationLink(destination: pv) { - ProfilePicView(pubkey: event.pubkey, size: PFP_SIZE!, highlight: highlight, image_cache: damus.image_cache) - .environmentObject(profiles) + ProfilePicView(pubkey: event.pubkey, size: PFP_SIZE!, highlight: highlight, image_cache: damus.image_cache, profiles: damus.profiles) } Spacer() @@ -69,21 +66,20 @@ struct EventView: View { } if event.is_reply { - Text("\(reply_desc(profiles: profiles, event: event))") + Text("\(reply_desc(profiles: damus.profiles, event: event))") .font(.footnote) .foregroundColor(.gray) .frame(maxWidth: .infinity, alignment: .leading) } - NoteContentView(event: event, profiles: profiles, content: event.content) + NoteContentView(event: event, profiles: damus.profiles, content: event.content) .frame(maxWidth: .infinity, alignment: .leading) .textSelection(.enabled) Spacer() if has_action_bar { - EventActionBar(event: event, our_pubkey: damus.pubkey, bar: make_actionbar_model(ev: event, counter: damus.likes)) - .environmentObject(profiles) + EventActionBar(event: event, our_pubkey: damus.pubkey, profiles: damus.profiles, bar: make_actionbar_model(ev: event, counter: damus.likes)) } Divider() diff --git a/damus/Views/MentionView.swift b/damus/Views/MentionView.swift @@ -9,14 +9,12 @@ import SwiftUI struct MentionView: View { let mention: Mention - - @EnvironmentObject var profiles: Profiles + let profiles: Profiles var body: some View { switch mention.type { case .pubkey: PubkeyView(pubkey: mention.ref.ref_id, relay: mention.ref.relay_id) - .environmentObject(profiles) case .event: Text("< e >") //EventBlockView(pubkey: mention.ref.ref_id, relay: mention.ref.relay_id) diff --git a/damus/Views/NoteContentView.swift b/damus/Views/NoteContentView.swift @@ -43,7 +43,7 @@ struct NoteContentView: View { .onAppear() { self.content = render_note_content(ev: event, profiles: profiles) } - .onReceive(handle_notify(.profile_update)) { notif in + .onReceive(handle_notify(.profile_updated)) { notif in let profile = notif.object as! ProfileUpdate for block in event.blocks { switch block { diff --git a/damus/Views/ProfileName.swift b/damus/Views/ProfileName.swift @@ -7,9 +7,24 @@ import SwiftUI -func ProfileName(pubkey: String, profile: Profile?) -> some View { - Text(String(Profile.displayName(profile: profile, pubkey: pubkey))) - //.foregroundColor(hex_to_rgb(pubkey)) - .bold() +struct ProfileName: View { + let pubkey: String + let profile: Profile? + + @State var display_name: String? + + var body: some View { + Text(String(display_name ?? Profile.displayName(profile: profile, pubkey: pubkey))) + //.foregroundColor(hex_to_rgb(pubkey)) + .bold() + .onReceive(handle_notify(.profile_updated)) { notif in + let update = notif.object as! ProfileUpdate + if update.pubkey != pubkey { + return + } + display_name = Profile.displayName(profile: update.profile, pubkey: pubkey) + } + } } + diff --git a/damus/Views/ProfilePicView.swift b/damus/Views/ProfilePicView.swift @@ -37,11 +37,11 @@ struct ProfilePicView: View { let size: CGFloat let highlight: Highlight let image_cache: ImageCache + let profiles: Profiles + @State var picture: String? = nil @State var img: Image? = nil - @EnvironmentObject var profiles: Profiles - var PlaceholderColor: Color { return id_to_color(pubkey) } @@ -76,7 +76,7 @@ struct ProfilePicView: View { var MainContent: some View { Group { - let picture = profiles.lookup(id: pubkey)?.picture + let picture = picture ?? profiles.lookup(id: pubkey)?.picture if let pic_url = picture.flatMap { URL(string: $0) } { ProfilePic(pic_url) } else { @@ -87,6 +87,16 @@ struct ProfilePicView: View { var body: some View { MainContent + .onReceive(handle_notify(.profile_updated)) { notif in + let updated = notif.object as! ProfileUpdate + if updated.pubkey != pubkey { + return + } + + if updated.profile.picture != picture { + picture = updated.profile.picture + } + } } } diff --git a/damus/Views/ProfileView.swift b/damus/Views/ProfileView.swift @@ -19,14 +19,12 @@ struct ProfileView: View { @StateObject var profile: ProfileModel //@EnvironmentObject var profile: ProfileModel - @EnvironmentObject var profiles: Profiles var TopSection: some View { VStack(alignment: .leading) { - let data = profiles.lookup(id: profile.pubkey) + let data = damus.profiles.lookup(id: profile.pubkey) HStack(alignment: .top) { - ProfilePicView(pubkey: profile.pubkey, size: PFP_SIZE!, highlight: .custom(Color.black, 2), image_cache: damus.image_cache) - .environmentObject(profiles) + ProfilePicView(pubkey: profile.pubkey, size: PFP_SIZE!, highlight: .custom(Color.black, 2), image_cache: damus.image_cache, profiles: damus.profiles) Spacer() @@ -56,7 +54,6 @@ struct ProfileView: View { Divider() InnerTimelineView(events: $profile.events, damus: damus) - .environmentObject(profiles) } .frame(maxHeight: .infinity, alignment: .topLeading) } diff --git a/damus/Views/ReplyQuoteView.swift b/damus/Views/ReplyQuoteView.swift @@ -11,8 +11,8 @@ struct ReplyQuoteView: View { let quoter: NostrEvent let event_id: String let image_cache: ImageCache + let profiles: Profiles - @EnvironmentObject var profiles: Profiles @EnvironmentObject var thread: ThreadModel func MainContent(event: NostrEvent) -> some View { @@ -23,8 +23,7 @@ struct ReplyQuoteView: View { VStack(alignment: .leading) { HStack(alignment: .top) { - ProfilePicView(pubkey: event.pubkey, size: 16, highlight: .reply, image_cache: image_cache) - .environmentObject(profiles) + ProfilePicView(pubkey: event.pubkey, size: 16, highlight: .reply, image_cache: image_cache, profiles: profiles) Text(Profile.displayName(profile: profiles.lookup(id: event.pubkey), pubkey: event.pubkey)) .foregroundColor(.accentColor) Text("\(format_relative_time(event.created_at))") diff --git a/damus/Views/ReplyView.swift b/damus/Views/ReplyView.swift @@ -18,8 +18,6 @@ struct ReplyView: View { let replying_to: NostrEvent let damus: DamusState - @EnvironmentObject var profiles: Profiles - var body: some View { VStack { Text("Replying to:") @@ -27,7 +25,7 @@ struct ReplyView: View { let names = all_referenced_pubkeys(replying_to) .map { pubkey in let pk = pubkey.ref_id - let prof = profiles.lookup(id: pk) + let prof = damus.profiles.lookup(id: pk) return Profile.displayName(profile: prof, pubkey: pk) } .joined(separator: ", ") diff --git a/damus/Views/SearchView.swift b/damus/Views/SearchView.swift @@ -0,0 +1,32 @@ +// +// SearchView.swift +// damus +// +// Created by William Casarin on 2022-05-09. +// + +import SwiftUI + +struct SearchView: View { + let appstate: DamusState + @StateObject var search: SearchModel + + var body: some View { + TimelineView(events: $search.events, damus: appstate) + .padding([.leading, .trailing], 6) + .onAppear() { + search.subscribe() + } + .onDisappear() { + search.unsubscribe() + } + } +} + +/* +struct SearchView_Previews: PreviewProvider { + static var previews: some View { + SearchView() + } +} + */ diff --git a/damus/Views/ThreadView.swift b/damus/Views/ThreadView.swift @@ -13,7 +13,6 @@ struct ThreadView: View { @StateObject var thread: ThreadModel let damus: DamusState - @EnvironmentObject var profiles: Profiles @Environment(\.dismiss) var dismiss var body: some View { @@ -21,12 +20,10 @@ struct ThreadView: View { if is_chatroom { ChatroomView(damus: damus) .navigationBarTitle("Chat") - .environmentObject(profiles) .environmentObject(thread) } else { EventDetailView(damus: damus, thread: thread) .navigationBarTitle("Thread") - .environmentObject(profiles) .environmentObject(thread) } diff --git a/damus/Views/TimelineView.swift b/damus/Views/TimelineView.swift @@ -14,14 +14,12 @@ enum TimelineAction { struct InnerTimelineView: View { @Binding var events: [NostrEvent] - @EnvironmentObject var profiles: Profiles let damus: DamusState var body: some View { LazyVStack { ForEach(events, id: \.id) { (ev: NostrEvent) in let tv = ThreadView(thread: ThreadModel(event: ev, pool: damus.pool), damus: damus) - .environmentObject(profiles) NavigationLink(destination: tv) { EventView(event: ev, highlight: .none, has_action_bar: true, damus: damus) @@ -36,21 +34,17 @@ struct InnerTimelineView: View { struct TimelineView: View { @Binding var events: [NostrEvent] - @EnvironmentObject var profiles: Profiles - let damus: DamusState var body: some View { MainContent .padding([.leading, .trailing], 6) - .environmentObject(profiles) } var MainContent: some View { ScrollViewReader { scroller in ScrollView { InnerTimelineView(events: $events, damus: damus) - .environmentObject(profiles) } .onReceive(NotificationCenter.default.publisher(for: .scroll_to_top)) { _ in guard let event = events.first else {