damus

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

commit 550f09680893543440a396caa52ea34a0cfa2e1a
parent 700a0e2625fb02aea4366476149ff3c88343ea61
Author: William Casarin <jb55@jb55.com>
Date:   Sun, 17 Apr 2022 11:11:11 -0700

make note of collapsed events

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

Diffstat:
Mdamus/ContentView.swift | 6+++++-
Mdamus/Nostr/NostrEvent.swift | 6+++---
Mdamus/Views/EventActionBar.swift | 12++++++++----
Mdamus/Views/EventDetailView.swift | 132+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Mdamus/Views/EventView.swift | 2+-
Mdamus/Views/PostView.swift | 4++--
Mdamus/Views/ProfilePicView.swift | 1-
7 files changed, 125 insertions(+), 38 deletions(-)

diff --git a/damus/ContentView.swift b/damus/ContentView.swift @@ -121,7 +121,11 @@ struct ContentView: View { let privkey = "" let new_ev = NostrEvent(content: post.content, pubkey: pubkey) for id in post.references { - new_ev.tags.append(["e", id]) + var tag = ["e", id.ref_id] + if let relay_id = id.relay_id { + tag.append(relay_id) + } + new_ev.tags.append(tag) } new_ev.calculate_id() new_ev.sign(privkey: privkey) diff --git a/damus/Nostr/NostrEvent.swift b/damus/Nostr/NostrEvent.swift @@ -86,9 +86,9 @@ class NostrEvent: Codable, Identifiable { return false } - public func reply_ids() -> [String] { - var ids = self.referenced_ids.map { $0.ref_id } - ids.append(self.id) + public func reply_ids() -> [ReferencedId] { + var ids = self.referenced_ids.first.map { [$0] } ?? [] + ids.append(ReferencedId(ref_id: self.id, relay_id: nil)) return ids } diff --git a/damus/Views/EventActionBar.swift b/damus/Views/EventActionBar.swift @@ -31,14 +31,18 @@ struct EventActionBar: View { var body: some View { HStack { - EventActionButton(img: "bubble.left") { - self.sheet = .reply + Spacer() + + /* + EventActionButton(img: "square.and.arrow.up") { + print("share") } Spacer() + */ - EventActionButton(img: "square.and.arrow.up") { - print("share") + EventActionButton(img: "bubble.left") { + self.sheet = .reply } } .sheet(item: $sheet) { sheet in diff --git a/damus/Views/EventDetailView.swift b/damus/Views/EventDetailView.swift @@ -7,6 +7,20 @@ import SwiftUI +enum CollapsedEvent: Identifiable { + case event(NostrEvent, Highlight) + case collapsed(Int, String) + + var id: String { + switch self { + case .event(let ev, _): + return ev.id + case .collapsed(_, let id): + return id + } + } +} + struct EventDetailView: View { @State var event: NostrEvent @@ -14,6 +28,7 @@ struct EventDetailView: View { @State var events: [NostrEvent] = [] @State var has_event: [String: ()] = [:] + @State var collapsed: Bool = true @EnvironmentObject var profiles: Profiles @@ -39,7 +54,13 @@ struct EventDetailView: View { pool.register_handler(sub_id: sub_id, handler: handle_event) pool.send(.subscribe(.init(filters: [ref_events, events], sub_id: sub_id))) } - + + func add_event(ev: NostrEvent) { + if sub_id != self.sub_id || self.has_event[ev.id] != nil { + return + } + self.add_event(ev) + } func handle_event(relay_id: String, ev: NostrConnectionEvent) { switch ev { @@ -48,11 +69,8 @@ struct EventDetailView: View { case .nostr_event(let res): switch res { case .event(let sub_id, let ev): - if sub_id != self.sub_id || self.has_event[ev.id] != nil { - return - } - self.add_event(ev) - + add_event(ev: ev) + case .notice(let note): if note.contains("Too many subscription filters") { // TODO: resend filters? @@ -62,29 +80,54 @@ struct EventDetailView: View { } } } - + + func toggle_collapse_thread(scroller: ScrollViewProxy, id: String) { + self.collapsed = !self.collapsed + if !self.collapsed { + scroll_to_event(scroller: scroller, id: id, delay: 0.1) + } + } + + func scroll_to_event(scroller: ScrollViewProxy, id: String, delay: Double) { + DispatchQueue.main.asyncAfter(deadline: .now() + delay) { + withAnimation { + scroller.scrollTo(event.id) + } + } + } + + func OurEventView(proxy: ScrollViewProxy, ev: NostrEvent, highlight: Highlight) -> some View { + Group { + if ev.id == event.id { + EventView(event: ev, highlight: .main, has_action_bar: true) + .onAppear() { + scroll_to_event(scroller: proxy, id: ev.id, delay: 0.5) + } + .onTapGesture { + 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 { + self.event = ev + } + } + } + } + } + var body: some View { ScrollViewReader { proxy in ScrollView { - ForEach(events, id: \.id) { ev in - Group { - let is_active_id = ev.id == event.id - if is_active_id { - EventView(event: ev, highlight: .main, has_action_bar: true) - .onAppear() { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - withAnimation { - proxy.scrollTo(event.id) - } - } - } - } else { - let highlight = determine_highlight(current: ev, active: event) - EventView(event: ev, highlight: highlight, has_action_bar: true) - .onTapGesture { - self.event = ev - } - } + ForEach(calculated_collapsed_events(collapsed: self.collapsed, active: self.event, events: self.events), id: \.id) { cev in + switch cev { + case .collapsed(let i, _): + Text("··· \(i) notes hidden ···") + .font(.footnote) + .foregroundColor(.gray) + case .event(let ev, let highlight): + OurEventView(proxy: proxy, ev: ev, highlight: highlight) } } } @@ -120,6 +163,9 @@ struct EventDetailView_Previews: PreviewProvider { func determine_highlight(current: NostrEvent, active: NostrEvent) -> Highlight { + if current.id == active.id { + return .main + } if active.references(id: current.id, key: "e") { return .replied_to(active.id) } else if current.references(id: active.id, key: "e") { @@ -127,3 +173,37 @@ func determine_highlight(current: NostrEvent, active: NostrEvent) -> Highlight } return .none } + +func calculated_collapsed_events(collapsed: Bool, active: NostrEvent, events: [NostrEvent]) -> [CollapsedEvent] { + var count: Int = 0 + + if !collapsed { + return events.reduce(into: []) { acc, ev in + let highlight = determine_highlight(current: ev, active: active) + return acc.append(.event(ev, highlight)) + } + } + + return events.reduce(into: []) { (acc, ev) in + let highlight = determine_highlight(current: ev, active: active) + + switch highlight { + case .none: + count += 1 + case .main: + if count != 0 { + acc.append(.collapsed(count, UUID().description)) + count = 0 + } + acc.append(.event(ev, .main)) + case .replied_to: + if count != 0 { + acc.append(.collapsed(count, UUID().description)) + count = 0 + } + acc.append(.event(ev, highlight)) + } + + } +} + diff --git a/damus/Views/EventView.swift b/damus/Views/EventView.swift @@ -12,7 +12,6 @@ import CachedAsyncImage enum Highlight { case none case main - case referenced(String) case replied_to(String) var is_none: Bool { @@ -75,6 +74,7 @@ struct EventView: View { Divider() .padding([.top], 4) } + .padding([.leading], 2) } .id(event.id) .frame(minHeight: PFP_SIZE) diff --git a/damus/Views/PostView.swift b/damus/Views/PostView.swift @@ -15,14 +15,14 @@ extension Notification.Name { struct NostrPost { let content: String - let references: [String] + let references: [ReferencedId] } struct PostView: View { @State var post: String = "" @FocusState var focus: Bool - let references: [String] + let references: [ReferencedId] @Environment(\.presentationMode) var presmode diff --git a/damus/Views/ProfilePicView.swift b/damus/Views/ProfilePicView.swift @@ -19,7 +19,6 @@ func highlight_color(_ h: Highlight) -> Color { switch h { case .none: return Color.black case .main: return Color.red - case .referenced(let id): return Color.blue case .replied_to: return Color.blue } }