damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | LICENSE

commit 090385d3da19b7a7eafadd1c15f65a03feb9d403
parent 4de2ce402eff8363a8d0557e94c1695731cf1a70
Author: William Casarin <jb55@jb55.com>
Date:   Sat, 23 Apr 2022 17:10:51 -0700

better chat view

Signed-off-by: William Casarin <jb55@jb55.com>

Diffstat:
Mdamus/Notifications.swift | 6++++++
Mdamus/Views/ChatView.swift | 114+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Mdamus/Views/ChatroomView.swift | 9++++++++-
Mdamus/Views/EventActionBar.swift | 2--
Mdamus/Views/EventView.swift | 2+-
Mdamus/Views/ProfilePicView.swift | 16+++++++++-------
Mdamus/Views/ReplyQuoteView.swift | 17++++++++---------
7 files changed, 106 insertions(+), 60 deletions(-)

diff --git a/damus/Notifications.swift b/damus/Notifications.swift @@ -20,6 +20,12 @@ extension Notification.Name { } extension Notification.Name { + static var select_quote: Notification.Name { + return Notification.Name("select quote") + } +} + +extension Notification.Name { static var broadcast_event: Notification.Name { return Notification.Name("broadcast event") } diff --git a/damus/Views/ChatView.swift b/damus/Views/ChatView.swift @@ -19,6 +19,26 @@ struct ChatView: View { return prev_ev == nil || prev_ev!.pubkey != event.pubkey } + func next_replies_to_this() -> Bool { + guard let next = next_ev else { + return false + } + + return thread.replies.lookup(next.id) != nil + } + + func is_reply_to_prev() -> Bool { + guard let prev = prev_ev else { + return true + } + + if let rep = thread.replies.lookup(event.id) { + return rep == prev.id + } + + return false + } + var is_active: Bool { guard let ev = thread.event else { return false @@ -56,65 +76,79 @@ struct ChatView: View { Text("\(reply_desc(profiles: profiles, event: event))") .font(.footnote) .foregroundColor(.gray) - .frame(maxWidth: .infinity, alignment: .leading) + .frame(alignment: .leading) } + @Environment(\.colorScheme) var colorScheme + var body: some View { let profile = profiles.lookup(id: event.pubkey) HStack { - VStack { - if is_active || just_started { - ProfilePicView(picture: profile?.picture, size: 32, highlight: is_active ? .main : .none) - } - /* - if just_started { - ProfilePicView(picture: profile?.picture, size: 32, highlight: thread.event.id == event.id ? .main : .none) - } else { - Text("\(format_relative_time(event.created_at))") - .font(.footnote) - .foregroundColor(.gray.opacity(0.5)) - } - */ + //ZStack { + //Rectangle() + //.foregroundColor(Color.gray) + //.frame(width: 2) + + VStack { + if is_active || just_started { + ProfilePicView(picture: profile?.picture, size: 32, highlight: is_active ? .main : .none) + } + /* + if just_started { + ProfilePicView(picture: profile?.picture, size: 32, highlight: thread.event.id == event.id ? .main : .none) + } else { + Text("\(format_relative_time(event.created_at))") + .font(.footnote) + .foregroundColor(.gray.opacity(0.5)) + } + */ - Spacer() - } - .frame(maxWidth: 32) + Spacer() + } + .frame(maxWidth: 32) + //} - VStack { - if just_started { - HStack { + Group { + VStack(alignment: .leading) { + if just_started { + HStack { ProfileName(pubkey: event.pubkey, profile: profile) Text("\(format_relative_time(event.created_at))") - .foregroundColor(.gray) - Spacer() + .foregroundColor(.gray) + } } - } - - if let ref_id = thread.replies.lookup(event.id) { - ReplyQuoteView(quoter: event, event_id: ref_id) - .environmentObject(thread) - .environmentObject(profiles) - ReplyDescription - } - - Text(event.content) - .frame(maxWidth: .infinity, alignment: .leading) - .textSelection(.enabled) - if is_active || next_ev == nil || next_ev!.pubkey != event.pubkey { - EventActionBar(event: event) - .environmentObject(profiles) - } + if let ref_id = thread.replies.lookup(event.id) { + if !is_reply_to_prev() { + ReplyQuoteView(quoter: event, event_id: ref_id) + .environmentObject(thread) + .environmentObject(profiles) + ReplyDescription + } + } - Spacer() + Text(event.content) + .textSelection(.enabled) + + if is_active || next_ev == nil || next_ev!.pubkey != event.pubkey { + EventActionBar(event: event) + .environmentObject(profiles) + } + + //Spacer() + } + .padding(6) } .padding([.leading], 2) + .background(Color.secondary.opacity(0.1)) + .cornerRadius(8.0) + //.border(Color.red) } .contentShape(Rectangle()) .id(event.id) .frame(minHeight: just_started ? PFP_SIZE : 0) - .padding([.bottom], next_ev == nil ? 4 : 0) + //.padding([.bottom], next_ev == nil ? 2 : 0) //.border(Color.green) } diff --git a/damus/Views/ChatroomView.swift b/damus/Views/ChatroomView.swift @@ -14,7 +14,7 @@ struct ChatroomView: View { var body: some View { ScrollViewReader { scroller in ScrollView { - LazyVStack { + LazyVStack(alignment: .leading) { let count = thread.events.count ForEach(Array(zip(thread.events, thread.events.indices)), id: \.0.id) { (ev, ind) in ChatView(event: thread.events[ind], @@ -33,6 +33,13 @@ struct ChatroomView: View { } } } + .onReceive(NotificationCenter.default.publisher(for: .select_quote)) { notif in + let ev = notif.object as! NostrEvent + if ev.id != thread.event!.id { + thread.set_active_event(ev) + } + scroll_to_event(scroller: scroller, id: ev.id, delay: 0, animate: true, anchor: .top) + } .onAppear() { scroll_to_event(scroller: scroller, id: thread.event!.id, delay: 0.3, animate: true, anchor: .bottom) } diff --git a/damus/Views/EventActionBar.swift b/damus/Views/EventActionBar.swift @@ -25,8 +25,6 @@ struct EventActionBar: View { var body: some View { HStack { - Spacer() - /* EventActionButton(img: "square.and.arrow.up") { print("share") diff --git a/damus/Views/EventView.swift b/damus/Views/EventView.swift @@ -39,7 +39,7 @@ struct EventView: View { let profile = profiles.lookup(id: event.pubkey) HStack { VStack { - ProfilePicView(picture: profile?.picture, size: 64, highlight: highlight) + ProfilePicView(picture: profile?.picture, size: PFP_SIZE!, highlight: highlight) Spacer() } diff --git a/damus/Views/ProfilePicView.swift b/damus/Views/ProfilePicView.swift @@ -7,7 +7,7 @@ import SwiftUI -let PFP_SIZE: CGFloat? = 64 +let PFP_SIZE: CGFloat? = 52.0 let CORNER_RADIUS: CGFloat = 32 func id_to_color(_ id: String) -> Color { @@ -27,7 +27,7 @@ func pfp_line_width(_ h: Highlight) -> CGFloat { case .none: fallthrough case .reply: return 0 - case .main: return 4 + case .main: return 2 } } @@ -35,20 +35,22 @@ struct ProfilePicView: View { let picture: String? let size: CGFloat let highlight: Highlight - + + var Placeholder: some View { + Color.purple.opacity(0.2) + } + var body: some View { if let pic = picture.flatMap({ URL(string: $0) }) { AsyncImage(url: pic) { img in img.resizable() - } placeholder: { - Color.purple.opacity(0.2) - } + } placeholder: { Placeholder } .frame(width: size, height: size) .clipShape(Circle()) .overlay(Circle().stroke(highlight_color(highlight), lineWidth: pfp_line_width(highlight))) .padding(2) } else { - Color.purple.opacity(0.2) + Placeholder .frame(width: size, height: size) .cornerRadius(CORNER_RADIUS) .overlay(Circle().stroke(highlight_color(highlight), lineWidth: pfp_line_width(highlight))) diff --git a/damus/Views/ReplyQuoteView.swift b/damus/Views/ReplyQuoteView.swift @@ -16,19 +16,20 @@ struct ReplyQuoteView: View { func MainContent(event: NostrEvent) -> some View { HStack(alignment: .top) { - ProfilePicView(picture: profiles.lookup(id: event.pubkey)?.picture, size: 16, highlight: .none) - //.border(Color.blue) + Rectangle().frame(width: 2) + .padding([.leading], 4) - VStack { - HStack { + + VStack(alignment: .leading) { + HStack(alignment: .top) { + ProfilePicView(picture: profiles.lookup(id: event.pubkey)?.picture, size: 16, highlight: .none) ProfileName(pubkey: event.pubkey, profile: profiles.lookup(id: event.pubkey)) Text("\(format_relative_time(event.created_at))") .foregroundColor(.gray) - Spacer() } Text(event.content) - .frame(maxWidth: .infinity, alignment: .leading) + //.frame(maxWidth: .infinity, alignment: .leading) .textSelection(.enabled) //Spacer() @@ -44,11 +45,9 @@ struct ReplyQuoteView: View { MainContent(event: event) .padding(4) .frame(maxHeight: 100) - .background(event.id == thread.event!.id ? Color.red.opacity(0.2) : Color.secondary.opacity(0.2)) - .cornerRadius(8.0) .contentShape(Rectangle()) .onTapGesture { - thread.set_active_event(event) + NotificationCenter.default.post(name: .select_quote, object: event) } } else { ProgressView()