Fix message completion trigger to work anywhere in the message (#3696)

* Fix message completion trigger to work anywhere in the message

* Add tests for display suggestion in difference message positions

* Fix suggestion trigger could support multiple mention symbol

* Remove optional parameter type and tweak argument name to hide

* Optimize the suggestions trigger using regex

* Fix the cursor at wrong position after selected suggestion

* Modify the rawSuggestionText pass data in right way

* Modify mention symbol to use regex type

* Update ElementX/Sources/Screens/RoomScreen/ComposerToolbar/CompletionSuggestionService.swift

Co-authored-by: Doug <6060466+pixlwave@users.noreply.github.com>

* Update generated mock

---------

Co-authored-by: Doug <6060466+pixlwave@users.noreply.github.com>
This commit is contained in:
Vickcoo
2025-02-13 23:13:09 +08:00
committed by GitHub
parent 8ee1427809
commit 4b43d901f8
14 changed files with 151 additions and 59 deletions
@@ -12,6 +12,7 @@ struct MessageComposerTextField: View {
let placeholder: String
@Binding var text: NSAttributedString
@Binding var presendCallback: (() -> Void)?
@Binding var selectedRange: NSRange
let maxHeight: CGFloat
let keyHandler: GenericKeyHandler
@@ -20,6 +21,7 @@ struct MessageComposerTextField: View {
var body: some View {
UITextViewWrapper(text: $text,
presendCallback: $presendCallback,
selectedRange: $selectedRange,
maxHeight: maxHeight,
keyHandler: keyHandler,
pasteHandler: pasteHandler)
@@ -54,6 +56,7 @@ private struct UITextViewWrapper: UIViewRepresentable {
@Binding var text: NSAttributedString
@Binding var presendCallback: (() -> Void)?
@Binding var selectedRange: NSRange
let maxHeight: CGFloat
@@ -135,12 +138,15 @@ private struct UITextViewWrapper: UIViewRepresentable {
// moves the caret back to the bottom of the composer.
// https://github.com/element-hq/element-x-ios/issues/3104
textView.selectedTextRange = selection
} else {
textView.selectedRange = selectedRange
}
}
}
func makeCoordinator() -> Coordinator {
Coordinator(text: $text,
selectedRange: $selectedRange,
maxHeight: maxHeight,
keyHandler: keyHandler,
pasteHandler: pasteHandler)
@@ -148,6 +154,7 @@ private struct UITextViewWrapper: UIViewRepresentable {
final class Coordinator: NSObject, UITextViewDelegate, ElementTextViewDelegate {
private var text: Binding<NSAttributedString>
private var selectedRange: Binding<NSRange>
private let maxHeight: CGFloat
@@ -155,10 +162,12 @@ private struct UITextViewWrapper: UIViewRepresentable {
private let pasteHandler: PasteHandler
init(text: Binding<NSAttributedString>,
selectedRange: Binding<NSRange>,
maxHeight: CGFloat,
keyHandler: @escaping GenericKeyHandler,
pasteHandler: @escaping PasteHandler) {
self.text = text
self.selectedRange = selectedRange
self.maxHeight = maxHeight
self.keyHandler = keyHandler
self.pasteHandler = pasteHandler
@@ -179,6 +188,14 @@ private struct UITextViewWrapper: UIViewRepresentable {
func textView(_ textView: UITextView, didReceivePasteWith provider: NSItemProvider) {
pasteHandler(provider)
}
func textViewDidChangeSelection(_ textView: UITextView) {
if selectedRange.wrappedValue != textView.selectedRange {
DispatchQueue.main.async {
self.selectedRange.wrappedValue = textView.selectedRange
}
}
}
}
}
@@ -331,6 +348,7 @@ struct MessageComposerTextField_Previews: PreviewProvider, TestablePreview {
MessageComposerTextField(placeholder: "Placeholder",
text: $text,
presendCallback: .constant(nil),
selectedRange: .constant(NSRange(location: 0, length: 0)),
maxHeight: 300,
keyHandler: { _ in },
pasteHandler: { _ in })