Thread Caching
Changelog-Added: Threads now load instantly and are cached
This commit is contained in:
@@ -35,14 +35,14 @@ struct ChatView: View {
|
||||
}
|
||||
|
||||
if let rep = thread.replies.lookup(event.id) {
|
||||
return rep == prev.id
|
||||
return rep.contains(prev.id)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
var is_active: Bool {
|
||||
return thread.initial_event.id == event.id
|
||||
return thread.event.id == event.id
|
||||
}
|
||||
|
||||
func prev_reply_is_same() -> String? {
|
||||
@@ -161,7 +161,7 @@ func prev_reply_is_same(event: NostrEvent, prev_ev: NostrEvent?, replies: ReplyM
|
||||
if let prev_reply_id = replies.lookup(prev.id) {
|
||||
if let cur_reply_id = replies.lookup(event.id) {
|
||||
if prev_reply_id != cur_reply_id {
|
||||
return cur_reply_id
|
||||
return cur_reply_id.first
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
import SwiftUI
|
||||
|
||||
/*
|
||||
struct ChatroomView: View {
|
||||
@EnvironmentObject var thread: ThreadModel
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@@ -26,7 +27,7 @@ struct ChatroomView: View {
|
||||
)
|
||||
.event_context_menu(ev, keypair: damus.keypair, target_pubkey: ev.pubkey, bookmarks: damus.bookmarks)
|
||||
.onTapGesture {
|
||||
if thread.initial_event.id == ev.id {
|
||||
if thread.event.id == ev.id {
|
||||
//dismiss()
|
||||
toggle_thread_view()
|
||||
} else {
|
||||
@@ -44,7 +45,7 @@ struct ChatroomView: View {
|
||||
}
|
||||
.onReceive(NotificationCenter.default.publisher(for: .select_quote)) { notif in
|
||||
let ev = notif.object as! NostrEvent
|
||||
if ev.id != thread.initial_event.id {
|
||||
if ev.id != thread.event.id {
|
||||
thread.set_active_event(ev, privkey: damus.keypair.privkey)
|
||||
}
|
||||
scroll_to_event(scroller: scroller, id: ev.id, delay: 0, animate: true)
|
||||
@@ -57,7 +58,7 @@ struct ChatroomView: View {
|
||||
once = true
|
||||
}
|
||||
.onAppear() {
|
||||
scroll_to_event(scroller: scroller, id: thread.initial_event.id, delay: 0.1, animate: false)
|
||||
scroll_to_event(scroller: scroller, id: thread.event.id, delay: 0.1, animate: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,7 +77,9 @@ struct ChatroomView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let state = test_damus_state()
|
||||
ChatroomView(damus: state)
|
||||
.environmentObject(ThreadModel(evid: "&849ab9bb263ed2819db06e05f1a1a3b72878464e8c7146718a2fc1bf1912f893", damus_state: state))
|
||||
.environmentObject(ThreadModel(event: test_event, damus_state: state))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
@@ -16,7 +16,7 @@ struct EventDetailView: View {
|
||||
|
||||
func scroll_after_load(thread: ThreadModel, proxy: ScrollViewProxy) {
|
||||
if !thread.loading {
|
||||
let id = thread.initial_event.id
|
||||
let id = thread.event.id
|
||||
scroll_to_event(scroller: proxy, id: id, delay: 0.1, animate: false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,9 @@ struct BuilderEventView: View {
|
||||
VStack {
|
||||
if let event = event {
|
||||
let ev = event.inner_event ?? event
|
||||
NavigationLink(destination: BuildThreadV2View(damus: damus, event_id: ev.id)) {
|
||||
let thread = ThreadModel(event: ev, damus_state: damus)
|
||||
let dest = ThreadView(state: damus, thread: thread)
|
||||
NavigationLink(destination: dest) {
|
||||
EmbeddedEventView(damus_state: damus, event: event)
|
||||
.padding(8)
|
||||
}.buttonStyle(.plain)
|
||||
|
||||
@@ -13,18 +13,14 @@ struct MutedEventView: View {
|
||||
let scroller: ScrollViewProxy?
|
||||
|
||||
let selected: Bool
|
||||
@Binding var nav_target: String?
|
||||
@Binding var navigating: Bool
|
||||
@State var shown: Bool
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
|
||||
init(damus_state: DamusState, event: NostrEvent, scroller: ScrollViewProxy?, nav_target: Binding<String?>, navigating: Binding<Bool>, selected: Bool) {
|
||||
init(damus_state: DamusState, event: NostrEvent, scroller: ScrollViewProxy?, selected: Bool) {
|
||||
self.damus_state = damus_state
|
||||
self.event = event
|
||||
self.scroller = scroller
|
||||
self.selected = selected
|
||||
self._nav_target = nav_target
|
||||
self._navigating = navigating
|
||||
self._shown = State(initialValue: should_show_event(contacts: damus_state.contacts, ev: event))
|
||||
}
|
||||
|
||||
@@ -58,14 +54,6 @@ struct MutedEventView: View {
|
||||
SelectedEventView(damus: damus_state, event: event)
|
||||
} else {
|
||||
EventView(damus: damus_state, event: event)
|
||||
.onTapGesture {
|
||||
nav_target = event.id
|
||||
navigating = true
|
||||
}
|
||||
.onAppear {
|
||||
// TODO: find another solution to prevent layout shifting and layout blocking on large responses
|
||||
scroller?.scrollTo("main", anchor: .bottom)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,12 +89,12 @@ struct MutedEventView: View {
|
||||
}
|
||||
|
||||
struct MutedEventView_Previews: PreviewProvider {
|
||||
@State static var nav_target: String? = nil
|
||||
@State static var nav_target: NostrEvent = test_event
|
||||
@State static var navigating: Bool = false
|
||||
|
||||
static var previews: some View {
|
||||
|
||||
MutedEventView(damus_state: test_damus_state(), event: test_event, scroller: nil, nav_target: $nav_target, navigating: $navigating, selected: false)
|
||||
MutedEventView(damus_state: test_damus_state(), event: test_event, scroller: nil, selected: false)
|
||||
.frame(width: .infinity, height: 50)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,7 +207,9 @@ struct EventGroupView: View {
|
||||
GroupDescription
|
||||
|
||||
if let event {
|
||||
NavigationLink(destination: BuildThreadV2View(damus: state, event_id: event.id)) {
|
||||
let thread = ThreadModel(event: event, damus_state: state)
|
||||
let dest = ThreadView(state: state, thread: thread)
|
||||
NavigationLink(destination: dest) {
|
||||
Text(event.content)
|
||||
.padding([.top], 1)
|
||||
.foregroundColor(.gray)
|
||||
|
||||
@@ -51,7 +51,7 @@ struct NotificationItemView: View {
|
||||
EventGroupView(state: state, event: ev, group: .reaction(evgrp))
|
||||
|
||||
case .reply(let ev):
|
||||
NavigationLink(destination: BuildThreadV2View(damus: state, event_id: ev.id)) {
|
||||
NavigationLink(destination: ThreadView(state: state, thread: ThreadModel(event: ev, damus_state: state))) {
|
||||
EventView(damus: state, event: ev)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
|
||||
@@ -43,39 +43,36 @@ struct SearchResultsView: View {
|
||||
case .profile(let prof):
|
||||
let decoded = try? bech32_decode(prof)
|
||||
let hex = hex_encode(decoded!.data)
|
||||
let prof_model = ProfileModel(pubkey: hex, damus: damus_state)
|
||||
let f = FollowersModel(damus_state: damus_state, target: prof)
|
||||
let dst = ProfileView(damus_state: damus_state, profile: prof_model, followers: f)
|
||||
NavigationLink(destination: dst) {
|
||||
let prof_view = ProfileView(damus_state: damus_state, pubkey: hex)
|
||||
NavigationLink(destination: prof_view) {
|
||||
Text("Goto profile \(prof)", comment: "Navigation link to go to profile.")
|
||||
}
|
||||
case .hex(let h):
|
||||
let prof_model = ProfileModel(pubkey: h, damus: damus_state)
|
||||
let f = FollowersModel(damus_state: damus_state, target: h)
|
||||
let prof_view = ProfileView(damus_state: damus_state, profile: prof_model, followers: f)
|
||||
let ev_view = BuildThreadV2View(
|
||||
damus: damus_state,
|
||||
event_id: h
|
||||
)
|
||||
let prof_view = ProfileView(damus_state: damus_state, pubkey: h)
|
||||
//let ev_view = ThreadView(damus: damus_state, event_id: h)
|
||||
|
||||
NavigationLink(destination: prof_view) {
|
||||
Text("Goto profile \(h)", comment: "Navigation link to go to profile referenced by hex code.")
|
||||
}
|
||||
/*
|
||||
VStack(spacing: 50) {
|
||||
NavigationLink(destination: prof_view) {
|
||||
Text("Goto profile \(h)", comment: "Navigation link to go to profile referenced by hex code.")
|
||||
}
|
||||
NavigationLink(destination: ev_view) {
|
||||
Text("Goto post \(h)", comment: "Navigation link to go to post referenced by hex code.")
|
||||
}
|
||||
}
|
||||
*/
|
||||
case .note(let nid):
|
||||
/*
|
||||
let decoded = try? bech32_decode(nid)
|
||||
let hex = hex_encode(decoded!.data)
|
||||
let ev_view = BuildThreadV2View(
|
||||
damus: damus_state,
|
||||
event_id: hex
|
||||
)
|
||||
let ev_view = ThreadView(state: state, ev: ev)
|
||||
*/
|
||||
Text("Todo: fix this")
|
||||
/*
|
||||
NavigationLink(destination: ev_view) {
|
||||
Text("Goto post \(nid)", comment: "Navigation link to go to post referenced by note ID.")
|
||||
}
|
||||
*/
|
||||
case .none:
|
||||
Text("none", comment: "No search results.")
|
||||
}
|
||||
|
||||
@@ -1,336 +0,0 @@
|
||||
//
|
||||
// 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 extra_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_current_events(ev: NostrEvent) {
|
||||
if current_event != nil {
|
||||
return
|
||||
}
|
||||
|
||||
current_event = ev
|
||||
|
||||
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(
|
||||
kinds: [1],
|
||||
referenced_ids: [self.event_id],
|
||||
limit: 50
|
||||
)
|
||||
childs_events_uuid = subscribe(filters: [childs_events])
|
||||
print("ThreadV2View: Ask for children (\(childs_events) (\(childs_events_uuid))")
|
||||
}
|
||||
|
||||
func handle_parent_events(sub_id: String, nostr_event: NostrEvent) {
|
||||
|
||||
// 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(sub_id)
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
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 {
|
||||
handle_current_events(ev: nostr_event)
|
||||
return
|
||||
}
|
||||
|
||||
if parents_events_uuids.contains(id) {
|
||||
handle_parent_events(sub_id: id, nostr_event: nostr_event)
|
||||
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!")
|
||||
|
||||
var extra = NostrFilter.filter_kinds([9735, 6, 7])
|
||||
extra.referenced_ids = [ self.event_id ]
|
||||
|
||||
// Get the current event
|
||||
current_events_uuid = subscribe(filters: [
|
||||
NostrFilter(ids: [self.event_id], limit: 1)
|
||||
])
|
||||
|
||||
extra_events_uuid = subscribe(filters: [extra])
|
||||
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
|
||||
@State var nav_target: String? = nil
|
||||
@State var navigating: Bool = false
|
||||
|
||||
var MaybeBuildThreadView: some View {
|
||||
Group {
|
||||
if let evid = nav_target {
|
||||
BuildThreadV2View(damus: damus, event_id: evid)
|
||||
} else {
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
NavigationLink(destination: MaybeBuildThreadView, isActive: $navigating) {
|
||||
EmptyView()
|
||||
}
|
||||
ScrollViewReader { reader in
|
||||
ScrollView {
|
||||
VStack {
|
||||
// MARK: - Parents events view
|
||||
VStack {
|
||||
ForEach(thread.parentEvents, id: \.id) { event in
|
||||
MutedEventView(damus_state: damus,
|
||||
event: event,
|
||||
scroller: reader,
|
||||
nav_target: $nav_target,
|
||||
navigating: $navigating,
|
||||
selected: false
|
||||
)
|
||||
|
||||
Divider()
|
||||
.padding(.top, 4)
|
||||
.padding(.leading, 25 * 2)
|
||||
}
|
||||
}.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.25))
|
||||
.frame(width: 2, height: eventHeight)
|
||||
.offset(x: 25, y: 40)
|
||||
})
|
||||
|
||||
// MARK: - Actual event view
|
||||
MutedEventView(
|
||||
damus_state: damus,
|
||||
event: thread.current,
|
||||
scroller: reader,
|
||||
nav_target: $nav_target,
|
||||
navigating: $navigating,
|
||||
selected: true
|
||||
).id("main")
|
||||
|
||||
// MARK: - Responses of the actual event view
|
||||
LazyVStack {
|
||||
ForEach(thread.childEvents, id: \.id) { event in
|
||||
MutedEventView(
|
||||
damus_state: damus,
|
||||
event: event,
|
||||
scroller: nil,
|
||||
nav_target: $nav_target,
|
||||
navigating: $navigating,
|
||||
selected: false
|
||||
)
|
||||
|
||||
Divider()
|
||||
.padding([.top], 4)
|
||||
}
|
||||
}
|
||||
}.padding()
|
||||
}.navigationBarTitle(NSLocalizedString("Thread", comment: "Navigation bar title for note 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"),
|
||||
]
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
101
damus/Views/ThreadView.swift
Normal file
101
damus/Views/ThreadView.swift
Normal file
@@ -0,0 +1,101 @@
|
||||
//
|
||||
// ThreadV2View.swift
|
||||
// damus
|
||||
//
|
||||
// Created by Thomas Tastet on 25/12/2022.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ThreadView: View {
|
||||
let state: DamusState
|
||||
|
||||
@StateObject var thread: ThreadModel
|
||||
@Environment(\.dismiss) var dismiss
|
||||
|
||||
var parent_events: [NostrEvent] {
|
||||
state.events.parent_events(event: thread.event)
|
||||
}
|
||||
|
||||
var child_events: [NostrEvent] {
|
||||
state.events.child_events(event: thread.event)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ScrollViewReader { reader in
|
||||
ScrollView {
|
||||
LazyVStack {
|
||||
// MARK: - Parents events view
|
||||
ForEach(parent_events, id: \.id) { parent_event in
|
||||
MutedEventView(damus_state: state,
|
||||
event: parent_event,
|
||||
scroller: reader,
|
||||
selected: false
|
||||
)
|
||||
.onTapGesture {
|
||||
thread.set_active_event(parent_event, privkey: state.keypair.privkey)
|
||||
scroll_to_event(scroller: reader, id: parent_event.id, delay: 0.1, animate: false)
|
||||
}
|
||||
|
||||
Divider()
|
||||
.padding(.top, 4)
|
||||
.padding(.leading, 25 * 2)
|
||||
}.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.25))
|
||||
.frame(width: 2, height: eventHeight)
|
||||
.offset(x: 25, y: 40)
|
||||
})
|
||||
|
||||
// MARK: - Actual event view
|
||||
MutedEventView(
|
||||
damus_state: state,
|
||||
event: self.thread.event,
|
||||
scroller: reader,
|
||||
selected: true
|
||||
)
|
||||
.id(self.thread.event.id)
|
||||
|
||||
ForEach(child_events, id: \.id) { child_event in
|
||||
MutedEventView(
|
||||
damus_state: state,
|
||||
event: child_event,
|
||||
scroller: nil,
|
||||
selected: false
|
||||
)
|
||||
.onTapGesture {
|
||||
thread.set_active_event(child_event, privkey: state.keypair.privkey)
|
||||
scroll_to_event(scroller: reader, id: child_event.id, delay: 0.1, animate: false)
|
||||
}
|
||||
|
||||
Divider()
|
||||
.padding([.top], 4)
|
||||
}
|
||||
}.padding()
|
||||
}.navigationBarTitle(NSLocalizedString("Thread", comment: "Navigation bar title for note thread."))
|
||||
.onAppear {
|
||||
thread.subscribe()
|
||||
scroll_to_event(scroller: reader, id: self.thread.event.id, delay: 0.0, animate: false)
|
||||
}
|
||||
.onDisappear {
|
||||
thread.unsubscribe()
|
||||
}
|
||||
.onReceive(handle_notify(.switched_timeline)) { notif in
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ThreadView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let state = test_damus_state()
|
||||
let thread = ThreadModel(event: test_event, damus_state: state)
|
||||
ThreadView(state: state, thread: thread)
|
||||
}
|
||||
}
|
||||
@@ -13,21 +13,22 @@ struct InnerTimelineView: View {
|
||||
let damus: DamusState
|
||||
let show_friend_icon: Bool
|
||||
let filter: (NostrEvent) -> Bool
|
||||
@State var nav_target: NostrEvent? = nil
|
||||
@State var nav_target: NostrEvent
|
||||
@State var navigating: Bool = false
|
||||
|
||||
var MaybeBuildThreadView: some View {
|
||||
Group {
|
||||
if let ev = nav_target {
|
||||
BuildThreadV2View(damus: damus, event_id: (ev.inner_event ?? ev).id)
|
||||
} else {
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
init(events: EventHolder, damus: DamusState, show_friend_icon: Bool, filter: @escaping (NostrEvent) -> Bool) {
|
||||
self.events = events
|
||||
self.damus = damus
|
||||
self.show_friend_icon = show_friend_icon
|
||||
self.filter = filter
|
||||
// dummy event to avoid MaybeThreadView
|
||||
self._nav_target = State(initialValue: test_event)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
NavigationLink(destination: MaybeBuildThreadView, isActive: $navigating) {
|
||||
let thread = ThreadModel(event: nav_target, damus_state: damus)
|
||||
let dest = ThreadView(state: damus, thread: thread)
|
||||
NavigationLink(destination: dest, isActive: $navigating) {
|
||||
EmptyView()
|
||||
}
|
||||
LazyVStack(spacing: 0) {
|
||||
@@ -55,7 +56,7 @@ struct InnerTimelineView: View {
|
||||
|
||||
struct InnerTimelineView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
InnerTimelineView(events: test_event_holder, damus: test_damus_state(), show_friend_icon: true, filter: { _ in true }, nav_target: nil, navigating: false)
|
||||
InnerTimelineView(events: test_event_holder, damus: test_damus_state(), show_friend_icon: true, filter: { _ in true })
|
||||
.frame(width: 300, height: 500)
|
||||
.border(Color.red)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user