damus

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

commit 6ce5484d74a340dde77a76e9db5f6694c66022bf
parent cca8738519702fc9eff32bf398e1dd935a619bc3
Author: William Casarin <jb55@jb55.com>
Date:   Mon, 18 Apr 2022 10:31:54 -0700

calculate ancestor reply path

This works really well going back in time because no branching, assuming
the last referenced event id is the only note they are replying to...

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

Diffstat:
Mdamus/Views/EventDetailView.swift | 104++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Mdamus/Views/EventView.swift | 4++--
Mdamus/Views/ProfilePicView.swift | 2+-
3 files changed, 88 insertions(+), 22 deletions(-)

diff --git a/damus/Views/EventDetailView.swift b/damus/Views/EventDetailView.swift @@ -10,7 +10,7 @@ import SwiftUI enum CollapsedEvent: Identifiable { case event(NostrEvent, Highlight) case collapsed(Int, String) - + var id: String { switch self { case .event(let ev, _): @@ -29,9 +29,9 @@ struct EventDetailView: View { @State var events: [NostrEvent] = [] @State var has_event: [String: ()] = [:] @State var collapsed: Bool = true - + @EnvironmentObject var profiles: Profiles - + let pool: RelayPool func unsubscribe_to_thread() { @@ -54,7 +54,7 @@ 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 @@ -72,7 +72,7 @@ struct EventDetailView: View { if sub_id == self.sub_id { add_event(ev: ev) } - + case .notice(let note): if note.contains("Too many subscription filters") { // TODO: resend filters? @@ -82,14 +82,14 @@ 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 { @@ -97,7 +97,7 @@ struct EventDetailView: View { } } } - + func OurEventView(proxy: ScrollViewProxy, ev: NostrEvent, highlight: Highlight) -> some View { Group { if ev.id == event.id { @@ -121,7 +121,7 @@ struct EventDetailView: View { } } } - + var body: some View { ScrollViewReader { proxy in ScrollView { @@ -143,7 +143,7 @@ struct EventDetailView: View { .onAppear() { self.add_event(event) subscribe_to_thread() - + } } @@ -166,34 +166,100 @@ struct EventDetailView_Previews: PreviewProvider { } */ +/// Find the entire reply path for the active event +func make_reply_map(active: NostrEvent, events: [NostrEvent]) -> [String: ()] +{ + let event_map: [String: Int] = zip(events,0...events.count).reduce(into: [:]) { (acc, arg1) in + let (ev, i) = arg1 + acc[ev.id] = i + } + var is_reply: [String: ()] = [:] + var i: Int = 0 + var start: Int = 0 + var iterations: Int = 0 + + if events.count == 0 { + return is_reply + } + + for ev in events { + if ev.references(id: active.id, key: "e") { + is_reply[ev.id] = () + start = i + } else if active.references(id: ev.id, key: "e") { + is_reply[ev.id] = () + start = i + } + i += 1 + } + + i = start + + while true { + if iterations > 1024 { + // infinite loop? or super large thread + print("breaking from large reply_map... big thread??") + break + } + + let ev = events[i] + + let ref_ids = ev.referenced_ids + if ref_ids.count == 0 { + break + } + + let ref_id = ref_ids[ref_ids.count-1] + let pubkey = ref_id.ref_id + is_reply[pubkey] = () + + if let mi = event_map[pubkey] { + i = mi + } else { + break + } + + iterations += 1 + } + + return is_reply +} + 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) + return .reply } else if current.references(id: active.id, key: "e") { - return .replied_to(current.id) + return .reply } 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)) } } - + + let reply_map = make_reply_map(active: active, events: events) + let nevents = events.count var i: Int = 0 return events.reduce(into: []) { (acc, ev) in - let highlight = determine_highlight(current: ev, active: active) - + var highlight: Highlight = .none + if ev.id == active.id { + highlight = .main + } else if reply_map[ev.id] != nil { + highlight = .reply + } + switch highlight { case .none: count += 1 @@ -203,21 +269,21 @@ func calculated_collapsed_events(collapsed: Bool, active: NostrEvent, events: [N count = 0 } acc.append(.event(ev, .main)) - case .replied_to: + case .reply: if count != 0 { acc.append(.collapsed(count, UUID().description)) count = 0 } acc.append(.event(ev, highlight)) } - + if i == nevents-1 { if count != 0 { acc.append(.collapsed(count, UUID().description)) count = 0 } } - + i += 1 } } diff --git a/damus/Views/EventView.swift b/damus/Views/EventView.swift @@ -12,7 +12,7 @@ import CachedAsyncImage enum Highlight { case none case main - case replied_to(String) + case reply var is_none: Bool { switch self { @@ -23,7 +23,7 @@ enum Highlight { var is_replied_to: Bool { switch self { - case .replied_to: return true + case .reply: return true default: return false } } diff --git a/damus/Views/ProfilePicView.swift b/damus/Views/ProfilePicView.swift @@ -19,7 +19,7 @@ func highlight_color(_ h: Highlight) -> Color { switch h { case .none: return Color.black case .main: return Color.red - case .replied_to: return Color.blue + case .reply: return Color.blue } }