From 1001b5dc338d6498be991ccd129678906c0b51d1 Mon Sep 17 00:00:00 2001 From: Mauro <34335419+Velin92@users.noreply.github.com> Date: Fri, 12 Jan 2024 12:58:36 +0100 Subject: [PATCH] Reverting RTE to use SDK markdown when in markdown mode (#2327) --- .../Sources/Application/AppCoordinator.swift | 5 +++-- .../Mocks/Generated/GeneratedMocks.swift | 16 +++++++-------- .../ComposerToolbarModels.swift | 2 +- .../ComposerToolbarViewModel.swift | 15 +++++++++----- .../RoomScreen/RoomScreenViewModel.swift | 2 +- .../Timeline/IntentionalMentions.swift | 4 ++++ .../MockRoomTimelineController.swift | 4 ++-- .../RoomTimelineController.swift | 4 ++-- .../RoomTimelineControllerProtocol.swift | 6 +++--- .../Services/Timeline/TimelineProxy.swift | 20 +++++++++++++------ .../Timeline/TimelineProxyProtocol.swift | 15 ++++++++++++-- .../ComposerToolbarViewModelTests.swift | 14 +++++++++++++ changelog.d/pr-2327.bugfix | 1 + 13 files changed, 76 insertions(+), 32 deletions(-) create mode 100644 changelog.d/pr-2327.bugfix diff --git a/ElementX/Sources/Application/AppCoordinator.swift b/ElementX/Sources/Application/AppCoordinator.swift index f02e455f4..be634caad 100644 --- a/ElementX/Sources/Application/AppCoordinator.swift +++ b/ElementX/Sources/Application/AppCoordinator.swift @@ -267,8 +267,9 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate, return } let roomProxy = await userSession.clientProxy.roomForIdentifier(roomID) - let messageContent = messageEventContentFromMarkdown(md: replyText) - switch await roomProxy?.timeline.sendMessageEventContent(messageContent) { + switch await roomProxy?.timeline.sendMessage(replyText, + html: nil, + intentionalMentions: .empty) { case .success: break default: diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index e5fb1d207..0ac810545 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -2487,12 +2487,12 @@ class TimelineProxyMock: TimelineProxyProtocol { var editMessageHtmlOriginalIntentionalMentionsCalled: Bool { return editMessageHtmlOriginalIntentionalMentionsCallsCount > 0 } - var editMessageHtmlOriginalIntentionalMentionsReceivedArguments: (message: String, html: String, eventID: String, intentionalMentions: IntentionalMentions)? - var editMessageHtmlOriginalIntentionalMentionsReceivedInvocations: [(message: String, html: String, eventID: String, intentionalMentions: IntentionalMentions)] = [] + var editMessageHtmlOriginalIntentionalMentionsReceivedArguments: (message: String, html: String?, eventID: String, intentionalMentions: IntentionalMentions)? + var editMessageHtmlOriginalIntentionalMentionsReceivedInvocations: [(message: String, html: String?, eventID: String, intentionalMentions: IntentionalMentions)] = [] var editMessageHtmlOriginalIntentionalMentionsReturnValue: Result! - var editMessageHtmlOriginalIntentionalMentionsClosure: ((String, String, String, IntentionalMentions) async -> Result)? + var editMessageHtmlOriginalIntentionalMentionsClosure: ((String, String?, String, IntentionalMentions) async -> Result)? - func editMessage(_ message: String, html: String, original eventID: String, intentionalMentions: IntentionalMentions) async -> Result { + func editMessage(_ message: String, html: String?, original eventID: String, intentionalMentions: IntentionalMentions) async -> Result { editMessageHtmlOriginalIntentionalMentionsCallsCount += 1 editMessageHtmlOriginalIntentionalMentionsReceivedArguments = (message: message, html: html, eventID: eventID, intentionalMentions: intentionalMentions) editMessageHtmlOriginalIntentionalMentionsReceivedInvocations.append((message: message, html: html, eventID: eventID, intentionalMentions: intentionalMentions)) @@ -2767,12 +2767,12 @@ class TimelineProxyMock: TimelineProxyProtocol { var sendMessageHtmlInReplyToIntentionalMentionsCalled: Bool { return sendMessageHtmlInReplyToIntentionalMentionsCallsCount > 0 } - var sendMessageHtmlInReplyToIntentionalMentionsReceivedArguments: (message: String, html: String, eventID: String?, intentionalMentions: IntentionalMentions)? - var sendMessageHtmlInReplyToIntentionalMentionsReceivedInvocations: [(message: String, html: String, eventID: String?, intentionalMentions: IntentionalMentions)] = [] + var sendMessageHtmlInReplyToIntentionalMentionsReceivedArguments: (message: String, html: String?, eventID: String?, intentionalMentions: IntentionalMentions)? + var sendMessageHtmlInReplyToIntentionalMentionsReceivedInvocations: [(message: String, html: String?, eventID: String?, intentionalMentions: IntentionalMentions)] = [] var sendMessageHtmlInReplyToIntentionalMentionsReturnValue: Result! - var sendMessageHtmlInReplyToIntentionalMentionsClosure: ((String, String, String?, IntentionalMentions) async -> Result)? + var sendMessageHtmlInReplyToIntentionalMentionsClosure: ((String, String?, String?, IntentionalMentions) async -> Result)? - func sendMessage(_ message: String, html: String, inReplyTo eventID: String?, intentionalMentions: IntentionalMentions) async -> Result { + func sendMessage(_ message: String, html: String?, inReplyTo eventID: String?, intentionalMentions: IntentionalMentions) async -> Result { sendMessageHtmlInReplyToIntentionalMentionsCallsCount += 1 sendMessageHtmlInReplyToIntentionalMentionsReceivedArguments = (message: message, html: html, eventID: eventID, intentionalMentions: intentionalMentions) sendMessageHtmlInReplyToIntentionalMentionsReceivedInvocations.append((message: message, html: html, eventID: eventID, intentionalMentions: intentionalMentions)) diff --git a/ElementX/Sources/Screens/ComposerToolbar/ComposerToolbarModels.swift b/ElementX/Sources/Screens/ComposerToolbar/ComposerToolbarModels.swift index 56f6f562d..372d80149 100644 --- a/ElementX/Sources/Screens/ComposerToolbar/ComposerToolbarModels.swift +++ b/ElementX/Sources/Screens/ComposerToolbar/ComposerToolbarModels.swift @@ -31,7 +31,7 @@ enum ComposerToolbarVoiceMessageAction { } enum ComposerToolbarViewModelAction { - case sendMessage(plain: String, html: String, mode: RoomScreenComposerMode, intentionalMentions: IntentionalMentions) + case sendMessage(plain: String, html: String?, mode: RoomScreenComposerMode, intentionalMentions: IntentionalMentions) case attach(ComposerAttachmentType) case handlePasteOrDrop(provider: NSItemProvider) diff --git a/ElementX/Sources/Screens/ComposerToolbar/ComposerToolbarViewModel.swift b/ElementX/Sources/Screens/ComposerToolbar/ComposerToolbarViewModel.swift index 49163e52d..ac48b3043 100644 --- a/ElementX/Sources/Screens/ComposerToolbar/ComposerToolbarViewModel.swift +++ b/ElementX/Sources/Screens/ComposerToolbar/ComposerToolbarViewModel.swift @@ -87,11 +87,15 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool } .store(in: &cancellables) - appSettings.$richTextEditorEnabled - .map { !$0 } - .assign(to: &wysiwygViewModel.$plainTextMode) - completionSuggestionService.suggestionsPublisher + .combineLatest(appSettings.$richTextEditorEnabled) + .map { suggestions, richTextEditorEnabled in + // We ignore user suggestions when RTE is disabled since mentions would not work + guard richTextEditorEnabled else { + return [] + } + return suggestions + } .weakAssign(to: \.state.suggestions, on: self) .store(in: &cancellables) @@ -111,8 +115,9 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool case .previewVoiceMessage: actionsSubject.send(.voiceMessage(.send)) default: + let sendHTML = appSettings.richTextEditorEnabled actionsSubject.send(.sendMessage(plain: wysiwygViewModel.content.markdown, - html: wysiwygViewModel.content.html, + html: sendHTML ? wysiwygViewModel.content.html : nil, mode: state.composerMode, intentionalMentions: wysiwygViewModel .getMentionsState() diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift index 4545e73a7..ed460ae87 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift @@ -461,7 +461,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol state.showLoading = false } - private func sendCurrentMessage(_ message: String, html: String, mode: RoomScreenComposerMode, intentionalMentions: IntentionalMentions) async { + private func sendCurrentMessage(_ message: String, html: String?, mode: RoomScreenComposerMode, intentionalMentions: IntentionalMentions) async { guard !message.isEmpty else { fatalError("This message should never be empty") } diff --git a/ElementX/Sources/Services/Timeline/IntentionalMentions.swift b/ElementX/Sources/Services/Timeline/IntentionalMentions.swift index 36a9458f4..31d8c9202 100644 --- a/ElementX/Sources/Services/Timeline/IntentionalMentions.swift +++ b/ElementX/Sources/Services/Timeline/IntentionalMentions.swift @@ -25,6 +25,10 @@ struct IntentionalMentions: Equatable { } extension IntentionalMentions { + static var empty: Self { + IntentionalMentions(userIDs: .init(), atRoom: false) + } + func toRustMentions() -> Mentions { Mentions(userIds: Array(userIDs), room: atRoom) } diff --git a/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift b/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift index 09ccc4b86..7ce73fa9a 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift @@ -70,14 +70,14 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol { func processItemDisappearance(_ itemID: TimelineItemIdentifier) async { } func sendMessage(_ message: String, - html: String, + html: String?, inReplyTo itemID: TimelineItemIdentifier?, intentionalMentions: IntentionalMentions) async { } func toggleReaction(_ reaction: String, to itemID: TimelineItemIdentifier) async { } func editMessage(_ newMessage: String, - html: String, + html: String?, original itemID: TimelineItemIdentifier, intentionalMentions: IntentionalMentions) async { } diff --git a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift index 04a9dbaf6..95ab7dc61 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift @@ -114,7 +114,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol { func processItemDisappearance(_ itemID: TimelineItemIdentifier) { } func sendMessage(_ message: String, - html: String, + html: String?, inReplyTo itemID: TimelineItemIdentifier?, intentionalMentions: IntentionalMentions) async { var inReplyTo: String? @@ -155,7 +155,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol { } func editMessage(_ newMessage: String, - html: String, + html: String?, original itemID: TimelineItemIdentifier, intentionalMentions: IntentionalMentions) async { MXLog.info("Edit message in \(roomID)") diff --git a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift index a49c85e38..9338e64c9 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift @@ -52,12 +52,12 @@ protocol RoomTimelineControllerProtocol { func sendReadReceipt(for itemID: TimelineItemIdentifier) async -> Result func sendMessage(_ message: String, - html: String, + html: String?, inReplyTo itemID: TimelineItemIdentifier?, intentionalMentions: IntentionalMentions) async func editMessage(_ newMessage: String, - html: String, + html: String?, original itemID: TimelineItemIdentifier, intentionalMentions: IntentionalMentions) async @@ -78,7 +78,7 @@ protocol RoomTimelineControllerProtocol { extension RoomTimelineControllerProtocol { func sendMessage(_ message: String, - html: String, + html: String?, intentionalMentions: IntentionalMentions) async { await sendMessage(message, html: html, diff --git a/ElementX/Sources/Services/Timeline/TimelineProxy.swift b/ElementX/Sources/Services/Timeline/TimelineProxy.swift index 8d42760cc..06044ae98 100644 --- a/ElementX/Sources/Services/Timeline/TimelineProxy.swift +++ b/ElementX/Sources/Services/Timeline/TimelineProxy.swift @@ -96,11 +96,11 @@ final class TimelineProxy: TimelineProxyProtocol { } func editMessage(_ message: String, - html: String, + html: String?, original eventID: String, intentionalMentions: IntentionalMentions) async -> Result { MXLog.info("Editing message with original event ID: \(eventID)") - + sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) defer { sendMessageBackgroundTask?.stop() @@ -390,7 +390,7 @@ final class TimelineProxy: TimelineProxyProtocol { } func sendMessage(_ message: String, - html: String, + html: String?, inReplyTo eventID: String? = nil, intentionalMentions: IntentionalMentions) async -> Result { if let eventID { @@ -573,7 +573,7 @@ final class TimelineProxy: TimelineProxyProtocol { // MARK: - Private private func buildMessageContentFor(_ message: String, - html: String, + html: String?, intentionalMentions: Mentions) -> RoomMessageEventContentWithoutRelation { let emoteSlashCommand = "/me " let isEmote: Bool = message.starts(with: emoteSlashCommand) @@ -581,10 +581,18 @@ final class TimelineProxy: TimelineProxyProtocol { let content: RoomMessageEventContentWithoutRelation if isEmote { let emoteMessage = String(message.dropFirst(emoteSlashCommand.count)) - let emoteHtml = String(html.dropFirst(emoteSlashCommand.count)) + + var emoteHtml: String? + if let html { + emoteHtml = String(html.dropFirst(emoteSlashCommand.count)) + } content = buildEmoteMessageContentFor(emoteMessage, html: emoteHtml) } else { - content = messageEventContentFromHtml(body: message, htmlBody: html) + if let html { + content = messageEventContentFromHtml(body: message, htmlBody: html) + } else { + content = messageEventContentFromMarkdown(md: message) + } } return content.withMentions(mentions: intentionalMentions) } diff --git a/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift b/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift index b0a2811bc..55acf2884 100644 --- a/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift +++ b/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift @@ -51,7 +51,7 @@ protocol TimelineProxyProtocol { func cancelSend(transactionID: String) async func editMessage(_ message: String, - html: String, + html: String?, original eventID: String, intentionalMentions: IntentionalMentions) async -> Result @@ -107,7 +107,7 @@ protocol TimelineProxyProtocol { func sendMessageEventContent(_ messageContent: RoomMessageEventContentWithoutRelation) async -> Result func sendMessage(_ message: String, - html: String, + html: String?, inReplyTo eventID: String?, intentionalMentions: IntentionalMentions) async -> Result @@ -125,3 +125,14 @@ protocol TimelineProxyProtocol { func sendPollResponse(pollStartID: String, answers: [String]) async -> Result } + +extension TimelineProxyProtocol { + func sendMessage(_ message: String, + html: String?, + intentionalMentions: IntentionalMentions) async -> Result { + await sendMessage(message, + html: html, + inReplyTo: nil, + intentionalMentions: intentionalMentions) + } +} diff --git a/UnitTests/Sources/ComposerToolbarViewModelTests.swift b/UnitTests/Sources/ComposerToolbarViewModelTests.swift index 80c0c27c2..08d995f00 100644 --- a/UnitTests/Sources/ComposerToolbarViewModelTests.swift +++ b/UnitTests/Sources/ComposerToolbarViewModelTests.swift @@ -179,4 +179,18 @@ class ComposerToolbarViewModelTests: XCTestCase { try await deferred.fulfill() } + + func testSuggestionsAreIgnoredWhenRTEDisabled() async throws { + appSettings.richTextEditorEnabled = false + let suggestions: [SuggestionItem] = [.user(item: MentionSuggestionItem(id: "@user_mention_1:matrix.org", displayName: "User 1", avatarURL: nil)), + .user(item: MentionSuggestionItem(id: "@user_mention_2:matrix.org", displayName: "User 2", avatarURL: URL.documentsDirectory))] + let mockCompletionSuggestionService = CompletionSuggestionServiceMock(configuration: .init(suggestions: suggestions)) + viewModel = ComposerToolbarViewModel(wysiwygViewModel: wysiwygViewModel, + completionSuggestionService: mockCompletionSuggestionService, + mediaProvider: MockMediaProvider(), + appSettings: ServiceLocator.shared.settings, + mentionDisplayHelper: ComposerMentionDisplayHelper.mock) + + XCTAssertEqual(viewModel.state.suggestions, []) + } } diff --git a/changelog.d/pr-2327.bugfix b/changelog.d/pr-2327.bugfix new file mode 100644 index 000000000..82cb6dce8 --- /dev/null +++ b/changelog.d/pr-2327.bugfix @@ -0,0 +1 @@ +Improved plain text mode stability. \ No newline at end of file