diff options
| author | Jon Petersson <jon.petersson@kvadrat.se> | 2023-06-22 16:47:22 +0200 |
|---|---|---|
| committer | Bug Magnet <marco.nikic@mullvad.net> | 2023-08-10 11:50:39 +0200 |
| commit | fd1bfc1c7d3b1ff4614a4bd6047f89071891adbe (patch) | |
| tree | 4666deb88fb7a0467625b38a5585475e4ad3510c | |
| parent | 5b5a4a68b044729ae9d7f1f8247ed35dbb4e8863 (diff) | |
| download | mullvadvpn-fd1bfc1c7d3b1ff4614a4bd6047f89071891adbe.tar.xz mullvadvpn-fd1bfc1c7d3b1ff4614a4bd6047f89071891adbe.zip | |
Rework change log to be compliant with our design
13 files changed, 161 insertions, 279 deletions
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index d71fb082c5..6614c287fc 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -160,10 +160,8 @@ 5878A279290954790096FC88 /* TunnelViewControllerInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5878A278290954790096FC88 /* TunnelViewControllerInteractor.swift */; }; 5878A27B2909649A0096FC88 /* CustomOverlayRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5878A27A2909649A0096FC88 /* CustomOverlayRenderer.swift */; }; 5878A27D2909657C0096FC88 /* RevokedDeviceInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5878A27C2909657C0096FC88 /* RevokedDeviceInteractor.swift */; }; - 5878F4FC29CDA2E4003D4BE2 /* ChangeLogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5878F4FB29CDA2E4003D4BE2 /* ChangeLogViewController.swift */; }; 5878F50029CDA742003D4BE2 /* UIView+AutoLayoutBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5878F4FF29CDA742003D4BE2 /* UIView+AutoLayoutBuilder.swift */; }; 5878F50229CDB989003D4BE2 /* ChangeLogCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5878F50129CDB989003D4BE2 /* ChangeLogCoordinator.swift */; }; - 5878F50429CDC547003D4BE2 /* ChangeLogContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5878F50329CDC547003D4BE2 /* ChangeLogContentView.swift */; }; 587988C728A2A01F00E3DF54 /* AccountDataThrottling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587988C628A2A01F00E3DF54 /* AccountDataThrottling.swift */; }; 587A01FC23F1F0BE00B68763 /* SimulatorTunnelProviderHost.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587A01FB23F1F0BE00B68763 /* SimulatorTunnelProviderHost.swift */; }; 587AD7C623421D7000E93A53 /* TunnelSettingsV1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587AD7C523421D7000E93A53 /* TunnelSettingsV1.swift */; }; @@ -396,6 +394,7 @@ 7ABE318D2A1CDD4500DF4963 /* UIFont+Weight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABE318C2A1CDD4500DF4963 /* UIFont+Weight.swift */; }; 7AE47E522A17972A000418DA /* CustomAlertViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE47E512A17972A000418DA /* CustomAlertViewController.swift */; }; 7AF0419E29E957EB00D492DD /* AccountCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF0419D29E957EB00D492DD /* AccountCoordinator.swift */; }; + 7AF9BE992A4E0FE900DBFEDB /* MarkdownStylingOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF9BE982A4E0FE900DBFEDB /* MarkdownStylingOptions.swift */; }; A917351F29FAA9C400D5DCFD /* RESTTransportStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A917351E29FAA9C400D5DCFD /* RESTTransportStrategy.swift */; }; A917352129FAAA5200D5DCFD /* TransportStrategyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A917352029FAAA5200D5DCFD /* TransportStrategyTests.swift */; }; A92ECC212A77FFAF0052F1B1 /* TunnelSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92ECC202A77FFAF0052F1B1 /* TunnelSettings.swift */; }; @@ -1012,10 +1011,8 @@ 5878A278290954790096FC88 /* TunnelViewControllerInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelViewControllerInteractor.swift; sourceTree = "<group>"; }; 5878A27A2909649A0096FC88 /* CustomOverlayRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomOverlayRenderer.swift; sourceTree = "<group>"; }; 5878A27C2909657C0096FC88 /* RevokedDeviceInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RevokedDeviceInteractor.swift; sourceTree = "<group>"; }; - 5878F4FB29CDA2E4003D4BE2 /* ChangeLogViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangeLogViewController.swift; sourceTree = "<group>"; }; 5878F4FF29CDA742003D4BE2 /* UIView+AutoLayoutBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+AutoLayoutBuilder.swift"; sourceTree = "<group>"; }; 5878F50129CDB989003D4BE2 /* ChangeLogCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangeLogCoordinator.swift; sourceTree = "<group>"; }; - 5878F50329CDC547003D4BE2 /* ChangeLogContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangeLogContentView.swift; sourceTree = "<group>"; }; 587988C628A2A01F00E3DF54 /* AccountDataThrottling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDataThrottling.swift; sourceTree = "<group>"; }; 587A01FB23F1F0BE00B68763 /* SimulatorTunnelProviderHost.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimulatorTunnelProviderHost.swift; sourceTree = "<group>"; }; 587AD7C523421D7000E93A53 /* TunnelSettingsV1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TunnelSettingsV1.swift; sourceTree = "<group>"; }; @@ -1198,6 +1195,7 @@ 7ABE318C2A1CDD4500DF4963 /* UIFont+Weight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIFont+Weight.swift"; sourceTree = "<group>"; }; 7AE47E512A17972A000418DA /* CustomAlertViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomAlertViewController.swift; sourceTree = "<group>"; }; 7AF0419D29E957EB00D492DD /* AccountCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountCoordinator.swift; sourceTree = "<group>"; }; + 7AF9BE982A4E0FE900DBFEDB /* MarkdownStylingOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownStylingOptions.swift; sourceTree = "<group>"; }; A917351E29FAA9C400D5DCFD /* RESTTransportStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RESTTransportStrategy.swift; sourceTree = "<group>"; }; A917352029FAAA5200D5DCFD /* TransportStrategyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransportStrategyTests.swift; sourceTree = "<group>"; }; A92ECC202A77FFAF0052F1B1 /* TunnelSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelSettings.swift; sourceTree = "<group>"; }; @@ -1809,6 +1807,7 @@ 58138E60294871C600684F0C /* DeviceDataThrottling.swift */, 7A7AD28C29DC677800480EF1 /* FirstTimeLaunch.swift */, 582AE30F2440A6CA00E6733A /* InputTextFormatter.swift */, + 7AF9BE982A4E0FE900DBFEDB /* MarkdownStylingOptions.swift */, 58CC40EE24A601900019D96E /* ObserverList.swift */, 5872D6E7286304DE00DB5F4E /* TermsOfService.swift */, ); @@ -1919,15 +1918,6 @@ path = Operations; sourceTree = "<group>"; }; - 5878F4FA29CDA2D4003D4BE2 /* ChangeLog */ = { - isa = PBXGroup; - children = ( - 5878F4FB29CDA2E4003D4BE2 /* ChangeLogViewController.swift */, - 5878F50329CDC547003D4BE2 /* ChangeLogContentView.swift */, - ); - path = ChangeLog; - sourceTree = "<group>"; - }; 587B75422669034500DEF7E9 /* Notification Providers */ = { isa = PBXGroup; children = ( @@ -3268,13 +3258,13 @@ 5878A27B2909649A0096FC88 /* CustomOverlayRenderer.swift in Sources */, 5847D58D29B7740F008C3808 /* RevokedCoordinator.swift in Sources */, 588527B2276B3F0700BAA373 /* LoadTunnelConfigurationOperation.swift in Sources */, + 7AF9BE992A4E0FE900DBFEDB /* MarkdownStylingOptions.swift in Sources */, 5867770E29096984006F721F /* OutOfTimeInteractor.swift in Sources */, 58F185AA298A3E3E00075977 /* TunnelCoordinator.swift in Sources */, F03580252A13842C00E5DAFD /* IncreasedHitButton.swift in Sources */, 58F8AC0E25D3F8CE002BE0ED /* ProblemReportReviewViewController.swift in Sources */, F028A56E2A34DCC600C0CAA3 /* RedeemVoucherInteractor.swift in Sources */, 5878A27129091CF20096FC88 /* AccountInteractor.swift in Sources */, - 5878F4FC29CDA2E4003D4BE2 /* ChangeLogViewController.swift in Sources */, 068CE5742927B7A400A068BB /* Migration.swift in Sources */, A92ECC282A7802AB0052F1B1 /* StoredDeviceData.swift in Sources */, 58CCA010224249A1004F3011 /* TunnelViewController.swift in Sources */, @@ -3419,7 +3409,6 @@ 587D9676288989DB00CD8F1C /* NSLayoutConstraint+Helpers.swift in Sources */, F028A56C2A34D8E600C0CAA3 /* RedeemVoucherSucceededViewController.swift in Sources */, 58293FAE2510CA58005D0BB5 /* ProblemReportViewController.swift in Sources */, - 5878F50429CDC547003D4BE2 /* ChangeLogContentView.swift in Sources */, F028A5492A336E8500C0CAA3 /* VoucherTextField.swift in Sources */, 58B9EB152489139B00095626 /* RESTError+Display.swift in Sources */, 587B753F2668E5A700DEF7E9 /* NotificationContainerView.swift in Sources */, diff --git a/ios/MullvadVPN/Classes/CustomAlertViewController.swift b/ios/MullvadVPN/Classes/CustomAlertViewController.swift index 0720785419..cb6b92d1df 100644 --- a/ios/MullvadVPN/Classes/CustomAlertViewController.swift +++ b/ios/MullvadVPN/Classes/CustomAlertViewController.swift @@ -16,7 +16,7 @@ class CustomAlertViewController: UIViewController { case info case spinner - var image: UIImage? { + fileprivate var image: UIImage? { switch self { case .alert: return UIImage(named: "IconAlert")?.withTintColor(.dangerColor) @@ -59,38 +59,65 @@ class CustomAlertViewController: UIViewController { private var handlers = [UIButton: Handler]() - init(title: String? = nil, message: String? = nil, icon: Icon? = nil) { + init(header: String? = nil, title: String? = nil, message: String? = nil, icon: Icon? = nil) { super.init(nibName: nil, bundle: nil) + setUp(header: header, title: title, icon: icon) { + message.flatMap { addMessage($0) } + } + } + + init(header: String? = nil, title: String? = nil, attributedMessage: NSAttributedString?, icon: Icon? = nil) { + super.init(nibName: nil, bundle: nil) + + setUp(header: header, title: title, icon: icon) { + attributedMessage.flatMap { addMessage($0) } + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // This code runs before viewDidLoad(). As such, no implicit calls to self.view should be made before this point. + private func setUp(header: String?, title: String?, icon: Icon?, addMessageCallback: () -> Void) { modalPresentationStyle = .overFullScreen modalTransitionStyle = .crossDissolve icon.flatMap { addIcon($0) } + header.flatMap { addHeader($0) } title.flatMap { addTitle($0) } - message.flatMap { addMessage($0) } + addMessageCallback() containerView.arrangedSubviews.last.flatMap { containerView.setCustomSpacing(UIMetrics.CustomAlert.containerMargins.top, after: $0) } // Icon only alerts should have equal top and bottom margin. - if title == nil, message == nil { + if icon != nil, containerView.arrangedSubviews.count == 1 { containerView.directionalLayoutMargins.bottom = UIMetrics.CustomAlert.containerMargins.top } } - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .black.withAlphaComponent(0.5) view.addConstrainedSubviews([containerView]) { - containerView.pinEdges(.init([.leading(0), .trailing(0)]), to: view.layoutMarginsGuide) + containerView.centerXAnchor.constraint(equalTo: view.centerXAnchor) containerView.centerYAnchor.constraint(equalTo: view.centerYAnchor) + + containerView.widthAnchor + .constraint(lessThanOrEqualToConstant: UIMetrics.preferredFormSheetContentSize.width) + + containerView.leadingAnchor + .constraint(equalTo: view.layoutMarginsGuide.leadingAnchor) + .withPriority(.defaultHigh) + + view.layoutMarginsGuide.trailingAnchor + .constraint(equalTo: containerView.trailingAnchor) + .withPriority(.defaultHigh) } } @@ -107,29 +134,46 @@ class CustomAlertViewController: UIViewController { handler.flatMap { handlers[button] = $0 } } + private func addHeader(_ title: String) { + let header = UILabel() + + header.text = title + header.font = .preferredFont(forTextStyle: .largeTitle, weight: .bold) + header.textColor = .white + header.adjustsFontForContentSizeCategory = true + header.textAlignment = .center + header.numberOfLines = 0 + + containerView.addArrangedSubview(header) + containerView.setCustomSpacing(16, after: header) + } + private func addTitle(_ title: String) { let label = UILabel() label.text = title label.font = .preferredFont(forTextStyle: .title3, weight: .semibold) - label.textColor = .white + label.textColor = .white.withAlphaComponent(0.9) label.adjustsFontForContentSizeCategory = true label.numberOfLines = 0 containerView.addArrangedSubview(label) + containerView.setCustomSpacing(8, after: label) } private func addMessage(_ message: String) { let label = UILabel() + let font = UIFont.preferredFont(forTextStyle: .body) let style = NSMutableParagraphStyle() style.paragraphSpacing = 16 + style.lineBreakMode = .byWordWrapping label.attributedText = NSAttributedString( markdownString: message, - options: .init(font: .preferredFont(forTextStyle: .body), paragraphStyle: style) + options: MarkdownStylingOptions(font: font, paragraphStyle: style) ) - label.font = .preferredFont(forTextStyle: .body) + label.font = font label.textColor = .white.withAlphaComponent(0.8) label.adjustsFontForContentSizeCategory = true label.numberOfLines = 0 @@ -137,6 +181,16 @@ class CustomAlertViewController: UIViewController { containerView.addArrangedSubview(label) } + private func addMessage(_ message: NSAttributedString) { + let label = UILabel() + + label.attributedText = message + label.adjustsFontForContentSizeCategory = true + label.numberOfLines = 0 + + containerView.addArrangedSubview(label) + } + private func addIcon(_ icon: Icon) { let iconView = icon == .spinner ? getSpinnerView() : getImageView(for: icon) containerView.addArrangedSubview(iconView) diff --git a/ios/MullvadVPN/Classes/MarkdownStylingOptions.swift b/ios/MullvadVPN/Classes/MarkdownStylingOptions.swift new file mode 100644 index 0000000000..1b2e2390d7 --- /dev/null +++ b/ios/MullvadVPN/Classes/MarkdownStylingOptions.swift @@ -0,0 +1,19 @@ +// +// MarkdownStylingOptions.swift +// MullvadVPN +// +// Created by Jon Petersson on 2023-06-29. +// Copyright © 2023 Mullvad VPN AB. All rights reserved. +// + +import UIKit + +struct MarkdownStylingOptions { + var font: UIFont + var paragraphStyle: NSParagraphStyle = .default + + var boldFont: UIFont { + let fontDescriptor = font.fontDescriptor.withSymbolicTraits(.traitBold) ?? font.fontDescriptor + return UIFont(descriptor: fontDescriptor, size: font.pointSize) + } +} diff --git a/ios/MullvadVPN/Coordinators/App/ApplicationCoordinator.swift b/ios/MullvadVPN/Coordinators/App/ApplicationCoordinator.swift index ce421879e9..848dffe9af 100644 --- a/ios/MullvadVPN/Coordinators/App/ApplicationCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/App/ApplicationCoordinator.swift @@ -12,11 +12,6 @@ import RelayCache import UIKit /** - Preferred content size for controllers presented using formsheet modal presentation style. - */ -private let preferredFormSheetContentSize = CGSize(width: 480, height: 640) - -/** Application coordinator managing split view and two navigation contexts. */ final class ApplicationCoordinator: Coordinator, Presenting, RootContainerViewControllerDelegate, @@ -46,7 +41,7 @@ final class ApplicationCoordinator: Coordinator, Presenting, RootContainerViewCo private let secondaryNavigationContainer = RootContainerViewController() private lazy var secondaryRootConfiguration = ModalPresentationConfiguration( - preferredContentSize: preferredFormSheetContentSize, + preferredContentSize: UIMetrics.preferredFormSheetContentSize, modalPresentationStyle: .custom, isModalInPresentation: true, transitioningDelegate: SecondaryContextTransitioningDelegate() @@ -148,7 +143,7 @@ final class ApplicationCoordinator: Coordinator, Presenting, RootContainerViewCo presentLogin(animated: animated, completion: completion) case .changelog: - presentChangeLog(animated: animated, completion: completion) + presentChangeLog(completion: completion) case .tos: presentTOS(animated: animated, completion: completion) @@ -497,19 +492,14 @@ final class ApplicationCoordinator: Coordinator, Presenting, RootContainerViewCo } } - private func presentChangeLog(animated: Bool, completion: @escaping (Coordinator) -> Void) { - let coordinator = ChangeLogCoordinator(navigationController: horizontalFlowController) - - coordinator.didFinish = { [weak self] coordinator in - self?.continueFlow(animated: true) - } + private func presentChangeLog(completion: @escaping (Coordinator) -> Void) { + let coordinator = ChangeLogCoordinator(navigationController: primaryNavigationContainer) addChild(coordinator) - coordinator.start(animated: animated) + coordinator.start(animated: false) - beginHorizontalFlow(animated: animated) { - completion(coordinator) - } + continueFlow(animated: false) + completion(coordinator) } private func presentMain(animated: Bool, completion: @escaping (Coordinator) -> Void) { @@ -703,7 +693,7 @@ final class ApplicationCoordinator: Coordinator, Presenting, RootContainerViewCo coordinator, animated: animated, configuration: ModalPresentationConfiguration( - preferredContentSize: preferredFormSheetContentSize, + preferredContentSize: UIMetrics.preferredFormSheetContentSize, modalPresentationStyle: .formSheet ) ) { [weak self] in @@ -747,7 +737,7 @@ final class ApplicationCoordinator: Coordinator, Presenting, RootContainerViewCo coordinator, animated: animated, configuration: ModalPresentationConfiguration( - preferredContentSize: preferredFormSheetContentSize, + preferredContentSize: UIMetrics.preferredFormSheetContentSize, modalPresentationStyle: .formSheet ) ) { diff --git a/ios/MullvadVPN/Coordinators/App/ChangeLogCoordinator.swift b/ios/MullvadVPN/Coordinators/App/ChangeLogCoordinator.swift index feed4aa354..3b4f106205 100644 --- a/ios/MullvadVPN/Coordinators/App/ChangeLogCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/App/ChangeLogCoordinator.swift @@ -11,36 +11,71 @@ import UIKit final class ChangeLogCoordinator: Coordinator { private let logger = Logger(label: "ChangeLogCoordinator") + private let navigationController: UIViewController - let navigationController: RootContainerViewController + var presentedViewController: UIViewController { + return navigationController + } + + private var changeLogText: NSAttributedString? { + guard let changeLogText = try? ChangeLog.readFromFile() else { + logger.error("Cannot read changelog from bundle.") + return nil + } - var didFinish: ((ChangeLogCoordinator) -> Void)? + let bullet = "• " + let font = UIFont.preferredFont(forTextStyle: .body) - init(navigationController: RootContainerViewController) { + let bulletList = changeLogText.split(whereSeparator: { $0.isNewline }) + .map { "\(bullet)\($0)" } + .joined(separator: "\n") + + let paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.lineBreakMode = .byWordWrapping + paragraphStyle.headIndent = bullet.size(withAttributes: [.font: font]).width + + return NSAttributedString( + string: bulletList, + attributes: [ + .paragraphStyle: paragraphStyle, + .font: font, + .foregroundColor: UIColor.white.withAlphaComponent(0.8), + ] + ) + } + + init(navigationController: UIViewController) { self.navigationController = navigationController } func start(animated: Bool) { - let controller = ChangeLogViewController() - - controller.setApplicationVersion(Bundle.main.shortVersion) - - do { - let string = try ChangeLog.readFromFile() + ChangeLog.markAsSeen() - controller.setChangeLogText(string) - } catch { - logger.error(error: error, message: "Cannot read changelog from bundle.") + guard let changeLogText else { + return } - controller.onFinish = { [weak self] in - guard let self else { return } + let alertController = CustomAlertViewController( + header: Bundle.main.shortVersion, + title: NSLocalizedString( + "CHANGE_LOG_TITLE", + tableName: "Account", + value: "Changes in this version:", + comment: "" + ), + attributedMessage: changeLogText + ) - ChangeLog.markAsSeen() - - didFinish?(self) - } + alertController.addAction( + title: NSLocalizedString( + "CHANGE_LOG_OK_ACTION", + tableName: "Account", + value: "Got it!", + comment: "" + ), + style: .default + ) - navigationController.pushViewController(controller, animated: animated) + presentedViewController.present(alertController, animated: animated) } } diff --git a/ios/MullvadVPN/Extensions/NSAttributedString+Markdown.swift b/ios/MullvadVPN/Extensions/NSAttributedString+Markdown.swift index d273e2ad00..173888f606 100644 --- a/ios/MullvadVPN/Extensions/NSAttributedString+Markdown.swift +++ b/ios/MullvadVPN/Extensions/NSAttributedString+Markdown.swift @@ -13,16 +13,6 @@ extension NSAttributedString { case bold } - struct MarkdownStylingOptions { - var font: UIFont - var paragraphStyle: NSParagraphStyle = .default - - var boldFont: UIFont { - let fontDescriptor = font.fontDescriptor.withSymbolicTraits(.traitBold) ?? font.fontDescriptor - return UIFont(descriptor: fontDescriptor, size: font.pointSize) - } - } - convenience init( markdownString: String, options: MarkdownStylingOptions, diff --git a/ios/MullvadVPN/Notifications/Notification Providers/RegisteredDeviceInAppNotificationProvider.swift b/ios/MullvadVPN/Notifications/Notification Providers/RegisteredDeviceInAppNotificationProvider.swift index b00b9f0834..bd16fc41d0 100644 --- a/ios/MullvadVPN/Notifications/Notification Providers/RegisteredDeviceInAppNotificationProvider.swift +++ b/ios/MullvadVPN/Notifications/Notification Providers/RegisteredDeviceInAppNotificationProvider.swift @@ -32,7 +32,7 @@ final class RegisteredDeviceInAppNotificationProvider: NotificationProvider, let deviceName = storedDeviceData?.capitalizedName ?? "" let string = String(format: formattedString, deviceName) - let stylingOptions = NSAttributedString.MarkdownStylingOptions(font: .systemFont(ofSize: 14.0)) + let stylingOptions = MarkdownStylingOptions(font: .systemFont(ofSize: 14.0)) return NSAttributedString(markdownString: string, options: stylingOptions) { markdownType, string in switch markdownType { diff --git a/ios/MullvadVPN/UI appearance/UIMetrics.swift b/ios/MullvadVPN/UI appearance/UIMetrics.swift index 56f07102b3..7ae20ade94 100644 --- a/ios/MullvadVPN/UI appearance/UIMetrics.swift +++ b/ios/MullvadVPN/UI appearance/UIMetrics.swift @@ -129,4 +129,7 @@ extension UIMetrics { static let padding32: CGFloat = 32 static let padding40: CGFloat = 40 static let padding48: CGFloat = 48 + + /// Preferred content size for controllers presented using formsheet modal presentation style. + static let preferredFormSheetContentSize = CGSize(width: 480, height: 640) } diff --git a/ios/MullvadVPN/View controllers/AccountDeletion/AccountDeletionContentView.swift b/ios/MullvadVPN/View controllers/AccountDeletion/AccountDeletionContentView.swift index b2436e3c9c..5dbbd3ca4c 100644 --- a/ios/MullvadVPN/View controllers/AccountDeletion/AccountDeletionContentView.swift +++ b/ios/MullvadVPN/View controllers/AccountDeletion/AccountDeletionContentView.swift @@ -314,10 +314,9 @@ class AccountDeletionContentView: UIView { ) messageLabel.attributedText = NSAttributedString( markdownString: text, - options: NSAttributedString - .MarkdownStylingOptions( - font: .preferredFont(forTextStyle: .body) - ) + options: MarkdownStylingOptions( + font: .preferredFont(forTextStyle: .body) + ) ) } } diff --git a/ios/MullvadVPN/View controllers/ChangeLog/ChangeLogContentView.swift b/ios/MullvadVPN/View controllers/ChangeLog/ChangeLogContentView.swift deleted file mode 100644 index 39078e1067..0000000000 --- a/ios/MullvadVPN/View controllers/ChangeLog/ChangeLogContentView.swift +++ /dev/null @@ -1,144 +0,0 @@ -// -// ChangeLogContentView.swift -// MullvadVPN -// -// Created by pronebird on 24/03/2023. -// Copyright © 2023 Mullvad VPN AB. All rights reserved. -// - -import UIKit - -final class ChangeLogContentView: UIView { - private let titleLabel: UILabel = { - let titleLabel = UILabel() - titleLabel.font = .systemFont(ofSize: 24, weight: .bold) - titleLabel.numberOfLines = 0 - titleLabel.textColor = .white - titleLabel.allowsDefaultTighteningForTruncation = true - titleLabel.lineBreakMode = .byWordWrapping - if #available(iOS 14.0, *) { - // See: https://stackoverflow.com/q/46200027/351305 - titleLabel.lineBreakStrategy = [] - } - return titleLabel - }() - - private let subheadLabel: UILabel = { - let subheadLabel = UILabel() - subheadLabel.font = .systemFont(ofSize: 18, weight: .bold) - subheadLabel.numberOfLines = 0 - subheadLabel.textColor = .white - subheadLabel.allowsDefaultTighteningForTruncation = true - subheadLabel.lineBreakMode = .byWordWrapping - if #available(iOS 14.0, *) { - // See: https://stackoverflow.com/q/46200027/351305 - subheadLabel.lineBreakStrategy = [] - } - subheadLabel.text = NSLocalizedString( - "CHANGES_IN_THIS_VERSION", - tableName: "ChangeLog", - value: "Changes in this version:", - comment: "" - ) - return subheadLabel - }() - - private let textView: UITextView = { - let textView = UITextView() - textView.backgroundColor = .clear - textView.isEditable = false - textView.isSelectable = false - textView.textContainerInset = UIMetrics.contentInsets - return textView - }() - - private let okButton: AppButton = { - let button = AppButton(style: .default) - button.accessibilityIdentifier = "OkButton" - button.setTitle(NSLocalizedString( - "OK_BUTTON", - tableName: "ChangeLog", - value: "Got it", - comment: "" - ), for: .normal) - return button - }() - - private let footerContainer: UIView = { - let container = UIView() - container.directionalLayoutMargins = UIMetrics.contentLayoutMargins - container.backgroundColor = .secondaryColor - return container - }() - - var didTapButton: (() -> Void)? - - override init(frame: CGRect) { - super.init(frame: frame) - - backgroundColor = .primaryColor - directionalLayoutMargins = UIMetrics.contentLayoutMargins - - okButton.addTarget(self, action: #selector(handleButtonTap), for: .touchUpInside) - - addSubviews() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func setApplicationVersion(_ string: String) { - titleLabel.text = string - } - - func setChangeLogText(_ string: String) { - let bullet = "• " - let font = UIFont.systemFont(ofSize: 18) - - let bulletList = string.split(whereSeparator: { $0.isNewline }) - .map { "\(bullet)\($0)" } - .joined(separator: "\n") - - let paragraphStyle = NSMutableParagraphStyle() - paragraphStyle.lineHeightMultiple = 1.5 - paragraphStyle.lineBreakMode = .byWordWrapping - paragraphStyle.headIndent = bullet.size(withAttributes: [.font: font]).width - - textView.attributedText = NSAttributedString( - string: bulletList, - attributes: [ - .paragraphStyle: paragraphStyle, - .font: font, - .foregroundColor: UIColor.white, - ] - ) - } - - private func addSubviews() { - footerContainer.setContentCompressionResistancePriority(.defaultHigh, for: .vertical) - - footerContainer.addConstrainedSubviews([okButton]) { - okButton.pinEdgesToSuperviewMargins() - } - - addConstrainedSubviews([titleLabel, subheadLabel, textView, footerContainer]) { - titleLabel.pinEdgesToSuperviewMargins(.all().excluding(.bottom)) - subheadLabel.pinEdgesToSuperviewMargins(.init([.leading(0), .trailing(0)])) - subheadLabel.topAnchor.constraint( - equalToSystemSpacingBelow: titleLabel.bottomAnchor, - multiplier: 1 - ) - - textView.topAnchor.constraint(equalTo: subheadLabel.bottomAnchor) - textView.pinEdgesToSuperview(.init([.leading(0), .trailing(0)])) - - footerContainer.pinEdgesToSuperview(.all().excluding(.top)) - footerContainer.topAnchor.constraint(equalTo: textView.bottomAnchor) - } - } - - @objc private func handleButtonTap() { - didTapButton?() - } -} diff --git a/ios/MullvadVPN/View controllers/ChangeLog/ChangeLogViewController.swift b/ios/MullvadVPN/View controllers/ChangeLog/ChangeLogViewController.swift deleted file mode 100644 index 6bea0b3f37..0000000000 --- a/ios/MullvadVPN/View controllers/ChangeLog/ChangeLogViewController.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// ChangeLogViewController.swift -// MullvadVPN -// -// Created by pronebird on 24/03/2023. -// Copyright © 2023 Mullvad VPN AB. All rights reserved. -// - -import UIKit - -class ChangeLogViewController: UIViewController, RootContainment { - // MARK: - RootContainment - - var preferredHeaderBarPresentation: HeaderBarPresentation { - HeaderBarPresentation(style: .default, showsDivider: false) - } - - var prefersHeaderBarHidden: Bool { - false - } - - var prefersNotificationBarHidden: Bool { - true - } - - // MARK: - Public - - var onFinish: (() -> Void)? - - func setApplicationVersion(_ string: String) { - contentView.setApplicationVersion(string) - } - - func setChangeLogText(_ string: String) { - contentView.setChangeLogText(string) - } - - // MARK: - View lifecycle - - private let contentView = ChangeLogContentView() - - override var preferredStatusBarStyle: UIStatusBarStyle { - .lightContent - } - - override func loadView() { - view = contentView - - contentView.didTapButton = { [weak self] in - self?.onFinish?() - } - } -} diff --git a/ios/MullvadVPN/View controllers/OutOfTime/OutOfTimeContentView.swift b/ios/MullvadVPN/View controllers/OutOfTime/OutOfTimeContentView.swift index b55cfaf386..5f4113660d 100644 --- a/ios/MullvadVPN/View controllers/OutOfTime/OutOfTimeContentView.swift +++ b/ios/MullvadVPN/View controllers/OutOfTime/OutOfTimeContentView.swift @@ -146,7 +146,7 @@ class OutOfTimeContentView: UIView { func setBodyLabelText(_ text: String) { bodyLabel.attributedText = NSAttributedString( markdownString: text, - options: NSAttributedString.MarkdownStylingOptions(font: .systemFont(ofSize: 17)) + options: MarkdownStylingOptions(font: .systemFont(ofSize: 17)) ) } } diff --git a/ios/MullvadVPN/View controllers/Preferences/PreferencesViewModel.swift b/ios/MullvadVPN/View controllers/Preferences/PreferencesViewModel.swift index 2c68adb97b..091be496a5 100644 --- a/ios/MullvadVPN/View controllers/Preferences/PreferencesViewModel.swift +++ b/ios/MullvadVPN/View controllers/Preferences/PreferencesViewModel.swift @@ -55,7 +55,7 @@ enum CustomDNSPrecondition { value: "Tap **Edit** to add at least one DNS server.", comment: "Foot note displayed if there are no DNS entries, but table view is not in editing mode." ), - options: NSAttributedString.MarkdownStylingOptions(font: preferredFont) + options: MarkdownStylingOptions(font: preferredFont) ) } |
