2022-02-14 18:05:21 +02:00
|
|
|
//
|
2022-08-11 08:54:24 +01:00
|
|
|
// Copyright 2022 New Vector Ltd
|
2022-02-14 18:05:21 +02:00
|
|
|
//
|
2022-08-11 08:54:24 +01:00
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
2022-02-14 18:05:21 +02:00
|
|
|
//
|
|
|
|
|
2022-07-01 13:56:52 +03:00
|
|
|
import Combine
|
2022-07-27 16:06:40 +03:00
|
|
|
import MatrixRustSDK
|
2022-07-06 14:49:05 +01:00
|
|
|
import UIKit
|
2022-02-14 18:05:21 +02:00
|
|
|
|
2022-09-12 21:34:53 +03:00
|
|
|
struct ServiceLocator {
|
|
|
|
fileprivate static var serviceLocator: ServiceLocator?
|
|
|
|
static var shared: ServiceLocator {
|
|
|
|
guard let serviceLocator = serviceLocator else {
|
|
|
|
fatalError("The service locator should be setup at this point")
|
|
|
|
}
|
|
|
|
|
|
|
|
return serviceLocator
|
|
|
|
}
|
|
|
|
|
|
|
|
let userIndicatorPresenter: UserIndicatorTypePresenter
|
|
|
|
}
|
|
|
|
|
2022-09-15 12:41:37 +03:00
|
|
|
class AppCoordinator: Coordinator {
|
2022-02-14 18:05:21 +02:00
|
|
|
private let window: UIWindow
|
|
|
|
|
2022-07-01 13:56:52 +03:00
|
|
|
private let stateMachine: AppCoordinatorStateMachine
|
2022-05-27 13:02:36 +03:00
|
|
|
|
2022-02-14 18:05:21 +02:00
|
|
|
private let mainNavigationController: UINavigationController
|
|
|
|
private let splashViewController: UIViewController
|
|
|
|
|
|
|
|
private let navigationRouter: NavigationRouter
|
|
|
|
|
2022-06-15 10:10:22 +01:00
|
|
|
private let userSessionStore: UserSessionStoreProtocol
|
2022-02-14 18:05:21 +02:00
|
|
|
|
2022-09-15 12:41:37 +03:00
|
|
|
private var userSession: UserSessionProtocol! {
|
|
|
|
didSet {
|
|
|
|
deobserveUserSessionChanges()
|
|
|
|
if let userSession = userSession, !userSession.isSoftLogout {
|
|
|
|
observeUserSessionChanges()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-05-26 16:04:48 +03:00
|
|
|
|
2022-06-06 12:38:07 +03:00
|
|
|
private let bugReportService: BugReportServiceProtocol
|
|
|
|
private let screenshotDetector: ScreenshotDetector
|
2022-06-29 13:03:28 +03:00
|
|
|
private let backgroundTaskService: BackgroundTaskServiceProtocol
|
2022-06-06 12:38:07 +03:00
|
|
|
|
2022-05-11 16:27:30 +03:00
|
|
|
private var loadingIndicator: UserIndicator?
|
2022-06-20 13:06:29 +03:00
|
|
|
private var statusIndicator: UserIndicator?
|
2022-09-15 12:41:37 +03:00
|
|
|
|
|
|
|
private var cancellables = Set<AnyCancellable>()
|
2022-02-22 11:24:46 +02:00
|
|
|
|
2022-02-14 18:05:21 +02:00
|
|
|
var childCoordinators: [Coordinator] = []
|
|
|
|
|
|
|
|
init() {
|
2022-05-27 13:02:36 +03:00
|
|
|
stateMachine = AppCoordinatorStateMachine()
|
2022-09-12 21:34:53 +03:00
|
|
|
|
|
|
|
bugReportService = BugReportService(withBaseURL: BuildSettings.bugReportServiceBaseURL, sentryURL: BuildSettings.bugReportSentryURL)
|
2022-06-06 12:38:07 +03:00
|
|
|
|
2022-02-14 18:05:21 +02:00
|
|
|
splashViewController = SplashViewController()
|
2022-09-21 11:21:58 +03:00
|
|
|
|
2022-06-21 20:28:42 +03:00
|
|
|
mainNavigationController = ElementNavigationController(rootViewController: splashViewController)
|
2022-09-21 11:21:58 +03:00
|
|
|
mainNavigationController.navigationBar.prefersLargeTitles = true
|
|
|
|
|
2022-02-14 18:05:21 +02:00
|
|
|
window = UIWindow(frame: UIScreen.main.bounds)
|
|
|
|
window.rootViewController = mainNavigationController
|
2022-06-13 08:38:28 +01:00
|
|
|
window.tintColor = .element.accent
|
2022-02-14 18:05:21 +02:00
|
|
|
|
|
|
|
navigationRouter = NavigationRouter(navigationController: mainNavigationController)
|
|
|
|
|
2022-09-12 21:34:53 +03:00
|
|
|
ServiceLocator.serviceLocator = ServiceLocator(userIndicatorPresenter: UserIndicatorTypePresenter(presentingViewController: mainNavigationController))
|
2022-05-11 16:27:30 +03:00
|
|
|
|
2022-02-14 18:05:21 +02:00
|
|
|
guard let bundleIdentifier = Bundle.main.bundleIdentifier else {
|
|
|
|
fatalError("Should have a valid bundle identifier at this point")
|
|
|
|
}
|
2022-06-29 13:03:28 +03:00
|
|
|
|
|
|
|
backgroundTaskService = UIKitBackgroundTaskService(withApplication: UIApplication.shared)
|
2022-02-14 18:05:21 +02:00
|
|
|
|
2022-06-29 13:03:28 +03:00
|
|
|
userSessionStore = UserSessionStore(bundleIdentifier: bundleIdentifier,
|
|
|
|
backgroundTaskService: backgroundTaskService)
|
2022-06-06 12:38:07 +03:00
|
|
|
|
|
|
|
screenshotDetector = ScreenshotDetector()
|
|
|
|
screenshotDetector.callback = processScreenshotDetection
|
2022-04-05 10:01:16 +03:00
|
|
|
|
2022-05-27 13:02:36 +03:00
|
|
|
setupStateMachine()
|
|
|
|
|
2022-07-27 16:06:40 +03:00
|
|
|
setupLogging()
|
2022-04-05 10:01:16 +03:00
|
|
|
|
|
|
|
// Benchmark.trackingEnabled = true
|
2022-02-14 18:05:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func start() {
|
2022-06-14 18:04:42 +01:00
|
|
|
window.makeKeyAndVisible()
|
2022-06-14 17:36:28 +01:00
|
|
|
stateMachine.processEvent(userSessionStore.hasSessions ? .startWithExistingSession : .startWithAuthentication)
|
2022-02-14 18:05:21 +02:00
|
|
|
}
|
2022-09-15 12:41:37 +03:00
|
|
|
|
2022-05-27 13:02:36 +03:00
|
|
|
// MARK: - Private
|
|
|
|
|
2022-07-27 16:06:40 +03:00
|
|
|
private func setupLogging() {
|
|
|
|
let loggerConfiguration = MXLogConfiguration()
|
|
|
|
|
|
|
|
#if DEBUG
|
|
|
|
// This exposes the full Rust side tracing subscriber filter for more flexibility.
|
|
|
|
// We can filter by level, crate and even file. See more details here:
|
|
|
|
// https://docs.rs/tracing-subscriber/0.2.7/tracing_subscriber/filter/struct.EnvFilter.html#examples
|
2022-09-21 11:21:58 +03:00
|
|
|
setupTracing(configuration: "warn,hyper=warn,sled=warn,matrix_sdk_sled=warn")
|
2022-07-27 16:06:40 +03:00
|
|
|
|
|
|
|
loggerConfiguration.logLevel = .debug
|
|
|
|
#else
|
|
|
|
setupTracing(configuration: "info,hyper=warn,sled=warn,matrix_sdk_sled=warn")
|
|
|
|
loggerConfiguration.logLevel = .info
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Avoid redirecting NSLogs to files if we are attached to a debugger.
|
|
|
|
if isatty(STDERR_FILENO) == 0 {
|
|
|
|
loggerConfiguration.redirectLogsToFiles = true
|
|
|
|
}
|
|
|
|
|
|
|
|
MXLog.configure(loggerConfiguration)
|
|
|
|
}
|
|
|
|
|
2022-09-23 12:21:41 +03:00
|
|
|
// swiftlint:disable:next cyclomatic_complexity
|
2022-05-27 13:02:36 +03:00
|
|
|
private func setupStateMachine() {
|
|
|
|
stateMachine.addTransitionHandler { [weak self] context in
|
|
|
|
guard let self = self else { return }
|
2022-06-14 17:36:28 +01:00
|
|
|
|
2022-05-27 13:02:36 +03:00
|
|
|
switch (context.fromState, context.event, context.toState) {
|
2022-06-14 17:36:28 +01:00
|
|
|
case (.initial, .startWithAuthentication, .signedOut):
|
2022-06-15 10:10:22 +01:00
|
|
|
self.startAuthentication()
|
2022-07-04 10:00:27 +01:00
|
|
|
case (.signedOut, .succeededSigningIn, .homeScreen):
|
2022-06-14 17:36:28 +01:00
|
|
|
self.presentHomeScreen()
|
2022-07-01 13:56:52 +03:00
|
|
|
|
2022-06-14 17:36:28 +01:00
|
|
|
case (.initial, .startWithExistingSession, .restoringSession):
|
|
|
|
self.showLoadingIndicator()
|
|
|
|
self.restoreUserSession()
|
|
|
|
case (.restoringSession, .failedRestoringSession, .signedOut):
|
|
|
|
self.hideLoadingIndicator()
|
|
|
|
self.showLoginErrorToast()
|
|
|
|
case (.restoringSession, .succeededRestoringSession, .homeScreen):
|
2022-05-27 13:02:36 +03:00
|
|
|
self.hideLoadingIndicator()
|
|
|
|
self.presentHomeScreen()
|
2022-06-14 17:36:28 +01:00
|
|
|
|
2022-05-27 13:02:36 +03:00
|
|
|
case(_, _, .roomScreen(let roomId)):
|
|
|
|
self.presentRoomWithIdentifier(roomId)
|
2022-09-23 12:21:41 +03:00
|
|
|
case(.roomScreen(let roomId), .dismissedRoomScreen, .homeScreen):
|
|
|
|
self.tearDownDismissedRoomScreen(roomId)
|
2022-09-15 12:41:37 +03:00
|
|
|
|
|
|
|
case (_, .signOut, .signingOut):
|
|
|
|
self.showLoadingIndicator()
|
2022-05-27 13:02:36 +03:00
|
|
|
self.tearDownUserSession()
|
2022-09-15 12:41:37 +03:00
|
|
|
case (.signingOut, .completedSigningOut, .signedOut):
|
|
|
|
self.presentSplashScreen()
|
|
|
|
self.hideLoadingIndicator()
|
|
|
|
|
|
|
|
case (_, .remoteSignOut(let isSoft), .remoteSigningOut):
|
|
|
|
self.showLoadingIndicator()
|
|
|
|
self.tearDownUserSession(isSoftLogout: isSoft)
|
|
|
|
case (.remoteSigningOut(let isSoft), .completedSigningOut, .signedOut):
|
|
|
|
self.presentSplashScreen(isSoftLogout: isSoft)
|
|
|
|
self.hideLoadingIndicator()
|
2022-09-21 11:21:58 +03:00
|
|
|
|
2022-07-01 13:56:52 +03:00
|
|
|
case (.homeScreen, .showSessionVerificationScreen, .sessionVerificationScreen):
|
|
|
|
self.presentSessionVerification()
|
|
|
|
case (.sessionVerificationScreen, .dismissedSessionVerificationScreen, .homeScreen):
|
|
|
|
self.tearDownDismissedSessionVerificationScreen()
|
|
|
|
|
2022-05-27 13:02:36 +03:00
|
|
|
default:
|
|
|
|
fatalError("Unknown transition: \(context)")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
stateMachine.addErrorHandler { context in
|
|
|
|
fatalError("Failed transition with context: \(context)")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-14 17:36:28 +01:00
|
|
|
private func restoreUserSession() {
|
|
|
|
Task {
|
|
|
|
switch await userSessionStore.restoreUserSession() {
|
|
|
|
case .success(let userSession):
|
|
|
|
self.userSession = userSession
|
2022-09-15 12:41:37 +03:00
|
|
|
if userSession.isSoftLogout {
|
|
|
|
stateMachine.processEvent(.remoteSignOut(isSoft: true))
|
|
|
|
} else {
|
|
|
|
stateMachine.processEvent(.succeededRestoringSession)
|
|
|
|
}
|
2022-06-14 17:36:28 +01:00
|
|
|
case .failure:
|
|
|
|
MXLog.error("Failed to restore an existing session.")
|
|
|
|
stateMachine.processEvent(.failedRestoringSession)
|
|
|
|
}
|
2022-05-26 16:04:48 +03:00
|
|
|
}
|
2022-06-14 17:36:28 +01:00
|
|
|
}
|
|
|
|
|
2022-06-15 10:10:22 +01:00
|
|
|
private func startAuthentication() {
|
2022-07-27 10:57:16 +01:00
|
|
|
let authenticationService = AuthenticationServiceProxy(userSessionStore: userSessionStore)
|
2022-07-04 10:00:27 +01:00
|
|
|
let coordinator = AuthenticationCoordinator(authenticationService: authenticationService,
|
2022-06-14 17:36:28 +01:00
|
|
|
navigationRouter: navigationRouter)
|
|
|
|
coordinator.delegate = self
|
2022-05-26 16:04:48 +03:00
|
|
|
|
2022-06-14 17:36:28 +01:00
|
|
|
add(childCoordinator: coordinator)
|
|
|
|
coordinator.start()
|
2022-02-14 18:05:21 +02:00
|
|
|
}
|
2022-09-15 12:41:37 +03:00
|
|
|
|
|
|
|
private func startAuthenticationSoftLogout() {
|
|
|
|
Task {
|
|
|
|
var displayName = ""
|
|
|
|
if case .success(let name) = await userSession.clientProxy.loadUserDisplayName() {
|
|
|
|
displayName = name
|
|
|
|
}
|
|
|
|
|
|
|
|
let credentials = SoftLogoutCredentials(userId: userSession.userID,
|
|
|
|
homeserverName: userSession.homeserver,
|
|
|
|
userDisplayName: displayName,
|
|
|
|
deviceId: userSession.deviceId)
|
|
|
|
|
|
|
|
let authenticationService = AuthenticationServiceProxy(userSessionStore: userSessionStore)
|
|
|
|
_ = await authenticationService.configure(for: userSession.homeserver)
|
|
|
|
|
|
|
|
let parameters = SoftLogoutCoordinatorParameters(authenticationService: authenticationService,
|
|
|
|
credentials: credentials,
|
|
|
|
keyBackupNeeded: false)
|
|
|
|
let coordinator = SoftLogoutCoordinator(parameters: parameters)
|
|
|
|
coordinator.callback = { result in
|
|
|
|
switch result {
|
|
|
|
case .signedIn(let session):
|
|
|
|
self.userSession = session
|
|
|
|
self.remove(childCoordinator: coordinator)
|
|
|
|
self.stateMachine.processEvent(.succeededSigningIn)
|
|
|
|
case .clearAllData:
|
2022-09-23 12:21:41 +03:00
|
|
|
// clear user data
|
|
|
|
self.userSessionStore.logout(userSession: self.userSession)
|
|
|
|
self.userSession = nil
|
|
|
|
self.remove(childCoordinator: coordinator)
|
|
|
|
self.startAuthentication()
|
2022-09-15 12:41:37 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
add(childCoordinator: coordinator)
|
|
|
|
coordinator.start()
|
|
|
|
|
|
|
|
navigationRouter.setRootModule(coordinator)
|
|
|
|
}
|
|
|
|
}
|
2022-02-14 18:05:21 +02:00
|
|
|
|
2022-09-15 12:41:37 +03:00
|
|
|
private func tearDownUserSession(isSoftLogout: Bool = false) {
|
2022-09-21 11:21:58 +03:00
|
|
|
userSession.clientProxy.stopSync()
|
|
|
|
|
|
|
|
deobserveUserSessionChanges()
|
|
|
|
|
|
|
|
if !isSoftLogout {
|
|
|
|
Task {
|
2022-09-15 12:41:37 +03:00
|
|
|
// first log out from the server
|
|
|
|
_ = await userSession.clientProxy.logout()
|
2022-09-21 11:21:58 +03:00
|
|
|
|
2022-09-15 12:41:37 +03:00
|
|
|
// regardless of the result, clear user data
|
|
|
|
userSessionStore.logout(userSession: userSession)
|
|
|
|
userSession = nil
|
|
|
|
}
|
|
|
|
}
|
2022-09-21 11:21:58 +03:00
|
|
|
|
|
|
|
// complete logging out
|
|
|
|
stateMachine.processEvent(.completedSigningOut)
|
2022-09-15 12:41:37 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
private func presentSplashScreen(isSoftLogout: Bool = false) {
|
2022-03-01 14:05:35 +02:00
|
|
|
if let presentedCoordinator = childCoordinators.first {
|
|
|
|
remove(childCoordinator: presentedCoordinator)
|
|
|
|
}
|
2022-09-15 12:41:37 +03:00
|
|
|
|
2022-02-22 09:18:46 +02:00
|
|
|
mainNavigationController.setViewControllers([splashViewController], animated: false)
|
2022-09-15 12:41:37 +03:00
|
|
|
|
|
|
|
if isSoftLogout {
|
|
|
|
startAuthenticationSoftLogout()
|
|
|
|
} else {
|
|
|
|
startAuthentication()
|
|
|
|
}
|
2022-02-14 18:05:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private func presentHomeScreen() {
|
2022-09-21 11:21:58 +03:00
|
|
|
userSession.clientProxy.startSync()
|
|
|
|
|
2022-04-01 14:07:33 +03:00
|
|
|
let parameters = HomeScreenCoordinatorParameters(userSession: userSession,
|
2022-09-21 11:21:58 +03:00
|
|
|
attributedStringBuilder: AttributedStringBuilder())
|
2022-03-17 18:09:29 +02:00
|
|
|
let coordinator = HomeScreenCoordinator(parameters: parameters)
|
2022-02-14 18:05:21 +02:00
|
|
|
|
2022-05-26 16:04:48 +03:00
|
|
|
coordinator.callback = { [weak self] action in
|
2022-05-27 13:02:36 +03:00
|
|
|
guard let self = self else { return }
|
|
|
|
|
2022-05-26 16:04:48 +03:00
|
|
|
switch action {
|
2022-06-06 12:38:07 +03:00
|
|
|
case .presentRoom(let roomIdentifier):
|
2022-05-27 13:02:36 +03:00
|
|
|
self.stateMachine.processEvent(.showRoomScreen(roomId: roomIdentifier))
|
2022-06-06 12:38:07 +03:00
|
|
|
case .presentSettings:
|
2022-09-21 11:21:58 +03:00
|
|
|
self.presentSettingsScreen()
|
2022-09-14 22:42:48 +03:00
|
|
|
case .presentBugReport:
|
|
|
|
self.presentBugReportScreen()
|
2022-07-01 13:56:52 +03:00
|
|
|
case .verifySession:
|
|
|
|
self.stateMachine.processEvent(.showSessionVerificationScreen)
|
2022-09-14 22:42:48 +03:00
|
|
|
case .signOut:
|
2022-09-23 12:21:41 +03:00
|
|
|
self.stateMachine.processEvent(.signOut)
|
2022-02-22 09:18:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-14 18:05:21 +02:00
|
|
|
add(childCoordinator: coordinator)
|
|
|
|
navigationRouter.setRootModule(coordinator)
|
2022-06-06 12:38:07 +03:00
|
|
|
|
|
|
|
if bugReportService.crashedLastRun {
|
|
|
|
showCrashPopup()
|
|
|
|
}
|
|
|
|
}
|
2022-09-15 12:41:37 +03:00
|
|
|
|
|
|
|
private func observeUserSessionChanges() {
|
|
|
|
userSession.callbacks
|
|
|
|
.receive(on: DispatchQueue.main)
|
|
|
|
.sink { [weak self] callback in
|
|
|
|
guard let self = self else { return }
|
|
|
|
switch callback {
|
|
|
|
case .didReceiveAuthError(let isSoftLogout):
|
|
|
|
self.stateMachine.processEvent(.remoteSignOut(isSoft: isSoftLogout))
|
|
|
|
case .updateRestoreTokenNeeded:
|
|
|
|
if let userSession = self.userSession {
|
|
|
|
_ = self.userSessionStore.refreshRestoreToken(for: userSession)
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}.store(in: &cancellables)
|
|
|
|
}
|
|
|
|
|
|
|
|
private func deobserveUserSessionChanges() {
|
|
|
|
cancellables.forEach { $0.cancel() }
|
|
|
|
cancellables.removeAll()
|
|
|
|
}
|
2022-02-14 18:05:21 +02:00
|
|
|
|
2022-07-01 13:56:52 +03:00
|
|
|
// MARK: Rooms
|
|
|
|
|
2022-03-08 14:24:33 +02:00
|
|
|
private func presentRoomWithIdentifier(_ roomIdentifier: String) {
|
2022-09-21 11:21:58 +03:00
|
|
|
guard let roomProxy = userSession.clientProxy.roomForIdentifier(roomIdentifier) else {
|
2022-03-31 15:37:52 +03:00
|
|
|
MXLog.error("Invalid room identifier: \(roomIdentifier)")
|
|
|
|
return
|
|
|
|
}
|
2022-06-23 14:54:29 +03:00
|
|
|
let userId = userSession.clientProxy.userIdentifier
|
2022-03-31 15:37:52 +03:00
|
|
|
|
2022-09-21 11:21:58 +03:00
|
|
|
let timelineItemFactory = RoomTimelineItemFactory(userID: userId,
|
|
|
|
mediaProvider: userSession.mediaProvider,
|
|
|
|
roomProxy: roomProxy,
|
2022-03-31 15:37:52 +03:00
|
|
|
attributedStringBuilder: AttributedStringBuilder())
|
2022-09-21 11:21:58 +03:00
|
|
|
|
2022-06-23 14:54:29 +03:00
|
|
|
let timelineController = RoomTimelineController(userId: userId,
|
2022-09-12 21:34:53 +03:00
|
|
|
roomId: roomIdentifier,
|
2022-09-21 11:21:58 +03:00
|
|
|
timelineProvider: roomProxy.timelineProvider,
|
2022-03-31 15:37:52 +03:00
|
|
|
timelineItemFactory: timelineItemFactory,
|
|
|
|
mediaProvider: userSession.mediaProvider,
|
2022-09-21 11:21:58 +03:00
|
|
|
roomProxy: roomProxy)
|
|
|
|
|
2022-03-31 15:37:52 +03:00
|
|
|
let parameters = RoomScreenCoordinatorParameters(timelineController: timelineController,
|
2022-06-21 20:28:42 +03:00
|
|
|
roomName: roomProxy.displayName ?? roomProxy.name,
|
2022-09-23 12:21:41 +03:00
|
|
|
roomAvatar: userSession.mediaProvider.imageFromURLString(roomProxy.avatarURL, avatarSize: .room(on: .timeline)))
|
2022-03-31 15:37:52 +03:00
|
|
|
let coordinator = RoomScreenCoordinator(parameters: parameters)
|
2022-09-21 11:21:58 +03:00
|
|
|
|
2022-05-27 13:02:36 +03:00
|
|
|
add(childCoordinator: coordinator)
|
|
|
|
navigationRouter.push(coordinator) { [weak self] in
|
2022-03-08 14:24:33 +02:00
|
|
|
guard let self = self else { return }
|
2022-05-27 13:02:36 +03:00
|
|
|
self.stateMachine.processEvent(.dismissedRoomScreen)
|
2022-03-08 14:24:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-23 12:21:41 +03:00
|
|
|
private func tearDownDismissedRoomScreen(_ roomId: String) {
|
2022-05-27 13:02:36 +03:00
|
|
|
guard let coordinator = childCoordinators.last as? RoomScreenCoordinator else {
|
|
|
|
fatalError("Invalid coordinator hierarchy: \(childCoordinators)")
|
|
|
|
}
|
|
|
|
|
|
|
|
remove(childCoordinator: coordinator)
|
|
|
|
}
|
2022-07-01 13:56:52 +03:00
|
|
|
|
|
|
|
// MARK: Settings
|
|
|
|
|
|
|
|
private func presentSettingsScreen() {
|
2022-09-21 11:21:58 +03:00
|
|
|
let navController = ElementNavigationController()
|
|
|
|
let newNavigationRouter = NavigationRouter(navigationController: navController)
|
|
|
|
|
|
|
|
let parameters = SettingsCoordinatorParameters(navigationRouter: newNavigationRouter,
|
2022-09-02 10:49:59 +01:00
|
|
|
userSession: userSession,
|
2022-07-01 13:56:52 +03:00
|
|
|
bugReportService: bugReportService)
|
|
|
|
let coordinator = SettingsCoordinator(parameters: parameters)
|
|
|
|
coordinator.callback = { [weak self] action in
|
|
|
|
guard let self = self else { return }
|
|
|
|
switch action {
|
2022-09-21 11:21:58 +03:00
|
|
|
case .dismiss:
|
|
|
|
self.dismissSettingsScreen()
|
2022-07-01 13:56:52 +03:00
|
|
|
case .logout:
|
2022-09-21 11:21:58 +03:00
|
|
|
self.dismissSettingsScreen()
|
2022-09-15 12:41:37 +03:00
|
|
|
self.stateMachine.processEvent(.signOut)
|
2022-07-01 13:56:52 +03:00
|
|
|
}
|
|
|
|
}
|
2022-06-06 12:38:07 +03:00
|
|
|
|
2022-07-01 13:56:52 +03:00
|
|
|
add(childCoordinator: coordinator)
|
|
|
|
coordinator.start()
|
2022-09-21 11:21:58 +03:00
|
|
|
navController.viewControllers = [coordinator.toPresentable()]
|
|
|
|
navigationRouter.present(navController, animated: true)
|
2022-07-01 13:56:52 +03:00
|
|
|
}
|
2022-09-21 11:21:58 +03:00
|
|
|
|
|
|
|
@objc
|
|
|
|
private func dismissSettingsScreen() {
|
|
|
|
MXLog.debug("dismissSettingsScreen")
|
|
|
|
|
|
|
|
guard let coordinator = childCoordinators.first(where: { $0 is SettingsCoordinator }) else {
|
|
|
|
return
|
2022-06-06 12:38:07 +03:00
|
|
|
}
|
|
|
|
|
2022-09-21 11:21:58 +03:00
|
|
|
navigationRouter.dismissModule()
|
2022-06-06 12:38:07 +03:00
|
|
|
remove(childCoordinator: coordinator)
|
|
|
|
}
|
2022-05-27 13:02:36 +03:00
|
|
|
|
2022-06-06 12:38:07 +03:00
|
|
|
private func showCrashPopup() {
|
|
|
|
let alert = UIAlertController(title: nil,
|
|
|
|
message: ElementL10n.sendBugReportAppCrashed,
|
|
|
|
preferredStyle: .alert)
|
|
|
|
|
|
|
|
alert.addAction(UIAlertAction(title: ElementL10n.no, style: .cancel))
|
|
|
|
alert.addAction(UIAlertAction(title: ElementL10n.yes, style: .default) { [weak self] _ in
|
|
|
|
self?.presentBugReportScreen()
|
|
|
|
})
|
|
|
|
|
|
|
|
navigationRouter.present(alert, animated: true)
|
|
|
|
}
|
|
|
|
|
|
|
|
private func processScreenshotDetection(image: UIImage?, error: Error?) {
|
2022-08-30 18:05:21 +03:00
|
|
|
MXLog.debug("Detected screenshot: \(String(describing: image)), error: \(String(describing: error))")
|
2022-06-06 12:38:07 +03:00
|
|
|
|
|
|
|
let alert = UIAlertController(title: ElementL10n.screenshotDetectedTitle,
|
|
|
|
message: ElementL10n.screenshotDetectedMessage,
|
|
|
|
preferredStyle: .alert)
|
|
|
|
|
|
|
|
alert.addAction(UIAlertAction(title: ElementL10n.no, style: .cancel))
|
|
|
|
alert.addAction(UIAlertAction(title: ElementL10n.yes, style: .default) { [weak self] _ in
|
|
|
|
self?.presentBugReportScreen(for: image)
|
|
|
|
})
|
|
|
|
|
|
|
|
navigationRouter.present(alert, animated: true)
|
|
|
|
}
|
|
|
|
|
|
|
|
private func presentBugReportScreen(for image: UIImage? = nil) {
|
|
|
|
let parameters = BugReportCoordinatorParameters(bugReportService: bugReportService,
|
|
|
|
screenshot: image)
|
|
|
|
let coordinator = BugReportCoordinator(parameters: parameters)
|
|
|
|
coordinator.completion = { [weak self, weak coordinator] in
|
|
|
|
guard let self = self, let coordinator = coordinator else { return }
|
|
|
|
self.navigationRouter.dismissModule(animated: true)
|
|
|
|
self.remove(childCoordinator: coordinator)
|
|
|
|
}
|
|
|
|
|
|
|
|
add(childCoordinator: coordinator)
|
|
|
|
coordinator.start()
|
2022-06-21 20:28:42 +03:00
|
|
|
let navController = ElementNavigationController(rootViewController: coordinator.toPresentable())
|
2022-06-06 12:38:07 +03:00
|
|
|
navController.navigationBar.topItem?.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel,
|
|
|
|
target: self,
|
|
|
|
action: #selector(dismissBugReportScreen))
|
|
|
|
navController.isModalInPresentation = true
|
|
|
|
navigationRouter.present(navController, animated: true)
|
|
|
|
}
|
|
|
|
|
|
|
|
@objc
|
|
|
|
private func dismissBugReportScreen() {
|
2022-08-30 18:05:21 +03:00
|
|
|
MXLog.debug("dismissBugReportScreen")
|
2022-06-06 12:38:07 +03:00
|
|
|
|
|
|
|
guard let bugReportCoordinator = childCoordinators.first(where: { $0 is BugReportCoordinator }) else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
navigationRouter.dismissModule()
|
|
|
|
remove(childCoordinator: bugReportCoordinator)
|
|
|
|
}
|
2022-07-01 13:56:52 +03:00
|
|
|
|
|
|
|
// MARK: Session verification
|
|
|
|
|
|
|
|
private func presentSessionVerification() {
|
|
|
|
Task {
|
|
|
|
guard let sessionVerificationController = userSession.sessionVerificationController else {
|
|
|
|
fatalError("The sessionVerificationController should aways be valid at this point")
|
|
|
|
}
|
|
|
|
|
|
|
|
let parameters = SessionVerificationCoordinatorParameters(sessionVerificationControllerProxy: sessionVerificationController)
|
|
|
|
|
|
|
|
let coordinator = SessionVerificationCoordinator(parameters: parameters)
|
|
|
|
|
|
|
|
coordinator.callback = { [weak self] in
|
|
|
|
self?.navigationRouter.dismissModule()
|
|
|
|
self?.stateMachine.processEvent(.dismissedSessionVerificationScreen)
|
|
|
|
}
|
|
|
|
|
|
|
|
add(childCoordinator: coordinator)
|
|
|
|
navigationRouter.present(coordinator)
|
|
|
|
|
|
|
|
coordinator.start()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private func tearDownDismissedSessionVerificationScreen() {
|
|
|
|
guard let coordinator = childCoordinators.last as? SessionVerificationCoordinator else {
|
|
|
|
fatalError("Invalid coordinator hierarchy: \(childCoordinators)")
|
|
|
|
}
|
|
|
|
|
|
|
|
remove(childCoordinator: coordinator)
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: Toasts and loading indicators
|
|
|
|
|
|
|
|
private func showLoadingIndicator() {
|
2022-09-12 21:34:53 +03:00
|
|
|
loadingIndicator = ServiceLocator.shared.userIndicatorPresenter.present(.loading(label: ElementL10n.loading, isInteractionBlocking: true))
|
2022-07-01 13:56:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
private func hideLoadingIndicator() {
|
|
|
|
loadingIndicator = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
private func showLoginErrorToast() {
|
2022-09-12 21:34:53 +03:00
|
|
|
statusIndicator = ServiceLocator.shared.userIndicatorPresenter.present(.error(label: "Failed logging in"))
|
2022-07-01 13:56:52 +03:00
|
|
|
}
|
2022-09-15 12:41:37 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - AuthenticationCoordinatorDelegate
|
|
|
|
|
|
|
|
extension AppCoordinator: AuthenticationCoordinatorDelegate {
|
|
|
|
func authenticationCoordinator(_ authenticationCoordinator: AuthenticationCoordinator, didLoginWithSession userSession: UserSessionProtocol) {
|
|
|
|
self.userSession = userSession
|
|
|
|
remove(childCoordinator: authenticationCoordinator)
|
|
|
|
stateMachine.processEvent(.succeededSigningIn)
|
2022-07-01 13:56:52 +03:00
|
|
|
}
|
2022-02-14 18:05:21 +02:00
|
|
|
}
|