diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 1efe0f50e..a2215b29b 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -5801,7 +5801,7 @@ repositoryURL = "https://github.com/matrix-org/matrix-rust-components-swift"; requirement = { kind = exactVersion; - version = 1.1.20; + version = 1.1.21; }; }; 821C67C9A7F8CC3FD41B28B4 /* XCRemoteSwiftPackageReference "emojibase-bindings" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 4fd9ea8f9..0d1990987 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -129,8 +129,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/matrix-org/matrix-rust-components-swift", "state" : { - "revision" : "5f3a195f6a461b4d40a8a705a3af8235711f12f5", - "version" : "1.1.20" + "revision" : "22461632db17a6dc1193dcdcfa3231614649c517", + "version" : "1.1.21" } }, { diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift index 88535075e..8375ca837 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift @@ -615,10 +615,16 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol } else { text = messageTimelineItem.body } + case .emote(let emoteItem): + if ServiceLocator.shared.settings.richTextEditorEnabled, let formattedBodyHTMLString = emoteItem.formattedBodyHTMLString { + text = "/me " + formattedBodyHTMLString + } else { + text = "/me " + messageTimelineItem.body + } default: text = messageTimelineItem.body } - + actionsSubject.send(.composer(action: .setText(text: text))) actionsSubject.send(.composer(action: .setMode(mode: .edit(originalItemId: messageTimelineItem.id)))) case .copyPermalink: diff --git a/ElementX/Sources/Services/Room/RoomProxy.swift b/ElementX/Sources/Services/Room/RoomProxy.swift index 9f7c48705..fde921aab 100644 --- a/ElementX/Sources/Services/Room/RoomProxy.swift +++ b/ElementX/Sources/Services/Room/RoomProxy.swift @@ -252,19 +252,15 @@ class RoomProxy: RoomProxyProtocol { return .success(()) } } - + func sendMessage(_ message: String, html: String?, inReplyTo eventID: String? = nil) async -> Result { sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) defer { sendMessageBackgroundTask?.stop() } - let messageContent: RoomMessageEventContentWithoutRelation - if let html { - messageContent = messageEventContentFromHtml(body: message, htmlBody: html) - } else { - messageContent = messageEventContentFromMarkdown(md: message) - } + let messageContent = buildMessageContentFor(message, html: html) + return await Task.dispatch(on: messageSendingDispatchQueue) { do { if let eventID { @@ -435,23 +431,18 @@ class RoomProxy: RoomProxyProtocol { } } - func editMessage(_ newMessage: String, html: String?, original eventID: String) async -> Result { + func editMessage(_ message: String, html: String?, original eventID: String) async -> Result { sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) defer { sendMessageBackgroundTask?.stop() } - let newMessageContent: RoomMessageEventContentWithoutRelation - if let html { - newMessageContent = messageEventContentFromHtml(body: newMessage, htmlBody: html) - } else { - newMessageContent = messageEventContentFromMarkdown(md: newMessage) - } - + let messageContent = buildMessageContentFor(message, html: html) + return await Task.dispatch(on: messageSendingDispatchQueue) { do { let originalEvent = try self.room.getEventTimelineItemByEventId(eventId: eventID) - try self.room.edit(newContent: newMessageContent, editItem: originalEvent) + try self.room.edit(newContent: messageContent, editItem: originalEvent) return .success(()) } catch { return .failure(.failedEditingMessage) @@ -707,7 +698,37 @@ class RoomProxy: RoomProxyProtocol { } // MARK: - Private + + private func buildMessageContentFor(_ message: String, html: String?) -> RoomMessageEventContentWithoutRelation { + let emoteSlashCommand = "/me " + let isEmote: Bool = message.starts(with: emoteSlashCommand) + + guard isEmote else { + if let html { + return messageEventContentFromHtml(body: message, htmlBody: html) + } else { + return messageEventContentFromMarkdown(md: message) + } + } + + let emoteMessage = String(message.dropFirst(emoteSlashCommand.count)) + + var emoteHtml: String? + if let html { + emoteHtml = String(html.dropFirst(emoteSlashCommand.count)) + } + + return buildEmoteMessageContentFor(emoteMessage, html: emoteHtml) + } + private func buildEmoteMessageContentFor(_ message: String, html: String?) -> RoomMessageEventContentWithoutRelation { + if let html { + return messageEventContentFromHtmlAsEmote(body: message, htmlBody: html) + } else { + return messageEventContentFromMarkdownAsEmote(md: message) + } + } + /// Force the timeline to load member details so it can populate sender profiles whenever we add a timeline listener /// This should become automatic on the RustSDK side at some point private func fetchMembers() async { diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/EmoteRoomTimelineItemContent.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/EmoteRoomTimelineItemContent.swift index d190e369b..6afb97643 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/EmoteRoomTimelineItemContent.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/EmoteRoomTimelineItemContent.swift @@ -19,4 +19,6 @@ import UIKit struct EmoteRoomTimelineItemContent: Hashable { let body: String var formattedBody: AttributedString? + /// The original textual representation of the formatted body directly from the event (usually HTML code) + var formattedBodyHTMLString: String? } diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemFactory.swift b/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemFactory.swift index 92aa6a6c9..bec7d786a 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemFactory.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemFactory.swift @@ -558,7 +558,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol { formattedBody = attributedStringBuilder.fromPlain(L10n.commonEmote(name, messageContent.body)) } - return .init(body: messageContent.body, formattedBody: formattedBody) + return .init(body: messageContent.body, formattedBody: formattedBody, formattedBodyHTMLString: htmlBody) } // MARK: - State Events diff --git a/changelog.d/1841.feature b/changelog.d/1841.feature new file mode 100644 index 000000000..488d7887a --- /dev/null +++ b/changelog.d/1841.feature @@ -0,0 +1 @@ +Implement /me diff --git a/project.yml b/project.yml index b019236cb..4c0c702e9 100644 --- a/project.yml +++ b/project.yml @@ -46,7 +46,7 @@ packages: # Element/Matrix dependencies MatrixRustSDK: url: https://github.com/matrix-org/matrix-rust-components-swift - exactVersion: 1.1.20 + exactVersion: 1.1.21 # path: ../matrix-rust-sdk DesignKit: path: DesignKit