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>
This commit is contained in:
William Casarin
2022-04-18 10:31:54 -07:00
parent cca8738519
commit 6ce5484d74
3 changed files with 88 additions and 22 deletions

View File

@@ -10,7 +10,7 @@ import SwiftUI
enum CollapsedEvent: Identifiable { enum CollapsedEvent: Identifiable {
case event(NostrEvent, Highlight) case event(NostrEvent, Highlight)
case collapsed(Int, String) case collapsed(Int, String)
var id: String { var id: String {
switch self { switch self {
case .event(let ev, _): case .event(let ev, _):
@@ -29,9 +29,9 @@ struct EventDetailView: View {
@State var events: [NostrEvent] = [] @State var events: [NostrEvent] = []
@State var has_event: [String: ()] = [:] @State var has_event: [String: ()] = [:]
@State var collapsed: Bool = true @State var collapsed: Bool = true
@EnvironmentObject var profiles: Profiles @EnvironmentObject var profiles: Profiles
let pool: RelayPool let pool: RelayPool
func unsubscribe_to_thread() { func unsubscribe_to_thread() {
@@ -54,7 +54,7 @@ struct EventDetailView: View {
pool.register_handler(sub_id: sub_id, handler: handle_event) pool.register_handler(sub_id: sub_id, handler: handle_event)
pool.send(.subscribe(.init(filters: [ref_events, events], sub_id: sub_id))) pool.send(.subscribe(.init(filters: [ref_events, events], sub_id: sub_id)))
} }
func add_event(ev: NostrEvent) { func add_event(ev: NostrEvent) {
if sub_id != self.sub_id || self.has_event[ev.id] != nil { if sub_id != self.sub_id || self.has_event[ev.id] != nil {
return return
@@ -72,7 +72,7 @@ struct EventDetailView: View {
if sub_id == self.sub_id { if sub_id == self.sub_id {
add_event(ev: ev) add_event(ev: ev)
} }
case .notice(let note): case .notice(let note):
if note.contains("Too many subscription filters") { if note.contains("Too many subscription filters") {
// TODO: resend filters? // TODO: resend filters?
@@ -82,14 +82,14 @@ struct EventDetailView: View {
} }
} }
} }
func toggle_collapse_thread(scroller: ScrollViewProxy, id: String) { func toggle_collapse_thread(scroller: ScrollViewProxy, id: String) {
self.collapsed = !self.collapsed self.collapsed = !self.collapsed
if !self.collapsed { if !self.collapsed {
scroll_to_event(scroller: scroller, id: id, delay: 0.1) scroll_to_event(scroller: scroller, id: id, delay: 0.1)
} }
} }
func scroll_to_event(scroller: ScrollViewProxy, id: String, delay: Double) { func scroll_to_event(scroller: ScrollViewProxy, id: String, delay: Double) {
DispatchQueue.main.asyncAfter(deadline: .now() + delay) { DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
withAnimation { withAnimation {
@@ -97,7 +97,7 @@ struct EventDetailView: View {
} }
} }
} }
func OurEventView(proxy: ScrollViewProxy, ev: NostrEvent, highlight: Highlight) -> some View { func OurEventView(proxy: ScrollViewProxy, ev: NostrEvent, highlight: Highlight) -> some View {
Group { Group {
if ev.id == event.id { if ev.id == event.id {
@@ -121,7 +121,7 @@ struct EventDetailView: View {
} }
} }
} }
var body: some View { var body: some View {
ScrollViewReader { proxy in ScrollViewReader { proxy in
ScrollView { ScrollView {
@@ -143,7 +143,7 @@ struct EventDetailView: View {
.onAppear() { .onAppear() {
self.add_event(event) self.add_event(event)
subscribe_to_thread() 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 func determine_highlight(current: NostrEvent, active: NostrEvent) -> Highlight
{ {
if current.id == active.id { if current.id == active.id {
return .main return .main
} }
if active.references(id: current.id, key: "e") { if active.references(id: current.id, key: "e") {
return .replied_to(active.id) return .reply
} else if current.references(id: active.id, key: "e") { } else if current.references(id: active.id, key: "e") {
return .replied_to(current.id) return .reply
} }
return .none return .none
} }
func calculated_collapsed_events(collapsed: Bool, active: NostrEvent, events: [NostrEvent]) -> [CollapsedEvent] { func calculated_collapsed_events(collapsed: Bool, active: NostrEvent, events: [NostrEvent]) -> [CollapsedEvent] {
var count: Int = 0 var count: Int = 0
if !collapsed { if !collapsed {
return events.reduce(into: []) { acc, ev in return events.reduce(into: []) { acc, ev in
let highlight = determine_highlight(current: ev, active: active) let highlight = determine_highlight(current: ev, active: active)
return acc.append(.event(ev, highlight)) return acc.append(.event(ev, highlight))
} }
} }
let reply_map = make_reply_map(active: active, events: events)
let nevents = events.count let nevents = events.count
var i: Int = 0 var i: Int = 0
return events.reduce(into: []) { (acc, ev) in 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 { switch highlight {
case .none: case .none:
count += 1 count += 1
@@ -203,21 +269,21 @@ func calculated_collapsed_events(collapsed: Bool, active: NostrEvent, events: [N
count = 0 count = 0
} }
acc.append(.event(ev, .main)) acc.append(.event(ev, .main))
case .replied_to: case .reply:
if count != 0 { if count != 0 {
acc.append(.collapsed(count, UUID().description)) acc.append(.collapsed(count, UUID().description))
count = 0 count = 0
} }
acc.append(.event(ev, highlight)) acc.append(.event(ev, highlight))
} }
if i == nevents-1 { if i == nevents-1 {
if count != 0 { if count != 0 {
acc.append(.collapsed(count, UUID().description)) acc.append(.collapsed(count, UUID().description))
count = 0 count = 0
} }
} }
i += 1 i += 1
} }
} }

View File

@@ -12,7 +12,7 @@ import CachedAsyncImage
enum Highlight { enum Highlight {
case none case none
case main case main
case replied_to(String) case reply
var is_none: Bool { var is_none: Bool {
switch self { switch self {
@@ -23,7 +23,7 @@ enum Highlight {
var is_replied_to: Bool { var is_replied_to: Bool {
switch self { switch self {
case .replied_to: return true case .reply: return true
default: return false default: return false
} }
} }

View File

@@ -19,7 +19,7 @@ func highlight_color(_ h: Highlight) -> Color {
switch h { switch h {
case .none: return Color.black case .none: return Color.black
case .main: return Color.red case .main: return Color.red
case .replied_to: return Color.blue case .reply: return Color.blue
} }
} }