From e829a3ded6fdde48d64b34adcc31e118adcf2f69 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Tue, 7 May 2024 15:04:11 +0100 Subject: [PATCH] Move message forwarding out of the flow coordinator. (#2738) --- .../RoomFlowCoordinator.swift | 58 ++++++------------ ElementX/Sources/Mocks/RoomProxyMock.swift | 9 +-- .../MessageForwardingScreenCoordinator.swift | 18 +++--- .../MessageForwardingScreenModels.swift | 21 ++++++- .../MessageForwardingScreenViewModel.swift | 60 ++++++++++++------- .../View/MessageForwardingScreen.swift | 10 +++- .../RoomScreen/RoomScreenCoordinator.swift | 6 +- .../Screens/RoomScreen/RoomScreenModels.swift | 3 +- .../RoomScreen/RoomScreenViewModel.swift | 9 ++- .../MockRoomTimelineController.swift | 5 ++ .../RoomTimelineController.swift | 8 +++ .../RoomTimelineControllerProtocol.swift | 6 +- ...essageForwardingScreenViewModelTests.swift | 19 ++++-- 13 files changed, 140 insertions(+), 92 deletions(-) diff --git a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift index f8ffb82e7..d383aad2d 100644 --- a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift @@ -303,8 +303,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { case (.emojiPicker, .dismissEmojiPicker): return .room - case (.room, .presentMessageForwarding(let itemID)): - return .messageForwarding(itemID: itemID) + case (.room, .presentMessageForwarding(let forwardingItem)): + return .messageForwarding(forwardingItem: forwardingItem) case (.messageForwarding, .dismissMessageForwarding): return .room @@ -435,8 +435,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { case (.emojiPicker, .dismissEmojiPicker, .room): break - case (.room, .presentMessageForwarding(let itemID), .messageForwarding): - presentMessageForwarding(for: itemID) + case (.room, .presentMessageForwarding(let forwardingItem), .messageForwarding): + presentMessageForwarding(with: forwardingItem) case (.messageForwarding, .dismissMessageForwarding, .room): break @@ -579,8 +579,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { stateMachine.tryEvent(.presentMapNavigator(interactionMode: .viewOnly(geoURI: geoURI, description: description))) case .presentRoomMemberDetails(userID: let userID): stateMachine.tryEvent(.presentRoomMemberDetails(userID: userID)) - case .presentMessageForwarding(let itemID): - stateMachine.tryEvent(.presentMessageForwarding(itemID: itemID)) + case .presentMessageForwarding(let forwardingItem): + stateMachine.tryEvent(.presentMessageForwarding(forwardingItem: forwardingItem)) case .presentCallScreen: actionsSubject.send(.presentCallScreen(roomProxy: roomProxy)) } @@ -1100,16 +1100,18 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { } } - private func presentMessageForwarding(for itemID: TimelineItemIdentifier) { - guard let roomSummaryProvider = userSession.clientProxy.alternateRoomSummaryProvider, let eventID = itemID.eventID else { + private func presentMessageForwarding(with forwardingItem: MessageForwardingItem) { + guard let roomSummaryProvider = userSession.clientProxy.alternateRoomSummaryProvider else { fatalError() } let stackCoordinator = NavigationStackCoordinator() - let parameters = MessageForwardingScreenCoordinatorParameters(roomSummaryProvider: roomSummaryProvider, + let parameters = MessageForwardingScreenCoordinatorParameters(forwardingItem: forwardingItem, + clientProxy: userSession.clientProxy, + roomSummaryProvider: roomSummaryProvider, mediaProvider: userSession.mediaProvider, - sourceRoomID: roomProxy.id) + userIndicatorController: userIndicatorController) let coordinator = MessageForwardingScreenCoordinator(parameters: parameters) coordinator.actions.sink { [weak self] action in @@ -1118,12 +1120,10 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { switch action { case .dismiss: navigationStackCoordinator.setSheetCoordinator(nil) - case .send(let roomID): + case .sent(let roomID): navigationStackCoordinator.setSheetCoordinator(nil) - - Task { - await self.forward(eventID: eventID, toRoomID: roomID) - } + // Timelines are cached - the local echo will be visible when fetching the room by its ID. + stateMachine.tryEvent(.startChildFlow(roomID: roomID, entryPoint: .room)) } } .store(in: &cancellables) @@ -1135,30 +1135,6 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { } } - private func forward(eventID: String, toRoomID roomID: String) async { - guard let messageEventContent = await roomProxy.timeline.messageEventContent(for: eventID) else { - MXLog.error("Failed retrieving forwarded message event content for eventID: \(eventID)") - userIndicatorController.submitIndicator(UserIndicator(title: L10n.errorUnknown)) - return - } - - guard let targetRoomProxy = await userSession.clientProxy.roomForIdentifier(roomID) else { - MXLog.error("Failed retrieving room to forward to with id: \(roomID)") - userIndicatorController.submitIndicator(UserIndicator(title: L10n.errorUnknown)) - return - } - - if case .failure(let error) = await targetRoomProxy.timeline.sendMessageEventContent(messageEventContent) { - MXLog.error("Failed forwarding message with error: \(error)") - userIndicatorController.submitIndicator(UserIndicator(title: L10n.errorUnknown)) - return - } - - // We don't need to worry about passing in the room proxy as timelines are - // cached. The local echo will be visible when fetching the room by its ID. - stateMachine.tryEvent(.startChildFlow(roomID: roomID, entryPoint: .room)) - } - private func presentNotificationSettingsScreen() { let parameters = RoomNotificationSettingsScreenCoordinatorParameters(notificationSettingsProxy: userSession.clientProxy.notificationSettings, roomProxy: roomProxy, @@ -1356,7 +1332,7 @@ private extension RoomFlowCoordinator { case mediaUploadPreview(fileURL: URL) case emojiPicker(itemID: TimelineItemIdentifier, selectedEmojis: Set) case mapNavigator - case messageForwarding(itemID: TimelineItemIdentifier) + case messageForwarding(forwardingItem: MessageForwardingItem) case reportContent(itemID: TimelineItemIdentifier, senderID: String) case pollForm case pollsHistory @@ -1419,7 +1395,7 @@ private extension RoomFlowCoordinator { case presentMapNavigator(interactionMode: StaticLocationInteractionMode) case dismissMapNavigator - case presentMessageForwarding(itemID: TimelineItemIdentifier) + case presentMessageForwarding(forwardingItem: MessageForwardingItem) case dismissMessageForwarding case presentPollForm(mode: PollFormMode) diff --git a/ElementX/Sources/Mocks/RoomProxyMock.swift b/ElementX/Sources/Mocks/RoomProxyMock.swift index 9de3957f4..dd78d7b6a 100644 --- a/ElementX/Sources/Mocks/RoomProxyMock.swift +++ b/ElementX/Sources/Mocks/RoomProxyMock.swift @@ -40,14 +40,15 @@ struct RoomProxyMockConfiguration { var canUserJoinCall = true func makeTimeline() -> TimelineProxyMock { - let mock = TimelineProxyMock() - mock.underlyingActions = Empty(completeImmediately: false).eraseToAnyPublisher() + let timeline = TimelineProxyMock() + timeline.underlyingActions = Empty(completeImmediately: false).eraseToAnyPublisher() + timeline.sendMessageEventContentReturnValue = .success(()) let timelineProvider = RoomTimelineProviderMock() timelineProvider.paginationState = .init(backward: timelineStartReached ? .timelineEndReached : .idle, forward: .timelineEndReached) timelineProvider.underlyingMembershipChangePublisher = PassthroughSubject().eraseToAnyPublisher() - mock.underlyingTimelineProvider = timelineProvider - return mock + timeline.underlyingTimelineProvider = timelineProvider + return timeline } } diff --git a/ElementX/Sources/Screens/MessageForwardingScreen/MessageForwardingScreenCoordinator.swift b/ElementX/Sources/Screens/MessageForwardingScreen/MessageForwardingScreenCoordinator.swift index 8dbb2ae12..be1d635f2 100644 --- a/ElementX/Sources/Screens/MessageForwardingScreen/MessageForwardingScreenCoordinator.swift +++ b/ElementX/Sources/Screens/MessageForwardingScreen/MessageForwardingScreenCoordinator.swift @@ -18,14 +18,16 @@ import Combine import SwiftUI struct MessageForwardingScreenCoordinatorParameters { + let forwardingItem: MessageForwardingItem + let clientProxy: ClientProxyProtocol let roomSummaryProvider: RoomSummaryProviderProtocol let mediaProvider: MediaProviderProtocol - let sourceRoomID: String + let userIndicatorController: UserIndicatorControllerProtocol } enum MessageForwardingScreenCoordinatorAction { case dismiss - case send(roomID: String) + case sent(roomID: String) } final class MessageForwardingScreenCoordinator: CoordinatorProtocol { @@ -38,9 +40,11 @@ final class MessageForwardingScreenCoordinator: CoordinatorProtocol { } init(parameters: MessageForwardingScreenCoordinatorParameters) { - viewModel = MessageForwardingScreenViewModel(roomSummaryProvider: parameters.roomSummaryProvider, - mediaProvider: parameters.mediaProvider, - sourceRoomID: parameters.sourceRoomID) + viewModel = MessageForwardingScreenViewModel(forwardingItem: parameters.forwardingItem, + clientProxy: parameters.clientProxy, + roomSummaryProvider: parameters.roomSummaryProvider, + userIndicatorController: parameters.userIndicatorController, + mediaProvider: parameters.mediaProvider) } func start() { @@ -48,8 +52,8 @@ final class MessageForwardingScreenCoordinator: CoordinatorProtocol { switch action { case .dismiss: self?.actionsSubject.send(.dismiss) - case .send(let roomID): - self?.actionsSubject.send(.send(roomID: roomID)) + case .sent(let roomID): + self?.actionsSubject.send(.sent(roomID: roomID)) } } .store(in: &cancellables) diff --git a/ElementX/Sources/Screens/MessageForwardingScreen/MessageForwardingScreenModels.swift b/ElementX/Sources/Screens/MessageForwardingScreen/MessageForwardingScreenModels.swift index bd67b5d25..246860a1f 100644 --- a/ElementX/Sources/Screens/MessageForwardingScreen/MessageForwardingScreenModels.swift +++ b/ElementX/Sources/Screens/MessageForwardingScreen/MessageForwardingScreenModels.swift @@ -15,10 +15,11 @@ // import Foundation +import MatrixRustSDK enum MessageForwardingScreenViewModelAction { case dismiss - case send(roomID: String) + case sent(roomID: String) } struct MessageForwardingScreenViewState: BindableState { @@ -45,3 +46,21 @@ struct MessageForwardingRoom: Identifiable, Equatable { let alias: String? let avatarURL: URL? } + +struct MessageForwardingItem: Hashable { + /// The source item's timeline ID. Only necessary for a rough Hashable conformance. + let id: TimelineItemIdentifier + /// The source item's room ID. + let roomID: String + /// The item's content to be forwarded. + let content: RoomMessageEventContentWithoutRelation + + static func == (lhs: MessageForwardingItem, rhs: MessageForwardingItem) -> Bool { + lhs.id == rhs.id && lhs.roomID == rhs.roomID + } + + func hash(into hasher: inout Hasher) { + hasher.combine(id) + hasher.combine(roomID) + } +} diff --git a/ElementX/Sources/Screens/MessageForwardingScreen/MessageForwardingScreenViewModel.swift b/ElementX/Sources/Screens/MessageForwardingScreen/MessageForwardingScreenViewModel.swift index e3c06d72c..0be407e9e 100644 --- a/ElementX/Sources/Screens/MessageForwardingScreen/MessageForwardingScreenViewModel.swift +++ b/ElementX/Sources/Screens/MessageForwardingScreen/MessageForwardingScreenViewModel.swift @@ -20,8 +20,10 @@ import SwiftUI typealias MessageForwardingScreenViewModelType = StateStoreViewModel class MessageForwardingScreenViewModel: MessageForwardingScreenViewModelType, MessageForwardingScreenViewModelProtocol { - private let roomSummaryProvider: RoomSummaryProviderProtocol? - private let sourceRoomID: String + private let forwardingItem: MessageForwardingItem + private let clientProxy: ClientProxyProtocol + private let roomSummaryProvider: RoomSummaryProviderProtocol + private let userIndicatorController: UserIndicatorControllerProtocol private var actionsSubject: PassthroughSubject = .init() @@ -29,11 +31,15 @@ class MessageForwardingScreenViewModel: MessageForwardingScreenViewModelType, Me actionsSubject.eraseToAnyPublisher() } - init(roomSummaryProvider: RoomSummaryProviderProtocol, - mediaProvider: MediaProviderProtocol, - sourceRoomID: String) { + init(forwardingItem: MessageForwardingItem, + clientProxy: ClientProxyProtocol, + roomSummaryProvider: RoomSummaryProviderProtocol, + userIndicatorController: UserIndicatorControllerProtocol, + mediaProvider: MediaProviderProtocol) { + self.forwardingItem = forwardingItem + self.clientProxy = clientProxy self.roomSummaryProvider = roomSummaryProvider - self.sourceRoomID = sourceRoomID + self.userIndicatorController = userIndicatorController super.init(initialViewState: MessageForwardingScreenViewState(), imageProvider: mediaProvider) @@ -49,7 +55,7 @@ class MessageForwardingScreenViewModel: MessageForwardingScreenViewModelType, Me .removeDuplicates() .sink { [weak self] searchQuery in guard let self else { return } - self.roomSummaryProvider?.setFilter(.search(query: searchQuery)) + self.roomSummaryProvider.setFilter(.search(query: searchQuery)) } .store(in: &cancellables) @@ -60,13 +66,9 @@ class MessageForwardingScreenViewModel: MessageForwardingScreenViewModelType, Me switch viewAction { case .cancel: actionsSubject.send(.dismiss) - roomSummaryProvider?.setFilter(.all(filters: [])) + roomSummaryProvider.setFilter(.all(filters: [])) case .send: - guard let roomID = state.selectedRoomID else { - fatalError() - } - - actionsSubject.send(.send(roomID: roomID)) + Task { await forward() } case .selectRoom(let roomID): state.selectedRoomID = roomID case .reachedTop: @@ -79,11 +81,6 @@ class MessageForwardingScreenViewModel: MessageForwardingScreenViewModelType, Me // MARK: - Private private func updateRooms() { - guard let roomSummaryProvider else { - MXLog.error("Room summary provider unavailable") - return - } - MXLog.verbose("Updating rooms") var rooms = [MessageForwardingRoom]() @@ -93,7 +90,7 @@ class MessageForwardingScreenViewModel: MessageForwardingScreenViewModelType, Me case .empty, .invalidated: continue case .filled(let details): - if details.id == sourceRoomID { + if details.id == forwardingItem.roomID { continue } @@ -114,10 +111,6 @@ class MessageForwardingScreenViewModel: MessageForwardingScreenViewModelType, Me /// we just need the respective bounds to be there to trigger a next page load or /// a reset to just one page private func updateVisibleRange(edge: UIRectEdge) { - guard let roomSummaryProvider else { - return - } - switch edge { case .top: roomSummaryProvider.updateVisibleRange(0..<0) @@ -128,4 +121,25 @@ class MessageForwardingScreenViewModel: MessageForwardingScreenViewModelType, Me break } } + + private func forward() async { + guard let roomID = state.selectedRoomID else { + fatalError() + } + + guard let targetRoomProxy = await clientProxy.roomForIdentifier(roomID) else { + MXLog.error("Failed retrieving room to forward to with id: \(roomID)") + userIndicatorController.submitIndicator(UserIndicator(title: L10n.errorUnknown)) + return + } + + if case .failure(let error) = await targetRoomProxy.timeline.sendMessageEventContent(forwardingItem.content) { + MXLog.error("Failed forwarding message with error: \(error)") + userIndicatorController.submitIndicator(UserIndicator(title: L10n.errorUnknown)) + return + } + + // Timelines are cached - the local echo will be visible when fetching the room by its ID. + actionsSubject.send(.sent(roomID: roomID)) + } } diff --git a/ElementX/Sources/Screens/MessageForwardingScreen/View/MessageForwardingScreen.swift b/ElementX/Sources/Screens/MessageForwardingScreen/View/MessageForwardingScreen.swift index c594a930b..0205816bb 100644 --- a/ElementX/Sources/Screens/MessageForwardingScreen/View/MessageForwardingScreen.swift +++ b/ElementX/Sources/Screens/MessageForwardingScreen/View/MessageForwardingScreen.swift @@ -104,9 +104,13 @@ private struct MessageForwardingListRow: View { struct MessageForwardingScreen_Previews: PreviewProvider, TestablePreview { static var previews: some View { let summaryProvider = RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))) - let viewModel = MessageForwardingScreenViewModel(roomSummaryProvider: summaryProvider, - mediaProvider: MockMediaProvider(), - sourceRoomID: "") + let viewModel = MessageForwardingScreenViewModel(forwardingItem: .init(id: .init(timelineID: ""), + roomID: "", + content: .init(noPointer: .init())), + clientProxy: ClientProxyMock(), + roomSummaryProvider: summaryProvider, + userIndicatorController: UserIndicatorControllerMock(), + mediaProvider: MockMediaProvider()) NavigationStack { MessageForwardingScreen(context: viewModel.context) diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift index faf38faf2..0ee9183f0 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift @@ -42,7 +42,7 @@ enum RoomScreenCoordinatorAction { case presentLocationViewer(body: String, geoURI: GeoURI, description: String?) case presentEmojiPicker(itemID: TimelineItemIdentifier, selectedEmojis: Set) case presentRoomMemberDetails(userID: String) - case presentMessageForwarding(itemID: TimelineItemIdentifier) + case presentMessageForwarding(forwardingItem: MessageForwardingItem) case presentCallScreen } @@ -110,8 +110,8 @@ final class RoomScreenCoordinator: CoordinatorProtocol { actionsSubject.send(.presentMediaUploadPreviewScreen(url)) case .displayRoomMemberDetails(userID: let userID): actionsSubject.send(.presentRoomMemberDetails(userID: userID)) - case .displayMessageForwarding(let itemID): - actionsSubject.send(.presentMessageForwarding(itemID: itemID)) + case .displayMessageForwarding(let forwardingItem): + actionsSubject.send(.presentMessageForwarding(forwardingItem: forwardingItem)) case .displayLocation(let body, let geoURI, let description): actionsSubject.send(.presentLocationViewer(body: body, geoURI: geoURI, description: description)) case .composer(let action): diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift index 87da91e6b..a867f2c79 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift @@ -16,7 +16,6 @@ import Combine import SwiftUI -import UIKit import OrderedCollections @@ -31,7 +30,7 @@ enum RoomScreenViewModelAction { case displayPollForm(mode: PollFormMode) case displayMediaUploadPreviewScreen(url: URL) case displayRoomMemberDetails(userID: String) - case displayMessageForwarding(itemID: TimelineItemIdentifier) + case displayMessageForwarding(forwardingItem: MessageForwardingItem) case displayLocation(body: String, geoURI: GeoURI, description: String?) case composer(action: RoomScreenComposerAction) case displayCallScreen diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift index ca2edd74d..162f79d72 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift @@ -429,7 +429,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol case .displayEmojiPicker(let itemID, let selectedEmojis): actionsSubject.send(.displayEmojiPicker(itemID: itemID, selectedEmojis: selectedEmojis)) case .displayMessageForwarding(let itemID): - actionsSubject.send(.displayMessageForwarding(itemID: itemID)) + Task { await self.forwardMessage(itemID: itemID) } case .displayPollForm(let mode): actionsSubject.send(.displayPollForm(mode: mode)) case .displayReportContent(let itemID, let senderID): @@ -738,6 +738,13 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol state.bindings.readReceiptsSummaryInfo = .init(orderedReceipts: eventTimelineItem.properties.orderedReadReceipts, id: eventTimelineItem.id) } + // MARK: - Message forwarding + + private func forwardMessage(itemID: TimelineItemIdentifier) async { + guard let content = await timelineController.messageEventContent(for: itemID) else { return } + actionsSubject.send(.displayMessageForwarding(forwardingItem: .init(id: itemID, roomID: roomProxy.id, content: content))) + } + // MARK: - User Indicators private func showFocusLoadingIndicator() { diff --git a/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift b/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift index 51ed918f4..589945102 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift @@ -18,6 +18,7 @@ import Combine import Foundation +import MatrixRustSDK class MockRoomTimelineController: RoomTimelineControllerProtocol { /// An array of timeline item arrays that will be inserted in order for each back pagination request. @@ -99,6 +100,10 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol { func redact(_ itemID: TimelineItemIdentifier) async { } + func messageEventContent(for itemID: TimelineItemIdentifier) -> RoomMessageEventContentWithoutRelation? { + .init(noPointer: .init()) + } + func debugInfo(for itemID: TimelineItemIdentifier) -> TimelineItemDebugInfo { .init(model: "Mock debug description", originalJSON: nil, latestEditJSON: nil) } diff --git a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift index b46d6c01a..4d532748a 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift @@ -227,6 +227,14 @@ class RoomTimelineController: RoomTimelineControllerProtocol { } } + func messageEventContent(for itemID: TimelineItemIdentifier) async -> RoomMessageEventContentWithoutRelation? { + guard let eventID = itemID.eventID else { + MXLog.warning("The item doesn't have an event ID.") + return nil + } + return await activeTimeline.messageEventContent(for: eventID) + } + // Handle this parallel to the timeline items so we're not forced // to bundle the Rust side objects within them func debugInfo(for itemID: TimelineItemIdentifier) -> TimelineItemDebugInfo { diff --git a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift index d8f109c69..c063a0ddf 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift @@ -15,8 +15,8 @@ // import Combine -import Foundation -import UIKit +import MatrixRustSDK +import SwiftUI enum RoomTimelineControllerCallback { case updatedTimelineItems(timelineItems: [RoomTimelineItemProtocol], isSwitchingTimelines: Bool) @@ -68,6 +68,8 @@ protocol RoomTimelineControllerProtocol { func redact(_ itemID: TimelineItemIdentifier) async + func messageEventContent(for itemID: TimelineItemIdentifier) async -> RoomMessageEventContentWithoutRelation? + func debugInfo(for itemID: TimelineItemIdentifier) -> TimelineItemDebugInfo func retryDecryption(for sessionID: String) async diff --git a/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift b/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift index 6372b8ad0..00f29f0ac 100644 --- a/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift +++ b/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift @@ -21,20 +21,29 @@ import XCTest @MainActor class MessageForwardingScreenViewModelTests: XCTestCase { + let forwardingItem = MessageForwardingItem(id: .init(timelineID: "t1", eventID: "t1"), + roomID: "1", + content: .init(noPointer: .init())) var viewModel: MessageForwardingScreenViewModelProtocol! var context: MessageForwardingScreenViewModelType.Context! var cancellables = Set() override func setUpWithError() throws { cancellables.removeAll() - viewModel = MessageForwardingScreenViewModel(roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))), - mediaProvider: MockMediaProvider(), - sourceRoomID: "1") + + let clientProxy = ClientProxyMock(.init()) + clientProxy.roomForIdentifierClosure = { RoomProxyMock(with: .init(id: $0)) } + + viewModel = MessageForwardingScreenViewModel(forwardingItem: forwardingItem, + clientProxy: clientProxy, + roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))), + userIndicatorController: UserIndicatorControllerMock(), + mediaProvider: MockMediaProvider()) context = viewModel.context } func testInitialState() { - XCTAssertNil(context.viewState.rooms.first(where: { $0.id == "1" }), "The source room ID shouldn't be shown") + XCTAssertNil(context.viewState.rooms.first(where: { $0.id == forwardingItem.roomID }), "The source room ID shouldn't be shown") } func testRoomSelection() { @@ -61,7 +70,7 @@ class MessageForwardingScreenViewModelTests: XCTestCase { viewModel.actions .sink { action in switch action { - case .send(let roomID): + case .sent(let roomID): XCTAssertEqual(roomID, "2") expectation.fulfill() default: