From a4c0b7f91cfe92851634bd19b1cc41e4f20cc631 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Tue, 22 Jul 2025 14:16:40 +0300 Subject: [PATCH] Reorder the TimelineController send methods --- .../MockTimelineController.swift | 28 ++-- .../TimelineController.swift | 154 +++++++++--------- .../TimelineControllerProtocol.swift | 16 +- 3 files changed, 101 insertions(+), 97 deletions(-) diff --git a/ElementX/Sources/Services/Timeline/TimelineController/MockTimelineController.swift b/ElementX/Sources/Services/Timeline/TimelineController/MockTimelineController.swift index 18ec8a7bc..c272ce3ff 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/MockTimelineController.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/MockTimelineController.swift @@ -108,19 +108,6 @@ class MockTimelineController: TimelineControllerProtocol { func processItemAppearance(_ itemID: TimelineItemIdentifier) async { } func processItemDisappearance(_ itemID: TimelineItemIdentifier) async { } - - func sendMessage(_ message: String, - html: String?, - inReplyToEventID: String?, - intentionalMentions: IntentionalMentions) async { } - - func sendVoiceMessage(url: URL, audioInfo: AudioInfo, waveform: [UInt16]) async -> Result { - if let timelineProxy { - return await timelineProxy.sendVoiceMessage(url: url, audioInfo: audioInfo, waveform: waveform) { _ in } - } - - return .success(()) - } func toggleReaction(_ reaction: String, to eventID: TimelineItemIdentifier.EventOrTransactionID) async { } @@ -161,6 +148,21 @@ class MockTimelineController: TimelineControllerProtocol { timelineItemsTimestamp[itemID] ?? .now } + // MARK: - Sending + + func sendMessage(_ message: String, + html: String?, + inReplyToEventID: String?, + intentionalMentions: IntentionalMentions) async { } + + func sendVoiceMessage(url: URL, audioInfo: AudioInfo, waveform: [UInt16]) async -> Result { + if let timelineProxy { + return await timelineProxy.sendVoiceMessage(url: url, audioInfo: audioInfo, waveform: waveform) { _ in } + } + + return .success(()) + } + // MARK: - UI Test signalling /// The cancellable used for UI Tests signalling. diff --git a/ElementX/Sources/Services/Timeline/TimelineController/TimelineController.swift b/ElementX/Sources/Services/Timeline/TimelineController/TimelineController.swift index 5907517c2..d551a19f4 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/TimelineController.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/TimelineController.swift @@ -145,83 +145,6 @@ class TimelineController: TimelineControllerProtocol { func processItemDisappearance(_ itemID: TimelineItemIdentifier) { } - // MARK: - Sending - - func sendMessage(_ message: String, - html: String?, - inReplyToEventID: String?, - intentionalMentions: IntentionalMentions) async { - MXLog.info("Send message in \(roomID)") - - switch await activeTimeline.sendMessage(message, - html: html, - inReplyToEventID: inReplyToEventID, - intentionalMentions: intentionalMentions) { - case .success: - MXLog.info("Finished sending message") - await donateSendMessageIntent() - case .failure(let error): - MXLog.error("Failed sending message with error: \(error)") - } - } - - func sendVoiceMessage(url: URL, audioInfo: AudioInfo, waveform: [UInt16]) async -> Result { - await activeTimeline.sendVoiceMessage(url: url, - audioInfo: audioInfo, - waveform: waveform) { _ in } - } - - private func donateSendMessageIntent() async { - guard let displayName = roomProxy.details.name ?? roomProxy.details.canonicalAlias, !displayName.isEmpty else { - MXLog.error("Failed donating send message intent, room missing name or alias.") - return - } - - let groupName = INSpeakableString(spokenPhrase: displayName) - - let sendMessageIntent = INSendMessageIntent(recipients: nil, - outgoingMessageType: .outgoingMessageText, - content: nil, - speakableGroupName: groupName, - conversationIdentifier: roomProxy.id, - serviceName: nil, - sender: nil, - attachments: nil) - - let avatarURL: URL? = switch roomProxy.details.avatar { - case .room(_, _, let avatarURL): - avatarURL - case .heroes(let userProfiles): - userProfiles.first?.avatarURL - case .tombstoned: - nil - } - - func addPlacehoder() { - if let imageData = Avatars.generatePlaceholderAvatarImageData(name: displayName, id: roomProxy.id, size: .init(width: 100, height: 100)) { - sendMessageIntent.setImage(INImage(imageData: imageData), forParameterNamed: \.speakableGroupName) - } - } - - if let avatarURL, let mediaSource = try? MediaSourceProxy(url: avatarURL, mimeType: nil) { - if case let .success(avatarData) = await mediaProvider.loadThumbnailForSource(source: mediaSource, size: .init(width: 100, height: 100)) { - sendMessageIntent.setImage(INImage(imageData: avatarData), forParameterNamed: \.speakableGroupName) - } else { - addPlacehoder() - } - } else { - addPlacehoder() - } - - let interaction = INInteraction(intent: sendMessageIntent, response: nil) - - do { - try await interaction.donate() - } catch { - MXLog.error("Failed donating send message intent with error: \(error)") - } - } - func toggleReaction(_ reaction: String, to eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID) async { MXLog.info("Toggle reaction \(reaction) to \(eventOrTransactionID)") @@ -358,6 +281,32 @@ class TimelineController: TimelineControllerProtocol { return nil } + // MARK: - Sending + + func sendMessage(_ message: String, + html: String?, + inReplyToEventID: String?, + intentionalMentions: IntentionalMentions) async { + MXLog.info("Send message in \(roomID)") + + switch await activeTimeline.sendMessage(message, + html: html, + inReplyToEventID: inReplyToEventID, + intentionalMentions: intentionalMentions) { + case .success: + MXLog.info("Finished sending message") + await donateSendMessageIntent() + case .failure(let error): + MXLog.error("Failed sending message with error: \(error)") + } + } + + func sendVoiceMessage(url: URL, audioInfo: AudioInfo, waveform: [UInt16]) async -> Result { + await activeTimeline.sendVoiceMessage(url: url, + audioInfo: audioInfo, + waveform: waveform) { _ in } + } + // MARK: - Private /// The cancellable used to update the timeline items. @@ -529,6 +478,57 @@ class TimelineController: TimelineControllerProtocol { } return nil } + + private func donateSendMessageIntent() async { + guard let displayName = roomProxy.details.name ?? roomProxy.details.canonicalAlias, !displayName.isEmpty else { + MXLog.error("Failed donating send message intent, room missing name or alias.") + return + } + + let groupName = INSpeakableString(spokenPhrase: displayName) + + let sendMessageIntent = INSendMessageIntent(recipients: nil, + outgoingMessageType: .outgoingMessageText, + content: nil, + speakableGroupName: groupName, + conversationIdentifier: roomProxy.id, + serviceName: nil, + sender: nil, + attachments: nil) + + let avatarURL: URL? = switch roomProxy.details.avatar { + case .room(_, _, let avatarURL): + avatarURL + case .heroes(let userProfiles): + userProfiles.first?.avatarURL + case .tombstoned: + nil + } + + func addPlacehoder() { + if let imageData = Avatars.generatePlaceholderAvatarImageData(name: displayName, id: roomProxy.id, size: .init(width: 100, height: 100)) { + sendMessageIntent.setImage(INImage(imageData: imageData), forParameterNamed: \.speakableGroupName) + } + } + + if let avatarURL, let mediaSource = try? MediaSourceProxy(url: avatarURL, mimeType: nil) { + if case let .success(avatarData) = await mediaProvider.loadThumbnailForSource(source: mediaSource, size: .init(width: 100, height: 100)) { + sendMessageIntent.setImage(INImage(imageData: avatarData), forParameterNamed: \.speakableGroupName) + } else { + addPlacehoder() + } + } else { + addPlacehoder() + } + + let interaction = INInteraction(intent: sendMessageIntent, response: nil) + + do { + try await interaction.donate() + } catch { + MXLog.error("Failed donating send message intent with error: \(error)") + } + } } private extension TimelineItemProxy { diff --git a/ElementX/Sources/Services/Timeline/TimelineController/TimelineControllerProtocol.swift b/ElementX/Sources/Services/Timeline/TimelineController/TimelineControllerProtocol.swift index 9d6277aaf..ab6e3e102 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/TimelineControllerProtocol.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/TimelineControllerProtocol.swift @@ -63,13 +63,6 @@ protocol TimelineControllerProtocol { func sendReadReceipt(for itemID: TimelineItemIdentifier) async - func sendMessage(_ message: String, - html: String?, - inReplyToEventID: String?, - intentionalMentions: IntentionalMentions) async - - func sendVoiceMessage(url: URL, audioInfo: AudioInfo, waveform: [UInt16]) async -> Result - func edit(_ eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID, message: String, html: String?, @@ -97,4 +90,13 @@ protocol TimelineControllerProtocol { func sendHandle(for itemID: TimelineItemIdentifier) -> SendHandleProxy? func eventTimestamp(for itemID: TimelineItemIdentifier) -> Date? + + // MARK: - Sending + + func sendMessage(_ message: String, + html: String?, + inReplyToEventID: String?, + intentionalMentions: IntentionalMentions) async + + func sendVoiceMessage(url: URL, audioInfo: AudioInfo, waveform: [UInt16]) async -> Result }