summaryrefslogtreecommitdiffhomepage
path: root/ios
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2023-08-15 14:18:37 +0200
committerAndrej Mihajlov <and@mullvad.net>2023-08-16 11:07:31 +0200
commit82f426e7b2b05ce480081ebba446ec8d6268876d (patch)
tree6ec3d6cff47e31db2f34ed6ef360baca5a64620f /ios
parentec960bbd6203c2a28957e68b1908e7c0dacae6b4 (diff)
downloadmullvadvpn-82f426e7b2b05ce480081ebba446ec8d6268876d.tar.xz
mullvadvpn-82f426e7b2b05ce480081ebba446ec8d6268876d.zip
Promote changelog to modal route
Diffstat (limited to 'ios')
-rw-r--r--ios/MullvadVPN/Coordinators/App/ApplicationCoordinator.swift79
-rw-r--r--ios/MullvadVPN/Coordinators/App/ApplicationRouter.swift22
-rw-r--r--ios/MullvadVPN/Coordinators/App/ChangeLogCoordinator.swift72
3 files changed, 104 insertions, 69 deletions
diff --git a/ios/MullvadVPN/Coordinators/App/ApplicationCoordinator.swift b/ios/MullvadVPN/Coordinators/App/ApplicationCoordinator.swift
index 848dffe9af..16f0dedd6b 100644
--- a/ios/MullvadVPN/Coordinators/App/ApplicationCoordinator.swift
+++ b/ios/MullvadVPN/Coordinators/App/ApplicationCoordinator.swift
@@ -172,8 +172,11 @@ final class ApplicationCoordinator: Coordinator, Presenting, RootContainerViewCo
endHorizontalFlow(animated: context.isAnimated, completion: completion)
context.dismissedRoutes.forEach { $0.coordinator.removeFromParent() }
- case .selectLocation, .account, .settings:
- let coordinator = dismissedRoute.coordinator as! Presentable
+ case .selectLocation, .account, .settings, .changelog:
+ guard let coordinator = dismissedRoute.coordinator as? Presentable else {
+ completion()
+ return assertionFailure("Expected presentable coordinator for \(dismissedRoute.route)")
+ }
coordinator.dismiss(animated: context.isAnimated, completion: completion)
}
@@ -278,39 +281,59 @@ final class ApplicationCoordinator: Coordinator, Presenting, RootContainerViewCo
Continues application flow by evaluating what route to present next.
*/
private func continueFlow(animated: Bool) {
- let next = evaluateNextRoute()
+ var nextRoutes = evaluateNextRoutes()
- /*
- On iPad the main route is always visible as it's a part of root controller hence we never
- ask router to navigate to it. Instead this is when we hide the primary horizontal
- navigation.
- */
- if isPad, next == .main {
- router.dismissAll(.primary, animated: animated)
- } else {
- router.present(next, animated: animated)
+ if isPad {
+ /*
+ On iPad the main route is always visible as it's a part of root controller hence we never
+ ask router to navigate to it. Instead this is when we hide the primary horizontal
+ navigation.
+ */
+ if nextRoutes.contains(.main) {
+ router.dismissAll(.primary, animated: animated)
+ }
+
+ nextRoutes.removeAll { $0 == .main }
+ }
+
+ for nextRoute in nextRoutes {
+ router.present(nextRoute, animated: animated)
}
}
- private func evaluateNextRoute() -> AppRoute {
+ /**
+ Evaluates conditions and returns the routes that need to be presented next.
+ */
+ private func evaluateNextRoutes() -> [AppRoute] {
+ // Show TOS alone blocking all other routes.
guard TermsOfService.isAgreed else {
- return .tos
+ return [.tos]
}
- guard ChangeLog.isSeen else {
- return .changelog
- }
+ var routes = [AppRoute]()
+ // Pick the primary route to present
switch tunnelManager.deviceState {
case .revoked:
- return .revoked
+ routes.append(.revoked)
case .loggedOut:
- return .login
+ routes.append(.login)
case let .loggedIn(accountData, _):
- return accountData.isExpired ? (accountData.isNew ? .welcome : .outOfTime) : .main
+ if accountData.isExpired {
+ routes.append(accountData.isNew ? .welcome : .outOfTime)
+ } else {
+ routes.append(.main)
+ }
}
+
+ // Changelog can be presented simultaneously with other routes.
+ if !ChangeLog.isSeen {
+ routes.append(.changelog)
+ }
+
+ return routes
}
private func logoutRevokedDevice() {
@@ -493,13 +516,19 @@ final class ApplicationCoordinator: Coordinator, Presenting, RootContainerViewCo
}
private func presentChangeLog(completion: @escaping (Coordinator) -> Void) {
- let coordinator = ChangeLogCoordinator(navigationController: primaryNavigationContainer)
+ let coordinator = ChangeLogCoordinator()
- addChild(coordinator)
- coordinator.start(animated: false)
+ coordinator.didFinish = { [weak self] in
+ ChangeLog.markAsSeen()
- continueFlow(animated: false)
- completion(coordinator)
+ self?.router.dismiss(.changelog, animated: true)
+ }
+
+ coordinator.start()
+
+ presentChild(coordinator, animated: false) {
+ completion(coordinator)
+ }
}
private func presentMain(animated: Bool, completion: @escaping (Coordinator) -> Void) {
diff --git a/ios/MullvadVPN/Coordinators/App/ApplicationRouter.swift b/ios/MullvadVPN/Coordinators/App/ApplicationRouter.swift
index c83bdf3fe1..5a8b8ecd7d 100644
--- a/ios/MullvadVPN/Coordinators/App/ApplicationRouter.swift
+++ b/ios/MullvadVPN/Coordinators/App/ApplicationRouter.swift
@@ -36,6 +36,11 @@ enum AppRouteGroup: Comparable, Equatable, Hashable {
case settings
/**
+ Changelog group.
+ */
+ case changelog
+
+ /**
Returns `true` if group is presented modally, otherwise `false` if group is a part of root view
controller.
*/
@@ -44,7 +49,7 @@ enum AppRouteGroup: Comparable, Equatable, Hashable {
case .primary:
return UIDevice.current.userInterfaceIdiom == .pad
- case .selectLocation, .account, .settings:
+ case .selectLocation, .account, .settings, .changelog:
return true
}
}
@@ -53,7 +58,7 @@ enum AppRouteGroup: Comparable, Equatable, Hashable {
switch self {
case .primary:
return 0
- case .settings, .account, .selectLocation:
+ case .settings, .account, .selectLocation, .changelog:
return 1
}
}
@@ -83,16 +88,21 @@ enum AppRoute: Equatable, Hashable {
case selectLocation
/**
+ Changelog route.
+ */
+ case changelog
+
+ /**
Routes that are part of primary horizontal navigation group.
*/
- case tos, changelog, login, main, revoked, outOfTime, welcome, setupAccountCompleted
+ case tos, login, main, revoked, outOfTime, welcome, setupAccountCompleted
/**
Returns `true` when only one route of a kind can be displayed.
*/
var isExclusive: Bool {
switch self {
- case .selectLocation, .account, .settings:
+ case .selectLocation, .account, .settings, .changelog:
return true
default:
return false
@@ -115,8 +125,10 @@ enum AppRoute: Equatable, Hashable {
*/
var routeGroup: AppRouteGroup {
switch self {
- case .tos, .changelog, .login, .main, .revoked, .outOfTime, .welcome, .setupAccountCompleted:
+ case .tos, .login, .main, .revoked, .outOfTime, .welcome, .setupAccountCompleted:
return .primary
+ case .changelog:
+ return .changelog
case .selectLocation:
return .selectLocation
case .account:
diff --git a/ios/MullvadVPN/Coordinators/App/ChangeLogCoordinator.swift b/ios/MullvadVPN/Coordinators/App/ChangeLogCoordinator.swift
index 3b4f106205..fe42cd9fbe 100644
--- a/ios/MullvadVPN/Coordinators/App/ChangeLogCoordinator.swift
+++ b/ios/MullvadVPN/Coordinators/App/ChangeLogCoordinator.swift
@@ -9,15 +9,44 @@
import MullvadLogging
import UIKit
-final class ChangeLogCoordinator: Coordinator {
+final class ChangeLogCoordinator: Coordinator, Presentable {
private let logger = Logger(label: "ChangeLogCoordinator")
- private let navigationController: UIViewController
+
+ private var alertController: CustomAlertViewController?
var presentedViewController: UIViewController {
- return navigationController
+ return alertController!
+ }
+
+ var didFinish: (() -> Void)?
+
+ func start() {
+ alertController = CustomAlertViewController(
+ header: Bundle.main.shortVersion,
+ title: NSLocalizedString(
+ "CHANGE_LOG_TITLE",
+ tableName: "Account",
+ value: "Changes in this version:",
+ comment: ""
+ ),
+ attributedMessage: readChangeLogFromFile()
+ )
+
+ alertController?.addAction(
+ title: NSLocalizedString(
+ "CHANGE_LOG_OK_ACTION",
+ tableName: "Account",
+ value: "Got it!",
+ comment: ""
+ ),
+ style: .default,
+ handler: { [weak self] in
+ self?.didFinish?()
+ }
+ )
}
- private var changeLogText: NSAttributedString? {
+ private func readChangeLogFromFile() -> NSAttributedString? {
guard let changeLogText = try? ChangeLog.readFromFile() else {
logger.error("Cannot read changelog from bundle.")
return nil
@@ -43,39 +72,4 @@ final class ChangeLogCoordinator: Coordinator {
]
)
}
-
- init(navigationController: UIViewController) {
- self.navigationController = navigationController
- }
-
- func start(animated: Bool) {
- ChangeLog.markAsSeen()
-
- guard let changeLogText else {
- return
- }
-
- let alertController = CustomAlertViewController(
- header: Bundle.main.shortVersion,
- title: NSLocalizedString(
- "CHANGE_LOG_TITLE",
- tableName: "Account",
- value: "Changes in this version:",
- comment: ""
- ),
- attributedMessage: changeLogText
- )
-
- alertController.addAction(
- title: NSLocalizedString(
- "CHANGE_LOG_OK_ACTION",
- tableName: "Account",
- value: "Got it!",
- comment: ""
- ),
- style: .default
- )
-
- presentedViewController.present(alertController, animated: animated)
- }
}