damus

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

commit d574e572d43390006663bdef46c1a833f28fa2a3
parent a88324333b2a616c6d9838af67e054cb63de5421
Author: William Casarin <jb55@jb55.com>
Date:   Sat, 30 Apr 2022 11:46:56 -0700

old style navigation

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

Diffstat:
Mdamus/ContentView.swift | 36++++--------------------------------
Mdamus/Models/ProfileModel.swift | 35+++++++++--------------------------
Mdamus/Models/ThreadModel.swift | 39+++++++++++++++++++--------------------
Mdamus/Notifications.swift | 18++++++++++++++++++
Mdamus/Views/ChatView.swift | 5+----
Mdamus/Views/ChatroomView.swift | 6+++---
Mdamus/Views/EventActionBar.swift | 27++++++++++++++++++++-------
Mdamus/Views/EventDetailView.swift | 68++++++++++++++++++++++++++++++++++----------------------------------
Mdamus/Views/EventView.swift | 12++++++++----
Mdamus/Views/ProfileView.swift | 12++++++++++--
Mdamus/Views/ReplyView.swift | 3++-
Mdamus/Views/ThreadView.swift | 6+++---
Mdamus/Views/TimelineView.swift | 14++++++++++----
13 files changed, 141 insertions(+), 140 deletions(-)

diff --git a/damus/ContentView.swift b/damus/ContentView.swift @@ -44,12 +44,10 @@ struct ContentView: View { @State var status: String = "Not connected" @State var active_sheet: Sheets? = nil @State var profiles: Profiles = Profiles() - @State var active_profile: ProfileModel = ProfileModel() @State var friends: [String: ()] = [:] @State var loading: Bool = true @State var pool: RelayPool? = nil @State var selected_timeline: Timeline? = .home - @StateObject var thread: ThreadModel = ThreadModel() @State var is_thread_open: Bool = false @State var is_profile_open: Bool = false @State var last_event_of_kind: [String: [Int: NostrEvent]] = [:] @@ -139,7 +137,6 @@ struct ContentView: View { ZStack { if let pool = self.pool { TimelineView(events: $friend_events, pool: pool) - .environmentObject(thread) .environmentObject(profiles) } PostButtonContainer @@ -169,23 +166,6 @@ struct ContentView: View { case .none: EmptyView() } - - let tv = ThreadView() - .environmentObject(thread) - .environmentObject(profiles) - .padding([.leading, .trailing], 6) - - let pv = ProfileView(pool: pool) - .environmentObject(active_profile) - .environmentObject(profiles) - - NavigationLink(destination: tv, isActive: $is_thread_open) { - EmptyView() - } - - NavigationLink(destination: pv, isActive: $is_profile_open) { - EmptyView() - } } .navigationBarTitle("Damus", displayMode: .inline) } @@ -212,7 +192,7 @@ struct ContentView: View { case .post: PostView(references: []) case .reply(let event): - ReplyView(replying_to: event) + ReplyView(replying_to: event, pool: pool!) .environmentObject(profiles) } } @@ -222,10 +202,9 @@ struct ContentView: View { self.pool?.send(.event(boost)) } .onReceive(handle_notify(.open_thread)) { obj in - let ev = obj.object as! NostrEvent - thread.reset_events() - thread.set_active_event(ev) - is_thread_open = true + //let ev = obj.object as! NostrEvent + //thread.set_active_event(ev) + //is_thread_open = true } .onReceive(handle_notify(.reply)) { notif in let ev = notif.object as! NostrEvent @@ -235,11 +214,6 @@ struct ContentView: View { let ev = obj.object as! NostrEvent self.pool?.send(.event(ev)) } - .onReceive(handle_notify(.click_profile_pic)) { obj in - let pubkey = obj.object as! String - self.active_profile.set_pubkey(pubkey) - self.is_profile_open = true - } .onReceive(handle_notify(.post)) { obj in let post_res = obj.object as! NostrPostResult switch post_res { @@ -318,8 +292,6 @@ struct ContentView: View { pool.register_handler(sub_id: sub_id, handler: handle_event) self.pool = pool - self.thread.pool = pool - self.active_profile.pool = pool pool.connect() } diff --git a/damus/Models/ProfileModel.swift b/damus/Models/ProfileModel.swift @@ -9,41 +9,24 @@ import Foundation class ProfileModel: ObservableObject { @Published var events: [NostrEvent] = [] - @Published var pubkey: String? - var seen_event: Set<String> = Set() + let pubkey: String + let pool: RelayPool + var seen_event: Set<String> = Set() var sub_id = UUID().description - var pool: RelayPool? = nil - deinit { - unsubscribe() + init(pubkey: String, pool: RelayPool) { + self.pubkey = pubkey + self.pool = pool } func unsubscribe() { - print("unsubscribing from profile \(pubkey ?? "?") with sub_id \(sub_id)") - pool?.unsubscribe(sub_id: sub_id) - } - - func set_pubkey(_ pk: String) { - if pk == self.pubkey { - return - } - - self.events.removeAll() - self.seen_event.removeAll() - - unsubscribe() - self.sub_id = UUID().description - self.pubkey = pk - subscribe() + print("unsubscribing from profile \(pubkey) with sub_id \(sub_id)") + pool.unsubscribe(sub_id: sub_id) } func subscribe() { - guard let pubkey = self.pubkey else { - return - } - let kinds: [Int] = [ NostrKind.text.rawValue, NostrKind.delete.rawValue, @@ -54,7 +37,7 @@ class ProfileModel: ObservableObject { filter.authors = [pubkey] print("subscribing to profile \(pubkey) with sub_id \(sub_id)") - pool?.subscribe(sub_id: sub_id, filters: [filter], handler: handle_event) + pool.subscribe(sub_id: sub_id, filters: [filter], handler: handle_event) } func add_event(_ ev: NostrEvent) { diff --git a/damus/Models/ThreadModel.swift b/damus/Models/ThreadModel.swift @@ -9,25 +9,27 @@ import Foundation /// manages the lifetime of a thread class ThreadModel: ObservableObject { - @Published var event: NostrEvent? = nil + @Published var event: NostrEvent @Published var events: [NostrEvent] = [] @Published var event_map: [String: Int] = [:] var replies: ReplyMap = ReplyMap() - var pool: RelayPool? = nil + let pool: RelayPool var sub_id = UUID().description + init(ev: NostrEvent, pool: RelayPool) { + self.event = ev + self.pool = pool + subscribe() + } + deinit { unsubscribe() } func unsubscribe() { - guard let event = self.event else { - return - } + self.pool.unsubscribe(sub_id: sub_id) print("unsubscribing from thread \(event.id) with sub_id \(sub_id)") - self.pool?.remove_handler(sub_id: sub_id) - self.pool?.send(.unsubscribe(sub_id)) } func reset_events() { @@ -41,16 +43,13 @@ class ThreadModel: ObservableObject { return true } - guard let ev_a = self.event else { - return true - } - if ev_b.is_root_event() { return false } // rough heuristic to save us from resubscribing all the time - return ev_b.count_ids() != ev_a.count_ids() + //return ev_b.count_ids() != self.event.count_ids() + return true } func set_active_event(_ ev: NostrEvent) { @@ -58,7 +57,7 @@ class ThreadModel: ObservableObject { unsubscribe() self.event = ev add_event(ev) - subscribe(ev) + subscribe() } else { self.event = ev if events.count == 0 { @@ -67,20 +66,20 @@ class ThreadModel: ObservableObject { } } - private func subscribe(_ ev: NostrEvent) { + func subscribe() { let kinds: [Int] = [1, 5, 6] var ref_events = NostrFilter.filter_kinds(kinds) var events_filter = NostrFilter.filter_kinds(kinds) // TODO: add referenced relays - ref_events.referenced_ids = ev.referenced_ids.map { $0.ref_id } - ref_events.referenced_ids!.append(ev.id) + ref_events.referenced_ids = event.referenced_ids.map { $0.ref_id } + ref_events.referenced_ids!.append(event.id) events_filter.ids = ref_events.referenced_ids! - print("subscribing to thread \(ev.id) with sub_id \(sub_id)") - pool?.register_handler(sub_id: sub_id, handler: handle_event) - pool?.send(.subscribe(.init(filters: [ref_events, events_filter], sub_id: sub_id))) + print("subscribing to thread \(event.id) with sub_id \(sub_id)") + pool.register_handler(sub_id: sub_id, handler: handle_event) + pool.send(.subscribe(.init(filters: [ref_events, events_filter], sub_id: sub_id))) } func lookup(_ event_id: String) -> NostrEvent? { @@ -122,7 +121,7 @@ class ThreadModel: ObservableObject { case .notice(let note): if note.contains("Too many subscription filters") { // TODO: resend filters? - pool?.reconnect(to: [relay_id]) + pool.reconnect(to: [relay_id]) } break } diff --git a/damus/Notifications.swift b/damus/Notifications.swift @@ -62,6 +62,24 @@ extension Notification.Name { } extension Notification.Name { + static var notice: Notification.Name { + return Notification.Name("notice") + } +} + +extension Notification.Name { + static var like: Notification.Name { + return Notification.Name("like note") + } +} + +extension Notification.Name { + static var delete: Notification.Name { + return Notification.Name("delete note") + } +} + +extension Notification.Name { static var post: Notification.Name { return Notification.Name("send post") } diff --git a/damus/Views/ChatView.swift b/damus/Views/ChatView.swift @@ -40,10 +40,7 @@ struct ChatView: View { } var is_active: Bool { - guard let ev = thread.event else { - return false - } - return ev.id == event.id + return thread.event.id == event.id } func prev_reply_is_same() -> String? { diff --git a/damus/Views/ChatroomView.swift b/damus/Views/ChatroomView.swift @@ -22,7 +22,7 @@ struct ChatroomView: View { next_ev: ind == count-1 ? nil : thread.events[ind+1] ) .onTapGesture { - if thread.event!.id == ev.id { + if thread.event.id == ev.id { //dismiss() toggle_thread_view() } else { @@ -35,13 +35,13 @@ struct ChatroomView: View { } .onReceive(NotificationCenter.default.publisher(for: .select_quote)) { notif in let ev = notif.object as! NostrEvent - if ev.id != thread.event!.id { + if ev.id != thread.event.id { thread.set_active_event(ev) } scroll_to_event(scroller: scroller, id: ev.id, delay: 0, animate: true, anchor: .top) } .onAppear() { - scroll_to_event(scroller: scroller, id: thread.event!.id, delay: 0.3, animate: true, anchor: .bottom) + scroll_to_event(scroller: scroller, id: thread.event.id, delay: 0.3, animate: true, anchor: .bottom) } } } diff --git a/damus/Views/EventActionBar.swift b/damus/Views/EventActionBar.swift @@ -19,10 +19,10 @@ enum ActionBarSheet: Identifiable { struct EventActionBar: View { let event: NostrEvent - @State var sheet: ActionBarSheet? = nil @EnvironmentObject var profiles: Profiles - + @StateObject var bar: ActionBarModel = ActionBarModel() + var body: some View { HStack { /* @@ -33,13 +33,26 @@ struct EventActionBar: View { Spacer() */ - EventActionButton(img: "bubble.left") { + EventActionButton(img: "bubble.left", col: nil) { notify(.reply, event) } .padding([.trailing], 40) - EventActionButton(img: "arrow.2.squarepath") { - notify(.boost, event) + EventActionButton(img: bar.liked ? "heart.fill" : "heart", col: bar.liked ? Color.red : nil) { + if bar.liked { + notify(.delete, bar.our_like_event) + } else { + notify(.like, event) + } + } + .padding([.trailing], 40) + + EventActionButton(img: "arrow.2.squarepath", col: bar.boosted ? Color.green : nil) { + if bar.boosted { + notify(.delete, bar.our_boost_event) + } else { + notify(.boost, event) + } } } @@ -47,11 +60,11 @@ struct EventActionBar: View { } -func EventActionButton(img: String, action: @escaping () -> ()) -> some View { +func EventActionButton(img: String, col: Color?, action: @escaping () -> ()) -> some View { Button(action: action) { Label("", systemImage: img) .font(.footnote) - .foregroundColor(.gray) + .foregroundColor(col == nil ? Color.gray : col!) } } diff --git a/damus/Views/EventDetailView.swift b/damus/Views/EventDetailView.swift @@ -31,8 +31,8 @@ enum CollapsedEvent: Identifiable { struct EventDetailView: View { - let sub_id = UUID().description + let pool: RelayPool @StateObject var thread: ThreadModel @State var collapsed: Bool = true @@ -48,37 +48,6 @@ struct EventDetailView: View { } } - /* - func OldEventView(proxy: ScrollViewProxy, ev: NostrEvent, highlight: Highlight, collapsed_events: [CollapsedEvent]) -> some View { - Group { - if ev.id == thread.event.id { - EventView(event: ev, highlight: .main, has_action_bar: true) - .onAppear() { - scroll_to_event(scroller: proxy, id: ev.id, delay: 0.5, animate: true) - } - .onTapGesture { - print_event(ev) - let any = any_collapsed(collapsed_events) - if (collapsed && any) || (!collapsed && !any) { - toggle_collapse_thread(scroller: proxy, id: ev.id) - } - } - } else { - if !(self.collapsed && highlight.is_none) { - EventView(event: ev, highlight: collapsed ? .none : highlight, has_action_bar: true) - .onTapGesture { - print_event(ev) - if !collapsed { - toggle_collapse_thread(scroller: proxy, id: ev.id) - } - thread.event = ev - } - } - } - } - } - */ - func uncollapse_section(scroller: ScrollViewProxy, c: CollapsedEvents) { let ev = thread.events[c.start] @@ -101,9 +70,9 @@ struct EventDetailView: View { toggle_thread_view() } case .event(let ev, let highlight): - EventView(event: ev, highlight: highlight, has_action_bar: true) + EventView(event: ev, highlight: highlight, has_action_bar: true, pool: pool) .onTapGesture { - if thread.event!.id == ev.id { + if thread.event.id == ev.id { toggle_thread_view() } else { thread.set_active_event(ev) @@ -324,3 +293,34 @@ func scroll_to_event(scroller: ScrollViewProxy, id: String, delay: Double, anima } } + + /* + func OldEventView(proxy: ScrollViewProxy, ev: NostrEvent, highlight: Highlight, collapsed_events: [CollapsedEvent]) -> some View { + Group { + if ev.id == thread.event.id { + EventView(event: ev, highlight: .main, has_action_bar: true) + .onAppear() { + scroll_to_event(scroller: proxy, id: ev.id, delay: 0.5, animate: true) + } + .onTapGesture { + print_event(ev) + let any = any_collapsed(collapsed_events) + if (collapsed && any) || (!collapsed && !any) { + toggle_collapse_thread(scroller: proxy, id: ev.id) + } + } + } else { + if !(self.collapsed && highlight.is_none) { + EventView(event: ev, highlight: collapsed ? .none : highlight, has_action_bar: true) + .onTapGesture { + print_event(ev) + if !collapsed { + toggle_collapse_thread(scroller: proxy, id: ev.id) + } + thread.event = ev + } + } + } + } + } + */ diff --git a/damus/Views/EventView.swift b/damus/Views/EventView.swift @@ -40,17 +40,21 @@ struct EventView: View { let event: NostrEvent let highlight: Highlight let has_action_bar: Bool + let pool: RelayPool @EnvironmentObject var profiles: Profiles + @EnvironmentObject var action_bar: ActionBarModel var body: some View { let profile = profiles.lookup(id: event.pubkey) HStack { VStack { - ProfilePicView(picture: profile?.picture, size: PFP_SIZE!, highlight: highlight) - .onTapGesture { - NotificationCenter.default.post(name: .click_profile_pic, object: event.pubkey) - } + let pv = ProfileView(pool: pool, profile: ProfileModel(pubkey: event.pubkey, pool: pool)) + .environmentObject(profiles) + + NavigationLink(destination: pv) { + ProfilePicView(picture: profile?.picture, size: PFP_SIZE!, highlight: highlight) + } Spacer() } diff --git a/damus/Views/ProfileView.swift b/damus/Views/ProfileView.swift @@ -15,13 +15,14 @@ enum ProfileTab: Hashable { struct ProfileView: View { let pool: RelayPool @State private var selected_tab: ProfileTab = .posts + @StateObject var profile: ProfileModel - @EnvironmentObject var profile: ProfileModel + //@EnvironmentObject var profile: ProfileModel @EnvironmentObject var profiles: Profiles var TopSection: some View { HStack(alignment: .top) { - let data = profile.pubkey.flatMap { profiles.lookup(id: $0) } + let data = profiles.lookup(id: profile.pubkey) ProfilePicView(picture: data?.picture, size: 64, highlight: .custom(Color.black, 4)) //.border(Color.blue) VStack(alignment: .leading) { @@ -64,6 +65,13 @@ struct ProfileView: View { //.border(Color.white) .frame(maxWidth: .infinity, alignment: .topLeading) .navigationBarTitle("Profile") + .onAppear() { + profile.subscribe() + } + .onDisappear { + profile.unsubscribe() + // our profilemodel needs a bit more help + } } } diff --git a/damus/Views/ReplyView.swift b/damus/Views/ReplyView.swift @@ -16,6 +16,7 @@ func all_referenced_pubkeys(_ ev: NostrEvent) -> [ReferencedId] { struct ReplyView: View { let replying_to: NostrEvent + let pool: RelayPool @EnvironmentObject var profiles: Profiles @@ -34,7 +35,7 @@ struct ReplyView: View { .foregroundColor(.gray) .font(.footnote) } - EventView(event: replying_to, highlight: .none, has_action_bar: false) + EventView(event: replying_to, highlight: .none, has_action_bar: false, pool: pool) PostView(references: replying_to.reply_ids()) Spacer() diff --git a/damus/Views/ThreadView.swift b/damus/Views/ThreadView.swift @@ -10,9 +10,10 @@ import SwiftUI struct ThreadView: View { @State var is_chatroom: Bool = false + @StateObject var thread: ThreadModel + let pool: RelayPool @EnvironmentObject var profiles: Profiles - @EnvironmentObject var thread: ThreadModel @Environment(\.dismiss) var dismiss var body: some View { @@ -23,13 +24,12 @@ struct ThreadView: View { .environmentObject(profiles) .environmentObject(thread) } else { - EventDetailView(thread: thread) + EventDetailView(pool: pool, thread: thread) .navigationBarTitle("Thread") .environmentObject(profiles) .environmentObject(thread) } - /* NavigationLink(destination: edv, isActive: $is_chatroom) { EmptyView() diff --git a/damus/Views/TimelineView.swift b/damus/Views/TimelineView.swift @@ -37,10 +37,16 @@ struct TimelineView: View { .environmentObject(profiles) */ - EventView(event: ev, highlight: .none, has_action_bar: true) - .onTapGesture { - NotificationCenter.default.post(name: .open_thread, object: ev) - } + let tv = ThreadView(thread: ThreadModel(ev: ev, pool: pool), pool: pool) + .environmentObject(profiles) + + NavigationLink(destination: tv) { + EventView(event: ev, highlight: .none, has_action_bar: true, pool: pool) + } + .buttonStyle(PlainButtonStyle()) + //.onTapGesture { + //NotificationCenter.default.post(name: .open_thread, object: ev) + //} } } }