old style navigation

Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
William Casarin
2022-04-30 11:46:56 -07:00
parent a88324333b
commit d574e572d4
13 changed files with 141 additions and 140 deletions

View File

@@ -44,12 +44,10 @@ struct ContentView: View {
@State var status: String = "Not connected" @State var status: String = "Not connected"
@State var active_sheet: Sheets? = nil @State var active_sheet: Sheets? = nil
@State var profiles: Profiles = Profiles() @State var profiles: Profiles = Profiles()
@State var active_profile: ProfileModel = ProfileModel()
@State var friends: [String: ()] = [:] @State var friends: [String: ()] = [:]
@State var loading: Bool = true @State var loading: Bool = true
@State var pool: RelayPool? = nil @State var pool: RelayPool? = nil
@State var selected_timeline: Timeline? = .home @State var selected_timeline: Timeline? = .home
@StateObject var thread: ThreadModel = ThreadModel()
@State var is_thread_open: Bool = false @State var is_thread_open: Bool = false
@State var is_profile_open: Bool = false @State var is_profile_open: Bool = false
@State var last_event_of_kind: [String: [Int: NostrEvent]] = [:] @State var last_event_of_kind: [String: [Int: NostrEvent]] = [:]
@@ -139,7 +137,6 @@ struct ContentView: View {
ZStack { ZStack {
if let pool = self.pool { if let pool = self.pool {
TimelineView(events: $friend_events, pool: pool) TimelineView(events: $friend_events, pool: pool)
.environmentObject(thread)
.environmentObject(profiles) .environmentObject(profiles)
} }
PostButtonContainer PostButtonContainer
@@ -169,23 +166,6 @@ struct ContentView: View {
case .none: case .none:
EmptyView() EmptyView()
} }
let tv = ThreadView()
.environmentObject(thread)
.environmentObject(profiles)
.padding([.leading, .trailing], 6)
let pv = ProfileView(pool: pool)
.environmentObject(active_profile)
.environmentObject(profiles)
NavigationLink(destination: tv, isActive: $is_thread_open) {
EmptyView()
}
NavigationLink(destination: pv, isActive: $is_profile_open) {
EmptyView()
}
} }
.navigationBarTitle("Damus", displayMode: .inline) .navigationBarTitle("Damus", displayMode: .inline)
} }
@@ -212,7 +192,7 @@ struct ContentView: View {
case .post: case .post:
PostView(references: []) PostView(references: [])
case .reply(let event): case .reply(let event):
ReplyView(replying_to: event) ReplyView(replying_to: event, pool: pool!)
.environmentObject(profiles) .environmentObject(profiles)
} }
} }
@@ -222,10 +202,9 @@ struct ContentView: View {
self.pool?.send(.event(boost)) self.pool?.send(.event(boost))
} }
.onReceive(handle_notify(.open_thread)) { obj in .onReceive(handle_notify(.open_thread)) { obj in
let ev = obj.object as! NostrEvent //let ev = obj.object as! NostrEvent
thread.reset_events() //thread.set_active_event(ev)
thread.set_active_event(ev) //is_thread_open = true
is_thread_open = true
} }
.onReceive(handle_notify(.reply)) { notif in .onReceive(handle_notify(.reply)) { notif in
let ev = notif.object as! NostrEvent let ev = notif.object as! NostrEvent
@@ -235,11 +214,6 @@ struct ContentView: View {
let ev = obj.object as! NostrEvent let ev = obj.object as! NostrEvent
self.pool?.send(.event(ev)) self.pool?.send(.event(ev))
} }
.onReceive(handle_notify(.click_profile_pic)) { obj in
let pubkey = obj.object as! String
self.active_profile.set_pubkey(pubkey)
self.is_profile_open = true
}
.onReceive(handle_notify(.post)) { obj in .onReceive(handle_notify(.post)) { obj in
let post_res = obj.object as! NostrPostResult let post_res = obj.object as! NostrPostResult
switch post_res { switch post_res {
@@ -318,8 +292,6 @@ struct ContentView: View {
pool.register_handler(sub_id: sub_id, handler: handle_event) pool.register_handler(sub_id: sub_id, handler: handle_event)
self.pool = pool self.pool = pool
self.thread.pool = pool
self.active_profile.pool = pool
pool.connect() pool.connect()
} }

View File

@@ -9,41 +9,24 @@ import Foundation
class ProfileModel: ObservableObject { class ProfileModel: ObservableObject {
@Published var events: [NostrEvent] = [] @Published var events: [NostrEvent] = []
@Published var pubkey: String? let pubkey: String
var seen_event: Set<String> = Set() let pool: RelayPool
var seen_event: Set<String> = Set()
var sub_id = UUID().description var sub_id = UUID().description
var pool: RelayPool? = nil
deinit { init(pubkey: String, pool: RelayPool) {
unsubscribe() self.pubkey = pubkey
self.pool = pool
} }
func unsubscribe() { func unsubscribe() {
print("unsubscribing from profile \(pubkey ?? "?") with sub_id \(sub_id)") print("unsubscribing from profile \(pubkey) with sub_id \(sub_id)")
pool?.unsubscribe(sub_id: sub_id) pool.unsubscribe(sub_id: sub_id)
}
func set_pubkey(_ pk: String) {
if pk == self.pubkey {
return
}
self.events.removeAll()
self.seen_event.removeAll()
unsubscribe()
self.sub_id = UUID().description
self.pubkey = pk
subscribe()
} }
func subscribe() { func subscribe() {
guard let pubkey = self.pubkey else {
return
}
let kinds: [Int] = [ let kinds: [Int] = [
NostrKind.text.rawValue, NostrKind.text.rawValue,
NostrKind.delete.rawValue, NostrKind.delete.rawValue,
@@ -54,7 +37,7 @@ class ProfileModel: ObservableObject {
filter.authors = [pubkey] filter.authors = [pubkey]
print("subscribing to profile \(pubkey) with sub_id \(sub_id)") print("subscribing to profile \(pubkey) with sub_id \(sub_id)")
pool?.subscribe(sub_id: sub_id, filters: [filter], handler: handle_event) pool.subscribe(sub_id: sub_id, filters: [filter], handler: handle_event)
} }
func add_event(_ ev: NostrEvent) { func add_event(_ ev: NostrEvent) {

View File

@@ -9,25 +9,27 @@ import Foundation
/// manages the lifetime of a thread /// manages the lifetime of a thread
class ThreadModel: ObservableObject { class ThreadModel: ObservableObject {
@Published var event: NostrEvent? = nil @Published var event: NostrEvent
@Published var events: [NostrEvent] = [] @Published var events: [NostrEvent] = []
@Published var event_map: [String: Int] = [:] @Published var event_map: [String: Int] = [:]
var replies: ReplyMap = ReplyMap() var replies: ReplyMap = ReplyMap()
var pool: RelayPool? = nil let pool: RelayPool
var sub_id = UUID().description var sub_id = UUID().description
init(ev: NostrEvent, pool: RelayPool) {
self.event = ev
self.pool = pool
subscribe()
}
deinit { deinit {
unsubscribe() unsubscribe()
} }
func unsubscribe() { func unsubscribe() {
guard let event = self.event else { self.pool.unsubscribe(sub_id: sub_id)
return
}
print("unsubscribing from thread \(event.id) with sub_id \(sub_id)") print("unsubscribing from thread \(event.id) with sub_id \(sub_id)")
self.pool?.remove_handler(sub_id: sub_id)
self.pool?.send(.unsubscribe(sub_id))
} }
func reset_events() { func reset_events() {
@@ -41,16 +43,13 @@ class ThreadModel: ObservableObject {
return true return true
} }
guard let ev_a = self.event else {
return true
}
if ev_b.is_root_event() { if ev_b.is_root_event() {
return false return false
} }
// rough heuristic to save us from resubscribing all the time // rough heuristic to save us from resubscribing all the time
return ev_b.count_ids() != ev_a.count_ids() //return ev_b.count_ids() != self.event.count_ids()
return true
} }
func set_active_event(_ ev: NostrEvent) { func set_active_event(_ ev: NostrEvent) {
@@ -58,7 +57,7 @@ class ThreadModel: ObservableObject {
unsubscribe() unsubscribe()
self.event = ev self.event = ev
add_event(ev) add_event(ev)
subscribe(ev) subscribe()
} else { } else {
self.event = ev self.event = ev
if events.count == 0 { if events.count == 0 {
@@ -67,20 +66,20 @@ class ThreadModel: ObservableObject {
} }
} }
private func subscribe(_ ev: NostrEvent) { func subscribe() {
let kinds: [Int] = [1, 5, 6] let kinds: [Int] = [1, 5, 6]
var ref_events = NostrFilter.filter_kinds(kinds) var ref_events = NostrFilter.filter_kinds(kinds)
var events_filter = NostrFilter.filter_kinds(kinds) var events_filter = NostrFilter.filter_kinds(kinds)
// TODO: add referenced relays // TODO: add referenced relays
ref_events.referenced_ids = ev.referenced_ids.map { $0.ref_id } ref_events.referenced_ids = event.referenced_ids.map { $0.ref_id }
ref_events.referenced_ids!.append(ev.id) ref_events.referenced_ids!.append(event.id)
events_filter.ids = ref_events.referenced_ids! events_filter.ids = ref_events.referenced_ids!
print("subscribing to thread \(ev.id) with sub_id \(sub_id)") print("subscribing to thread \(event.id) with sub_id \(sub_id)")
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_filter], sub_id: sub_id))) pool.send(.subscribe(.init(filters: [ref_events, events_filter], sub_id: sub_id)))
} }
func lookup(_ event_id: String) -> NostrEvent? { func lookup(_ event_id: String) -> NostrEvent? {
@@ -122,7 +121,7 @@ class ThreadModel: ObservableObject {
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?
pool?.reconnect(to: [relay_id]) pool.reconnect(to: [relay_id])
} }
break break
} }

View File

@@ -61,6 +61,24 @@ extension Notification.Name {
} }
} }
extension Notification.Name {
static var notice: Notification.Name {
return Notification.Name("notice")
}
}
extension Notification.Name {
static var like: Notification.Name {
return Notification.Name("like note")
}
}
extension Notification.Name {
static var delete: Notification.Name {
return Notification.Name("delete note")
}
}
extension Notification.Name { extension Notification.Name {
static var post: Notification.Name { static var post: Notification.Name {
return Notification.Name("send post") return Notification.Name("send post")

View File

@@ -40,10 +40,7 @@ struct ChatView: View {
} }
var is_active: Bool { var is_active: Bool {
guard let ev = thread.event else { return thread.event.id == event.id
return false
}
return ev.id == event.id
} }
func prev_reply_is_same() -> String? { func prev_reply_is_same() -> String? {

View File

@@ -22,7 +22,7 @@ struct ChatroomView: View {
next_ev: ind == count-1 ? nil : thread.events[ind+1] next_ev: ind == count-1 ? nil : thread.events[ind+1]
) )
.onTapGesture { .onTapGesture {
if thread.event!.id == ev.id { if thread.event.id == ev.id {
//dismiss() //dismiss()
toggle_thread_view() toggle_thread_view()
} else { } else {
@@ -35,13 +35,13 @@ struct ChatroomView: View {
} }
.onReceive(NotificationCenter.default.publisher(for: .select_quote)) { notif in .onReceive(NotificationCenter.default.publisher(for: .select_quote)) { notif in
let ev = notif.object as! NostrEvent let ev = notif.object as! NostrEvent
if ev.id != thread.event!.id { if ev.id != thread.event.id {
thread.set_active_event(ev) thread.set_active_event(ev)
} }
scroll_to_event(scroller: scroller, id: ev.id, delay: 0, animate: true, anchor: .top) scroll_to_event(scroller: scroller, id: ev.id, delay: 0, animate: true, anchor: .top)
} }
.onAppear() { .onAppear() {
scroll_to_event(scroller: scroller, id: thread.event!.id, delay: 0.3, animate: true, anchor: .bottom) scroll_to_event(scroller: scroller, id: thread.event.id, delay: 0.3, animate: true, anchor: .bottom)
} }
} }
} }

View File

@@ -19,10 +19,10 @@ enum ActionBarSheet: Identifiable {
struct EventActionBar: View { struct EventActionBar: View {
let event: NostrEvent let event: NostrEvent
@State var sheet: ActionBarSheet? = nil @State var sheet: ActionBarSheet? = nil
@EnvironmentObject var profiles: Profiles @EnvironmentObject var profiles: Profiles
@StateObject var bar: ActionBarModel = ActionBarModel()
var body: some View { var body: some View {
HStack { HStack {
/* /*
@@ -33,13 +33,26 @@ struct EventActionBar: View {
Spacer() Spacer()
*/ */
EventActionButton(img: "bubble.left") { EventActionButton(img: "bubble.left", col: nil) {
notify(.reply, event) notify(.reply, event)
} }
.padding([.trailing], 40) .padding([.trailing], 40)
EventActionButton(img: "arrow.2.squarepath") { EventActionButton(img: bar.liked ? "heart.fill" : "heart", col: bar.liked ? Color.red : nil) {
notify(.boost, event) if bar.liked {
notify(.delete, bar.our_like_event)
} else {
notify(.like, event)
}
}
.padding([.trailing], 40)
EventActionButton(img: "arrow.2.squarepath", col: bar.boosted ? Color.green : nil) {
if bar.boosted {
notify(.delete, bar.our_boost_event)
} else {
notify(.boost, event)
}
} }
} }
@@ -47,11 +60,11 @@ struct EventActionBar: View {
} }
func EventActionButton(img: String, action: @escaping () -> ()) -> some View { func EventActionButton(img: String, col: Color?, action: @escaping () -> ()) -> some View {
Button(action: action) { Button(action: action) {
Label("", systemImage: img) Label("", systemImage: img)
.font(.footnote) .font(.footnote)
.foregroundColor(.gray) .foregroundColor(col == nil ? Color.gray : col!)
} }
} }

View File

@@ -31,8 +31,8 @@ enum CollapsedEvent: Identifiable {
struct EventDetailView: View { struct EventDetailView: View {
let sub_id = UUID().description let sub_id = UUID().description
let pool: RelayPool
@StateObject var thread: ThreadModel @StateObject var thread: ThreadModel
@State var collapsed: Bool = true @State var collapsed: Bool = true
@@ -48,37 +48,6 @@ struct EventDetailView: View {
} }
} }
/*
func OldEventView(proxy: ScrollViewProxy, ev: NostrEvent, highlight: Highlight, collapsed_events: [CollapsedEvent]) -> some View {
Group {
if ev.id == thread.event.id {
EventView(event: ev, highlight: .main, has_action_bar: true)
.onAppear() {
scroll_to_event(scroller: proxy, id: ev.id, delay: 0.5, animate: true)
}
.onTapGesture {
print_event(ev)
let any = any_collapsed(collapsed_events)
if (collapsed && any) || (!collapsed && !any) {
toggle_collapse_thread(scroller: proxy, id: ev.id)
}
}
} else {
if !(self.collapsed && highlight.is_none) {
EventView(event: ev, highlight: collapsed ? .none : highlight, has_action_bar: true)
.onTapGesture {
print_event(ev)
if !collapsed {
toggle_collapse_thread(scroller: proxy, id: ev.id)
}
thread.event = ev
}
}
}
}
}
*/
func uncollapse_section(scroller: ScrollViewProxy, c: CollapsedEvents) func uncollapse_section(scroller: ScrollViewProxy, c: CollapsedEvents)
{ {
let ev = thread.events[c.start] let ev = thread.events[c.start]
@@ -101,9 +70,9 @@ struct EventDetailView: View {
toggle_thread_view() toggle_thread_view()
} }
case .event(let ev, let highlight): case .event(let ev, let highlight):
EventView(event: ev, highlight: highlight, has_action_bar: true) EventView(event: ev, highlight: highlight, has_action_bar: true, pool: pool)
.onTapGesture { .onTapGesture {
if thread.event!.id == ev.id { if thread.event.id == ev.id {
toggle_thread_view() toggle_thread_view()
} else { } else {
thread.set_active_event(ev) thread.set_active_event(ev)
@@ -324,3 +293,34 @@ func scroll_to_event(scroller: ScrollViewProxy, id: String, delay: Double, anima
} }
} }
/*
func OldEventView(proxy: ScrollViewProxy, ev: NostrEvent, highlight: Highlight, collapsed_events: [CollapsedEvent]) -> some View {
Group {
if ev.id == thread.event.id {
EventView(event: ev, highlight: .main, has_action_bar: true)
.onAppear() {
scroll_to_event(scroller: proxy, id: ev.id, delay: 0.5, animate: true)
}
.onTapGesture {
print_event(ev)
let any = any_collapsed(collapsed_events)
if (collapsed && any) || (!collapsed && !any) {
toggle_collapse_thread(scroller: proxy, id: ev.id)
}
}
} else {
if !(self.collapsed && highlight.is_none) {
EventView(event: ev, highlight: collapsed ? .none : highlight, has_action_bar: true)
.onTapGesture {
print_event(ev)
if !collapsed {
toggle_collapse_thread(scroller: proxy, id: ev.id)
}
thread.event = ev
}
}
}
}
}
*/

View File

@@ -40,17 +40,21 @@ struct EventView: View {
let event: NostrEvent let event: NostrEvent
let highlight: Highlight let highlight: Highlight
let has_action_bar: Bool let has_action_bar: Bool
let pool: RelayPool
@EnvironmentObject var profiles: Profiles @EnvironmentObject var profiles: Profiles
@EnvironmentObject var action_bar: ActionBarModel
var body: some View { var body: some View {
let profile = profiles.lookup(id: event.pubkey) let profile = profiles.lookup(id: event.pubkey)
HStack { HStack {
VStack { VStack {
ProfilePicView(picture: profile?.picture, size: PFP_SIZE!, highlight: highlight) let pv = ProfileView(pool: pool, profile: ProfileModel(pubkey: event.pubkey, pool: pool))
.onTapGesture { .environmentObject(profiles)
NotificationCenter.default.post(name: .click_profile_pic, object: event.pubkey)
} NavigationLink(destination: pv) {
ProfilePicView(picture: profile?.picture, size: PFP_SIZE!, highlight: highlight)
}
Spacer() Spacer()
} }

View File

@@ -15,13 +15,14 @@ enum ProfileTab: Hashable {
struct ProfileView: View { struct ProfileView: View {
let pool: RelayPool let pool: RelayPool
@State private var selected_tab: ProfileTab = .posts @State private var selected_tab: ProfileTab = .posts
@StateObject var profile: ProfileModel
@EnvironmentObject var profile: ProfileModel //@EnvironmentObject var profile: ProfileModel
@EnvironmentObject var profiles: Profiles @EnvironmentObject var profiles: Profiles
var TopSection: some View { var TopSection: some View {
HStack(alignment: .top) { HStack(alignment: .top) {
let data = profile.pubkey.flatMap { profiles.lookup(id: $0) } let data = profiles.lookup(id: profile.pubkey)
ProfilePicView(picture: data?.picture, size: 64, highlight: .custom(Color.black, 4)) ProfilePicView(picture: data?.picture, size: 64, highlight: .custom(Color.black, 4))
//.border(Color.blue) //.border(Color.blue)
VStack(alignment: .leading) { VStack(alignment: .leading) {
@@ -64,6 +65,13 @@ struct ProfileView: View {
//.border(Color.white) //.border(Color.white)
.frame(maxWidth: .infinity, alignment: .topLeading) .frame(maxWidth: .infinity, alignment: .topLeading)
.navigationBarTitle("Profile") .navigationBarTitle("Profile")
.onAppear() {
profile.subscribe()
}
.onDisappear {
profile.unsubscribe()
// our profilemodel needs a bit more help
}
} }
} }

View File

@@ -16,6 +16,7 @@ func all_referenced_pubkeys(_ ev: NostrEvent) -> [ReferencedId] {
struct ReplyView: View { struct ReplyView: View {
let replying_to: NostrEvent let replying_to: NostrEvent
let pool: RelayPool
@EnvironmentObject var profiles: Profiles @EnvironmentObject var profiles: Profiles
@@ -34,7 +35,7 @@ struct ReplyView: View {
.foregroundColor(.gray) .foregroundColor(.gray)
.font(.footnote) .font(.footnote)
} }
EventView(event: replying_to, highlight: .none, has_action_bar: false) EventView(event: replying_to, highlight: .none, has_action_bar: false, pool: pool)
PostView(references: replying_to.reply_ids()) PostView(references: replying_to.reply_ids())
Spacer() Spacer()

View File

@@ -10,9 +10,10 @@ import SwiftUI
struct ThreadView: View { struct ThreadView: View {
@State var is_chatroom: Bool = false @State var is_chatroom: Bool = false
@StateObject var thread: ThreadModel
let pool: RelayPool
@EnvironmentObject var profiles: Profiles @EnvironmentObject var profiles: Profiles
@EnvironmentObject var thread: ThreadModel
@Environment(\.dismiss) var dismiss @Environment(\.dismiss) var dismiss
var body: some View { var body: some View {
@@ -23,13 +24,12 @@ struct ThreadView: View {
.environmentObject(profiles) .environmentObject(profiles)
.environmentObject(thread) .environmentObject(thread)
} else { } else {
EventDetailView(thread: thread) EventDetailView(pool: pool, thread: thread)
.navigationBarTitle("Thread") .navigationBarTitle("Thread")
.environmentObject(profiles) .environmentObject(profiles)
.environmentObject(thread) .environmentObject(thread)
} }
/* /*
NavigationLink(destination: edv, isActive: $is_chatroom) { NavigationLink(destination: edv, isActive: $is_chatroom) {
EmptyView() EmptyView()

View File

@@ -37,10 +37,16 @@ struct TimelineView: View {
.environmentObject(profiles) .environmentObject(profiles)
*/ */
EventView(event: ev, highlight: .none, has_action_bar: true) let tv = ThreadView(thread: ThreadModel(ev: ev, pool: pool), pool: pool)
.onTapGesture { .environmentObject(profiles)
NotificationCenter.default.post(name: .open_thread, object: ev)
} NavigationLink(destination: tv) {
EventView(event: ev, highlight: .none, has_action_bar: true, pool: pool)
}
.buttonStyle(PlainButtonStyle())
//.onTapGesture {
//NotificationCenter.default.post(name: .open_thread, object: ev)
//}
} }
} }
} }