mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-05 11:58:32 +00:00
Introduce a StartChatFlowCoordinator instead of handing a navigation stack to the Screen Coordinator. (#4674)
* Introduce a basic StartChatFlowCoordinator. * Move the rest of the start chat flow from the screen coordinator into the flow coordinator. * Add a UI test for the entire start chat flow. * Refactor CreateRoom… to CreateRoomScreen…
This commit is contained in:
@@ -10,38 +10,26 @@ import Combine
|
||||
import SwiftUI
|
||||
|
||||
struct StartChatScreenCoordinatorParameters {
|
||||
let orientationManager: OrientationManagerProtocol
|
||||
let userSession: UserSessionProtocol
|
||||
let userIndicatorController: UserIndicatorControllerProtocol
|
||||
weak var navigationStackCoordinator: NavigationStackCoordinator?
|
||||
let userDiscoveryService: UserDiscoveryServiceProtocol
|
||||
let mediaUploadingPreprocessor: MediaUploadingPreprocessor
|
||||
let userIndicatorController: UserIndicatorControllerProtocol
|
||||
let appSettings: AppSettings
|
||||
let analytics: AnalyticsService
|
||||
}
|
||||
|
||||
enum StartChatScreenCoordinatorAction {
|
||||
case close
|
||||
case openRoom(withIdentifier: String)
|
||||
case createRoom
|
||||
case openRoom(roomID: String)
|
||||
case openRoomDirectorySearch
|
||||
}
|
||||
|
||||
final class StartChatScreenCoordinator: CoordinatorProtocol {
|
||||
private let parameters: StartChatScreenCoordinatorParameters
|
||||
private var viewModel: StartChatScreenViewModelProtocol
|
||||
private let actionsSubject: PassthroughSubject<StartChatScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
private var createRoomParameters = CurrentValueSubject<CreateRoomFlowParameters, Never>(.init())
|
||||
private var createRoomParametersPublisher: CurrentValuePublisher<CreateRoomFlowParameters, Never> {
|
||||
createRoomParameters.asCurrentValuePublisher()
|
||||
}
|
||||
|
||||
private let selectedUsers = CurrentValueSubject<[UserProfileProxy], Never>([])
|
||||
private var selectedUsersPublisher: CurrentValuePublisher<[UserProfileProxy], Never> {
|
||||
selectedUsers.asCurrentValuePublisher()
|
||||
}
|
||||
|
||||
private let actionsSubject: PassthroughSubject<StartChatScreenCoordinatorAction, Never> = .init()
|
||||
var actions: AnyPublisher<StartChatScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
@@ -63,10 +51,9 @@ final class StartChatScreenCoordinator: CoordinatorProtocol {
|
||||
case .close:
|
||||
actionsSubject.send(.close)
|
||||
case .createRoom:
|
||||
// before creating a room we select the users we would like to invite in that room
|
||||
presentInviteUsersScreen()
|
||||
case .showRoom(let identifier):
|
||||
actionsSubject.send(.openRoom(withIdentifier: identifier))
|
||||
actionsSubject.send(.createRoom)
|
||||
case .showRoom(let roomID):
|
||||
actionsSubject.send(.openRoom(roomID: roomID))
|
||||
case .openRoomDirectorySearch:
|
||||
actionsSubject.send(.openRoomDirectorySearch)
|
||||
}
|
||||
@@ -79,126 +66,4 @@ final class StartChatScreenCoordinator: CoordinatorProtocol {
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(StartChatScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func presentInviteUsersScreen() {
|
||||
let inviteParameters = InviteUsersScreenCoordinatorParameters(userSession: parameters.userSession,
|
||||
selectedUsers: selectedUsersPublisher,
|
||||
roomType: .draft,
|
||||
userDiscoveryService: parameters.userDiscoveryService,
|
||||
userIndicatorController: parameters.userIndicatorController,
|
||||
appSettings: parameters.appSettings)
|
||||
let coordinator = InviteUsersScreenCoordinator(parameters: inviteParameters)
|
||||
coordinator.actions.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .dismiss:
|
||||
fatalError("Not shown in this flow.")
|
||||
case .proceed(let selectedUsers):
|
||||
self.selectedUsers.send(selectedUsers)
|
||||
openCreateRoomScreen()
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
parameters.navigationStackCoordinator?.push(coordinator) { [weak self] in
|
||||
self?.createRoomParameters.send(.init())
|
||||
self?.selectedUsers.send([])
|
||||
}
|
||||
}
|
||||
|
||||
private func openCreateRoomScreen() {
|
||||
let createParameters = CreateRoomCoordinatorParameters(userSession: parameters.userSession,
|
||||
userIndicatorController: parameters.userIndicatorController,
|
||||
createRoomParameters: createRoomParametersPublisher,
|
||||
selectedUsers: selectedUsers.value,
|
||||
appSettings: parameters.appSettings,
|
||||
analytics: parameters.analytics)
|
||||
let coordinator = CreateRoomCoordinator(parameters: createParameters)
|
||||
coordinator.actions.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .updateSelectedUsers(let users):
|
||||
self.selectedUsers.send(users)
|
||||
case .updateDetails(let details):
|
||||
self.createRoomParameters.send(details)
|
||||
case .openRoom(let identifier):
|
||||
self.actionsSubject.send(.openRoom(withIdentifier: identifier))
|
||||
case .displayMediaPickerWithMode(let mode):
|
||||
self.displayMediaPickerWithMode(mode)
|
||||
case .removeImage:
|
||||
var parameters = self.createRoomParameters.value
|
||||
parameters.avatarImageMedia = nil
|
||||
self.createRoomParameters.send(parameters)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
parameters.navigationStackCoordinator?.push(coordinator)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func displayMediaPickerWithMode(_ mode: MediaPickerScreenMode) {
|
||||
let stackCoordinator = NavigationStackCoordinator()
|
||||
|
||||
let mediaPickerCoordinator = MediaPickerScreenCoordinator(mode: mode,
|
||||
userIndicatorController: parameters.userIndicatorController,
|
||||
orientationManager: parameters.orientationManager) { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .cancel:
|
||||
parameters.navigationStackCoordinator?.setSheetCoordinator(nil)
|
||||
case .selectedMediaAtURLs(let urls):
|
||||
guard urls.count == 1,
|
||||
let url = urls.first else {
|
||||
fatalError("Received an invalid number of URLs")
|
||||
}
|
||||
|
||||
processAvatar(from: url)
|
||||
}
|
||||
}
|
||||
|
||||
stackCoordinator.setRootCoordinator(mediaPickerCoordinator)
|
||||
|
||||
parameters.navigationStackCoordinator?.setSheetCoordinator(stackCoordinator)
|
||||
}
|
||||
|
||||
private func processAvatar(from url: URL) {
|
||||
parameters.navigationStackCoordinator?.setSheetCoordinator(nil)
|
||||
showLoadingIndicator()
|
||||
Task { [weak self] in
|
||||
guard let self else { return }
|
||||
do {
|
||||
guard case let .success(maxUploadSize) = await parameters.userSession.clientProxy.maxMediaUploadSize else {
|
||||
MXLog.error("Failed to get max upload size")
|
||||
parameters.userIndicatorController.alertInfo = AlertInfo(id: .init())
|
||||
return
|
||||
}
|
||||
let media = try await parameters.mediaUploadingPreprocessor.processMedia(at: url, maxUploadSize: maxUploadSize).get()
|
||||
var parameters = createRoomParameters.value
|
||||
parameters.avatarImageMedia = media
|
||||
createRoomParameters.send(parameters)
|
||||
} catch {
|
||||
parameters.userIndicatorController.alertInfo = AlertInfo(id: .init())
|
||||
}
|
||||
hideLoadingIndicator()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Loading indicator
|
||||
|
||||
private static let loadingIndicatorIdentifier = "\(StartChatScreenCoordinator.self)-Loading"
|
||||
|
||||
private func showLoadingIndicator() {
|
||||
parameters.userIndicatorController.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier,
|
||||
type: .modal,
|
||||
title: L10n.commonLoading,
|
||||
persistent: true))
|
||||
}
|
||||
|
||||
private func hideLoadingIndicator() {
|
||||
parameters.userIndicatorController.retractIndicatorWithId(Self.loadingIndicatorIdentifier)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user