mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 21:33:26 +00:00
Restore Mentions in plain text mode (#3018)
This commit is contained in:
@@ -506,10 +506,54 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool
|
|||||||
wysiwygViewModel.textView.flushPills()
|
wysiwygViewModel.textView.flushPills()
|
||||||
wysiwygViewModel.setHtmlContent(text)
|
wysiwygViewModel.setHtmlContent(text)
|
||||||
} else {
|
} else {
|
||||||
state.bindings.plainComposerText = .init(string: text)
|
let attributedString = NSMutableAttributedString(string: text)
|
||||||
|
|
||||||
|
parseUserMentionsMarkdown(text) { range, url in
|
||||||
|
// Call your handleUserMention function here
|
||||||
|
attributedString.addAttribute(.link, value: url, range: range)
|
||||||
|
}
|
||||||
|
|
||||||
|
let matches = MatrixEntityRegex.allUsersRegex.matches(in: attributedString.string)
|
||||||
|
for match in matches {
|
||||||
|
attributedString.addAttribute(.MatrixAllUsersMention, value: true, range: match.range)
|
||||||
|
}
|
||||||
|
|
||||||
|
attributedStringBuilder.detectPermalinks(attributedString)
|
||||||
|
|
||||||
|
state.bindings.plainComposerText = attributedString
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func parseUserMentionsMarkdown(_ text: String, callback: (NSRange, URL) -> Void) {
|
||||||
|
// Define the regex pattern
|
||||||
|
let pattern = "\\[(.*?)\\]\\(https://matrix\\.to/#/(@.*?)\\)"
|
||||||
|
|
||||||
|
// Create the regex
|
||||||
|
guard let regex = try? NSRegularExpression(pattern: pattern, options: []) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find matches in the input text
|
||||||
|
let nsText = text as NSString
|
||||||
|
let matches = regex.matches(in: text, options: [], range: NSRange(location: 0, length: nsText.length))
|
||||||
|
|
||||||
|
// Process each match
|
||||||
|
for match in matches.sorted(by: { $0.range.length > $1.range.length }) {
|
||||||
|
// Extract the display name, user ID, and full URL
|
||||||
|
guard match.numberOfRanges == 3 else { continue }
|
||||||
|
|
||||||
|
let userIDRange = match.range(at: 2)
|
||||||
|
let fullRange = match.range(at: 0)
|
||||||
|
|
||||||
|
let userID = nsText.substring(with: userIDRange)
|
||||||
|
let fullURLString = "https://matrix.to/#/\(userID)"
|
||||||
|
|
||||||
|
if let url = URL(string: fullURLString) {
|
||||||
|
callback(fullRange, url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func createLinkAlert() {
|
private func createLinkAlert() {
|
||||||
let linkAction = wysiwygViewModel.getLinkAction()
|
let linkAction = wysiwygViewModel.getLinkAction()
|
||||||
currentLinkData = WysiwygLinkData(action: linkAction,
|
currentLinkData = WysiwygLinkData(action: linkAction,
|
||||||
|
|||||||
@@ -553,6 +553,85 @@ class ComposerToolbarViewModelTests: XCTestCase {
|
|||||||
await fulfillment(of: [expectation1, expectation2])
|
await fulfillment(of: [expectation1, expectation2])
|
||||||
XCTAssertEqual(viewModel.context.plainComposerText, NSAttributedString(string: "Hello world"))
|
XCTAssertEqual(viewModel.context.plainComposerText, NSAttributedString(string: "Hello world"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testRestoreUserMentionInPlainText() async throws {
|
||||||
|
viewModel.context.composerFormattingEnabled = false
|
||||||
|
let text = "Hello [TestName](https://matrix.to/#/@test:matrix.org)!"
|
||||||
|
viewModel.process(roomAction: .setText(plainText: text, htmlText: nil))
|
||||||
|
|
||||||
|
let deferred = deferFulfillment(viewModel.actions) { action in
|
||||||
|
switch action {
|
||||||
|
case let .sendMessage(plainText, _, _, intentionalMentions):
|
||||||
|
// As of right now the markdown loses the display name when restored
|
||||||
|
return plainText == "Hello [@test:matrix.org](https://matrix.to/#/@test:matrix.org)!" &&
|
||||||
|
intentionalMentions == IntentionalMentions(userIDs: ["@test:matrix.org"], atRoom: false)
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.process(viewAction: .sendMessage)
|
||||||
|
try await deferred.fulfill()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testRestoreAllUsersMentionInPlainText() async throws {
|
||||||
|
viewModel.context.composerFormattingEnabled = false
|
||||||
|
let text = "Hello @room"
|
||||||
|
viewModel.process(roomAction: .setText(plainText: text, htmlText: nil))
|
||||||
|
|
||||||
|
let deferred = deferFulfillment(viewModel.actions) { action in
|
||||||
|
switch action {
|
||||||
|
case let .sendMessage(plainText, _, _, intentionalMentions):
|
||||||
|
return plainText == "Hello @room" &&
|
||||||
|
intentionalMentions == IntentionalMentions(userIDs: [], atRoom: true)
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.process(viewAction: .sendMessage)
|
||||||
|
try await deferred.fulfill()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testRestoreMixedMentionsInPlainText() async throws {
|
||||||
|
viewModel.context.composerFormattingEnabled = false
|
||||||
|
let text = "Hello [User1](https://matrix.to/#/@user1:matrix.org), [User2](https://matrix.to/#/@user2:matrix.org) and @room"
|
||||||
|
viewModel.process(roomAction: .setText(plainText: text, htmlText: nil))
|
||||||
|
|
||||||
|
let deferred = deferFulfillment(viewModel.actions) { action in
|
||||||
|
switch action {
|
||||||
|
case let .sendMessage(plainText, _, _, intentionalMentions):
|
||||||
|
// As of right now the markdown loses the display name when restored
|
||||||
|
return plainText == "Hello [@user1:matrix.org](https://matrix.to/#/@user1:matrix.org), [@user2:matrix.org](https://matrix.to/#/@user2:matrix.org) and @room" &&
|
||||||
|
intentionalMentions == IntentionalMentions(userIDs: ["@user1:matrix.org", "@user2:matrix.org"], atRoom: true)
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.process(viewAction: .sendMessage)
|
||||||
|
try await deferred.fulfill()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testRestoreAmbiguousMention() async throws {
|
||||||
|
viewModel.context.composerFormattingEnabled = false
|
||||||
|
let text = "Hello [User1](https://matrix.to/#/@roomuser:matrix.org)"
|
||||||
|
viewModel.process(roomAction: .setText(plainText: text, htmlText: nil))
|
||||||
|
|
||||||
|
let deferred = deferFulfillment(viewModel.actions) { action in
|
||||||
|
switch action {
|
||||||
|
case let .sendMessage(plainText, _, _, intentionalMentions):
|
||||||
|
// As of right now the markdown loses the display name when restored
|
||||||
|
return plainText == "Hello [@roomuser:matrix.org](https://matrix.to/#/@roomuser:matrix.org)" &&
|
||||||
|
intentionalMentions == IntentionalMentions(userIDs: ["@roomuser:matrix.org"], atRoom: false)
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.process(viewAction: .sendMessage)
|
||||||
|
try await deferred.fulfill()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension MentionSuggestionItem {
|
private extension MentionSuggestionItem {
|
||||||
|
|||||||
Reference in New Issue
Block a user