diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index b051f22756..2c1be96f38 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -6594,7 +6594,7 @@ repositoryURL = "https://github.com/matrix-org/matrix-wysiwyg-composer-swift"; requirement = { kind = exactVersion; - version = 2.19.0; + version = 2.22.0; }; }; 96495DD8554E2F39D3954354 /* XCRemoteSwiftPackageReference "posthog-ios" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 458ad44abb..ef34263168 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -139,8 +139,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/matrix-org/matrix-wysiwyg-composer-swift", "state" : { - "revision" : "0aa1308c43451fd077e332f72d6a32135f258834", - "version" : "2.19.0" + "revision" : "5d3bdd55f8c68c216f91191a501c0d7225057f92", + "version" : "2.22.0" } }, { diff --git a/ElementX/Sources/Application/AppCoordinator.swift b/ElementX/Sources/Application/AppCoordinator.swift index 992cfb5d86..1ae38e61e3 100644 --- a/ElementX/Sources/Application/AppCoordinator.swift +++ b/ElementX/Sources/Application/AppCoordinator.swift @@ -267,9 +267,8 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate, return } let roomProxy = await userSession.clientProxy.roomForIdentifier(roomID) - switch await roomProxy?.timeline.sendMessage(replyText, - html: nil, - intentionalMentions: .empty) { + let messageContent = messageEventContentFromMarkdown(md: replyText) + switch await roomProxy?.timeline.sendMessageEventContent(messageContent) { case .success: break default: diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index 769f2b7d90..f32148ab70 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -2628,12 +2628,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)) @@ -2887,12 +2887,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/CallScreen/CallScreenViewModel.swift b/ElementX/Sources/Screens/CallScreen/CallScreenViewModel.swift index bad56557b6..82abff0ea9 100644 --- a/ElementX/Sources/Screens/CallScreen/CallScreenViewModel.swift +++ b/ElementX/Sources/Screens/CallScreen/CallScreenViewModel.swift @@ -93,8 +93,6 @@ class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol switch action { case .callEnded: actionsSubject.send(.dismiss) - default: - break } } .store(in: &cancellables) diff --git a/ElementX/Sources/Screens/ComposerToolbar/ComposerToolbarModels.swift b/ElementX/Sources/Screens/ComposerToolbar/ComposerToolbarModels.swift index 372d801490..56f6f562d4 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 1d273abf4a..afef6cd938 100644 --- a/ElementX/Sources/Screens/ComposerToolbar/ComposerToolbarViewModel.swift +++ b/ElementX/Sources/Screens/ComposerToolbar/ComposerToolbarViewModel.swift @@ -87,6 +87,10 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool } .store(in: &cancellables) + appSettings.$richTextEditorEnabled + .map { !$0 } + .assign(to: &wysiwygViewModel.$plainTextMode) + completionSuggestionService.suggestionsPublisher .weakAssign(to: \.state.suggestions, on: self) .store(in: &cancellables) @@ -107,9 +111,8 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool case .previewVoiceMessage: actionsSubject.send(.voiceMessage(.send)) default: - let sendHTML = ServiceLocator.shared.settings.richTextEditorEnabled actionsSubject.send(.sendMessage(plain: wysiwygViewModel.content.markdown, - html: sendHTML ? wysiwygViewModel.content.html : nil, + html: wysiwygViewModel.content.html, mode: state.composerMode, intentionalMentions: wysiwygViewModel .getMentionsState() @@ -154,15 +157,13 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool set(text: "") } } - - func handleKeyCommand(_ keyCommand: WysiwygKeyCommand) -> Bool { - switch keyCommand { - case .enter: - process(viewAction: .sendMessage) - return true - case .shiftEnter: - return false - } + + var keyCommands: [WysiwygKeyCommand] { + [ + .enter { [weak self] in + self?.process(viewAction: .sendMessage) + } + ] } // MARK: - Private diff --git a/ElementX/Sources/Screens/ComposerToolbar/View/ComposerToolbar.swift b/ElementX/Sources/Screens/ComposerToolbar/View/ComposerToolbar.swift index 530dcad0ad..5472b039b2 100644 --- a/ElementX/Sources/Screens/ComposerToolbar/View/ComposerToolbar.swift +++ b/ElementX/Sources/Screens/ComposerToolbar/View/ComposerToolbar.swift @@ -24,7 +24,7 @@ struct ComposerToolbar: View { // Needs to be observable or the placeholder and the dictation state are not managed correctly. @ObservedObject var wysiwygViewModel: WysiwygComposerViewModel - let keyCommandHandler: KeyCommandHandler + let keyCommands: [WysiwygKeyCommand] @FocusState private var composerFocused: Bool @State private var frame: CGRect = .zero @@ -201,7 +201,7 @@ struct ComposerToolbar: View { placeholderColor: .compound.textSecondary, viewModel: wysiwygViewModel, itemProviderHelper: ItemProviderHelper(), - keyCommandHandler: keyCommandHandler) { provider in + keyCommands: keyCommands) { provider in context.send(viewAction: .handlePasteOrDrop(provider: provider)) } } @@ -306,7 +306,7 @@ struct ComposerToolbar_Previews: PreviewProvider, TestablePreview { // The mock functon can't be used in this context because it does not hold a reference to the view model, losing the combine subscriptions ComposerToolbar(context: composerViewModel.context, wysiwygViewModel: wysiwygViewModel, - keyCommandHandler: { _ in false }) + keyCommands: []) } .previewDisplayName("With Suggestions") @@ -336,7 +336,7 @@ extension ComposerToolbar { } return ComposerToolbar(context: composerViewModel.context, wysiwygViewModel: wysiwygViewModel, - keyCommandHandler: { _ in false }) + keyCommands: []) } static func textWithVoiceMessage(focused: Bool = true) -> ComposerToolbar { @@ -352,7 +352,7 @@ extension ComposerToolbar { } return ComposerToolbar(context: composerViewModel.context, wysiwygViewModel: wysiwygViewModel, - keyCommandHandler: { _ in false }) + keyCommands: []) } static func voiceMessageRecordingMock() -> ComposerToolbar { @@ -368,7 +368,7 @@ extension ComposerToolbar { } return ComposerToolbar(context: composerViewModel.context, wysiwygViewModel: wysiwygViewModel, - keyCommandHandler: { _ in false }) + keyCommands: []) } static func voiceMessagePreviewMock(uploading: Bool) -> ComposerToolbar { @@ -385,6 +385,6 @@ extension ComposerToolbar { } return ComposerToolbar(context: composerViewModel.context, wysiwygViewModel: wysiwygViewModel, - keyCommandHandler: { _ in false }) + keyCommands: []) } } diff --git a/ElementX/Sources/Screens/ComposerToolbar/View/MessageComposer.swift b/ElementX/Sources/Screens/ComposerToolbar/View/MessageComposer.swift index 04ed571a0f..f904754c6c 100644 --- a/ElementX/Sources/Screens/ComposerToolbar/View/MessageComposer.swift +++ b/ElementX/Sources/Screens/ComposerToolbar/View/MessageComposer.swift @@ -212,7 +212,7 @@ struct MessageComposer_Previews: PreviewProvider, TestablePreview { let composerView = WysiwygComposerView(placeholder: L10n.richTextEditorComposerPlaceholder, viewModel: viewModel, itemProviderHelper: nil, - keyCommandHandler: nil, + keyCommands: nil, pasteHandler: nil) return MessageComposer(composerView: composerView, diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift index 4d53a2ca28..d43125a469 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift @@ -138,7 +138,7 @@ final class RoomScreenCoordinator: CoordinatorProtocol { func toPresentable() -> AnyView { let composerToolbar = ComposerToolbar(context: composerViewModel.context, wysiwygViewModel: wysiwygViewModel, - keyCommandHandler: composerViewModel.handleKeyCommand) + keyCommands: composerViewModel.keyCommands) return AnyView(RoomScreen(context: viewModel.context, composerToolbar: composerToolbar)) } diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift index ed460ae87b..4545e73a75 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/TimelineController/MockRoomTimelineController.swift b/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift index 48f4a7b699..c8651aa62f 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift @@ -65,14 +65,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 bb39191a1c..1464aa85d1 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift @@ -107,7 +107,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? @@ -148,7 +148,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 061f1957d8..3ff2a35b42 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift @@ -50,12 +50,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 @@ -74,7 +74,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 cb3328c1b7..0267ba460a 100644 --- a/ElementX/Sources/Services/Timeline/TimelineProxy.swift +++ b/ElementX/Sources/Services/Timeline/TimelineProxy.swift @@ -93,7 +93,7 @@ final class TimelineProxy: TimelineProxyProtocol { } func editMessage(_ message: String, - html: String?, + html: String, original eventID: String, intentionalMentions: IntentionalMentions) async -> Result { sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) @@ -320,7 +320,7 @@ final class TimelineProxy: TimelineProxyProtocol { } func sendMessage(_ message: String, - html: String?, + html: String, inReplyTo eventID: String? = nil, intentionalMentions: IntentionalMentions) async -> Result { sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) @@ -454,7 +454,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) @@ -462,18 +462,10 @@ final class TimelineProxy: TimelineProxyProtocol { let content: RoomMessageEventContentWithoutRelation if isEmote { let emoteMessage = String(message.dropFirst(emoteSlashCommand.count)) - - var emoteHtml: String? - if let html { - emoteHtml = String(html.dropFirst(emoteSlashCommand.count)) - } + let emoteHtml = String(html.dropFirst(emoteSlashCommand.count)) content = buildEmoteMessageContentFor(emoteMessage, html: emoteHtml) } else { - if let html { - content = messageEventContentFromHtml(body: message, htmlBody: html) - } else { - content = messageEventContentFromMarkdown(md: message) - } + content = messageEventContentFromHtml(body: message, htmlBody: html) } return content.withMentions(mentions: intentionalMentions) } diff --git a/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift b/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift index 131152ac97..c35687188f 100644 --- a/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift +++ b/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift @@ -49,7 +49,7 @@ protocol TimelineProxyProtocol { func cancelSend(transactionID: String) async func editMessage(_ message: String, - html: String?, + html: String, original eventID: String, intentionalMentions: IntentionalMentions) async -> Result @@ -103,7 +103,7 @@ protocol TimelineProxyProtocol { func sendMessageEventContent(_ messageContent: RoomMessageEventContentWithoutRelation) async -> Result func sendMessage(_ message: String, - html: String?, + html: String, inReplyTo eventID: String?, intentionalMentions: IntentionalMentions) async -> Result @@ -124,7 +124,7 @@ protocol TimelineProxyProtocol { extension TimelineProxyProtocol { func sendMessage(_ message: String, - html: String?, + html: String, intentionalMentions: IntentionalMentions) async -> Result { await sendMessage(message, html: html, diff --git a/UnitTests/Sources/ComposerToolbarViewModelTests.swift b/UnitTests/Sources/ComposerToolbarViewModelTests.swift index 0e487626b1..80c0c27c23 100644 --- a/UnitTests/Sources/ComposerToolbarViewModelTests.swift +++ b/UnitTests/Sources/ComposerToolbarViewModelTests.swift @@ -81,8 +81,7 @@ class ComposerToolbarViewModelTests: XCTestCase { } func testHandleKeyCommand() { - XCTAssertTrue(viewModel.handleKeyCommand(.enter)) - XCTAssertFalse(viewModel.handleKeyCommand(.shiftEnter)) + XCTAssertTrue(viewModel.keyCommands.count == 1) } func testComposerFocusAfterEnablingRTE() { diff --git a/changelog.d/pr-2246.bugfix b/changelog.d/pr-2246.bugfix new file mode 100644 index 0000000000..085c03e3b9 --- /dev/null +++ b/changelog.d/pr-2246.bugfix @@ -0,0 +1 @@ +Fix for plain text mode, using the RTE markdown to html conversion properly. \ No newline at end of file diff --git a/project.yml b/project.yml index 2e1160d1a1..4576b71a70 100644 --- a/project.yml +++ b/project.yml @@ -64,7 +64,7 @@ packages: branch: 0.0.1 WysiwygComposer: url: https://github.com/matrix-org/matrix-wysiwyg-composer-swift - exactVersion: 2.19.0 + exactVersion: 2.22.0 # path: ../matrix-wysiwyg/platforms/ios/lib/WysiwygComposer # External dependencies