damus

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

commit 7bdd8048b074b12717afed96b9cebb971866785c
parent 492786f66d9048f7f244ff7d566aed52cb268596
Author: William Casarin <jb55@jb55.com>
Date:   Tue, 13 Sep 2022 17:23:56 -0700

better channels

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

Diffstat:
Mdamus.xcodeproj/project.pbxproj | 4++++
Mdamus/ContentView.swift | 2+-
Adamus/Models/ChatroomMetadata.swift | 15+++++++++++++++
Mdamus/Models/HomeModel.swift | 36+++++++++++++++++++++++++++++++++---
Mdamus/Models/ProfileModel.swift | 5+++--
Mdamus/Models/SearchHomeModel.swift | 46+++++++++++++++++++++++++++++++---------------
Mdamus/Models/SearchModel.swift | 39++++++++++++++++++++++++++-------------
Mdamus/Models/ThreadModel.swift | 78++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Mdamus/Nostr/NostrKind.swift | 2++
Mdamus/Util/ImageCache.swift | 2+-
Mdamus/Util/Notifications.swift | 6++++++
Mdamus/Views/ChatView.swift | 5++---
Mdamus/Views/ChatroomView.swift | 12++++++++++--
Mdamus/Views/EventDetailView.swift | 19++++++++++---------
Mdamus/Views/ProfilePicView.swift | 6+++---
Mdamus/Views/ReplyQuoteView.swift | 5+----
Mdamus/Views/ThreadView.swift | 15++++++++++-----
Mdamus/Views/TimelineView.swift | 2+-
18 files changed, 211 insertions(+), 88 deletions(-)

diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj @@ -81,6 +81,7 @@ 4C90BD1A283AA67F008EE7EF /* Bech32.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C90BD19283AA67F008EE7EF /* Bech32.swift */; }; 4C90BD1C283AC38E008EE7EF /* Bech32Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C90BD1B283AC38E008EE7EF /* Bech32Tests.swift */; }; 4C987B57283FD07F0042CE38 /* FollowersModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C987B56283FD07F0042CE38 /* FollowersModel.swift */; }; + 4C99737B28C92A9200E53835 /* ChatroomMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C99737A28C92A9200E53835 /* ChatroomMetadata.swift */; }; 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 */; }; @@ -200,6 +201,7 @@ 4C90BD19283AA67F008EE7EF /* Bech32.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bech32.swift; sourceTree = "<group>"; }; 4C90BD1B283AC38E008EE7EF /* Bech32Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bech32Tests.swift; sourceTree = "<group>"; }; 4C987B56283FD07F0042CE38 /* FollowersModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowersModel.swift; sourceTree = "<group>"; }; + 4C99737A28C92A9200E53835 /* ChatroomMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatroomMetadata.swift; sourceTree = "<group>"; }; 4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineView.swift; sourceTree = "<group>"; }; 4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyView.swift; sourceTree = "<group>"; }; 4CACA9DB280C38C000D9BBE8 /* Profiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Profiles.swift; sourceTree = "<group>"; }; @@ -285,6 +287,7 @@ 4C649843285A952100EAE2B3 /* LocalUserConfig.swift */, 4C64987D286D082C00EAE2B3 /* DirectMessagesModel.swift */, 4C216F372871EDE300040376 /* DirectMessageModel.swift */, + 4C99737A28C92A9200E53835 /* ChatroomMetadata.swift */, ); path = Models; sourceTree = "<group>"; @@ -673,6 +676,7 @@ 4C90BD1A283AA67F008EE7EF /* Bech32.swift in Sources */, 4CACA9D5280C31E100D9BBE8 /* ReplyView.swift in Sources */, 4C0A3F97280F8E02000448DE /* ThreadView.swift in Sources */, + 4C99737B28C92A9200E53835 /* ChatroomMetadata.swift in Sources */, 4C75EFA427FA577B0006080F /* PostView.swift in Sources */, 4C75EFB528049D790006080F /* Relay.swift in Sources */, 4CEE2AF1280B216B00AB5EEF /* EventDetailView.swift in Sources */, diff --git a/damus/ContentView.swift b/damus/ContentView.swift @@ -146,7 +146,7 @@ struct ContentView: View { var MaybeThreadView: some View { Group { if let evid = self.active_event_id { - let thread_model = ThreadModel(evid: evid, pool: damus_state!.pool, privkey: damus_state!.keypair.privkey) + let thread_model = ThreadModel(evid: evid, damus_state: damus_state!) ThreadView(thread: thread_model, damus: damus_state!, is_chatroom: false) } else { EmptyView() diff --git a/damus/Models/ChatroomMetadata.swift b/damus/Models/ChatroomMetadata.swift @@ -0,0 +1,15 @@ +// +// ChatroomMetadata.swift +// damus +// +// Created by William Casarin on 2022-09-07. +// + +import Foundation + + +struct ChatroomMetadata: Decodable { + let name: String? + let about: String? + let picture: String? +} diff --git a/damus/Models/HomeModel.swift b/damus/Models/HomeModel.swift @@ -34,6 +34,8 @@ class HomeModel: ObservableObject { var damus_state: DamusState var has_event: [String: Set<String>] = [:] + var deleted_events: Set<String> = Set() + var channels: [String: NostrEvent] = [:] var last_event_of_kind: [String: [Int: NostrEvent]] = [:] var done_init: Bool = false @@ -100,9 +102,32 @@ class HomeModel: ObservableObject { case .dm: handle_dm(ev) case .delete: - break + handle_delete_event(ev) + case .channel_create: + handle_channel_create(ev) + case .channel_meta: + handle_channel_meta(ev) } } + + func handle_channel_create(_ ev: NostrEvent) { + guard ev.is_valid else { + return + } + + self.channels[ev.id] = ev + } + + func handle_channel_meta(_ ev: NostrEvent) { + } + + func handle_delete_event(_ ev: NostrEvent) { + guard ev.is_valid else { + return + } + + self.deleted_events.insert(ev.id) + } 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) @@ -119,11 +144,14 @@ class HomeModel: ObservableObject { func handle_boost_event(sub_id: String, _ ev: NostrEvent) { var boost_ev_id = ev.last_refid()?.ref_id - // CHECK SIGS ON THESE if let inner_ev = ev.inner_event { boost_ev_id = inner_ev.id + + guard inner_ev.is_valid else { + return + } - if inner_ev.kind == 1 { + if inner_ev.is_textlike { handle_text_event(sub_id: sub_id, ev) } } @@ -259,6 +287,7 @@ class HomeModel: ObservableObject { // TODO: separate likes? var home_filter = NostrFilter.filter_kinds([ NostrKind.text.rawValue, + NostrKind.chat.rawValue, NostrKind.like.rawValue, NostrKind.boost.rawValue, ]) @@ -268,6 +297,7 @@ class HomeModel: ObservableObject { var notifications_filter = NostrFilter.filter_kinds([ NostrKind.text.rawValue, + NostrKind.chat.rawValue, NostrKind.like.rawValue, NostrKind.boost.rawValue, ]) diff --git a/damus/Models/ProfileModel.swift b/damus/Models/ProfileModel.swift @@ -39,7 +39,8 @@ class ProfileModel: ObservableObject { func subscribe() { var text_filter = NostrFilter.filter_kinds([ - NostrKind.text.rawValue + NostrKind.text.rawValue, + NostrKind.chat.rawValue, ]) var profile_filter = NostrFilter.filter_kinds([ @@ -75,7 +76,7 @@ class ProfileModel: ObservableObject { if seen_event.contains(ev.id) { return } - if ev.known_kind == .text || ev.known_kind == .boost { + if ev.is_textlike || ev.known_kind == .boost { let _ = insert_uniq_sorted_event(events: &self.events, new_ev: ev, cmp: { $0.created_at > $1.created_at}) } else if ev.known_kind == .contacts { handle_profile_contact_event(ev) diff --git a/damus/Models/SearchHomeModel.swift b/damus/Models/SearchHomeModel.swift @@ -40,16 +40,6 @@ class SearchHomeModel: ObservableObject { damus_state.pool.unsubscribe(sub_id: base_subid) } - func load_profiles(relay_id: String) { - var filter = NostrFilter.filter_profiles - let authors = find_profiles_to_fetch(profiles: damus_state.profiles, events: events) - filter.authors = authors - - if !authors.isEmpty { - damus_state.pool.subscribe(sub_id: profiles_subid, filters: [filter], handler: handle_event) - } - } - func handle_event(relay_id: String, conn_ev: NostrConnectionEvent) { switch conn_ev { case .ws_event: @@ -68,8 +58,6 @@ class SearchHomeModel: ObservableObject { let _ = insert_uniq_sorted_event(events: &events, new_ev: ev) { $0.created_at > $1.created_at } - } else if ev.known_kind == .metadata { - process_metadata_event(image_cache: damus_state.image_cache, profiles: damus_state.profiles, ev: ev) } case .notice(let msg): print("search home notice: \(msg)") @@ -77,9 +65,7 @@ class SearchHomeModel: ObservableObject { loading = false if sub_id == self.base_subid { - load_profiles(relay_id: relay_id) - } else if sub_id == self.profiles_subid { - damus_state.pool.unsubscribe(sub_id: self.profiles_subid) + load_profiles(profiles_subid: profiles_subid, relay_id: relay_id, events: events, damus_state: damus_state) } break @@ -115,3 +101,33 @@ func find_profiles_to_fetch(profiles: Profiles, events: [NostrEvent]) -> [String return Array(pubkeys) } + +func load_profiles(profiles_subid: String, relay_id: String, events: [NostrEvent], damus_state: DamusState) { + var filter = NostrFilter.filter_profiles + let authors = find_profiles_to_fetch(profiles: damus_state.profiles, events: events) + filter.authors = authors + + if !authors.isEmpty { + print("loading \(authors.count) profiles from \(relay_id)") + damus_state.pool.subscribe_to(sub_id: profiles_subid, filters: [filter], to: [relay_id]) { sub_id, conn_ev in + let (sid, done) = handle_subid_event(pool: damus_state.pool, relay_id: relay_id, ev: conn_ev) { sub_id, ev in + guard sub_id == profiles_subid else { + return + } + + if ev.known_kind == .metadata { + process_metadata_event(image_cache: damus_state.image_cache, profiles: damus_state.profiles, ev: ev) + } + + } + + guard done && sid == profiles_subid else { + return + } + + print("done loading \(authors.count) profiles from \(relay_id)") + damus_state.pool.unsubscribe(sub_id: profiles_subid, to: [relay_id]) + } + } +} + diff --git a/damus/Models/SearchModel.swift b/damus/Models/SearchModel.swift @@ -11,6 +11,8 @@ import Foundation class SearchModel: ObservableObject { @Published var events: [NostrEvent] = [] @Published var loading: Bool = false + @Published var channel_name: String? = nil + let pool: RelayPool var search: NostrFilter let sub_id = UUID().description @@ -50,10 +52,24 @@ class SearchModel: ObservableObject { } } + func handle_channel_create(_ ev: NostrEvent) { + self.channel_name = ev.content + return + } + + func handle_channel_meta(_ ev: NostrEvent) { + self.channel_name = ev.content + return + } + func handle_event(relay_id: String, ev: NostrConnectionEvent) { - let done = handle_subid_event(pool: pool, sub_id: sub_id, relay_id: relay_id, ev: ev) { ev in - if ev.known_kind == .text && ev.should_show_event { + let (_, done) = handle_subid_event(pool: pool, relay_id: relay_id, ev: ev) { sub_id, ev in + if ev.is_textlike && ev.should_show_event { self.add_event(ev) + } else if ev.known_kind == .channel_create { + handle_channel_create(ev) + } else if ev.known_kind == .channel_meta { + handle_channel_meta(ev) } } @@ -79,29 +95,26 @@ func event_matches_filter(_ ev: NostrEvent, filter: NostrFilter) -> Bool { return true } -func handle_subid_event(pool: RelayPool, sub_id: String, relay_id: String, ev: NostrConnectionEvent, handle: (NostrEvent) -> ()) -> Bool { +func handle_subid_event(pool: RelayPool, relay_id: String, ev: NostrConnectionEvent, handle: (String, NostrEvent) -> ()) -> (String?, Bool) { switch ev { case .ws_event: - break + return (nil, false) + case .nostr_event(let res): switch res { case .event(let ev_subid, let ev): - if ev_subid == sub_id { - handle(ev) - } - break + handle(ev_subid, ev) + return (ev_subid, false) case .notice(let note): if note.contains("Too many subscription filters") { // TODO: resend filters? pool.reconnect(to: [relay_id]) } - break + return (nil, false) - case .eose: - return true + case .eose(let subid): + return (subid, true) } } - - return false } diff --git a/damus/Models/ThreadModel.swift b/damus/Models/ThreadModel.swift @@ -30,13 +30,13 @@ enum InitialEvent { /// manages the lifetime of a thread class ThreadModel: ObservableObject { - let privkey: String? let kind: Int + @Published var initial_event: InitialEvent @Published var events: [NostrEvent] = [] @Published var event_map: [String: Int] = [:] @Published var loading: Bool = false - + var replies: ReplyMap = ReplyMap() var event: NostrEvent? { @@ -53,33 +53,32 @@ class ThreadModel: ObservableObject { } } - let pool: RelayPool - var sub_id = UUID().description + let damus_state: DamusState + + let profiles_subid = UUID().description + var base_subid = UUID().description - init(evid: String, pool: RelayPool, privkey: String?) { - self.pool = pool + init(evid: String, damus_state: DamusState) { + self.damus_state = damus_state self.initial_event = .event_id(evid) - self.privkey = privkey self.kind = NostrKind.text.rawValue } - init(event: NostrEvent, pool: RelayPool, privkey: String?) { - self.pool = pool + init(event: NostrEvent, damus_state: DamusState) { + self.damus_state = damus_state self.initial_event = .event(event) - self.privkey = privkey self.kind = NostrKind.text.rawValue } - init(event: NostrEvent, pool: RelayPool, privkey: String?, kind: Int) { - self.pool = pool + init(event: NostrEvent, damus_state: DamusState, kind: Int) { + self.damus_state = damus_state self.initial_event = .event(event) - self.privkey = privkey self.kind = kind } func unsubscribe() { - self.pool.unsubscribe(sub_id: sub_id) - print("unsubscribing from thread \(initial_event.id) with sub_id \(sub_id)") + self.damus_state.pool.unsubscribe(sub_id: base_subid) + print("unsubscribing from thread \(initial_event.id) with sub_id \(base_subid)") } func reset_events() { @@ -125,19 +124,23 @@ class ThreadModel: ObservableObject { case .event(let ev): ref_events.referenced_ids = ev.referenced_ids.map { $0.ref_id } ref_events.referenced_ids?.append(ev.id) + ref_events.limit = 50 events_filter.ids = ref_events.referenced_ids! + events_filter.limit = 100 events_filter.ids?.append(ev.id) case .event_id(let evid): events_filter.ids = [evid] + events_filter.limit = 100 ref_events.referenced_ids = [evid] + ref_events.limit = 50 } //likes_filter.ids = ref_events.referenced_ids! - print("subscribing to thread \(initial_event.id) with sub_id \(sub_id)") - pool.register_handler(sub_id: sub_id, handler: handle_event) + print("subscribing to thread \(initial_event.id) with sub_id \(base_subid)") + damus_state.pool.register_handler(sub_id: base_subid, handler: handle_event) loading = true - pool.send(.subscribe(.init(filters: [ref_events, events_filter], sub_id: sub_id))) + damus_state.pool.send(.subscribe(.init(filters: [ref_events, events_filter], sub_id: base_subid))) } func lookup(_ event_id: String) -> NostrEvent? { @@ -180,18 +183,41 @@ class ThreadModel: ObservableObject { } } - + + func handle_channel_meta(_ ev: NostrEvent) { + guard let meta: ChatroomMetadata = decode_json(ev.content) else { + return + } + + notify(.chatroom_meta, meta) + } + func handle_event(relay_id: String, ev: NostrConnectionEvent) { - let done = handle_subid_event(pool: pool, sub_id: sub_id, relay_id: relay_id, ev: ev) { ev in - if ev.is_textlike { - self.add_event(ev, privkey: self.privkey) + + let (sub_id, done) = handle_subid_event(pool: damus_state.pool, relay_id: relay_id, ev: ev) { sid, ev in + guard sid == base_subid || sid == profiles_subid else { + return + } + + if ev.known_kind == .metadata { + process_metadata_event(image_cache: damus_state.image_cache, profiles: damus_state.profiles, ev: ev) + } else if ev.is_textlike { + self.add_event(ev, privkey: self.damus_state.keypair.privkey) + } else if ev.known_kind == .channel_meta || ev.known_kind == .channel_create { + handle_channel_meta(ev) } } - if done { - if (events.contains { ev in ev.id == initial_event.id }) { - loading = false - } + guard done && (sub_id == base_subid || sub_id == profiles_subid) else { + return + } + + if (events.contains { ev in ev.id == initial_event.id }) { + loading = false + } + + if sub_id == self.base_subid { + load_profiles(profiles_subid: self.profiles_subid, relay_id: relay_id, events: events, damus_state: damus_state) } } diff --git a/damus/Nostr/NostrKind.swift b/damus/Nostr/NostrKind.swift @@ -16,5 +16,7 @@ enum NostrKind: Int { case delete = 5 case boost = 6 case like = 7 + case channel_create = 40 + case channel_meta = 41 case chat = 42 } diff --git a/damus/Util/ImageCache.swift b/damus/Util/ImageCache.swift @@ -132,7 +132,7 @@ class ImageCache { func insert(_ image: UIImage, key: String) async -> UIImage? { let scale = await UIScreen.main.scale let size = CGSize(width: PFP_SIZE * scale, height: PFP_SIZE * scale) - + set_state(key, new_state: .processing) let decoded_image = await image.byPreparingThumbnail(ofSize: size) diff --git a/damus/Util/Notifications.swift b/damus/Util/Notifications.swift @@ -140,6 +140,12 @@ extension Notification.Name { } extension Notification.Name { + static var chatroom_meta: Notification.Name { + return Notification.Name("chatroom_meta") + } +} + +extension Notification.Name { static var unfollowed: Notification.Name { return Notification.Name("unfollowed") } diff --git a/damus/Views/ChatView.swift b/damus/Views/ChatView.swift @@ -124,12 +124,11 @@ struct ChatView: View { } .contentShape(Rectangle()) .id(event.id) - .frame(minHeight: just_started ? PFP_SIZE : 0) - .padding([.bottom], next_ev == nil ? 30 : 0) + //.frame(minHeight: just_started ? PFP_SIZE : 0) + .padding([.bottom], 6) //.border(Color.green) } - } extension Notification.Name { diff --git a/damus/Views/ChatroomView.swift b/damus/Views/ChatroomView.swift @@ -10,12 +10,13 @@ import SwiftUI struct ChatroomView: View { @EnvironmentObject var thread: ThreadModel @Environment(\.dismiss) var dismiss + @State var once: Bool = false let damus: DamusState var body: some View { ScrollViewReader { scroller in ScrollView(.vertical) { - LazyVStack(alignment: .leading) { + VStack(alignment: .leading) { let count = thread.events.count ForEach(Array(zip(thread.events, thread.events.indices)), id: \.0.id) { (ev, ind) in ChatView(event: thread.events[ind], @@ -46,6 +47,13 @@ struct ChatroomView: View { } scroll_to_event(scroller: scroller, id: ev.id, delay: 0, animate: true) } + .onChange(of: thread.loading) { _ in + guard !thread.loading && !once else { + return + } + scroll_after_load(thread: thread, proxy: scroller) + once = true + } .onAppear() { scroll_to_event(scroller: scroller, id: thread.initial_event.id, delay: 0.1, animate: false) } @@ -66,7 +74,7 @@ struct ChatroomView_Previews: PreviewProvider { static var previews: some View { let state = test_damus_state() ChatroomView(damus: state) - .environmentObject(ThreadModel(evid: "&849ab9bb263ed2819db06e05f1a1a3b72878464e8c7146718a2fc1bf1912f893", pool: state.pool, privkey: state.keypair.privkey)) + .environmentObject(ThreadModel(evid: "&849ab9bb263ed2819db06e05f1a1a3b72878464e8c7146718a2fc1bf1912f893", damus_state: state)) } } diff --git a/damus/Views/EventDetailView.swift b/damus/Views/EventDetailView.swift @@ -104,28 +104,29 @@ struct EventDetailView: View { EndBlock() } .onChange(of: thread.loading) { val in - scroll_after_load(proxy) + scroll_after_load(thread: thread, proxy: proxy) } .onAppear() { - scroll_after_load(proxy) + scroll_after_load(thread: thread, proxy: proxy) } } .navigationBarTitle("Thread") } - func scroll_after_load(_ proxy: ScrollViewProxy) { - if !thread.loading { - let id = thread.initial_event.id - scroll_to_event(scroller: proxy, id: id, delay: 0.1, animate: false) - } - } - func toggle_thread_view() { NotificationCenter.default.post(name: .toggle_thread_view, object: nil) } } +func scroll_after_load(thread: ThreadModel, proxy: ScrollViewProxy) { + if !thread.loading { + let id = thread.initial_event.id + scroll_to_event(scroller: proxy, id: id, delay: 0.1, animate: false) + } +} + + /* struct EventDetailView_Previews: PreviewProvider { static var previews: some View { diff --git a/damus/Views/ProfilePicView.swift b/damus/Views/ProfilePicView.swift @@ -126,14 +126,14 @@ struct ProfilePicView_Previews: PreviewProvider { func hex_to_rgb(_ hex: String) -> Color { guard hex.count >= 6 else { - return Color.black + return Color.white } let arr = Array(hex.utf8) var rgb: [UInt8] = [] - var i: Int = 0 + var i: Int = arr.count - 12 - while i < 6 { + while i < arr.count { let cs1 = arr[i] let cs2 = arr[i+1] diff --git a/damus/Views/ReplyQuoteView.swift b/damus/Views/ReplyQuoteView.swift @@ -50,9 +50,6 @@ struct ReplyQuoteView: View { .padding(4) .frame(maxWidth: .infinity, alignment: .leading) .contentShape(Rectangle()) - } else { - ProgressView() - .progressViewStyle(.circular) } } } @@ -63,6 +60,6 @@ struct ReplyQuoteView_Previews: PreviewProvider { let s = test_damus_state() let quoter = NostrEvent(content: "a\nb\nc", pubkey: "pubkey") ReplyQuoteView(privkey: s.keypair.privkey, quoter: quoter, event_id: "pubkey2", image_cache: s.image_cache, profiles: s.profiles) - .environmentObject(ThreadModel(event: quoter, pool: s.pool, privkey: s.keypair.privkey)) + .environmentObject(ThreadModel(event: quoter, damus_state: s)) } } diff --git a/damus/Views/ThreadView.swift b/damus/Views/ThreadView.swift @@ -12,6 +12,7 @@ struct ThreadView: View { @StateObject var thread: ThreadModel let damus: DamusState @State var is_chatroom: Bool + @State var metadata: ChatroomMetadata? = nil @State var seen_first: Bool = false @Environment(\.dismiss) var dismiss @@ -20,11 +21,11 @@ struct ThreadView: View { Group { if is_chatroom { ChatroomView(damus: damus) - .navigationBarTitle("Chat") + .navigationBarTitle(metadata?.name ?? "Chat") .environmentObject(thread) } else { EventDetailView(damus: damus, thread: thread) - .navigationBarTitle("Thread") + .navigationBarTitle(metadata?.name ?? "Thread") .environmentObject(thread) } @@ -35,13 +36,17 @@ struct ThreadView: View { */ } .padding([.leading, .trailing], 6) - .onReceive(NotificationCenter.default.publisher(for: .switched_timeline)) { n in + .onReceive(handle_notify(.switched_timeline)) { n in dismiss() } - .onReceive(NotificationCenter.default.publisher(for: .toggle_thread_view)) { _ in + .onReceive(handle_notify(.toggle_thread_view)) { _ in is_chatroom = !is_chatroom //print("is_chatroom: \(is_chatroom)") } + .onReceive(handle_notify(.chatroom_meta)) { n in + let meta = n.object as! ChatroomMetadata + self.metadata = meta + } .onChange(of: thread.events) { val in if seen_first { return @@ -72,7 +77,7 @@ struct ThreadView_Previews: PreviewProvider { */ func should_show_chatroom(_ ev: NostrEvent) -> Bool { - if ev.known_kind == .chat { + if ev.known_kind == .chat || ev.known_kind == .channel_create { return true } diff --git a/damus/Views/TimelineView.swift b/damus/Views/TimelineView.swift @@ -20,7 +20,7 @@ struct InnerTimelineView: View { var body: some View { LazyVStack { ForEach(events, id: \.id) { (ev: NostrEvent) in - let tm = ThreadModel(event: inner_event_or_self(ev: ev), pool: damus.pool, privkey: damus.keypair.privkey) + let tm = ThreadModel(event: inner_event_or_self(ev: ev), damus_state: damus) let is_chatroom = should_show_chatroom(ev) let tv = ThreadView(thread: tm, damus: damus, is_chatroom: is_chatroom)