diff --git a/damus/Views/EventDetailView.swift b/damus/Views/EventDetailView.swift index df64884..cd682d3 100644 --- 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 index d193a71..3f4cb09 100644 --- 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 index 97d8aa3..4cda5af 100644 --- 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 } }