mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 10:12:26 +00:00
Move message forwarding out of the flow coordinator. (#2738)
This commit is contained in:
@@ -303,8 +303,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
case (.emojiPicker, .dismissEmojiPicker):
|
case (.emojiPicker, .dismissEmojiPicker):
|
||||||
return .room
|
return .room
|
||||||
|
|
||||||
case (.room, .presentMessageForwarding(let itemID)):
|
case (.room, .presentMessageForwarding(let forwardingItem)):
|
||||||
return .messageForwarding(itemID: itemID)
|
return .messageForwarding(forwardingItem: forwardingItem)
|
||||||
case (.messageForwarding, .dismissMessageForwarding):
|
case (.messageForwarding, .dismissMessageForwarding):
|
||||||
return .room
|
return .room
|
||||||
|
|
||||||
@@ -435,8 +435,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
case (.emojiPicker, .dismissEmojiPicker, .room):
|
case (.emojiPicker, .dismissEmojiPicker, .room):
|
||||||
break
|
break
|
||||||
|
|
||||||
case (.room, .presentMessageForwarding(let itemID), .messageForwarding):
|
case (.room, .presentMessageForwarding(let forwardingItem), .messageForwarding):
|
||||||
presentMessageForwarding(for: itemID)
|
presentMessageForwarding(with: forwardingItem)
|
||||||
case (.messageForwarding, .dismissMessageForwarding, .room):
|
case (.messageForwarding, .dismissMessageForwarding, .room):
|
||||||
break
|
break
|
||||||
|
|
||||||
@@ -579,8 +579,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
stateMachine.tryEvent(.presentMapNavigator(interactionMode: .viewOnly(geoURI: geoURI, description: description)))
|
stateMachine.tryEvent(.presentMapNavigator(interactionMode: .viewOnly(geoURI: geoURI, description: description)))
|
||||||
case .presentRoomMemberDetails(userID: let userID):
|
case .presentRoomMemberDetails(userID: let userID):
|
||||||
stateMachine.tryEvent(.presentRoomMemberDetails(userID: userID))
|
stateMachine.tryEvent(.presentRoomMemberDetails(userID: userID))
|
||||||
case .presentMessageForwarding(let itemID):
|
case .presentMessageForwarding(let forwardingItem):
|
||||||
stateMachine.tryEvent(.presentMessageForwarding(itemID: itemID))
|
stateMachine.tryEvent(.presentMessageForwarding(forwardingItem: forwardingItem))
|
||||||
case .presentCallScreen:
|
case .presentCallScreen:
|
||||||
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy))
|
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy))
|
||||||
}
|
}
|
||||||
@@ -1100,16 +1100,18 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func presentMessageForwarding(for itemID: TimelineItemIdentifier) {
|
private func presentMessageForwarding(with forwardingItem: MessageForwardingItem) {
|
||||||
guard let roomSummaryProvider = userSession.clientProxy.alternateRoomSummaryProvider, let eventID = itemID.eventID else {
|
guard let roomSummaryProvider = userSession.clientProxy.alternateRoomSummaryProvider else {
|
||||||
fatalError()
|
fatalError()
|
||||||
}
|
}
|
||||||
|
|
||||||
let stackCoordinator = NavigationStackCoordinator()
|
let stackCoordinator = NavigationStackCoordinator()
|
||||||
|
|
||||||
let parameters = MessageForwardingScreenCoordinatorParameters(roomSummaryProvider: roomSummaryProvider,
|
let parameters = MessageForwardingScreenCoordinatorParameters(forwardingItem: forwardingItem,
|
||||||
|
clientProxy: userSession.clientProxy,
|
||||||
|
roomSummaryProvider: roomSummaryProvider,
|
||||||
mediaProvider: userSession.mediaProvider,
|
mediaProvider: userSession.mediaProvider,
|
||||||
sourceRoomID: roomProxy.id)
|
userIndicatorController: userIndicatorController)
|
||||||
let coordinator = MessageForwardingScreenCoordinator(parameters: parameters)
|
let coordinator = MessageForwardingScreenCoordinator(parameters: parameters)
|
||||||
|
|
||||||
coordinator.actions.sink { [weak self] action in
|
coordinator.actions.sink { [weak self] action in
|
||||||
@@ -1118,12 +1120,10 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
switch action {
|
switch action {
|
||||||
case .dismiss:
|
case .dismiss:
|
||||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||||
case .send(let roomID):
|
case .sent(let roomID):
|
||||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||||
|
// Timelines are cached - the local echo will be visible when fetching the room by its ID.
|
||||||
Task {
|
stateMachine.tryEvent(.startChildFlow(roomID: roomID, entryPoint: .room))
|
||||||
await self.forward(eventID: eventID, toRoomID: roomID)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.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() {
|
private func presentNotificationSettingsScreen() {
|
||||||
let parameters = RoomNotificationSettingsScreenCoordinatorParameters(notificationSettingsProxy: userSession.clientProxy.notificationSettings,
|
let parameters = RoomNotificationSettingsScreenCoordinatorParameters(notificationSettingsProxy: userSession.clientProxy.notificationSettings,
|
||||||
roomProxy: roomProxy,
|
roomProxy: roomProxy,
|
||||||
@@ -1356,7 +1332,7 @@ private extension RoomFlowCoordinator {
|
|||||||
case mediaUploadPreview(fileURL: URL)
|
case mediaUploadPreview(fileURL: URL)
|
||||||
case emojiPicker(itemID: TimelineItemIdentifier, selectedEmojis: Set<String>)
|
case emojiPicker(itemID: TimelineItemIdentifier, selectedEmojis: Set<String>)
|
||||||
case mapNavigator
|
case mapNavigator
|
||||||
case messageForwarding(itemID: TimelineItemIdentifier)
|
case messageForwarding(forwardingItem: MessageForwardingItem)
|
||||||
case reportContent(itemID: TimelineItemIdentifier, senderID: String)
|
case reportContent(itemID: TimelineItemIdentifier, senderID: String)
|
||||||
case pollForm
|
case pollForm
|
||||||
case pollsHistory
|
case pollsHistory
|
||||||
@@ -1419,7 +1395,7 @@ private extension RoomFlowCoordinator {
|
|||||||
case presentMapNavigator(interactionMode: StaticLocationInteractionMode)
|
case presentMapNavigator(interactionMode: StaticLocationInteractionMode)
|
||||||
case dismissMapNavigator
|
case dismissMapNavigator
|
||||||
|
|
||||||
case presentMessageForwarding(itemID: TimelineItemIdentifier)
|
case presentMessageForwarding(forwardingItem: MessageForwardingItem)
|
||||||
case dismissMessageForwarding
|
case dismissMessageForwarding
|
||||||
|
|
||||||
case presentPollForm(mode: PollFormMode)
|
case presentPollForm(mode: PollFormMode)
|
||||||
|
|||||||
@@ -40,14 +40,15 @@ struct RoomProxyMockConfiguration {
|
|||||||
var canUserJoinCall = true
|
var canUserJoinCall = true
|
||||||
|
|
||||||
func makeTimeline() -> TimelineProxyMock {
|
func makeTimeline() -> TimelineProxyMock {
|
||||||
let mock = TimelineProxyMock()
|
let timeline = TimelineProxyMock()
|
||||||
mock.underlyingActions = Empty(completeImmediately: false).eraseToAnyPublisher()
|
timeline.underlyingActions = Empty(completeImmediately: false).eraseToAnyPublisher()
|
||||||
|
timeline.sendMessageEventContentReturnValue = .success(())
|
||||||
|
|
||||||
let timelineProvider = RoomTimelineProviderMock()
|
let timelineProvider = RoomTimelineProviderMock()
|
||||||
timelineProvider.paginationState = .init(backward: timelineStartReached ? .timelineEndReached : .idle, forward: .timelineEndReached)
|
timelineProvider.paginationState = .init(backward: timelineStartReached ? .timelineEndReached : .idle, forward: .timelineEndReached)
|
||||||
timelineProvider.underlyingMembershipChangePublisher = PassthroughSubject().eraseToAnyPublisher()
|
timelineProvider.underlyingMembershipChangePublisher = PassthroughSubject().eraseToAnyPublisher()
|
||||||
mock.underlyingTimelineProvider = timelineProvider
|
timeline.underlyingTimelineProvider = timelineProvider
|
||||||
return mock
|
return timeline
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-7
@@ -18,14 +18,16 @@ import Combine
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct MessageForwardingScreenCoordinatorParameters {
|
struct MessageForwardingScreenCoordinatorParameters {
|
||||||
|
let forwardingItem: MessageForwardingItem
|
||||||
|
let clientProxy: ClientProxyProtocol
|
||||||
let roomSummaryProvider: RoomSummaryProviderProtocol
|
let roomSummaryProvider: RoomSummaryProviderProtocol
|
||||||
let mediaProvider: MediaProviderProtocol
|
let mediaProvider: MediaProviderProtocol
|
||||||
let sourceRoomID: String
|
let userIndicatorController: UserIndicatorControllerProtocol
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MessageForwardingScreenCoordinatorAction {
|
enum MessageForwardingScreenCoordinatorAction {
|
||||||
case dismiss
|
case dismiss
|
||||||
case send(roomID: String)
|
case sent(roomID: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
final class MessageForwardingScreenCoordinator: CoordinatorProtocol {
|
final class MessageForwardingScreenCoordinator: CoordinatorProtocol {
|
||||||
@@ -38,9 +40,11 @@ final class MessageForwardingScreenCoordinator: CoordinatorProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init(parameters: MessageForwardingScreenCoordinatorParameters) {
|
init(parameters: MessageForwardingScreenCoordinatorParameters) {
|
||||||
viewModel = MessageForwardingScreenViewModel(roomSummaryProvider: parameters.roomSummaryProvider,
|
viewModel = MessageForwardingScreenViewModel(forwardingItem: parameters.forwardingItem,
|
||||||
mediaProvider: parameters.mediaProvider,
|
clientProxy: parameters.clientProxy,
|
||||||
sourceRoomID: parameters.sourceRoomID)
|
roomSummaryProvider: parameters.roomSummaryProvider,
|
||||||
|
userIndicatorController: parameters.userIndicatorController,
|
||||||
|
mediaProvider: parameters.mediaProvider)
|
||||||
}
|
}
|
||||||
|
|
||||||
func start() {
|
func start() {
|
||||||
@@ -48,8 +52,8 @@ final class MessageForwardingScreenCoordinator: CoordinatorProtocol {
|
|||||||
switch action {
|
switch action {
|
||||||
case .dismiss:
|
case .dismiss:
|
||||||
self?.actionsSubject.send(.dismiss)
|
self?.actionsSubject.send(.dismiss)
|
||||||
case .send(let roomID):
|
case .sent(let roomID):
|
||||||
self?.actionsSubject.send(.send(roomID: roomID))
|
self?.actionsSubject.send(.sent(roomID: roomID))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|||||||
+20
-1
@@ -15,10 +15,11 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import MatrixRustSDK
|
||||||
|
|
||||||
enum MessageForwardingScreenViewModelAction {
|
enum MessageForwardingScreenViewModelAction {
|
||||||
case dismiss
|
case dismiss
|
||||||
case send(roomID: String)
|
case sent(roomID: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MessageForwardingScreenViewState: BindableState {
|
struct MessageForwardingScreenViewState: BindableState {
|
||||||
@@ -45,3 +46,21 @@ struct MessageForwardingRoom: Identifiable, Equatable {
|
|||||||
let alias: String?
|
let alias: String?
|
||||||
let avatarURL: URL?
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+37
-23
@@ -20,8 +20,10 @@ import SwiftUI
|
|||||||
typealias MessageForwardingScreenViewModelType = StateStoreViewModel<MessageForwardingScreenViewState, MessageForwardingScreenViewAction>
|
typealias MessageForwardingScreenViewModelType = StateStoreViewModel<MessageForwardingScreenViewState, MessageForwardingScreenViewAction>
|
||||||
|
|
||||||
class MessageForwardingScreenViewModel: MessageForwardingScreenViewModelType, MessageForwardingScreenViewModelProtocol {
|
class MessageForwardingScreenViewModel: MessageForwardingScreenViewModelType, MessageForwardingScreenViewModelProtocol {
|
||||||
private let roomSummaryProvider: RoomSummaryProviderProtocol?
|
private let forwardingItem: MessageForwardingItem
|
||||||
private let sourceRoomID: String
|
private let clientProxy: ClientProxyProtocol
|
||||||
|
private let roomSummaryProvider: RoomSummaryProviderProtocol
|
||||||
|
private let userIndicatorController: UserIndicatorControllerProtocol
|
||||||
|
|
||||||
private var actionsSubject: PassthroughSubject<MessageForwardingScreenViewModelAction, Never> = .init()
|
private var actionsSubject: PassthroughSubject<MessageForwardingScreenViewModelAction, Never> = .init()
|
||||||
|
|
||||||
@@ -29,11 +31,15 @@ class MessageForwardingScreenViewModel: MessageForwardingScreenViewModelType, Me
|
|||||||
actionsSubject.eraseToAnyPublisher()
|
actionsSubject.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
init(roomSummaryProvider: RoomSummaryProviderProtocol,
|
init(forwardingItem: MessageForwardingItem,
|
||||||
mediaProvider: MediaProviderProtocol,
|
clientProxy: ClientProxyProtocol,
|
||||||
sourceRoomID: String) {
|
roomSummaryProvider: RoomSummaryProviderProtocol,
|
||||||
|
userIndicatorController: UserIndicatorControllerProtocol,
|
||||||
|
mediaProvider: MediaProviderProtocol) {
|
||||||
|
self.forwardingItem = forwardingItem
|
||||||
|
self.clientProxy = clientProxy
|
||||||
self.roomSummaryProvider = roomSummaryProvider
|
self.roomSummaryProvider = roomSummaryProvider
|
||||||
self.sourceRoomID = sourceRoomID
|
self.userIndicatorController = userIndicatorController
|
||||||
|
|
||||||
super.init(initialViewState: MessageForwardingScreenViewState(), imageProvider: mediaProvider)
|
super.init(initialViewState: MessageForwardingScreenViewState(), imageProvider: mediaProvider)
|
||||||
|
|
||||||
@@ -49,7 +55,7 @@ class MessageForwardingScreenViewModel: MessageForwardingScreenViewModelType, Me
|
|||||||
.removeDuplicates()
|
.removeDuplicates()
|
||||||
.sink { [weak self] searchQuery in
|
.sink { [weak self] searchQuery in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
self.roomSummaryProvider?.setFilter(.search(query: searchQuery))
|
self.roomSummaryProvider.setFilter(.search(query: searchQuery))
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
@@ -60,13 +66,9 @@ class MessageForwardingScreenViewModel: MessageForwardingScreenViewModelType, Me
|
|||||||
switch viewAction {
|
switch viewAction {
|
||||||
case .cancel:
|
case .cancel:
|
||||||
actionsSubject.send(.dismiss)
|
actionsSubject.send(.dismiss)
|
||||||
roomSummaryProvider?.setFilter(.all(filters: []))
|
roomSummaryProvider.setFilter(.all(filters: []))
|
||||||
case .send:
|
case .send:
|
||||||
guard let roomID = state.selectedRoomID else {
|
Task { await forward() }
|
||||||
fatalError()
|
|
||||||
}
|
|
||||||
|
|
||||||
actionsSubject.send(.send(roomID: roomID))
|
|
||||||
case .selectRoom(let roomID):
|
case .selectRoom(let roomID):
|
||||||
state.selectedRoomID = roomID
|
state.selectedRoomID = roomID
|
||||||
case .reachedTop:
|
case .reachedTop:
|
||||||
@@ -79,11 +81,6 @@ class MessageForwardingScreenViewModel: MessageForwardingScreenViewModelType, Me
|
|||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
private func updateRooms() {
|
private func updateRooms() {
|
||||||
guard let roomSummaryProvider else {
|
|
||||||
MXLog.error("Room summary provider unavailable")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
MXLog.verbose("Updating rooms")
|
MXLog.verbose("Updating rooms")
|
||||||
|
|
||||||
var rooms = [MessageForwardingRoom]()
|
var rooms = [MessageForwardingRoom]()
|
||||||
@@ -93,7 +90,7 @@ class MessageForwardingScreenViewModel: MessageForwardingScreenViewModelType, Me
|
|||||||
case .empty, .invalidated:
|
case .empty, .invalidated:
|
||||||
continue
|
continue
|
||||||
case .filled(let details):
|
case .filled(let details):
|
||||||
if details.id == sourceRoomID {
|
if details.id == forwardingItem.roomID {
|
||||||
continue
|
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
|
/// we just need the respective bounds to be there to trigger a next page load or
|
||||||
/// a reset to just one page
|
/// a reset to just one page
|
||||||
private func updateVisibleRange(edge: UIRectEdge) {
|
private func updateVisibleRange(edge: UIRectEdge) {
|
||||||
guard let roomSummaryProvider else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch edge {
|
switch edge {
|
||||||
case .top:
|
case .top:
|
||||||
roomSummaryProvider.updateVisibleRange(0..<0)
|
roomSummaryProvider.updateVisibleRange(0..<0)
|
||||||
@@ -128,4 +121,25 @@ class MessageForwardingScreenViewModel: MessageForwardingScreenViewModelType, Me
|
|||||||
break
|
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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,9 +104,13 @@ private struct MessageForwardingListRow: View {
|
|||||||
struct MessageForwardingScreen_Previews: PreviewProvider, TestablePreview {
|
struct MessageForwardingScreen_Previews: PreviewProvider, TestablePreview {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
let summaryProvider = RoomSummaryProviderMock(.init(state: .loaded(.mockRooms)))
|
let summaryProvider = RoomSummaryProviderMock(.init(state: .loaded(.mockRooms)))
|
||||||
let viewModel = MessageForwardingScreenViewModel(roomSummaryProvider: summaryProvider,
|
let viewModel = MessageForwardingScreenViewModel(forwardingItem: .init(id: .init(timelineID: ""),
|
||||||
mediaProvider: MockMediaProvider(),
|
roomID: "",
|
||||||
sourceRoomID: "")
|
content: .init(noPointer: .init())),
|
||||||
|
clientProxy: ClientProxyMock(),
|
||||||
|
roomSummaryProvider: summaryProvider,
|
||||||
|
userIndicatorController: UserIndicatorControllerMock(),
|
||||||
|
mediaProvider: MockMediaProvider())
|
||||||
|
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
MessageForwardingScreen(context: viewModel.context)
|
MessageForwardingScreen(context: viewModel.context)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ enum RoomScreenCoordinatorAction {
|
|||||||
case presentLocationViewer(body: String, geoURI: GeoURI, description: String?)
|
case presentLocationViewer(body: String, geoURI: GeoURI, description: String?)
|
||||||
case presentEmojiPicker(itemID: TimelineItemIdentifier, selectedEmojis: Set<String>)
|
case presentEmojiPicker(itemID: TimelineItemIdentifier, selectedEmojis: Set<String>)
|
||||||
case presentRoomMemberDetails(userID: String)
|
case presentRoomMemberDetails(userID: String)
|
||||||
case presentMessageForwarding(itemID: TimelineItemIdentifier)
|
case presentMessageForwarding(forwardingItem: MessageForwardingItem)
|
||||||
case presentCallScreen
|
case presentCallScreen
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,8 +110,8 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
|
|||||||
actionsSubject.send(.presentMediaUploadPreviewScreen(url))
|
actionsSubject.send(.presentMediaUploadPreviewScreen(url))
|
||||||
case .displayRoomMemberDetails(userID: let userID):
|
case .displayRoomMemberDetails(userID: let userID):
|
||||||
actionsSubject.send(.presentRoomMemberDetails(userID: userID))
|
actionsSubject.send(.presentRoomMemberDetails(userID: userID))
|
||||||
case .displayMessageForwarding(let itemID):
|
case .displayMessageForwarding(let forwardingItem):
|
||||||
actionsSubject.send(.presentMessageForwarding(itemID: itemID))
|
actionsSubject.send(.presentMessageForwarding(forwardingItem: forwardingItem))
|
||||||
case .displayLocation(let body, let geoURI, let description):
|
case .displayLocation(let body, let geoURI, let description):
|
||||||
actionsSubject.send(.presentLocationViewer(body: body, geoURI: geoURI, description: description))
|
actionsSubject.send(.presentLocationViewer(body: body, geoURI: geoURI, description: description))
|
||||||
case .composer(let action):
|
case .composer(let action):
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
import Combine
|
import Combine
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import UIKit
|
|
||||||
|
|
||||||
import OrderedCollections
|
import OrderedCollections
|
||||||
|
|
||||||
@@ -31,7 +30,7 @@ enum RoomScreenViewModelAction {
|
|||||||
case displayPollForm(mode: PollFormMode)
|
case displayPollForm(mode: PollFormMode)
|
||||||
case displayMediaUploadPreviewScreen(url: URL)
|
case displayMediaUploadPreviewScreen(url: URL)
|
||||||
case displayRoomMemberDetails(userID: String)
|
case displayRoomMemberDetails(userID: String)
|
||||||
case displayMessageForwarding(itemID: TimelineItemIdentifier)
|
case displayMessageForwarding(forwardingItem: MessageForwardingItem)
|
||||||
case displayLocation(body: String, geoURI: GeoURI, description: String?)
|
case displayLocation(body: String, geoURI: GeoURI, description: String?)
|
||||||
case composer(action: RoomScreenComposerAction)
|
case composer(action: RoomScreenComposerAction)
|
||||||
case displayCallScreen
|
case displayCallScreen
|
||||||
|
|||||||
@@ -429,7 +429,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
|||||||
case .displayEmojiPicker(let itemID, let selectedEmojis):
|
case .displayEmojiPicker(let itemID, let selectedEmojis):
|
||||||
actionsSubject.send(.displayEmojiPicker(itemID: itemID, selectedEmojis: selectedEmojis))
|
actionsSubject.send(.displayEmojiPicker(itemID: itemID, selectedEmojis: selectedEmojis))
|
||||||
case .displayMessageForwarding(let itemID):
|
case .displayMessageForwarding(let itemID):
|
||||||
actionsSubject.send(.displayMessageForwarding(itemID: itemID))
|
Task { await self.forwardMessage(itemID: itemID) }
|
||||||
case .displayPollForm(let mode):
|
case .displayPollForm(let mode):
|
||||||
actionsSubject.send(.displayPollForm(mode: mode))
|
actionsSubject.send(.displayPollForm(mode: mode))
|
||||||
case .displayReportContent(let itemID, let senderID):
|
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)
|
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
|
// MARK: - User Indicators
|
||||||
|
|
||||||
private func showFocusLoadingIndicator() {
|
private func showFocusLoadingIndicator() {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
import Combine
|
import Combine
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import MatrixRustSDK
|
||||||
|
|
||||||
class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
||||||
/// An array of timeline item arrays that will be inserted in order for each back pagination request.
|
/// 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 redact(_ itemID: TimelineItemIdentifier) async { }
|
||||||
|
|
||||||
|
func messageEventContent(for itemID: TimelineItemIdentifier) -> RoomMessageEventContentWithoutRelation? {
|
||||||
|
.init(noPointer: .init())
|
||||||
|
}
|
||||||
|
|
||||||
func debugInfo(for itemID: TimelineItemIdentifier) -> TimelineItemDebugInfo {
|
func debugInfo(for itemID: TimelineItemIdentifier) -> TimelineItemDebugInfo {
|
||||||
.init(model: "Mock debug description", originalJSON: nil, latestEditJSON: nil)
|
.init(model: "Mock debug description", originalJSON: nil, latestEditJSON: nil)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
// Handle this parallel to the timeline items so we're not forced
|
||||||
// to bundle the Rust side objects within them
|
// to bundle the Rust side objects within them
|
||||||
func debugInfo(for itemID: TimelineItemIdentifier) -> TimelineItemDebugInfo {
|
func debugInfo(for itemID: TimelineItemIdentifier) -> TimelineItemDebugInfo {
|
||||||
|
|||||||
+4
-2
@@ -15,8 +15,8 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Combine
|
import Combine
|
||||||
import Foundation
|
import MatrixRustSDK
|
||||||
import UIKit
|
import SwiftUI
|
||||||
|
|
||||||
enum RoomTimelineControllerCallback {
|
enum RoomTimelineControllerCallback {
|
||||||
case updatedTimelineItems(timelineItems: [RoomTimelineItemProtocol], isSwitchingTimelines: Bool)
|
case updatedTimelineItems(timelineItems: [RoomTimelineItemProtocol], isSwitchingTimelines: Bool)
|
||||||
@@ -68,6 +68,8 @@ protocol RoomTimelineControllerProtocol {
|
|||||||
|
|
||||||
func redact(_ itemID: TimelineItemIdentifier) async
|
func redact(_ itemID: TimelineItemIdentifier) async
|
||||||
|
|
||||||
|
func messageEventContent(for itemID: TimelineItemIdentifier) async -> RoomMessageEventContentWithoutRelation?
|
||||||
|
|
||||||
func debugInfo(for itemID: TimelineItemIdentifier) -> TimelineItemDebugInfo
|
func debugInfo(for itemID: TimelineItemIdentifier) -> TimelineItemDebugInfo
|
||||||
|
|
||||||
func retryDecryption(for sessionID: String) async
|
func retryDecryption(for sessionID: String) async
|
||||||
|
|||||||
@@ -21,20 +21,29 @@ import XCTest
|
|||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
class MessageForwardingScreenViewModelTests: XCTestCase {
|
class MessageForwardingScreenViewModelTests: XCTestCase {
|
||||||
|
let forwardingItem = MessageForwardingItem(id: .init(timelineID: "t1", eventID: "t1"),
|
||||||
|
roomID: "1",
|
||||||
|
content: .init(noPointer: .init()))
|
||||||
var viewModel: MessageForwardingScreenViewModelProtocol!
|
var viewModel: MessageForwardingScreenViewModelProtocol!
|
||||||
var context: MessageForwardingScreenViewModelType.Context!
|
var context: MessageForwardingScreenViewModelType.Context!
|
||||||
var cancellables = Set<AnyCancellable>()
|
var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
override func setUpWithError() throws {
|
override func setUpWithError() throws {
|
||||||
cancellables.removeAll()
|
cancellables.removeAll()
|
||||||
viewModel = MessageForwardingScreenViewModel(roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))),
|
|
||||||
mediaProvider: MockMediaProvider(),
|
let clientProxy = ClientProxyMock(.init())
|
||||||
sourceRoomID: "1")
|
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
|
context = viewModel.context
|
||||||
}
|
}
|
||||||
|
|
||||||
func testInitialState() {
|
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() {
|
func testRoomSelection() {
|
||||||
@@ -61,7 +70,7 @@ class MessageForwardingScreenViewModelTests: XCTestCase {
|
|||||||
viewModel.actions
|
viewModel.actions
|
||||||
.sink { action in
|
.sink { action in
|
||||||
switch action {
|
switch action {
|
||||||
case .send(let roomID):
|
case .sent(let roomID):
|
||||||
XCTAssertEqual(roomID, "2")
|
XCTAssertEqual(roomID, "2")
|
||||||
expectation.fulfill()
|
expectation.fulfill()
|
||||||
default:
|
default:
|
||||||
|
|||||||
Reference in New Issue
Block a user