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:
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user