damus

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

commit 0dd74fde7f73ee21dee938849ec4cc1501ca3043
parent 7a55ea13e3c548221a2dd11145fa060d16b43ab9
Author: ericholguin <eric.holguinsanchez@gmail.com>
Date:   Wed, 29 Mar 2023 15:05:33 -0600

Improve reply view

Changelog-Changed: Improved look of reply view

Diffstat:
Mdamus/ContentView.swift | 4++--
Mdamus/Views/ParicipantsView.swift | 21++++++++++++++++-----
Mdamus/Views/PostView.swift | 154++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Mdamus/Views/ReplyView.swift | 88++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mdamus/Views/TextViewWrapper.swift | 1+
5 files changed, 152 insertions(+), 116 deletions(-)

diff --git a/damus/ContentView.swift b/damus/ContentView.swift @@ -312,9 +312,9 @@ struct ContentView: View { case .report(let target): MaybeReportView(target: target) case .post: - PostView(replying_to: nil, references: [], damus_state: damus_state!) + PostView(replying_to: nil, damus_state: damus_state!) case .reply(let event): - ReplyView(replying_to: event, damus: damus_state!) + PostView(replying_to: event, damus_state: damus_state!) case .event: EventDetailView() case .filter: diff --git a/damus/Views/ParicipantsView.swift b/damus/Views/ParicipantsView.swift @@ -16,23 +16,34 @@ struct ParticipantsView: View { var body: some View { VStack { - Text("Edit participants", comment: "Text indicating that the view is used for editing which participants are replied to in a note.") + Text("Replying to", comment: "Text indicating that the view is used for editing which participants are replied to in a note.") + .font(.headline) HStack { Spacer() + Button { // Remove all "p" refs, keep "e" refs references = originalReferences.eRefs } label: { Text("Remove all", comment: "Button label to remove all participants from a note reply.") } - .buttonStyle(.borderedProminent) - Spacer() + .font(.system(size: 14, weight: .bold)) + .frame(width: 100, height: 30) + .foregroundColor(.white) + .background(LINEAR_GRADIENT) + .clipShape(Capsule()) + Button { references = originalReferences } label: { Text("Add all", comment: "Button label to re-add all original participants as profiles to reply to in a note") } - .buttonStyle(.borderedProminent) + .font(.system(size: 14, weight: .bold)) + .frame(width: 80, height: 30) + .foregroundColor(.white) + .background(LINEAR_GRADIENT) + .clipShape(Capsule()) + Spacer() } VStack { @@ -56,7 +67,7 @@ struct ParticipantsView: View { Image(systemName: "checkmark.circle.fill") .font(.system(size: 30)) - .foregroundColor(references.contains(participant) ? .purple : .gray) + .foregroundColor(references.contains(participant) ? DamusColors.purple : .gray) } .onTapGesture { if references.contains(participant) { diff --git a/damus/Views/PostView.swift b/damus/Views/PostView.swift @@ -22,10 +22,12 @@ struct PostView: View { @State var attach_camera: Bool = false @State var error: String? = nil + @State var originalReferences: [ReferencedId] = [] + @State var references: [ReferencedId] = [] + @StateObject var image_upload: ImageUploadModel = ImageUploadModel() let replying_to: NostrEvent? - let references: [ReferencedId] let damus_state: DamusState @Environment(\.presentationMode) var presentationMode @@ -105,10 +107,12 @@ struct PostView: View { self.send_post() } } + .disabled(is_post_empty) .font(.system(size: 14, weight: .bold)) .frame(width: 80, height: 30) .foregroundColor(.white) .background(LINEAR_GRADIENT) + .opacity(is_post_empty ? 0.5 : 1.0) .clipShape(Capsule()) } @@ -150,9 +154,7 @@ struct PostView: View { Spacer() - if !is_post_empty { - PostButton - } + PostButton } if let progress = image_upload.progress { @@ -161,7 +163,7 @@ struct PostView: View { } } .frame(height: 30) - .padding([.top, .bottom], 4) + .padding([.bottom], 10) } func append_url(_ url: String) { @@ -200,75 +202,97 @@ struct PostView: View { } var body: some View { - VStack(alignment: .leading, spacing: 0) { - let searching = get_searching_string(post.string) - - TopBar - - HStack(alignment: .top) { - ProfilePicView(pubkey: damus_state.pubkey, size: 45.0, highlight: .none, profiles: damus_state.profiles) + GeometryReader { (deviceSize: GeometryProxy) in + VStack(alignment: .leading, spacing: 0) { - TextEntry - } - .frame(maxHeight: searching == nil ? .infinity : 50) - - // This if-block observes @ for tagging - if let searching { - UserSearch(damus_state: damus_state, search: searching, post: $post) - .frame(maxHeight: .infinity) - } else { - Divider() - .padding([.bottom], 10) + let searching = get_searching_string(post.string) + + TopBar - AttachmentBar + ScrollViewReader { scroller in + ScrollView { + if let replying_to = replying_to { + ReplyView(replying_to: replying_to, damus: damus_state, originalReferences: $originalReferences, references: $references) + } + VStack(alignment: .leading, spacing: 0) { + HStack(alignment: .top) { + ProfilePicView(pubkey: damus_state.pubkey, size: PFP_SIZE, highlight: .none, profiles: damus_state.profiles) + .padding(.leading, replying_to != nil ? 15 : 0) + + TextEntry + } + .frame(height: deviceSize.size.height*0.78) + .id("post") + } + } + .frame(maxHeight: searching == nil ? .infinity : 70) + .onAppear { + scroll_to_event(scroller: scroller, id: "post", delay: 1.0, animate: true, anchor: .top) + } + } + + // This if-block observes @ for tagging + if let searching { + UserSearch(damus_state: damus_state, search: searching, post: $post) + .padding(.leading, replying_to != nil ? 15 : 0) + .frame(maxHeight: .infinity) + } else { + Divider() + .padding([.top, .bottom], 10) + VStack(alignment: .leading) { + AttachmentBar + } + } } - } - .padding() - .sheet(isPresented: $attach_media) { - ImagePicker(sourceType: .photoLibrary, damusState: damus_state) { img in - handle_upload(media: .image(img)) - } onVideoPicked: { url in - handle_upload(media: .video(url)) + .padding() + .sheet(isPresented: $attach_media) { + ImagePicker(sourceType: .photoLibrary, damusState: damus_state) { img in + handle_upload(media: .image(img)) + } onVideoPicked: { url in + handle_upload(media: .video(url)) + } } - } - .sheet(isPresented: $attach_camera) { - ImagePicker(sourceType: .camera, damusState: damus_state) { img in - handle_upload(media: .image(img)) - } onVideoPicked: { url in - handle_upload(media: .video(url)) + .sheet(isPresented: $attach_camera) { + ImagePicker(sourceType: .camera, damusState: damus_state) { img in + handle_upload(media: .image(img)) + } onVideoPicked: { url in + handle_upload(media: .video(url)) + } } - } - .onAppear() { - if let replying_to { - if damus_state.drafts.replies[replying_to] == nil { - damus_state.drafts.post = NSMutableAttributedString(string: "") + .onAppear() { + if let replying_to { + references = gather_reply_ids(our_pubkey: damus_state.pubkey, from: replying_to) + originalReferences = references + if damus_state.drafts.replies[replying_to] == nil { + damus_state.drafts.post = NSMutableAttributedString(string: "") + } + if let p = damus_state.drafts.replies[replying_to] { + post = p + } + } else { + post = damus_state.drafts.post } - if let p = damus_state.drafts.replies[replying_to] { - post = p + + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + self.focus = true } - } else { - post = damus_state.drafts.post - } - - DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - self.focus = true } - } - .onDisappear { - if let replying_to, let reply = damus_state.drafts.replies[replying_to], reply.string.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { - damus_state.drafts.replies.removeValue(forKey: replying_to) - } else if replying_to == nil && damus_state.drafts.post.string.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { - damus_state.drafts.post = NSMutableAttributedString(string : "") + .onDisappear { + if let replying_to, let reply = damus_state.drafts.replies[replying_to], reply.string.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { + damus_state.drafts.replies.removeValue(forKey: replying_to) + } else if replying_to == nil && damus_state.drafts.post.string.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { + damus_state.drafts.post = NSMutableAttributedString(string : "") + } } + .alert(NSLocalizedString("Note contains \"nsec1\" private key. Are you sure?", comment: "Alert user that they might be attempting to paste a private key and ask them to confirm."), isPresented: $showPrivateKeyWarning, actions: { + Button(NSLocalizedString("No", comment: "Button to cancel out of posting a note after being alerted that it looks like they might be posting a private key."), role: .cancel) { + showPrivateKeyWarning = false + } + Button(NSLocalizedString("Yes, Post with Private Key", comment: "Button to proceed with posting a note even though it looks like they might be posting a private key."), role: .destructive) { + self.send_post() + } + }) } - .alert(NSLocalizedString("Note contains \"nsec1\" private key. Are you sure?", comment: "Alert user that they might be attempting to paste a private key and ask them to confirm."), isPresented: $showPrivateKeyWarning, actions: { - Button(NSLocalizedString("No", comment: "Button to cancel out of posting a note after being alerted that it looks like they might be posting a private key."), role: .cancel) { - showPrivateKeyWarning = false - } - Button(NSLocalizedString("Yes, Post with Private Key", comment: "Button to proceed with posting a note even though it looks like they might be posting a private key."), role: .destructive) { - self.send_post() - } - }) } } @@ -295,6 +319,6 @@ func get_searching_string(_ post: String) -> String? { struct PostView_Previews: PreviewProvider { static var previews: some View { - PostView(replying_to: nil, references: [], damus_state: test_damus_state()) + PostView(replying_to: nil, damus_state: test_damus_state()) } } diff --git a/damus/Views/ReplyView.swift b/damus/Views/ReplyView.swift @@ -7,71 +7,71 @@ import SwiftUI -func all_referenced_pubkeys(_ ev: NostrEvent) -> [ReferencedId] { - var keys = ev.referenced_pubkeys - let ref = ReferencedId(ref_id: ev.pubkey, relay_id: nil, key: "p") - keys.insert(ref, at: 0) - return keys -} - struct ReplyView: View { let replying_to: NostrEvent let damus: DamusState - @State var originalReferences: [ReferencedId] = [] - @State var references: [ReferencedId] = [] - + @Binding var originalReferences: [ReferencedId] + @Binding var references: [ReferencedId] @State var participantsShown: Bool = false - - var body: some View { - VStack { - Text("Replying to:", comment: "Indicating that the user is replying to the following listed people.") - - HStack(alignment: .top) { + + var ReplyingToSection: some View { + HStack { + Group { let names = references.pRefs .map { pubkey in let pk = pubkey.ref_id let prof = damus.profiles.lookup(id: pk) - return Profile.displayName(profile: prof, pubkey: pk).username + return "@" + Profile.displayName(profile: prof, pubkey: pk).username } - .joined(separator: ", ") - Text(names) + .joined(separator: " ") + Text("Replying to ", comment: "Indicating that the user is replying to the following listed people.") .foregroundColor(.gray) + .font(.footnote) + + Text(names.isEmpty ? "self" : names) + .foregroundColor(.accentColor) .font(.footnote) } .onTapGesture { participantsShown.toggle() } .sheet(isPresented: $participantsShown) { - ParticipantsView(damus_state: damus, references: $references, originalReferences: $originalReferences) - } - - ScrollViewReader { scroller in - ScrollView { - EventView(damus: damus, event: replying_to, options: [.no_action_bar]) - - PostView(replying_to: replying_to, references: references, damus_state: damus) - .frame(minHeight: 500, maxHeight: .infinity) - .id("post") - } - .frame(maxHeight: .infinity) - .onAppear { - scroll_to_event(scroller: scroller, id: "post", delay: 1.0, animate: true, anchor: .top) + if #available(iOS 16.0, *) { + ParticipantsView(damus_state: damus, references: $references, originalReferences: $originalReferences) + .presentationDetents([.medium, .large]) + .presentationDragIndicator(.visible) + } else { + ParticipantsView(damus_state: damus, references: $references, originalReferences: $originalReferences) } } - } - .padding() - .onAppear { - references = gather_reply_ids(our_pubkey: damus.pubkey, from: replying_to) - originalReferences = references + .padding(.leading, 75) + Spacer() } } - - -} + + var body: some View { + VStack(alignment: .leading) { -struct ReplyView_Previews: PreviewProvider { - static var previews: some View { - ReplyView(replying_to: NostrEvent(content: "hi", pubkey: "pubkey"), damus: test_damus_state(), references: []) + EventView(damus: damus, event: replying_to, options: [.no_action_bar]) + .padding() + .background(GeometryReader { geometry in + let eventHeight = geometry.frame(in: .global).height + Rectangle() + .fill(Color.gray.opacity(0.25)) + .frame(width: 2, height: eventHeight + 7) + .offset(x: 25, y: 40) + .padding(.leading) + }) + + ReplyingToSection + .background(GeometryReader { geometry in + let replyingToHeight = geometry.frame(in: .global).height + Rectangle() + .fill(Color.gray.opacity(0.25)) + .frame(width: 2, height: replyingToHeight) + .offset(x: 25, y: 40) + .padding(.leading) + }) + } } } diff --git a/damus/Views/TextViewWrapper.swift b/damus/Views/TextViewWrapper.swift @@ -13,6 +13,7 @@ struct TextViewWrapper: UIViewRepresentable { func makeUIView(context: Context) -> UITextView { let textView = UITextView() textView.delegate = context.coordinator + textView.showsVerticalScrollIndicator = false TextViewWrapper.setTextProperties(textView) return textView }