mirror of https://github.com/lukechilds/damus.git
Thomas
2 years ago
committed by
William Casarin
14 changed files with 564 additions and 50 deletions
@ -0,0 +1,314 @@ |
|||||
|
// |
||||
|
// ThreadV2View.swift |
||||
|
// damus |
||||
|
// |
||||
|
// Created by Thomas Tastet on 25/12/2022. |
||||
|
// |
||||
|
|
||||
|
import SwiftUI |
||||
|
|
||||
|
struct ThreadV2 { |
||||
|
var parentEvents: [NostrEvent] |
||||
|
var current: NostrEvent |
||||
|
var childEvents: [NostrEvent] |
||||
|
|
||||
|
mutating func clean() { |
||||
|
// remove duplicates |
||||
|
self.parentEvents = Array(Set(self.parentEvents)) |
||||
|
self.childEvents = Array(Set(self.childEvents)) |
||||
|
|
||||
|
// remove empty contents |
||||
|
self.parentEvents = self.parentEvents.filter { event in |
||||
|
return !event.content.isEmpty |
||||
|
} |
||||
|
self.childEvents = self.childEvents.filter { event in |
||||
|
return !event.content.isEmpty |
||||
|
} |
||||
|
|
||||
|
// sort events by publication date |
||||
|
self.parentEvents = self.parentEvents.sorted { event1, event2 in |
||||
|
return event1 < event2 |
||||
|
} |
||||
|
self.childEvents = self.childEvents.sorted { event1, event2 in |
||||
|
return event1 < event2 |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
struct BuildThreadV2View: View { |
||||
|
let damus: DamusState |
||||
|
|
||||
|
@State var parents_ids: [String] = [] |
||||
|
let event_id: String |
||||
|
|
||||
|
@State var current_event: NostrEvent? = nil |
||||
|
|
||||
|
@State var thread: ThreadV2? = nil |
||||
|
|
||||
|
@State var current_events_uuid: String = "" |
||||
|
@State var childs_events_uuid: String = "" |
||||
|
@State var parents_events_uuids: [String] = [] |
||||
|
|
||||
|
@State var subscriptions_uuids: [String] = [] |
||||
|
|
||||
|
@Environment(\.dismiss) var dismiss |
||||
|
|
||||
|
init(damus: DamusState, event_id: String) { |
||||
|
self.damus = damus |
||||
|
self.event_id = event_id |
||||
|
} |
||||
|
|
||||
|
func unsubscribe_all() { |
||||
|
print("ThreadV2View: Unsubscribe all..") |
||||
|
|
||||
|
for subscriptions in subscriptions_uuids { |
||||
|
unsubscribe(subscriptions) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func unsubscribe(_ sub_id: String) { |
||||
|
if subscriptions_uuids.contains(sub_id) { |
||||
|
damus.pool.unsubscribe(sub_id: sub_id) |
||||
|
|
||||
|
subscriptions_uuids.remove(at: subscriptions_uuids.firstIndex(of: sub_id)!) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func subscribe(filters: [NostrFilter], sub_id: String = UUID().description) -> String { |
||||
|
damus.pool.register_handler(sub_id: sub_id, handler: handle_event) |
||||
|
damus.pool.send(.subscribe(.init(filters: filters, sub_id: sub_id))) |
||||
|
|
||||
|
subscriptions_uuids.append(sub_id) |
||||
|
|
||||
|
return sub_id |
||||
|
} |
||||
|
|
||||
|
func handle_event(relay_id: String, ev: NostrConnectionEvent) { |
||||
|
guard case .nostr_event(let nostr_response) = ev else { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
guard case .event(let id, let nostr_event) = nostr_response else { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// Is current event |
||||
|
if id == current_events_uuid { |
||||
|
if current_event != nil { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
current_event = nostr_event |
||||
|
|
||||
|
thread = ThreadV2( |
||||
|
parentEvents: [], |
||||
|
current: current_event!, |
||||
|
childEvents: [] |
||||
|
) |
||||
|
|
||||
|
// Get parents |
||||
|
parents_ids = current_event!.tags.enumerated().filter { (index, tag) in |
||||
|
return tag.count >= 2 && tag[0] == "e" && !current_event!.content.contains("#[\(index)]") |
||||
|
}.map { tag in |
||||
|
return tag.1[1] |
||||
|
} |
||||
|
|
||||
|
print("ThreadV2View: Parents list: (\(parents_ids)") |
||||
|
|
||||
|
if parents_ids.count > 0 { |
||||
|
// Ask for parents |
||||
|
let parents_events = NostrFilter( |
||||
|
ids: parents_ids, |
||||
|
limit: UInt32(parents_ids.count) |
||||
|
) |
||||
|
|
||||
|
let uuid = subscribe(filters: [parents_events]) |
||||
|
parents_events_uuids.append(uuid) |
||||
|
print("ThreadV2View: Ask for parents (\(uuid)) (\(parents_events))") |
||||
|
} |
||||
|
|
||||
|
// Ask for children |
||||
|
let childs_events = NostrFilter( |
||||
|
referenced_ids: [self.event_id], |
||||
|
limit: 50 |
||||
|
) |
||||
|
childs_events_uuid = subscribe(filters: [childs_events]) |
||||
|
print("ThreadV2View: Ask for children (\(childs_events) (\(childs_events_uuid))") |
||||
|
|
||||
|
return |
||||
|
} |
||||
|
|
||||
|
if parents_events_uuids.contains(id) { |
||||
|
// We are filtering this later |
||||
|
thread!.parentEvents.append(nostr_event) |
||||
|
|
||||
|
// Get parents of parents |
||||
|
let local_parents_ids = nostr_event.tags.enumerated().filter { (index, tag) in |
||||
|
return tag.count >= 2 && tag[0] == "e" && !nostr_event.content.contains("#[\(index)]") |
||||
|
}.map { tag in |
||||
|
return tag.1[1] |
||||
|
}.filter { tag_id in |
||||
|
return !parents_ids.contains(tag_id) |
||||
|
} |
||||
|
|
||||
|
print("ThreadV2View: Sub Parents list: (\(local_parents_ids))") |
||||
|
|
||||
|
// Expand new parents id |
||||
|
parents_ids.append(contentsOf: local_parents_ids) |
||||
|
|
||||
|
if local_parents_ids.count > 0 { |
||||
|
// Ask for parents |
||||
|
let parents_events = NostrFilter( |
||||
|
ids: local_parents_ids, |
||||
|
limit: UInt32(local_parents_ids.count) |
||||
|
) |
||||
|
let uuid = subscribe(filters: [parents_events]) |
||||
|
parents_events_uuids.append(uuid) |
||||
|
print("ThreadV2View: Ask for sub_parents (\(local_parents_ids)) \(uuid)") |
||||
|
} |
||||
|
|
||||
|
thread!.clean() |
||||
|
unsubscribe(id) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
if id == childs_events_uuid { |
||||
|
// We are filtering this later |
||||
|
thread!.childEvents.append(nostr_event) |
||||
|
|
||||
|
thread!.clean() |
||||
|
return |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func reload() { |
||||
|
self.unsubscribe_all() |
||||
|
print("ThreadV2View: Reload!") |
||||
|
|
||||
|
// Get the current event |
||||
|
current_events_uuid = subscribe(filters: [ |
||||
|
NostrFilter( |
||||
|
ids: [self.event_id], |
||||
|
limit: 1 |
||||
|
) |
||||
|
]) |
||||
|
print("subscribing to threadV2 \(event_id) with sub_id \(current_events_uuid)") |
||||
|
} |
||||
|
|
||||
|
var body: some View { |
||||
|
VStack { |
||||
|
if thread == nil { |
||||
|
ProgressView() |
||||
|
} else { |
||||
|
ThreadV2View(damus: damus, thread: thread!) |
||||
|
} |
||||
|
} |
||||
|
.onAppear { |
||||
|
if self.thread == nil { |
||||
|
self.reload() |
||||
|
} |
||||
|
} |
||||
|
.onDisappear { |
||||
|
self.unsubscribe_all() |
||||
|
} |
||||
|
.onReceive(handle_notify(.switched_timeline)) { n in |
||||
|
dismiss() |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
struct ThreadV2View: View { |
||||
|
let damus: DamusState |
||||
|
let thread: ThreadV2 |
||||
|
|
||||
|
var body: some View { |
||||
|
ScrollViewReader { reader in |
||||
|
ScrollView { |
||||
|
VStack { |
||||
|
// MARK: - Parents events view |
||||
|
VStack { |
||||
|
ForEach(thread.parentEvents, id: \.id) { event in |
||||
|
NavigationLink(destination: BuildThreadV2View( |
||||
|
damus: damus, |
||||
|
event_id: event.id |
||||
|
)){ |
||||
|
EventView( |
||||
|
event: event, |
||||
|
highlight: .none, |
||||
|
has_action_bar: true, |
||||
|
damus: damus, |
||||
|
show_friend_icon: true, // TODO: change it |
||||
|
size: .small |
||||
|
) |
||||
|
} |
||||
|
.buttonStyle(.plain) |
||||
|
.onAppear { |
||||
|
// TODO: find another solution to prevent layout shifting and layout blocking on large responses |
||||
|
reader.scrollTo("main", anchor: .center) |
||||
|
} |
||||
|
} |
||||
|
}.background(GeometryReader { geometry in |
||||
|
// get the height and width of the EventView view |
||||
|
let eventHeight = geometry.frame(in: .global).height |
||||
|
// let eventWidth = geometry.frame(in: .global).width |
||||
|
|
||||
|
// vertical gray line in the background |
||||
|
Rectangle() |
||||
|
.fill(Color.gray.opacity(0.5)) |
||||
|
.frame(width: 2, height: eventHeight) |
||||
|
.offset(x: 25, y: 40) |
||||
|
}) |
||||
|
|
||||
|
// MARK: - Actual event view |
||||
|
EventView( |
||||
|
event: thread.current, |
||||
|
highlight: .none, |
||||
|
has_action_bar: true, |
||||
|
damus: damus, |
||||
|
show_friend_icon: true, // TODO: change it |
||||
|
size: .selected |
||||
|
).id("main") |
||||
|
|
||||
|
// MARK: - Responses of the actual event view |
||||
|
ForEach(thread.childEvents, id: \.id) { event in |
||||
|
NavigationLink(destination: BuildThreadV2View( |
||||
|
damus: damus, |
||||
|
event_id: event.id |
||||
|
)){ |
||||
|
EventView( |
||||
|
event: event, |
||||
|
highlight: .none, |
||||
|
has_action_bar: true, |
||||
|
damus: damus, |
||||
|
show_friend_icon: true, // TODO: change it |
||||
|
size: .small |
||||
|
) |
||||
|
}.buttonStyle(.plain) |
||||
|
} |
||||
|
} |
||||
|
}.padding().navigationBarTitle("Thread") |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
struct ThreadV2View_Previews: PreviewProvider { |
||||
|
static var previews: some View { |
||||
|
BuildThreadV2View(damus: test_damus_state(), event_id: "ac9fd97b53b0c1d22b3aea2a3d62e11ae393960f5f91ee1791987d60151339a7") |
||||
|
ThreadV2View( |
||||
|
damus: test_damus_state(), |
||||
|
thread: ThreadV2( |
||||
|
parentEvents: [ |
||||
|
NostrEvent(id: "1", content: "hello there https://jb55.com/s/Oct12-150217.png https://jb55.com/red-me.jb55 cool 4", pubkey: "916b7aca250f43b9f842faccc831db4d155088632a8c27c0d140f2043331ba57"), |
||||
|
NostrEvent(id: "2", content: "hello there https://jb55.com/s/Oct12-150217.png https://jb55.com/red-me.jb55 cool 4", pubkey: "916b7aca250f43b9f842faccc831db4d155088632a8c27c0d140f2043331ba57"), |
||||
|
NostrEvent(id: "3", content: "hello there https://jb55.com/s/Oct12-150217.png https://jb55.com/red-me.jb55 cool 4", pubkey: "916b7aca250f43b9f842faccc831db4d155088632a8c27c0d140f2043331ba57"), |
||||
|
], |
||||
|
current: NostrEvent(id: "4", content: "hello there https://jb55.com/s/Oct12-150217.png https://jb55.com/red-me.jb55 cool 4", pubkey: "916b7aca250f43b9f842faccc831db4d155088632a8c27c0d140f2043331ba57"), |
||||
|
childEvents: [ |
||||
|
NostrEvent(id: "5", content: "hello there https://jb55.com/s/Oct12-150217.png https://jb55.com/red-me.jb55 cool 4", pubkey: "916b7aca250f43b9f842faccc831db4d155088632a8c27c0d140f2043331ba57"), |
||||
|
NostrEvent(id: "6", content: "hello there https://jb55.com/s/Oct12-150217.png https://jb55.com/red-me.jb55 cool 4", pubkey: "916b7aca250f43b9f842faccc831db4d155088632a8c27c0d140f2043331ba57"), |
||||
|
] |
||||
|
) |
||||
|
) |
||||
|
} |
||||
|
} |
Loading…
Reference in new issue