WIP Fix mentions when writing notes

This commit is contained in:
2023-03-25 22:52:20 -06:00
parent 9590166367
commit d946dbe50d
3 changed files with 48 additions and 18 deletions

View File

@@ -16,6 +16,7 @@ let POST_PLACEHOLDER = NSLocalizedString("Type your post here...", comment: "Tex
struct PostView: View { struct PostView: View {
@State var post: NSMutableAttributedString = NSMutableAttributedString() @State var post: NSMutableAttributedString = NSMutableAttributedString()
@State var cursor: Int = 0
@FocusState var focus: Bool @FocusState var focus: Bool
@State var showPrivateKeyWarning: Bool = false @State var showPrivateKeyWarning: Bool = false
@State var attach_media: Bool = false @State var attach_media: Bool = false
@@ -104,7 +105,7 @@ struct PostView: View {
var TextEntry: some View { var TextEntry: some View {
ZStack(alignment: .topLeading) { ZStack(alignment: .topLeading) {
TextViewWrapper(attributedText: $post) TextViewWrapper(attributedText: $post, cursor: $cursor)
.focused($focus) .focused($focus)
.textInputAutocapitalization(.sentences) .textInputAutocapitalization(.sentences)
.onChange(of: post) { _ in .onChange(of: post) { _ in
@@ -191,7 +192,7 @@ struct PostView: View {
var body: some View { var body: some View {
VStack(alignment: .leading, spacing: 0) { VStack(alignment: .leading, spacing: 0) {
let searching = get_searching_string(post.string) let searching = get_searching_string(post.string, cursor: cursor)
TopBar TopBar
@@ -204,7 +205,7 @@ struct PostView: View {
// This if-block observes @ for tagging // This if-block observes @ for tagging
if let searching { if let searching {
UserSearch(damus_state: damus_state, search: searching, post: $post) UserSearch(damus_state: damus_state, search: searching, post: $post, cursor: $cursor)
.frame(maxHeight: .infinity) .frame(maxHeight: .infinity)
} else { } else {
Divider() Divider()
@@ -253,8 +254,12 @@ struct PostView: View {
} }
} }
func get_searching_string(_ post: String) -> String? { func get_searching_string(_ post: String, cursor: Int) -> String? {
guard let last_word = post.components(separatedBy: .whitespacesAndNewlines).last else { guard cursor > 0 else {
return nil
}
guard let last_word = post[...post.index(post.startIndex, offsetBy: cursor - 1)].components(separatedBy: .whitespacesAndNewlines).last else {
return nil return nil
} }

View File

@@ -22,6 +22,7 @@ struct UserSearch: View {
let search: String let search: String
@Binding var post: NSMutableAttributedString @Binding var post: NSMutableAttributedString
@Binding var cursor: Int
var users: [SearchedUser] { var users: [SearchedUser] {
guard let contacts = damus_state.contacts.event else { guard let contacts = damus_state.contacts.event else {
@@ -36,38 +37,57 @@ struct UserSearch: View {
return return
} }
// Remove all characters after the last '@' // Remove all characters after the '@' and before the cursor
removeCharactersAfterLastAtSymbol() let newCursor = removeCharactersAfterAtSymbol()
// Create and append the user tag // Create and append the user tag
let tagAttributedString = createUserTag(for: user, with: pk) let tagAttributedString = createUserTag(for: user, with: pk)
appendUserTag(tagAttributedString) insertUserTag(tagAttributedString, cursor: newCursor)
cursor = newCursor
} }
private func removeCharactersAfterLastAtSymbol() { private func removeCharactersAfterAtSymbol() -> Int {
while post.string.last != "@" { let newCursor = cursor
post.deleteCharacters(in: NSRange(location: post.length - 1, length: 1))
guard newCursor > 0 else {
return 0
} }
post.deleteCharacters(in: NSRange(location: post.length - 1, length: 1))
var atSymbolOffset = newCursor
while atSymbolOffset > 0 && post.string[post.string.index(post.string.startIndex, offsetBy: atSymbolOffset - 1)] != "@" {
atSymbolOffset -= 1
}
var endOfWordOffset = newCursor
while endOfWordOffset < post.string.count && !post.string[post.string.index(post.string.startIndex, offsetBy: endOfWordOffset)].isWhitespace {
endOfWordOffset += 1
}
post.deleteCharacters(in: NSRange(location: atSymbolOffset - 1, length: endOfWordOffset - atSymbolOffset + 1))
return atSymbolOffset - 1
} }
private func createUserTag(for user: SearchedUser, with pk: String) -> NSMutableAttributedString { private func createUserTag(for user: SearchedUser, with pk: String) -> NSMutableAttributedString {
let name = Profile.displayName(profile: user.profile, pubkey: pk).username let name = Profile.displayName(profile: user.profile, pubkey: pk).username
let tagString = "@\(name)\u{200B} " let tagString = "\u{200B}@\(name)\u{200B} "
let tagAttributedString = NSMutableAttributedString(string: tagString, let tagAttributedString = NSMutableAttributedString(string: tagString,
attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 18.0), attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 18.0),
NSAttributedString.Key.link: "@\(pk)"]) NSAttributedString.Key.link: "@\(pk)"])
tagAttributedString.removeAttribute(.link, range: NSRange(location: 0, length: 1))
tagAttributedString.removeAttribute(.link, range: NSRange(location: tagAttributedString.length - 2, length: 2)) tagAttributedString.removeAttribute(.link, range: NSRange(location: tagAttributedString.length - 2, length: 2))
tagAttributedString.addAttributes([NSAttributedString.Key.foregroundColor: UIColor.label], range: NSRange(location: 0, length: 1))
tagAttributedString.addAttributes([NSAttributedString.Key.foregroundColor: UIColor.label], range: NSRange(location: tagAttributedString.length - 2, length: 2)) tagAttributedString.addAttributes([NSAttributedString.Key.foregroundColor: UIColor.label], range: NSRange(location: tagAttributedString.length - 2, length: 2))
return tagAttributedString return tagAttributedString
} }
private func appendUserTag(_ tagAttributedString: NSMutableAttributedString) { private func insertUserTag(_ tagAttributedString: NSMutableAttributedString, cursor: Int) {
let mutableString = NSMutableAttributedString() let mutableString = NSMutableAttributedString()
mutableString.append(post) mutableString.append(post)
mutableString.append(tagAttributedString) mutableString.insert(tagAttributedString, at: cursor)
post = mutableString post = mutableString
} }
@@ -88,9 +108,10 @@ struct UserSearch: View {
struct UserSearch_Previews: PreviewProvider { struct UserSearch_Previews: PreviewProvider {
static let search: String = "jb55" static let search: String = "jb55"
@State static var post: NSMutableAttributedString = NSMutableAttributedString(string: "some @jb55") @State static var post: NSMutableAttributedString = NSMutableAttributedString(string: "some @jb55")
@State static var cursor: Int = 0
static var previews: some View { static var previews: some View {
UserSearch(damus_state: test_damus_state(), search: search, post: $post) UserSearch(damus_state: test_damus_state(), search: search, post: $post, cursor: $cursor)
} }
} }

View File

@@ -9,6 +9,7 @@ import SwiftUI
struct TextViewWrapper: UIViewRepresentable { struct TextViewWrapper: UIViewRepresentable {
@Binding var attributedText: NSMutableAttributedString @Binding var attributedText: NSMutableAttributedString
@Binding var cursor: Int
func makeUIView(context: Context) -> UITextView { func makeUIView(context: Context) -> UITextView {
let textView = UITextView() let textView = UITextView()
@@ -31,18 +32,21 @@ struct TextViewWrapper: UIViewRepresentable {
} }
func makeCoordinator() -> Coordinator { func makeCoordinator() -> Coordinator {
Coordinator(attributedText: $attributedText) Coordinator(attributedText: $attributedText, cursor: $cursor)
} }
class Coordinator: NSObject, UITextViewDelegate { class Coordinator: NSObject, UITextViewDelegate {
@Binding var attributedText: NSMutableAttributedString @Binding var attributedText: NSMutableAttributedString
@Binding var cursor: Int
init(attributedText: Binding<NSMutableAttributedString>) { init(attributedText: Binding<NSMutableAttributedString>, cursor: Binding<Int>) {
_attributedText = attributedText _attributedText = attributedText
_cursor = cursor
} }
func textViewDidChange(_ textView: UITextView) { func textViewDidChange(_ textView: UITextView) {
attributedText = NSMutableAttributedString(attributedString: textView.attributedText) attributedText = NSMutableAttributedString(attributedString: textView.attributedText)
cursor = textView.selectedRange.upperBound
} }
} }
} }