Improve reply view
Changelog-Changed: Improved look of reply view
This commit is contained in:
committed by
William Casarin
parent
7a55ea13e3
commit
0dd74fde7f
@@ -312,9 +312,9 @@ struct ContentView: View {
|
|||||||
case .report(let target):
|
case .report(let target):
|
||||||
MaybeReportView(target: target)
|
MaybeReportView(target: target)
|
||||||
case .post:
|
case .post:
|
||||||
PostView(replying_to: nil, references: [], damus_state: damus_state!)
|
PostView(replying_to: nil, damus_state: damus_state!)
|
||||||
case .reply(let event):
|
case .reply(let event):
|
||||||
ReplyView(replying_to: event, damus: damus_state!)
|
PostView(replying_to: event, damus_state: damus_state!)
|
||||||
case .event:
|
case .event:
|
||||||
EventDetailView()
|
EventDetailView()
|
||||||
case .filter:
|
case .filter:
|
||||||
|
|||||||
@@ -16,23 +16,34 @@ struct ParticipantsView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
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 {
|
HStack {
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
// Remove all "p" refs, keep "e" refs
|
// Remove all "p" refs, keep "e" refs
|
||||||
references = originalReferences.eRefs
|
references = originalReferences.eRefs
|
||||||
} label: {
|
} label: {
|
||||||
Text("Remove all", comment: "Button label to remove all participants from a note reply.")
|
Text("Remove all", comment: "Button label to remove all participants from a note reply.")
|
||||||
}
|
}
|
||||||
.buttonStyle(.borderedProminent)
|
.font(.system(size: 14, weight: .bold))
|
||||||
Spacer()
|
.frame(width: 100, height: 30)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.background(LINEAR_GRADIENT)
|
||||||
|
.clipShape(Capsule())
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
references = originalReferences
|
references = originalReferences
|
||||||
} label: {
|
} label: {
|
||||||
Text("Add all", comment: "Button label to re-add all original participants as profiles to reply to in a note")
|
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()
|
Spacer()
|
||||||
}
|
}
|
||||||
VStack {
|
VStack {
|
||||||
@@ -56,7 +67,7 @@ struct ParticipantsView: View {
|
|||||||
|
|
||||||
Image(systemName: "checkmark.circle.fill")
|
Image(systemName: "checkmark.circle.fill")
|
||||||
.font(.system(size: 30))
|
.font(.system(size: 30))
|
||||||
.foregroundColor(references.contains(participant) ? .purple : .gray)
|
.foregroundColor(references.contains(participant) ? DamusColors.purple : .gray)
|
||||||
}
|
}
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
if references.contains(participant) {
|
if references.contains(participant) {
|
||||||
|
|||||||
@@ -22,10 +22,12 @@ struct PostView: View {
|
|||||||
@State var attach_camera: Bool = false
|
@State var attach_camera: Bool = false
|
||||||
@State var error: String? = nil
|
@State var error: String? = nil
|
||||||
|
|
||||||
|
@State var originalReferences: [ReferencedId] = []
|
||||||
|
@State var references: [ReferencedId] = []
|
||||||
|
|
||||||
@StateObject var image_upload: ImageUploadModel = ImageUploadModel()
|
@StateObject var image_upload: ImageUploadModel = ImageUploadModel()
|
||||||
|
|
||||||
let replying_to: NostrEvent?
|
let replying_to: NostrEvent?
|
||||||
let references: [ReferencedId]
|
|
||||||
let damus_state: DamusState
|
let damus_state: DamusState
|
||||||
|
|
||||||
@Environment(\.presentationMode) var presentationMode
|
@Environment(\.presentationMode) var presentationMode
|
||||||
@@ -105,10 +107,12 @@ struct PostView: View {
|
|||||||
self.send_post()
|
self.send_post()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.disabled(is_post_empty)
|
||||||
.font(.system(size: 14, weight: .bold))
|
.font(.system(size: 14, weight: .bold))
|
||||||
.frame(width: 80, height: 30)
|
.frame(width: 80, height: 30)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.background(LINEAR_GRADIENT)
|
.background(LINEAR_GRADIENT)
|
||||||
|
.opacity(is_post_empty ? 0.5 : 1.0)
|
||||||
.clipShape(Capsule())
|
.clipShape(Capsule())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,9 +154,7 @@ struct PostView: View {
|
|||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
if !is_post_empty {
|
PostButton
|
||||||
PostButton
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let progress = image_upload.progress {
|
if let progress = image_upload.progress {
|
||||||
@@ -161,7 +163,7 @@ struct PostView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(height: 30)
|
.frame(height: 30)
|
||||||
.padding([.top, .bottom], 4)
|
.padding([.bottom], 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
func append_url(_ url: String) {
|
func append_url(_ url: String) {
|
||||||
@@ -200,75 +202,97 @@ struct PostView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading, spacing: 0) {
|
GeometryReader { (deviceSize: GeometryProxy) in
|
||||||
let searching = get_searching_string(post.string)
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
|
|
||||||
TopBar
|
let searching = get_searching_string(post.string)
|
||||||
|
|
||||||
HStack(alignment: .top) {
|
TopBar
|
||||||
ProfilePicView(pubkey: damus_state.pubkey, size: 45.0, highlight: .none, profiles: damus_state.profiles)
|
|
||||||
|
|
||||||
TextEntry
|
ScrollViewReader { scroller in
|
||||||
}
|
ScrollView {
|
||||||
.frame(maxHeight: searching == nil ? .infinity : 50)
|
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)
|
||||||
|
|
||||||
// This if-block observes @ for tagging
|
TextEntry
|
||||||
if let searching {
|
}
|
||||||
UserSearch(damus_state: damus_state, search: searching, post: $post)
|
.frame(height: deviceSize.size.height*0.78)
|
||||||
.frame(maxHeight: .infinity)
|
.id("post")
|
||||||
} else {
|
}
|
||||||
Divider()
|
}
|
||||||
.padding([.bottom], 10)
|
.frame(maxHeight: searching == nil ? .infinity : 70)
|
||||||
|
.onAppear {
|
||||||
AttachmentBar
|
scroll_to_event(scroller: scroller, id: "post", delay: 1.0, animate: true, anchor: .top)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
.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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onAppear() {
|
|
||||||
if let replying_to {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
// This if-block observes @ for tagging
|
||||||
self.focus = true
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.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 {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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 : "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.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()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
.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()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,6 +319,6 @@ func get_searching_string(_ post: String) -> String? {
|
|||||||
|
|
||||||
struct PostView_Previews: PreviewProvider {
|
struct PostView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
PostView(replying_to: nil, references: [], damus_state: test_damus_state())
|
PostView(replying_to: nil, damus_state: test_damus_state())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,71 +7,71 @@
|
|||||||
|
|
||||||
import SwiftUI
|
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 {
|
struct ReplyView: View {
|
||||||
let replying_to: NostrEvent
|
let replying_to: NostrEvent
|
||||||
let damus: DamusState
|
let damus: DamusState
|
||||||
|
|
||||||
@State var originalReferences: [ReferencedId] = []
|
@Binding var originalReferences: [ReferencedId]
|
||||||
@State var references: [ReferencedId] = []
|
@Binding var references: [ReferencedId]
|
||||||
|
|
||||||
@State var participantsShown: Bool = false
|
@State var participantsShown: Bool = false
|
||||||
|
|
||||||
var body: some View {
|
var ReplyingToSection: some View {
|
||||||
VStack {
|
HStack {
|
||||||
Text("Replying to:", comment: "Indicating that the user is replying to the following listed people.")
|
Group {
|
||||||
|
|
||||||
HStack(alignment: .top) {
|
|
||||||
let names = references.pRefs
|
let names = references.pRefs
|
||||||
.map { pubkey in
|
.map { pubkey in
|
||||||
let pk = pubkey.ref_id
|
let pk = pubkey.ref_id
|
||||||
let prof = damus.profiles.lookup(id: pk)
|
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: ", ")
|
.joined(separator: " ")
|
||||||
Text(names)
|
Text("Replying to ", comment: "Indicating that the user is replying to the following listed people.")
|
||||||
.foregroundColor(.gray)
|
.foregroundColor(.gray)
|
||||||
|
.font(.footnote) +
|
||||||
|
Text(names.isEmpty ? "self" : names)
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
.font(.footnote)
|
.font(.footnote)
|
||||||
}
|
}
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
participantsShown.toggle()
|
participantsShown.toggle()
|
||||||
}
|
}
|
||||||
.sheet(isPresented: $participantsShown) {
|
.sheet(isPresented: $participantsShown) {
|
||||||
ParticipantsView(damus_state: damus, references: $references, originalReferences: $originalReferences)
|
if #available(iOS 16.0, *) {
|
||||||
}
|
ParticipantsView(damus_state: damus, references: $references, originalReferences: $originalReferences)
|
||||||
|
.presentationDetents([.medium, .large])
|
||||||
ScrollViewReader { scroller in
|
.presentationDragIndicator(.visible)
|
||||||
ScrollView {
|
} else {
|
||||||
EventView(damus: damus, event: replying_to, options: [.no_action_bar])
|
ParticipantsView(damus_state: damus, references: $references, originalReferences: $originalReferences)
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
.padding(.leading, 75)
|
||||||
.padding()
|
Spacer()
|
||||||
.onAppear {
|
|
||||||
references = gather_reply_ids(our_pubkey: damus.pubkey, from: replying_to)
|
|
||||||
originalReferences = references
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
|
||||||
}
|
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)
|
||||||
|
})
|
||||||
|
|
||||||
struct ReplyView_Previews: PreviewProvider {
|
ReplyingToSection
|
||||||
static var previews: some View {
|
.background(GeometryReader { geometry in
|
||||||
ReplyView(replying_to: NostrEvent(content: "hi", pubkey: "pubkey"), damus: test_damus_state(), references: [])
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ struct TextViewWrapper: UIViewRepresentable {
|
|||||||
func makeUIView(context: Context) -> UITextView {
|
func makeUIView(context: Context) -> UITextView {
|
||||||
let textView = UITextView()
|
let textView = UITextView()
|
||||||
textView.delegate = context.coordinator
|
textView.delegate = context.coordinator
|
||||||
|
textView.showsVerticalScrollIndicator = false
|
||||||
TextViewWrapper.setTextProperties(textView)
|
TextViewWrapper.setTextProperties(textView)
|
||||||
return textView
|
return textView
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user