@@ -20,7 +20,10 @@ struct ChatView: View {
|
||||
}
|
||||
|
||||
var is_active: Bool {
|
||||
thread.event.id == event.id
|
||||
guard let ev = thread.event else {
|
||||
return false
|
||||
}
|
||||
return ev.id == event.id
|
||||
}
|
||||
|
||||
func prev_reply_is_same() -> String? {
|
||||
@@ -98,7 +101,7 @@ struct ChatView: View {
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.textSelection(.enabled)
|
||||
|
||||
if next_ev == nil || next_ev!.pubkey != event.pubkey {
|
||||
if is_active || next_ev == nil || next_ev!.pubkey != event.pubkey {
|
||||
EventActionBar(event: event)
|
||||
.environmentObject(profiles)
|
||||
}
|
||||
@@ -112,30 +115,14 @@ struct ChatView: View {
|
||||
.id(event.id)
|
||||
.frame(minHeight: just_started ? PFP_SIZE : 0)
|
||||
.padding([.bottom], next_ev == nil ? 4 : 0)
|
||||
.onTapGesture {
|
||||
if is_active {
|
||||
convert_to_thread()
|
||||
} else {
|
||||
thread.event = event
|
||||
}
|
||||
}
|
||||
//.border(Color.green)
|
||||
|
||||
}
|
||||
|
||||
@Environment(\.presentationMode) var presmode
|
||||
|
||||
func dismiss() {
|
||||
presmode.wrappedValue.dismiss()
|
||||
}
|
||||
|
||||
func convert_to_thread() {
|
||||
NotificationCenter.default.post(name: .convert_to_thread, object: nil)
|
||||
}
|
||||
}
|
||||
|
||||
extension Notification.Name {
|
||||
static var convert_to_thread: Notification.Name {
|
||||
static var toggle_thread_view: Notification.Name {
|
||||
return Notification.Name("convert_to_thread")
|
||||
}
|
||||
}
|
||||
@@ -149,3 +136,5 @@ struct ChatView_Previews: PreviewProvider {
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -9,26 +9,39 @@ import SwiftUI
|
||||
|
||||
struct ChatroomView: View {
|
||||
@EnvironmentObject var thread: ThreadModel
|
||||
@Environment(\.dismiss) var dismiss
|
||||
|
||||
var body: some View {
|
||||
ScrollViewReader { scroller in
|
||||
ScrollView {
|
||||
VStack {
|
||||
LazyVStack {
|
||||
let count = thread.events.count
|
||||
ForEach(Array(zip(thread.events, thread.events.indices)), id: \.0.id) { (ev, ind) in
|
||||
ChatView(event: thread.events[ind],
|
||||
prev_ev: ind > 0 ? thread.events[ind-1] : nil,
|
||||
next_ev: ind == count-1 ? nil : thread.events[ind+1]
|
||||
)
|
||||
.onTapGesture {
|
||||
if thread.event!.id == ev.id {
|
||||
//dismiss()
|
||||
toggle_thread_view()
|
||||
} else {
|
||||
thread.set_active_event(ev)
|
||||
}
|
||||
}
|
||||
.environmentObject(thread)
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear() {
|
||||
scroll_to_event(scroller: scroller, id: thread.event.id, delay: 0.5, animate: true, anchor: .center)
|
||||
scroll_to_event(scroller: scroller, id: thread.event!.id, delay: 0.3, animate: true, anchor: .bottom)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func toggle_thread_view() {
|
||||
NotificationCenter.default.post(name: .toggle_thread_view, object: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,12 +7,6 @@
|
||||
|
||||
import SwiftUI
|
||||
|
||||
extension Notification.Name {
|
||||
static var thread_focus: Notification.Name {
|
||||
return Notification.Name("thread focus")
|
||||
}
|
||||
}
|
||||
|
||||
enum ActionBarSheet: Identifiable {
|
||||
case reply
|
||||
|
||||
@@ -50,6 +44,15 @@ struct EventActionBar: View {
|
||||
case .reply:
|
||||
ReplyView(replying_to: event)
|
||||
.environmentObject(profiles)
|
||||
.onReceive(NotificationCenter.default.publisher(for: .post)) { obj in
|
||||
let res = obj.object as! NostrPostResult
|
||||
switch res {
|
||||
case .cancel:
|
||||
self.sheet = nil
|
||||
case .post:
|
||||
self.sheet = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,8 +47,9 @@ struct EventDetailView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func OurEventView(proxy: ScrollViewProxy, ev: NostrEvent, highlight: Highlight, collapsed_events: [CollapsedEvent]) -> some 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)
|
||||
@@ -76,6 +77,7 @@ struct EventDetailView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func uncollapse_section(scroller: ScrollViewProxy, c: CollapsedEvents)
|
||||
{
|
||||
@@ -86,28 +88,52 @@ struct EventDetailView: View {
|
||||
toggle_collapse_thread(scroller: scroller, id: start_id, animate: true, anchor: .top)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ScrollViewReader { proxy in
|
||||
ScrollView {
|
||||
let collapsed_events = calculated_collapsed_events(collapsed: self.collapsed, active: thread.event, events: thread.events)
|
||||
ForEach(collapsed_events, id: \.id) { cev in
|
||||
switch cev {
|
||||
case .collapsed(let c):
|
||||
Text("··· \(c.count) other replies ···")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.gray)
|
||||
.onTapGesture {
|
||||
self.uncollapse_section(scroller: proxy, c: c)
|
||||
//self.toggle_collapse_thread(scroller: proxy, id: nil)
|
||||
}
|
||||
case .event(let ev, let highlight):
|
||||
OurEventView(proxy: proxy, ev: ev, highlight: highlight, collapsed_events: collapsed_events)
|
||||
func CollapsedEventView(_ cev: CollapsedEvent, scroller: ScrollViewProxy) -> some View {
|
||||
Group {
|
||||
switch cev {
|
||||
case .collapsed(let c):
|
||||
Text("··· \(c.count) other replies ···")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.gray)
|
||||
.onTapGesture {
|
||||
//self.uncollapse_section(scroller: proxy, c: c)
|
||||
//self.toggle_collapse_thread(scroller: proxy, id: nil)
|
||||
toggle_thread_view()
|
||||
}
|
||||
case .event(let ev, let highlight):
|
||||
EventView(event: ev, highlight: highlight, has_action_bar: true)
|
||||
.onTapGesture {
|
||||
if thread.event!.id == ev.id {
|
||||
toggle_thread_view()
|
||||
} else {
|
||||
thread.set_active_event(ev)
|
||||
}
|
||||
}
|
||||
.onAppear() {
|
||||
if highlight == .main {
|
||||
scroll_to_event(scroller: scroller, id: ev.id, delay: 0.5, animate: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ScrollViewReader { proxy in
|
||||
ScrollView {
|
||||
let collapsed_events = calculated_collapsed_events(collapsed: self.collapsed, active: thread.event, events: thread.events)
|
||||
ForEach(collapsed_events, id: \.id) { cev in
|
||||
CollapsedEventView(cev, scroller: proxy)
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationBarTitle("Thread")
|
||||
|
||||
}
|
||||
|
||||
func toggle_thread_view() {
|
||||
NotificationCenter.default.post(name: .toggle_thread_view, object: nil)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -214,9 +240,13 @@ func determine_highlight(reply_map: [String: ()], current: NostrEvent, active: N
|
||||
*/
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
|
||||
guard let active = active else {
|
||||
return []
|
||||
}
|
||||
|
||||
let reply_map = make_reply_map(active: active, events: events)
|
||||
|
||||
if !collapsed {
|
||||
|
||||
@@ -50,9 +50,6 @@ struct EventView: View {
|
||||
Text("\(format_relative_time(event.created_at))")
|
||||
.foregroundColor(.gray)
|
||||
Spacer()
|
||||
if (event.pow ?? 0) >= 10 {
|
||||
PowView(event.pow)
|
||||
}
|
||||
}
|
||||
|
||||
if event.is_reply {
|
||||
@@ -82,6 +79,25 @@ struct EventView: View {
|
||||
.id(event.id)
|
||||
.frame(minHeight: PFP_SIZE)
|
||||
.padding([.bottom], 4)
|
||||
.contextMenu {
|
||||
Button {
|
||||
UIPasteboard.general.string = event.content
|
||||
} label: {
|
||||
Label("Copy", systemImage: "doc.on.doc")
|
||||
}
|
||||
|
||||
Button {
|
||||
UIPasteboard.general.string = event.id
|
||||
} label: {
|
||||
Label("Copy ID", systemImage: "tag")
|
||||
}
|
||||
|
||||
Button {
|
||||
NotificationCenter.default.post(name: .broadcast_event, object: event)
|
||||
} label: {
|
||||
Label("Broadcast", systemImage: "globe")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,3 +135,5 @@ func reply_others_desc(n: Int, n_pubkeys: Int) -> String {
|
||||
let plural = other == 1 ? "" : "s"
|
||||
return n > 1 ? " & \(other) other\(plural)" : ""
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,10 +7,9 @@
|
||||
|
||||
import SwiftUI
|
||||
|
||||
extension Notification.Name {
|
||||
static var post: Notification.Name {
|
||||
return Notification.Name("send post")
|
||||
}
|
||||
enum NostrPostResult {
|
||||
case post(NostrPost)
|
||||
case cancel
|
||||
}
|
||||
|
||||
struct NostrPost {
|
||||
@@ -45,28 +44,33 @@ struct PostView: View {
|
||||
@State var post: String = ""
|
||||
@FocusState var focus: Bool
|
||||
let references: [ReferencedId]
|
||||
|
||||
@Environment(\.presentationMode) var presmode
|
||||
|
||||
@Environment(\.presentationMode) var presentationMode
|
||||
|
||||
enum FocusField: Hashable {
|
||||
case post
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
NotificationCenter.default.post(name: .post, object: NostrPostResult.cancel)
|
||||
dismiss()
|
||||
}
|
||||
|
||||
func dismiss() {
|
||||
presmode.wrappedValue.dismiss()
|
||||
self.presentationMode.wrappedValue.dismiss()
|
||||
}
|
||||
|
||||
func send_post() {
|
||||
let new_post = NostrPost(content: self.post, references: references)
|
||||
NotificationCenter.default.post(name: .post, object: new_post)
|
||||
dismiss()
|
||||
NotificationCenter.default.post(name: .post, object: NostrPostResult.post(new_post))
|
||||
//dismiss()
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
HStack {
|
||||
Button("Cancel") {
|
||||
self.dismiss()
|
||||
self.cancel()
|
||||
}
|
||||
.foregroundColor(.primary)
|
||||
|
||||
|
||||
@@ -16,17 +16,19 @@ func id_to_color(_ id: String) -> Color {
|
||||
|
||||
func highlight_color(_ h: Highlight) -> Color {
|
||||
switch h {
|
||||
case .reply: fallthrough
|
||||
case .none: return Color.black
|
||||
case .main: return Color.red
|
||||
case .reply: return Color.blue
|
||||
}
|
||||
}
|
||||
|
||||
func pfp_line_width(_ h: Highlight) -> CGFloat {
|
||||
if h.is_none {
|
||||
switch h {
|
||||
case .none: fallthrough
|
||||
case .reply:
|
||||
return 0
|
||||
case .main: return 4
|
||||
}
|
||||
return 4
|
||||
}
|
||||
|
||||
struct ProfilePicView: View {
|
||||
|
||||
@@ -41,12 +41,15 @@ struct ReplyQuoteView: View {
|
||||
var body: some View {
|
||||
Group {
|
||||
if let event = thread.lookup(event_id) {
|
||||
Group {
|
||||
MainContent(event: event)
|
||||
.padding(4)
|
||||
}
|
||||
.background(Color.secondary.opacity(0.2))
|
||||
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)
|
||||
}
|
||||
} else {
|
||||
ProgressView()
|
||||
.progressViewStyle(.circular)
|
||||
|
||||
@@ -7,30 +7,37 @@
|
||||
|
||||
import SwiftUI
|
||||
|
||||
|
||||
struct ThreadView: View {
|
||||
@StateObject var thread: ThreadModel
|
||||
@State var is_thread: Bool = false
|
||||
@State var is_chatroom: Bool = false
|
||||
|
||||
@EnvironmentObject var profiles: Profiles
|
||||
@EnvironmentObject var thread: ThreadModel
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
ChatroomView()
|
||||
.environmentObject(thread)
|
||||
.onReceive(NotificationCenter.default.publisher(for: .convert_to_thread)) { _ in
|
||||
is_thread = true
|
||||
}
|
||||
if is_chatroom {
|
||||
ChatroomView()
|
||||
.navigationBarTitle("Chat")
|
||||
.environmentObject(profiles)
|
||||
.environmentObject(thread)
|
||||
} else {
|
||||
EventDetailView(thread: thread)
|
||||
.navigationBarTitle("Thread")
|
||||
.environmentObject(profiles)
|
||||
.environmentObject(thread)
|
||||
}
|
||||
|
||||
let edv = EventDetailView(thread: thread).environmentObject(profiles)
|
||||
NavigationLink(destination: edv, isActive: $is_thread) {
|
||||
|
||||
/*
|
||||
NavigationLink(destination: edv, isActive: $is_chatroom) {
|
||||
EmptyView()
|
||||
}
|
||||
*/
|
||||
}
|
||||
.onDisappear() {
|
||||
thread.unsubscribe()
|
||||
}
|
||||
.onAppear() {
|
||||
thread.subscribe()
|
||||
.onReceive(NotificationCenter.default.publisher(for: .toggle_thread_view)) { _ in
|
||||
is_chatroom = !is_chatroom
|
||||
//print("is_chatroom: \(is_chatroom)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,13 +7,25 @@
|
||||
|
||||
import SwiftUI
|
||||
|
||||
enum TimelineAction {
|
||||
case chillin
|
||||
case navigating
|
||||
}
|
||||
|
||||
struct TimelineView: View {
|
||||
@Binding var events: [NostrEvent]
|
||||
|
||||
@EnvironmentObject var profiles: Profiles
|
||||
|
||||
let pool: RelayPool
|
||||
|
||||
var body: some View {
|
||||
MainContent
|
||||
.padding([.leading, .trailing], 6)
|
||||
.environmentObject(profiles)
|
||||
}
|
||||
|
||||
var MainContent: some View {
|
||||
ScrollView {
|
||||
LazyVStack {
|
||||
ForEach(events, id: \.id) { (ev: NostrEvent) in
|
||||
@@ -24,20 +36,13 @@ struct TimelineView: View {
|
||||
.environmentObject(profiles)
|
||||
*/
|
||||
|
||||
let evdet = ThreadView(thread: ThreadModel(event: ev, pool: pool))
|
||||
.navigationBarTitle("Chat")
|
||||
.padding([.leading, .trailing], 6)
|
||||
.environmentObject(profiles)
|
||||
|
||||
NavigationLink(destination: evdet) {
|
||||
EventView(event: ev, highlight: .none, has_action_bar: true)
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
EventView(event: ev, highlight: .none, has_action_bar: true)
|
||||
.onTapGesture {
|
||||
NotificationCenter.default.post(name: .open_thread, object: ev)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding([.leading, .trailing], 6)
|
||||
.environmentObject(profiles)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,3 +53,14 @@ struct TimelineView_Previews: PreviewProvider {
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
struct NavigationLazyView<Content: View>: View {
|
||||
let build: () -> Content
|
||||
init(_ build: @autoclosure @escaping () -> Content) {
|
||||
self.build = build
|
||||
}
|
||||
var body: Content {
|
||||
build()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user