1
0
mirror of git://jb55.com/damus synced 2024-09-19 19:46:51 +00:00

Rich tagging

Changelog-Changed: No more inline npubs when tagging users
Closes: #691
This commit is contained in:
Swift 2023-02-24 13:28:47 -05:00 committed by William Casarin
parent 810b3e1fa5
commit 436d20dfbd
5 changed files with 91 additions and 17 deletions

View File

@ -219,6 +219,7 @@
7C95CAEE299DCEF1009DCB67 /* KFOptionSetter+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C95CAED299DCEF1009DCB67 /* KFOptionSetter+.swift */; };
7CFF6317299FEFE5005D382A /* SelectableText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CFF6316299FEFE5005D382A /* SelectableText.swift */; };
9609F058296E220800069BF3 /* BannerImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9609F057296E220800069BF3 /* BannerImageView.swift */; };
9C83F89329A937B900136C08 /* TextViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C83F89229A937B900136C08 /* TextViewWrapper.swift */; };
BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA693073295D649800ADDB87 /* UserSettingsStore.swift */; };
BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */; };
DD597CBD2963D85A00C64D32 /* MarkdownTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */; };
@ -548,6 +549,7 @@
7C95CAED299DCEF1009DCB67 /* KFOptionSetter+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KFOptionSetter+.swift"; sourceTree = "<group>"; };
7CFF6316299FEFE5005D382A /* SelectableText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectableText.swift; sourceTree = "<group>"; };
9609F057296E220800069BF3 /* BannerImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BannerImageView.swift; sourceTree = "<group>"; };
9C83F89229A937B900136C08 /* TextViewWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextViewWrapper.swift; sourceTree = "<group>"; };
BA693073295D649800ADDB87 /* UserSettingsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSettingsStore.swift; sourceTree = "<group>"; };
BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectWalletView.swift; sourceTree = "<group>"; };
DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarkdownTests.swift; sourceTree = "<group>"; };
@ -741,6 +743,7 @@
4C363A8D28236FE4006E126D /* NoteContentView.swift */,
4C75EFAC28049CFB0006080F /* PostButton.swift */,
4C75EFA327FA577B0006080F /* PostView.swift */,
9C83F89229A937B900136C08 /* TextViewWrapper.swift */,
4CEE2AF8280B2EAC00AB5EEF /* PowView.swift */,
4CEE2AF6280B2DEA00AB5EEF /* ProfileName.swift */,
4C285C892838B985008A31F1 /* ProfilePictureSelector.swift */,
@ -1301,6 +1304,7 @@
E9E4ED0B295867B900DD7078 /* ThreadV2View.swift in Sources */,
4C3BEFDC281DCE6100B3DE84 /* Liked.swift in Sources */,
4CF0ABE7298444FD00D66079 /* MutedEventView.swift in Sources */,
9C83F89329A937B900136C08 /* TextViewWrapper.swift in Sources */,
4CF0ABE12981A83900D66079 /* MutelistView.swift in Sources */,
4CB883A82975FC1800DC99E7 /* Zaps.swift in Sources */,
4C75EFB128049D510006080F /* NostrResponse.swift in Sources */,

View File

@ -8,6 +8,6 @@
import Foundation
class Drafts: ObservableObject {
@Published var post: String = ""
@Published var replies: [NostrEvent: String] = [:]
@Published var post: NSMutableAttributedString = NSMutableAttributedString(string: "")
@Published var replies: [NostrEvent: NSMutableAttributedString] = [:]
}

View File

@ -15,7 +15,7 @@ enum NostrPostResult {
let POST_PLACEHOLDER = NSLocalizedString("Type your post here...", comment: "Text box prompt to ask user to type their post.")
struct PostView: View {
@State var post: String = ""
@State var post: NSMutableAttributedString = NSMutableAttributedString()
@FocusState var focus: Bool
@State var showPrivateKeyWarning: Bool = false
@ -44,7 +44,14 @@ struct PostView: View {
if replying_to?.known_kind == .chat {
kind = .chat
}
let content = self.post.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
post.enumerateAttributes(in: NSRange(location: 0, length: post.length), options: []) { attributes, range, stop in
if let link = attributes[.link] as? String {
post.replaceCharacters(in: range, with: link)
}
}
let content = self.post.string.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
let new_post = NostrPost(content: content, references: references, kind: kind)
NotificationCenter.default.post(name: .post, object: NostrPostResult.post(new_post))
@ -52,14 +59,14 @@ struct PostView: View {
if let replying_to {
damus_state.drafts.replies.removeValue(forKey: replying_to)
} else {
damus_state.drafts.post = ""
damus_state.drafts.post = NSMutableAttributedString(string: "")
}
dismiss()
}
var is_post_empty: Bool {
return post.allSatisfy { $0.isWhitespace }
return post.string.allSatisfy { $0.isWhitespace }
}
var body: some View {
@ -74,7 +81,7 @@ struct PostView: View {
if !is_post_empty {
Button(NSLocalizedString("Post", comment: "Button to post a note.")) {
showPrivateKeyWarning = contentContainsPrivateKey(self.post)
showPrivateKeyWarning = contentContainsPrivateKey(self.post.string)
if !showPrivateKeyWarning {
self.send_post()
@ -97,7 +104,7 @@ struct PostView: View {
VStack(alignment: .leading) {
ZStack(alignment: .topLeading) {
TextEditor(text: $post)
TextViewWrapper(attributedText: $post)
.focused($focus)
.textInputAutocapitalization(.sentences)
.onChange(of: post) { _ in
@ -108,7 +115,7 @@ struct PostView: View {
}
}
if post.isEmpty {
if post.string.isEmpty {
Text(POST_PLACEHOLDER)
.padding(.top, 8)
.padding(.leading, 4)
@ -120,7 +127,7 @@ struct PostView: View {
}
// This if-block observes @ for tagging
if let searching = get_searching_string(post) {
if let searching = get_searching_string(post.string) {
VStack {
Spacer()
UserSearch(damus_state: damus_state, search: searching, post: $post)
@ -130,7 +137,7 @@ struct PostView: View {
.onAppear() {
if let replying_to {
if damus_state.drafts.replies[replying_to] == nil {
damus_state.drafts.replies[replying_to] = ""
damus_state.drafts.post = NSMutableAttributedString(string: "")
}
if let p = damus_state.drafts.replies[replying_to] {
post = p
@ -144,10 +151,10 @@ struct PostView: View {
}
}
.onDisappear {
if let replying_to, let reply = damus_state.drafts.replies[replying_to], reply.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
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.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
damus_state.drafts.post = ""
} else if replying_to == nil && damus_state.drafts.post.string.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
damus_state.drafts.post = NSMutableAttributedString(string : "")
}
}
.padding()

View File

@ -20,7 +20,8 @@ struct SearchedUser: Identifiable {
struct UserSearch: View {
let damus_state: DamusState
let search: String
@Binding var post: String
@Binding var post: NSMutableAttributedString
var users: [SearchedUser] {
guard let contacts = damus_state.contacts.event else {
@ -39,7 +40,25 @@ struct UserSearch: View {
guard let pk = bech32_pubkey(user.pubkey) else {
return
}
post = post.replacingOccurrences(of: "@"+search, with: "@"+pk+" ")
while post.string.last != "@" {
post.deleteCharacters(in: NSRange(location: post.length - 1, length: 1))
}
post.deleteCharacters(in: NSRange(location: post.length - 1, length: 1))
var tagString = ""
if let name = user.profile?.name {
tagString = "@\(name)\u{200B} "
}
let tagAttributedString = NSMutableAttributedString(string: tagString,
attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 18.0),
NSAttributedString.Key.link: "@\(pk)"])
tagAttributedString.removeAttribute(.link, range: NSRange(location: tagAttributedString.length - 2, length: 2))
let mutableString = NSMutableAttributedString()
mutableString.append(post)
mutableString.append(tagAttributedString)
post = mutableString
}
}
}
@ -49,7 +68,7 @@ struct UserSearch: View {
struct UserSearch_Previews: PreviewProvider {
static let search: String = "jb55"
@State static var post: String = "some @jb55"
@State static var post: NSMutableAttributedString = NSMutableAttributedString(string: "some @jb55")
static var previews: some View {
UserSearch(damus_state: test_damus_state(), search: search, post: $post)

View File

@ -0,0 +1,44 @@
//
// TextViewWrapper.swift
// damus
//
// Created by Swift on 2/24/23.
//
import SwiftUI
struct TextViewWrapper: UIViewRepresentable {
@Binding var attributedText: NSMutableAttributedString
func makeUIView(context: Context) -> UITextView {
let textView = UITextView()
textView.delegate = context.coordinator
textView.font = UIFont.systemFont(ofSize: 18)
textView.textColor = UIColor.black
let linkAttributes: [NSAttributedString.Key : Any] = [
NSAttributedString.Key.foregroundColor: UIColor(Color.accentColor)]
textView.linkTextAttributes = linkAttributes
return textView
}
func updateUIView(_ uiView: UITextView, context: Context) {
uiView.attributedText = attributedText
}
func makeCoordinator() -> Coordinator {
Coordinator(attributedText: $attributedText)
}
class Coordinator: NSObject, UITextViewDelegate {
@Binding var attributedText: NSMutableAttributedString
init(attributedText: Binding<NSMutableAttributedString>) {
_attributedText = attributedText
}
func textViewDidChange(_ textView: UITextView) {
attributedText = NSMutableAttributedString(attributedString: textView.attributedText)
}
}
}