Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ enum PinnedEventsTimelineFlowCoordinatorAction {
case finished
case displayUser(userID: String)
case forwardedMessageToRoom(roomID: String)
case displayRoomScreenWithFocussedPin(eventID: String)
case displayRoomScreenWithFocussedPin(eventID: String, threadRootEventID: String?)
}

class PinnedEventsTimelineFlowCoordinator: FlowCoordinatorProtocol {
Expand Down Expand Up @@ -87,8 +87,8 @@ class PinnedEventsTimelineFlowCoordinator: FlowCoordinatorProtocol {
presentMapNavigator(geoURI: geoURI, description: description, timelineController: timelineController)
case .displayMessageForwarding(let forwardingItem):
presentMessageForwarding(with: forwardingItem)
case .displayRoomScreenWithFocussedPin(let eventID):
actionsSubject.send(.displayRoomScreenWithFocussedPin(eventID: eventID))
case .displayRoomScreenWithFocussedPin(let eventID, let threadRootEventID):
actionsSubject.send(.displayRoomScreenWithFocussedPin(eventID: eventID, threadRootEventID: threadRootEventID))
}
}
.store(in: &cancellables)
Expand Down
43 changes: 29 additions & 14 deletions ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,15 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
}
case .thread(let roomID, let threadRootEventID, let focusEventID):
Task {
let focusEvent: FocusEvent? = if let focusEventID {
.init(eventID: focusEventID, shouldSetPin: false)
} else {
nil
}
await handleRoomRoute(roomID: roomID,
via: [],
presentationAction: .thread(rootEventID: threadRootEventID,
focusEventID: focusEventID),
focusEvent: focusEvent),
animated: animated)
}
case .event(let eventID, let roomID, let via):
Expand Down Expand Up @@ -304,7 +309,10 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
switch await roomProxy.loadOrFetchEventDetails(for: focusEvent.eventID) {
case .success(let event):
if flowParameters.appSettings.threadsEnabled, let threadRootEventID = event.threadRootEventId() {
stateMachine.tryEvent(.presentRoom(presentationAction: .thread(rootEventID: threadRootEventID, focusEventID: focusEvent.eventID)), userInfo: EventUserInfo(animated: animated))
stateMachine.tryEvent(.presentRoom(presentationAction: .thread(rootEventID: threadRootEventID,
focusEvent: .init(eventID: focusEvent.eventID,
shouldSetPin: focusEvent.shouldSetPin))),
userInfo: EventUserInfo(animated: animated))
} else {
stateMachine.tryEvent(.presentRoom(presentationAction: presentationAction), userInfo: EventUserInfo(animated: animated))
}
Expand Down Expand Up @@ -570,9 +578,15 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
userInfo: EventUserInfo(animated: animated, timelineController: timelineController))
case .share(.text(_, let text)):
roomScreenCoordinator?.shareText(text)
case .thread(let rootEventID, let focusEventID):
roomScreenCoordinator?.focusOnEvent(.init(eventID: rootEventID, shouldSetPin: false))
stateMachine.tryEvent(.presentThread(threadRootEventID: rootEventID, focusEventID: focusEventID))
case .thread(let rootEventID, let focusEvent):
if let focusEvent, let roomScreenCoordinator {
roomScreenCoordinator.focusOnEvent(.init(eventID: rootEventID, shouldSetPin: false))
// Since the root is not the message we want to show in the pinned banner, but the actual event
if focusEvent.shouldSetPin {
roomScreenCoordinator.setSelectedPin(eventID: focusEvent.eventID)
}
}
stateMachine.tryEvent(.presentThread(threadRootEventID: rootEventID, focusEventID: focusEvent?.eventID))
case .none:
break
}
Expand Down Expand Up @@ -607,8 +621,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
case .share(.mediaFiles(_, let mediaFiles)):
stateMachine.tryEvent(.presentMediaUploadPreview(mediaURLs: mediaFiles.map(\.url)),
userInfo: EventUserInfo(animated: animated, timelineController: timelineController))
case .thread(let rootEventID, let focusEventID):
stateMachine.tryEvent(.presentThread(threadRootEventID: rootEventID, focusEventID: focusEventID))
case .thread(let rootEventID, let focusEvent):
stateMachine.tryEvent(.presentThread(threadRootEventID: rootEventID, focusEventID: focusEvent?.eventID))
case .share(.text), .eventFocus:
break // These are both handled in the coordinator's init.
case .none:
Expand Down Expand Up @@ -693,11 +707,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
sendHandle: sendHandle))
case .presentKnockRequestsList:
stateMachine.tryEvent(.presentKnockRequestsListScreen)
case .presentThread(let itemID):
guard let threadRootEventID = itemID.eventID else {
fatalError("A thread root has always an eventID")
}
stateMachine.tryEvent(.presentThread(threadRootEventID: threadRootEventID, focusEventID: nil))
case .presentThread(let threadRootEventID, let focussedEventID):
stateMachine.tryEvent(.presentThread(threadRootEventID: threadRootEventID, focusEventID: focussedEventID))
case .presentRoom(let roomID, let via):
stateMachine.tryEvent(.startChildFlow(roomID: roomID,
via: via,
Expand Down Expand Up @@ -1478,9 +1489,13 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
case .forwardedMessageToRoom(let roomID):
navigationStackCoordinator.setSheetCoordinator(nil)
stateMachine.tryEvent(.startChildFlow(roomID: roomID, via: [], entryPoint: .room))
case .displayRoomScreenWithFocussedPin(let eventID):
case .displayRoomScreenWithFocussedPin(let eventID, let threadRootEventID):
navigationStackCoordinator.setSheetCoordinator(nil)
stateMachine.tryEvent(.presentRoom(presentationAction: .eventFocus(.init(eventID: eventID, shouldSetPin: true))))
if let threadRootEventID {
stateMachine.tryEvent(.presentRoom(presentationAction: .thread(rootEventID: threadRootEventID, focusEvent: .init(eventID: eventID, shouldSetPin: true))))
} else {
stateMachine.tryEvent(.presentRoom(presentationAction: .eventFocus(.init(eventID: eventID, shouldSetPin: true))))
}
}
}
.store(in: &cancellables)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,18 @@ extension RoomFlowCoordinator {
enum PresentationAction: Hashable {
case eventFocus(FocusEvent)
case share(ShareExtensionPayload)
case thread(rootEventID: String, focusEventID: String?)
case thread(rootEventID: String, focusEvent: FocusEvent?)

var focusedEvent: FocusEvent? {
switch self {
case .eventFocus(let focusEvent):
focusEvent
case .thread(let rootEventID, let focusEventID):
case .thread(let rootEventID, let focusEvent):
// Since this enum is for the room and not the threaded timeline,
// we will focus the thread root event id, and not the event id itself
// which will be done at the thread presentation level
if focusEventID != nil {
.init(eventID: rootEventID, shouldSetPin: false)
if let focusEvent {
.init(eventID: rootEventID, shouldSetPin: focusEvent.shouldSetPin)
} else {
nil
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,10 @@ enum PinnedEventsTimelineScreenCoordinatorAction {
case displayUser(userID: String)
case presentLocationViewer(geoURI: GeoURI, description: String?)
case displayMessageForwarding(forwardingItem: MessageForwardingItem)
case displayRoomScreenWithFocussedPin(eventID: String)
case displayRoomScreenWithFocussedPin(eventID: String, threadRootEventID: String?)
}

final class PinnedEventsTimelineScreenCoordinator: CoordinatorProtocol {
private let parameters: PinnedEventsTimelineScreenCoordinatorParameters
private let viewModel: PinnedEventsTimelineScreenViewModelProtocol
private let timelineViewModel: TimelineViewModelProtocol

Expand All @@ -44,9 +43,10 @@ final class PinnedEventsTimelineScreenCoordinator: CoordinatorProtocol {
}

init(parameters: PinnedEventsTimelineScreenCoordinatorParameters) {
self.parameters = parameters

viewModel = PinnedEventsTimelineScreenViewModel(analyticsService: parameters.analytics)
viewModel = PinnedEventsTimelineScreenViewModel(roomProxy: parameters.roomProxy,
userIndicatorController: parameters.userIndicatorController,
appSettings: parameters.appSettings,
analyticsService: parameters.analytics)
timelineViewModel = TimelineViewModel(roomProxy: parameters.roomProxy,
timelineController: parameters.timelineController,
userSession: parameters.userSession,
Expand All @@ -68,9 +68,8 @@ final class PinnedEventsTimelineScreenCoordinator: CoordinatorProtocol {
switch action {
case .displayMessageForwarding(let forwardingItem):
actionsSubject.send(.displayMessageForwarding(forwardingItem: forwardingItem))
case .viewInRoomTimeline(let itemID):
guard let eventID = itemID.eventID else { fatalError("A pinned event must have an event ID.") }
actionsSubject.send(.displayRoomScreenWithFocussedPin(eventID: eventID))
case .viewInRoomTimeline(let eventID, let threadRootEventID):
actionsSubject.send(.displayRoomScreenWithFocussedPin(eventID: eventID, threadRootEventID: threadRootEventID))
case .dismiss:
self.actionsSubject.send(.dismiss)
}
Expand All @@ -90,8 +89,8 @@ final class PinnedEventsTimelineScreenCoordinator: CoordinatorProtocol {
viewModel.displayMediaPreview(mediaPreviewViewModel)
case .displayLocation(_, let geoURI, let description):
actionsSubject.send(.presentLocationViewer(geoURI: geoURI, description: description))
case .viewInRoomTimeline(let eventID):
actionsSubject.send(.displayRoomScreenWithFocussedPin(eventID: eventID))
case .viewInRoomTimeline(let eventID, let threadRootEventID):
actionsSubject.send(.displayRoomScreenWithFocussedPin(eventID: eventID, threadRootEventID: threadRootEventID))
// These other actions will not be handled in this view
case .displayEmojiPicker, .displayReportContent, .displayCameraPicker, .displayMediaPicker,
.displayDocumentPicker, .displayLocationPicker, .displayPollForm, .displayMediaUploadPreviewScreen,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import Foundation

enum PinnedEventsTimelineScreenViewModelAction {
case viewInRoomTimeline(itemID: TimelineItemIdentifier)
case viewInRoomTimeline(eventID: String, threadRootEventID: String?)
case displayMessageForwarding(MessageForwardingItem)
case dismiss
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,23 @@ import SwiftUI
typealias PinnedEventsTimelineScreenViewModelType = StateStoreViewModel<PinnedEventsTimelineScreenViewState, PinnedEventsTimelineScreenViewAction>

class PinnedEventsTimelineScreenViewModel: PinnedEventsTimelineScreenViewModelType, PinnedEventsTimelineScreenViewModelProtocol {
private let roomProxy: JoinedRoomProxyProtocol
private let userIndicatorController: UserIndicatorControllerProtocol
private let appSettings: AppSettings
private let analyticsService: AnalyticsService

private let actionsSubject: PassthroughSubject<PinnedEventsTimelineScreenViewModelAction, Never> = .init()
var actionsPublisher: AnyPublisher<PinnedEventsTimelineScreenViewModelAction, Never> {
actionsSubject.eraseToAnyPublisher()
}

init(analyticsService: AnalyticsService) {

init(roomProxy: JoinedRoomProxyProtocol,
userIndicatorController: UserIndicatorControllerProtocol,
appSettings: AppSettings,
analyticsService: AnalyticsService) {
self.roomProxy = roomProxy
self.userIndicatorController = userIndicatorController
self.appSettings = appSettings
self.analyticsService = analyticsService
super.init(initialViewState: PinnedEventsTimelineScreenViewState())
}
Expand Down Expand Up @@ -52,7 +61,10 @@ class PinnedEventsTimelineScreenViewModel: PinnedEventsTimelineScreenViewModelTy
self.actionsSubject.send(.displayMessageForwarding(forwardingItem))
}
case .viewInRoomTimeline(let itemID):
actionsSubject.send(.viewInRoomTimeline(itemID: itemID))
guard let eventID = itemID.eventID else {
return
}
Task { await self.viewInRoomTimeline(eventID: eventID) }
case .dismiss:
state.bindings.mediaPreviewViewModel = nil
}
Expand All @@ -61,4 +73,18 @@ class PinnedEventsTimelineScreenViewModel: PinnedEventsTimelineScreenViewModelTy

state.bindings.mediaPreviewViewModel = mediaPreviewViewModel
}

private func viewInRoomTimeline(eventID: String) async {
switch await roomProxy.loadOrFetchEventDetails(for: eventID) {
case .success(let event):
let threadRootEventID: String? = if appSettings.threadsEnabled {
event.threadRootEventId()
} else {
nil
}
actionsSubject.send(.viewInRoomTimeline(eventID: eventID, threadRootEventID: threadRootEventID))
case .failure:
userIndicatorController.submitIndicator(.init(title: L10n.errorUnknown))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,11 @@ struct PinnedEventsTimelineScreen: View {
// MARK: - Previews

struct PinnedEventsTimelineScreen_Previews: PreviewProvider, TestablePreview {
static let viewModel = PinnedEventsTimelineScreenViewModel(analyticsService: ServiceLocator.shared.analytics)
static let viewModel = PinnedEventsTimelineScreenViewModel(roomProxy: JoinedRoomProxyMock(.init()),
userIndicatorController: UserIndicatorControllerMock(),
appSettings: AppSettings(),
analyticsService: ServiceLocator.shared.analytics)

static let emptyTimelineViewModel: TimelineViewModel = {
let timelineController = MockTimelineController(timelineKind: .pinned)
timelineController.timelineItems = []
Expand Down
14 changes: 12 additions & 2 deletions ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ enum RoomScreenCoordinatorAction {
case presentPinnedEventsTimeline
case presentResolveSendFailure(failure: TimelineItemSendFailure.VerifiedUser, sendHandle: SendHandleProxy)
case presentKnockRequestsList
case presentThread(itemID: TimelineItemIdentifier)
case presentThread(threadRootEventID: String, focussedEventID: String?)
case presentRoom(roomID: String, via: [String])
}

Expand Down Expand Up @@ -142,7 +142,10 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
case .displayResolveSendFailure(let failure, let sendHandle):
actionsSubject.send(.presentResolveSendFailure(failure: failure, sendHandle: sendHandle))
case .displayThread(let itemID):
actionsSubject.send(.presentThread(itemID: itemID))
guard let eventID = itemID.eventID else {
fatalError("A thread root has always an eventID")
}
actionsSubject.send(.presentThread(threadRootEventID: eventID, focussedEventID: nil))
case .composer(let action):
composerViewModel.process(timelineAction: action)
case .hasScrolled(direction: let direction):
Expand Down Expand Up @@ -184,6 +187,8 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
actionsSubject.send(.presentRoom(roomID: roomID, via: via))
case .displayMessageForwarding(let forwardingItem):
actionsSubject.send(.presentMessageForwarding(forwardingItem: forwardingItem))
case .displayThread(let threadRootEventID, let focussedEventID):
actionsSubject.send(.presentThread(threadRootEventID: threadRootEventID, focussedEventID: focussedEventID))
}
}
.store(in: &cancellables)
Expand All @@ -201,6 +206,11 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
Task { await timelineViewModel.focusOnEvent(eventID: eventID) }
}

/// Sets the banner to selection to a specific event ID, even if not visible in the main timeline (like a threaded event).
func setSelectedPin(eventID: String) {
roomViewModel.setSelectedPinnedEventID(eventID)
}

func shareText(_ string: String) {
composerViewModel.process(timelineAction: .setMode(mode: .default)) // Make sure we're not e.g. replying.
composerViewModel.process(timelineAction: .setText(plainText: string, htmlText: nil))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import OrderedCollections

enum RoomScreenViewModelAction: Equatable {
case focusEvent(eventID: String)
case displayThread(threadRootEventID: String, focussedEventID: String)
case displayPinnedEventsTimeline
case displayRoomDetails
case displayCall
Expand Down
Loading
Loading