Setup action forwarding for threaded timelines.

This commit is contained in:
Stefan Ceriu
2025-05-28 15:13:00 +03:00
committed by Stefan Ceriu
parent f789bdcaa9
commit 210a0dba1a
3 changed files with 81 additions and 27 deletions
@@ -830,6 +830,34 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
emojiProvider: emojiProvider, emojiProvider: emojiProvider,
timelineControllerFactory: timelineControllerFactory, timelineControllerFactory: timelineControllerFactory,
clientProxy: userSession.clientProxy)) clientProxy: userSession.clientProxy))
coordinator.actions.sink { [weak self] action in
guard let self else { return }
switch action {
case .presentReportContent(let itemID, let senderID):
stateMachine.tryEvent(.presentReportContent(itemID: itemID, senderID: senderID))
case .presentMediaUploadPicker(let source):
stateMachine.tryEvent(.presentMediaUploadPicker(source: source))
case .presentMediaUploadPreviewScreen(let url):
stateMachine.tryEvent(.presentMediaUploadPreview(fileURL: url))
case .presentLocationPicker:
stateMachine.tryEvent(.presentMapNavigator(interactionMode: .picker))
case .presentPollForm(let mode):
stateMachine.tryEvent(.presentPollForm(mode: mode))
case .presentLocationViewer(_, let geoURI, let description):
stateMachine.tryEvent(.presentMapNavigator(interactionMode: .viewOnly(geoURI: geoURI, description: description)))
case .presentEmojiPicker(let itemID, let selectedEmojis):
stateMachine.tryEvent(.presentEmojiPicker(itemID: itemID, selectedEmojis: selectedEmojis))
case .presentRoomMemberDetails(let userID):
stateMachine.tryEvent(.presentRoomMemberDetails(userID: userID))
case .presentMessageForwarding(let forwardingItem):
stateMachine.tryEvent(.presentMessageForwarding(forwardingItem: forwardingItem))
case .presentResolveSendFailure(let failure, let sendHandle):
stateMachine.tryEvent(.presentResolveSendFailure(failure: failure, sendHandle: sendHandle))
}
}
.store(in: &cancellables)
navigationStackCoordinator.push(coordinator) { [weak self] in navigationStackCoordinator.push(coordinator) { [weak self] in
self?.stateMachine.tryEvent(.dismissThread) self?.stateMachine.tryEvent(.dismissThread)
@@ -21,11 +21,16 @@ struct ThreadTimelineScreenCoordinatorParameters {
} }
enum ThreadTimelineScreenCoordinatorAction { enum ThreadTimelineScreenCoordinatorAction {
case dismiss case presentReportContent(itemID: TimelineItemIdentifier, senderID: String)
case displayUser(userID: String) case presentMediaUploadPicker(MediaPickerScreenSource)
case presentLocationViewer(geoURI: GeoURI, description: String?) case presentMediaUploadPreviewScreen(URL)
case displayMessageForwarding(forwardingItem: MessageForwardingItem) case presentLocationPicker
case displayRoomScreenWithFocussedPin(eventID: String) case presentPollForm(mode: PollFormMode)
case presentLocationViewer(body: String, geoURI: GeoURI, description: String?)
case presentEmojiPicker(itemID: TimelineItemIdentifier, selectedEmojis: Set<String>)
case presentRoomMemberDetails(userID: String)
case presentMessageForwarding(forwardingItem: MessageForwardingItem)
case presentResolveSendFailure(failure: TimelineItemSendFailure.VerifiedUser, sendHandle: SendHandleProxy)
} }
final class ThreadTimelineScreenCoordinator: CoordinatorProtocol { final class ThreadTimelineScreenCoordinator: CoordinatorProtocol {
@@ -44,6 +49,7 @@ final class ThreadTimelineScreenCoordinator: CoordinatorProtocol {
self.parameters = parameters self.parameters = parameters
viewModel = ThreadTimelineScreenViewModel() viewModel = ThreadTimelineScreenViewModel()
timelineViewModel = TimelineViewModel(roomProxy: parameters.roomProxy, timelineViewModel = TimelineViewModel(roomProxy: parameters.roomProxy,
timelineController: parameters.timelineController, timelineController: parameters.timelineController,
mediaProvider: parameters.mediaProvider, mediaProvider: parameters.mediaProvider,
@@ -59,28 +65,48 @@ final class ThreadTimelineScreenCoordinator: CoordinatorProtocol {
} }
func start() { func start() {
timelineViewModel.actions.sink { [weak self] action in timelineViewModel.actions
MXLog.info("Coordinator: received timeline view model action: \(action)") .sink { [weak self] action in
guard let self else { return } guard let self else { return }
switch action { switch action {
case .displaySenderDetails(let userID): case .displayEmojiPicker(let itemID, let selectedEmojis):
actionsSubject.send(.displayUser(userID: userID)) actionsSubject.send(.presentEmojiPicker(itemID: itemID, selectedEmojis: selectedEmojis))
case .displayMessageForwarding(let forwardingItem): case .displayReportContent(let itemID, let senderID):
actionsSubject.send(.displayMessageForwarding(forwardingItem: forwardingItem)) actionsSubject.send(.presentReportContent(itemID: itemID, senderID: senderID))
case .displayLocation(_, let geoURI, let description): case .displayCameraPicker:
actionsSubject.send(.presentLocationViewer(geoURI: geoURI, description: description)) actionsSubject.send(.presentMediaUploadPicker(.camera))
case .viewInRoomTimeline(let eventID): case .displayMediaPicker:
actionsSubject.send(.displayRoomScreenWithFocussedPin(eventID: eventID)) actionsSubject.send(.presentMediaUploadPicker(.photoLibrary))
// These other actions will not be handled in this view case .displayDocumentPicker:
case .displayEmojiPicker, .displayReportContent, .displayCameraPicker, .displayMediaPicker, actionsSubject.send(.presentMediaUploadPicker(.documents))
.displayDocumentPicker, .displayLocationPicker, .displayPollForm, .displayMediaPreview, case .displayMediaPreview(let mediaPreviewViewModel):
.displayMediaUploadPreviewScreen, .displayResolveSendFailure, .displayThread, .composer, .hasScrolled: break
// These actions are not handled in this coordinator case .displayLocationPicker:
break actionsSubject.send(.presentLocationPicker)
case .displayPollForm(let mode):
actionsSubject.send(.presentPollForm(mode: mode))
case .displayMediaUploadPreviewScreen(let url):
actionsSubject.send(.presentMediaUploadPreviewScreen(url))
case .displaySenderDetails(userID: let userID):
actionsSubject.send(.presentRoomMemberDetails(userID: userID))
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 .displayResolveSendFailure(let failure, let sendHandle):
actionsSubject.send(.presentResolveSendFailure(failure: failure, sendHandle: sendHandle))
case .displayThread(let itemID):
break
case .composer(let action):
break
case .hasScrolled(direction: let direction):
break
case .viewInRoomTimeline:
fatalError("The action: \(action) should not be sent to this coordinator")
}
} }
} .store(in: &cancellables)
.store(in: &cancellables)
} }
func stop() { func stop() {
@@ -125,7 +125,7 @@ struct TimelineItemMenuActionProvider {
secondaryActions = secondaryActions.filter(\.canAppearInRedacted) secondaryActions = secondaryActions.filter(\.canAppearInRedacted)
} }
let isReactable = timelineKind == .live || timelineKind == .detached ? item.isReactable : false let isReactable = timelineKind == .live || timelineKind == .detached || timelineKind == .thread ? item.isReactable : false
return .init(isReactable: isReactable, actions: actions, secondaryActions: secondaryActions, emojiProvider: emojiProvider) return .init(isReactable: isReactable, actions: actions, secondaryActions: secondaryActions, emojiProvider: emojiProvider)
} }