WIP Fix mentions when writing notes
This commit is contained in:
@@ -16,6 +16,7 @@ let POST_PLACEHOLDER = NSLocalizedString("Type your post here...", comment: "Tex
|
||||
|
||||
struct PostView: View {
|
||||
@State var post: NSMutableAttributedString = NSMutableAttributedString()
|
||||
@State var cursor: Int = 0
|
||||
@FocusState var focus: Bool
|
||||
@State var showPrivateKeyWarning: Bool = false
|
||||
@State var attach_media: Bool = false
|
||||
@@ -104,7 +105,7 @@ struct PostView: View {
|
||||
|
||||
var TextEntry: some View {
|
||||
ZStack(alignment: .topLeading) {
|
||||
TextViewWrapper(attributedText: $post)
|
||||
TextViewWrapper(attributedText: $post, cursor: $cursor)
|
||||
.focused($focus)
|
||||
.textInputAutocapitalization(.sentences)
|
||||
.onChange(of: post) { _ in
|
||||
@@ -191,7 +192,7 @@ struct PostView: View {
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
let searching = get_searching_string(post.string)
|
||||
let searching = get_searching_string(post.string, cursor: cursor)
|
||||
|
||||
TopBar
|
||||
|
||||
@@ -204,7 +205,7 @@ struct PostView: View {
|
||||
|
||||
// This if-block observes @ for tagging
|
||||
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)
|
||||
} else {
|
||||
Divider()
|
||||
@@ -253,8 +254,12 @@ struct PostView: View {
|
||||
}
|
||||
}
|
||||
|
||||
func get_searching_string(_ post: String) -> String? {
|
||||
guard let last_word = post.components(separatedBy: .whitespacesAndNewlines).last else {
|
||||
func get_searching_string(_ post: String, cursor: Int) -> String? {
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ struct UserSearch: View {
|
||||
let search: String
|
||||
|
||||
@Binding var post: NSMutableAttributedString
|
||||
@Binding var cursor: Int
|
||||
|
||||
var users: [SearchedUser] {
|
||||
guard let contacts = damus_state.contacts.event else {
|
||||
@@ -36,38 +37,57 @@ struct UserSearch: View {
|
||||
return
|
||||
}
|
||||
|
||||
// Remove all characters after the last '@'
|
||||
removeCharactersAfterLastAtSymbol()
|
||||
// Remove all characters after the '@' and before the cursor
|
||||
let newCursor = removeCharactersAfterAtSymbol()
|
||||
|
||||
// Create and append the user tag
|
||||
let tagAttributedString = createUserTag(for: user, with: pk)
|
||||
appendUserTag(tagAttributedString)
|
||||
insertUserTag(tagAttributedString, cursor: newCursor)
|
||||
|
||||
cursor = newCursor
|
||||
}
|
||||
|
||||
private func removeCharactersAfterLastAtSymbol() {
|
||||
while post.string.last != "@" {
|
||||
post.deleteCharacters(in: NSRange(location: post.length - 1, length: 1))
|
||||
private func removeCharactersAfterAtSymbol() -> Int {
|
||||
let newCursor = cursor
|
||||
|
||||
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 {
|
||||
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,
|
||||
attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 18.0),
|
||||
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.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))
|
||||
|
||||
return tagAttributedString
|
||||
}
|
||||
|
||||
private func appendUserTag(_ tagAttributedString: NSMutableAttributedString) {
|
||||
private func insertUserTag(_ tagAttributedString: NSMutableAttributedString, cursor: Int) {
|
||||
let mutableString = NSMutableAttributedString()
|
||||
mutableString.append(post)
|
||||
mutableString.append(tagAttributedString)
|
||||
mutableString.insert(tagAttributedString, at: cursor)
|
||||
post = mutableString
|
||||
}
|
||||
|
||||
@@ -88,9 +108,10 @@ struct UserSearch: View {
|
||||
struct UserSearch_Previews: PreviewProvider {
|
||||
static let search: String = "jb55"
|
||||
@State static var post: NSMutableAttributedString = NSMutableAttributedString(string: "some @jb55")
|
||||
@State static var cursor: Int = 0
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import SwiftUI
|
||||
|
||||
struct TextViewWrapper: UIViewRepresentable {
|
||||
@Binding var attributedText: NSMutableAttributedString
|
||||
@Binding var cursor: Int
|
||||
|
||||
func makeUIView(context: Context) -> UITextView {
|
||||
let textView = UITextView()
|
||||
@@ -31,18 +32,21 @@ struct TextViewWrapper: UIViewRepresentable {
|
||||
}
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
Coordinator(attributedText: $attributedText)
|
||||
Coordinator(attributedText: $attributedText, cursor: $cursor)
|
||||
}
|
||||
|
||||
class Coordinator: NSObject, UITextViewDelegate {
|
||||
@Binding var attributedText: NSMutableAttributedString
|
||||
@Binding var cursor: Int
|
||||
|
||||
init(attributedText: Binding<NSMutableAttributedString>) {
|
||||
init(attributedText: Binding<NSMutableAttributedString>, cursor: Binding<Int>) {
|
||||
_attributedText = attributedText
|
||||
_cursor = cursor
|
||||
}
|
||||
|
||||
func textViewDidChange(_ textView: UITextView) {
|
||||
attributedText = NSMutableAttributedString(attributedString: textView.attributedText)
|
||||
cursor = textView.selectedRange.upperBound
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user