mirror of
https://github.com/bitwarden/ios.git
synced 2025-12-10 00:42:29 -06:00
232 lines
7.9 KiB
Swift
232 lines
7.9 KiB
Swift
import BitwardenKit
|
|
import BitwardenResources
|
|
import SwiftUI
|
|
|
|
// MARK: - SettingsCoordinator
|
|
|
|
/// A coordinator that manages navigation in the settings tab.
|
|
///
|
|
final class SettingsCoordinator: Coordinator, HasStackNavigator {
|
|
// MARK: Types
|
|
|
|
/// The module types required by this coordinator for creating child coordinators.
|
|
typealias Module = FileSelectionModule
|
|
& FlightRecorderModule
|
|
& TutorialModule
|
|
|
|
typealias Services = HasAppInfoService
|
|
& HasAppSettingsStore
|
|
& HasApplication
|
|
& HasAuthenticatorItemRepository
|
|
& HasBiometricsRepository
|
|
& HasCameraService
|
|
& HasConfigService
|
|
& HasErrorAlertServices.ErrorAlertServices
|
|
& HasErrorReporter
|
|
& HasExportItemsService
|
|
& HasFlightRecorder
|
|
& HasImportItemsService
|
|
& HasPasteboardService
|
|
& HasStateService
|
|
& HasTimeProvider
|
|
|
|
// MARK: Private Properties
|
|
|
|
/// The most recent coordinator used to navigate to a `FileSelectionRoute`. Used to keep the
|
|
/// coordinator in memory.
|
|
private var fileSelectionCoordinator: AnyCoordinator<FileSelectionRoute, FileSelectionEvent>?
|
|
|
|
/// The module used to create child coordinators.
|
|
private let module: Module
|
|
|
|
/// The services used by this coordinator.
|
|
private let services: Services
|
|
|
|
// MARK: Properties
|
|
|
|
/// The stack navigator that is managed by this coordinator.
|
|
private(set) weak var stackNavigator: StackNavigator?
|
|
|
|
// MARK: Initialization
|
|
|
|
/// Creates a new `SettingsCoordinator`.
|
|
///
|
|
/// - Parameters:
|
|
/// - delegate: The delegate for this coordinator, used to notify when the user logs out.
|
|
/// - module: The module used to create child coordinators.
|
|
/// - services: The services used by this coordinator.
|
|
/// - stackNavigator: The stack navigator that is managed by this coordinator.
|
|
///
|
|
init(
|
|
module: Module,
|
|
services: Services,
|
|
stackNavigator: StackNavigator,
|
|
) {
|
|
self.module = module
|
|
self.services = services
|
|
self.stackNavigator = stackNavigator
|
|
}
|
|
|
|
// MARK: Methods
|
|
|
|
func handleEvent(_ event: SettingsEvent, context: AnyObject?) async {
|
|
switch event {
|
|
case .importItemsQrCode:
|
|
guard let delegate = context as? AuthenticatorKeyCaptureDelegate else { return }
|
|
await showImportItemsQrCode(delegate: delegate)
|
|
}
|
|
}
|
|
|
|
func navigate(to route: SettingsRoute, context: AnyObject?) {
|
|
switch route {
|
|
case .dismiss:
|
|
stackNavigator?.dismiss()
|
|
case .exportItems:
|
|
showExportItems()
|
|
case let .flightRecorder(flightRecorderRoute):
|
|
showFlightRecorder(route: flightRecorderRoute)
|
|
case .importItems:
|
|
showImportItems()
|
|
case let .importItemsFileSelection(route):
|
|
guard let delegate = context as? FileSelectionDelegate else { return }
|
|
showImportItemsFileSelection(route: route, delegate: delegate)
|
|
case let .selectLanguage(currentLanguage: currentLanguage):
|
|
showSelectLanguage(currentLanguage: currentLanguage, delegate: context as? SelectLanguageDelegate)
|
|
case .settings:
|
|
showSettings()
|
|
case let .shareExportedItems(fileUrl):
|
|
showExportedItemsUrl(fileUrl)
|
|
case .tutorial:
|
|
showTutorial()
|
|
}
|
|
}
|
|
|
|
func start() {
|
|
navigate(to: .settings)
|
|
}
|
|
|
|
// MARK: Private Methods
|
|
|
|
/// Presents an activity controller for an exported items file URL.
|
|
///
|
|
private func showExportedItemsUrl(_ fileUrl: URL) {
|
|
let activityVC = UIActivityViewController(activityItems: [fileUrl], applicationActivities: nil)
|
|
activityVC.completionWithItemsHandler = { _, completed, _, _ in
|
|
if completed {
|
|
self.showToast(Localizations.itemsExported)
|
|
}
|
|
}
|
|
if let popoverController = activityVC.popoverPresentationController {
|
|
popoverController.sourceView = stackNavigator?.rootViewController?.view
|
|
popoverController.permittedArrowDirections = []
|
|
}
|
|
|
|
stackNavigator?.present(activityVC)
|
|
}
|
|
|
|
/// Shows the export vault screen.
|
|
///
|
|
private func showExportItems() {
|
|
let processor = ExportItemsProcessor(
|
|
coordinator: asAnyCoordinator(),
|
|
services: services,
|
|
)
|
|
let view = ExportItemsView(store: Store(processor: processor))
|
|
let navController = UINavigationController(rootViewController: UIHostingController(rootView: view))
|
|
stackNavigator?.present(navController)
|
|
}
|
|
|
|
/// Shows a flight recorder view.
|
|
///
|
|
/// - Parameter route: A `FlightRecorderRoute` to navigate to.
|
|
///
|
|
private func showFlightRecorder(route: FlightRecorderRoute) {
|
|
guard let stackNavigator else { return }
|
|
let coordinator = module.makeFlightRecorderCoordinator(stackNavigator: stackNavigator)
|
|
coordinator.start()
|
|
coordinator.navigate(to: route)
|
|
}
|
|
|
|
/// Presents an activity controller for importing items.
|
|
///
|
|
private func showImportItems() {
|
|
let processor = ImportItemsProcessor(
|
|
coordinator: asAnyCoordinator(),
|
|
services: services,
|
|
)
|
|
let view = ImportItemsView(store: Store(processor: processor))
|
|
let navController = UINavigationController(rootViewController: UIHostingController(rootView: view))
|
|
stackNavigator?.present(navController)
|
|
}
|
|
|
|
/// Presents an activity controller for importing items.
|
|
///
|
|
private func showImportItemsFileSelection(route: FileSelectionRoute, delegate: FileSelectionDelegate) {
|
|
guard let stackNavigator else { return }
|
|
let coordinator = module.makeFileSelectionCoordinator(
|
|
delegate: delegate,
|
|
stackNavigator: stackNavigator,
|
|
)
|
|
coordinator.start()
|
|
coordinator.navigate(to: route)
|
|
fileSelectionCoordinator = coordinator
|
|
}
|
|
|
|
private func showImportItemsQrCode(delegate: AuthenticatorKeyCaptureDelegate) async {
|
|
let navigationController = UINavigationController()
|
|
let coordinator = AuthenticatorKeyCaptureCoordinator(
|
|
delegate: delegate,
|
|
services: services,
|
|
showManualEntry: false,
|
|
stackNavigator: navigationController,
|
|
)
|
|
coordinator.start()
|
|
|
|
await coordinator.handleEvent(.showScanCode, context: self)
|
|
stackNavigator?.present(navigationController)
|
|
}
|
|
|
|
/// Shows the select language screen.
|
|
///
|
|
private func showSelectLanguage(currentLanguage: LanguageOption, delegate: SelectLanguageDelegate?) {
|
|
let processor = SelectLanguageProcessor(
|
|
coordinator: asAnyCoordinator(),
|
|
delegate: delegate,
|
|
services: services,
|
|
state: SelectLanguageState(currentLanguage: currentLanguage),
|
|
)
|
|
let view = SelectLanguageView(store: Store(processor: processor))
|
|
let navController = UINavigationController(rootViewController: UIHostingController(rootView: view))
|
|
stackNavigator?.present(navController)
|
|
}
|
|
|
|
/// Shows the settings screen.
|
|
///
|
|
private func showSettings() {
|
|
let processor = SettingsProcessor(
|
|
coordinator: asAnyCoordinator(),
|
|
services: services,
|
|
state: SettingsState(),
|
|
)
|
|
let view = SettingsView(store: Store(processor: processor))
|
|
stackNavigator?.push(view)
|
|
}
|
|
|
|
/// Shows the welcome tutorial.
|
|
///
|
|
private func showTutorial() {
|
|
let navigationController = UINavigationController()
|
|
let coordinator = module.makeTutorialCoordinator(
|
|
stackNavigator: navigationController,
|
|
)
|
|
coordinator.start()
|
|
stackNavigator?.present(navigationController, overFullscreen: true)
|
|
}
|
|
}
|
|
|
|
// MARK: - HasErrorAlertServices
|
|
|
|
extension SettingsCoordinator: HasErrorAlertServices {
|
|
var errorAlertServices: ErrorAlertServices { services }
|
|
}
|