diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2022-07-01 09:14:30 +0200 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2022-07-01 09:14:30 +0200 |
| commit | c2074223d06bb29b025d8541d26b23ae431f70a4 (patch) | |
| tree | b241541f08a5bf233ae8510b03f7cf2ae79a4e1e | |
| parent | 786b27643a0bd963bb180cc0af92c0e0ea466966 (diff) | |
| parent | d7dcf08bf267839fbdf67f454b617c96f37e0129 (diff) | |
| download | mullvadvpn-c2074223d06bb29b025d8541d26b23ae431f70a4.tar.xz mullvadvpn-c2074223d06bb29b025d8541d26b23ae431f70a4.zip | |
Merge branch 'update-account-view'
| -rw-r--r-- | ios/CHANGELOG.md | 1 | ||||
| -rw-r--r-- | ios/MullvadVPN.xcodeproj/project.pbxproj | 4 | ||||
| -rw-r--r-- | ios/MullvadVPN/AccountContentView.swift | 311 | ||||
| -rw-r--r-- | ios/MullvadVPN/AccountInputGroupView.swift | 6 | ||||
| -rw-r--r-- | ios/MullvadVPN/AccountViewController.swift | 36 | ||||
| -rw-r--r-- | ios/MullvadVPN/Assets.xcassets/IconCopy.imageset/Contents.json | 16 | ||||
| -rw-r--r-- | ios/MullvadVPN/Assets.xcassets/IconCopy.imageset/IconCopy.pdf | bin | 0 -> 1174 bytes | |||
| -rw-r--r-- | ios/MullvadVPN/Assets.xcassets/IconObscure.imageset/Contents.json | 16 | ||||
| -rw-r--r-- | ios/MullvadVPN/Assets.xcassets/IconObscure.imageset/IconObscure.pdf | bin | 0 -> 1481 bytes | |||
| -rw-r--r-- | ios/MullvadVPN/Assets.xcassets/IconUnobscure.imageset/Contents.json | 16 | ||||
| -rw-r--r-- | ios/MullvadVPN/Assets.xcassets/IconUnobscure.imageset/IconUnobscure.pdf | bin | 0 -> 1154 bytes | |||
| -rw-r--r-- | ios/MullvadVPN/ProblemReportReviewViewController.swift | 12 | ||||
| -rw-r--r-- | ios/MullvadVPN/UIFont+Monospaced.swift | 27 | ||||
| -rwxr-xr-x | ios/convert-assets.rb | 5 |
14 files changed, 370 insertions, 80 deletions
diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index 7efd846d3e..7847bb955f 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -26,6 +26,7 @@ Line wrap the file at 100 chars. Th ### Added - Add option to block gambling and adult content. - Add last used account field to login view. +- Display device name under account view. ### Fixed - Improve random port distribution. Should be less biased towards port 53. diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index 04b69c8b65..857f1c44be 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -58,6 +58,7 @@ 5820676426E771DB00655B05 /* TunnelManagerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5820676326E771DB00655B05 /* TunnelManagerError.swift */; }; 5820676826E79E7B00655B05 /* Result+UIBackgroundFetchResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5820676726E79E7B00655B05 /* Result+UIBackgroundFetchResult.swift */; }; 5823FA5426CE49F700283BF8 /* TunnelObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5823FA5326CE49F600283BF8 /* TunnelObserver.swift */; }; + 58289082286B590900478596 /* UIFont+Monospaced.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58289081286B590900478596 /* UIFont+Monospaced.swift */; }; 58293FAE2510CA58005D0BB5 /* ProblemReportViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58293FAC2510CA58005D0BB5 /* ProblemReportViewController.swift */; }; 58293FB125124117005D0BB5 /* CustomTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58293FB025124117005D0BB5 /* CustomTextField.swift */; }; 58293FB3251241B4005D0BB5 /* CustomTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58293FB2251241B3005D0BB5 /* CustomTextView.swift */; }; @@ -392,6 +393,7 @@ 5820676726E79E7B00655B05 /* Result+UIBackgroundFetchResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Result+UIBackgroundFetchResult.swift"; sourceTree = "<group>"; }; 5823FA4F26CA690600283BF8 /* OSLogHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSLogHandler.swift; sourceTree = "<group>"; }; 5823FA5326CE49F600283BF8 /* TunnelObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelObserver.swift; sourceTree = "<group>"; }; + 58289081286B590900478596 /* UIFont+Monospaced.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIFont+Monospaced.swift"; sourceTree = "<group>"; }; 58293FAC2510CA58005D0BB5 /* ProblemReportViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProblemReportViewController.swift; sourceTree = "<group>"; }; 58293FB025124117005D0BB5 /* CustomTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTextField.swift; sourceTree = "<group>"; }; 58293FB2251241B3005D0BB5 /* CustomTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTextView.swift; sourceTree = "<group>"; }; @@ -980,6 +982,7 @@ 58F7CA872692E34000FC59FD /* WireguardKeysContentView.swift */, 5877152F23981F7B001F8237 /* WireguardKeysViewController.swift */, 5872D6E7286304DE00DB5F4E /* TermsOfService.swift */, + 58289081286B590900478596 /* UIFont+Monospaced.swift */, ); path = MullvadVPN; sourceTree = "<group>"; @@ -1328,6 +1331,7 @@ 58059DE228468255002B1049 /* ResultOperation+Fallible.swift in Sources */, 582BB1B3229574F40055B6EF /* SettingsAccountCell.swift in Sources */, 588527B2276B3F0700BAA373 /* LoadTunnelConfigurationOperation.swift in Sources */, + 58289082286B590900478596 /* UIFont+Monospaced.swift in Sources */, 58F1311527E0B2AB007AC5BC /* Result+Extensions.swift in Sources */, 585DA88426B0270700B8C587 /* ServerRelaysResponse.swift in Sources */, 5875960726F36B3A00BF6711 /* TunnelIPCError.swift in Sources */, diff --git a/ios/MullvadVPN/AccountContentView.swift b/ios/MullvadVPN/AccountContentView.swift index e61dc0688b..b357f3d067 100644 --- a/ios/MullvadVPN/AccountContentView.swift +++ b/ios/MullvadVPN/AccountContentView.swift @@ -41,8 +41,14 @@ class AccountContentView: UIView { return button }() - let accountTokenRowView: AccountTokenRow = { - let view = AccountTokenRow() + let accountDeviceRow: AccountDeviceRow = { + let view = AccountDeviceRow() + view.translatesAutoresizingMaskIntoConstraints = false + return view + }() + + let accountTokenRowView: AccountNumberRow = { + let view = AccountNumberRow() view.translatesAutoresizingMaskIntoConstraints = false return view }() @@ -54,7 +60,7 @@ class AccountContentView: UIView { }() lazy var contentStackView: UIStackView = { - let stackView = UIStackView(arrangedSubviews: [accountTokenRowView, accountExpiryRowView]) + let stackView = UIStackView(arrangedSubviews: [accountDeviceRow, accountTokenRowView, accountExpiryRowView]) stackView.translatesAutoresizingMaskIntoConstraints = false stackView.axis = .vertical stackView.spacing = UIMetrics.sectionSpacing @@ -95,17 +101,79 @@ class AccountContentView: UIView { } } -class AccountTokenRow: UIView { +class AccountDeviceRow: UIView { - var value: String? { + var deviceName: String? { didSet { - valueButton.setTitle(value, for: .normal) - accessibilityValue = value + deviceLabel.text = deviceName?.capitalized ?? "" + accessibilityValue = deviceName } } - var actionHandler: (() -> Void)? - private let textLabel: UILabel = { + private let titleLabel: UILabel = { + let label = UILabel() + label.translatesAutoresizingMaskIntoConstraints = false + label.text = NSLocalizedString( + "DEVICE_NAME", + tableName: "Account", + value: "Device name", + comment: "" + ) + label.font = UIFont.systemFont(ofSize: 14) + label.textColor = UIColor(white: 1.0, alpha: 0.6) + return label + }() + + private let deviceLabel: UILabel = { + let label = UILabel() + label.translatesAutoresizingMaskIntoConstraints = false + label.font = UIFont.systemFont(ofSize: 17) + label.textColor = .white + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + addSubview(titleLabel) + addSubview(deviceLabel) + + NSLayoutConstraint.activate([ + titleLabel.topAnchor.constraint(equalTo: topAnchor), + titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor), + titleLabel.trailingAnchor.constraint(equalTo: trailingAnchor), + + deviceLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 8), + deviceLabel.leadingAnchor.constraint(equalTo: leadingAnchor), + deviceLabel.trailingAnchor.constraint(equalTo: trailingAnchor), + deviceLabel.bottomAnchor.constraint(equalTo: bottomAnchor) + ]) + + isAccessibilityElement = true + accessibilityLabel = titleLabel.text + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +class AccountNumberRow: UIView { + var accountNumber: String? { + didSet { + updateView() + } + } + + var isObscured = true { + didSet { + updateView() + } + } + + var copyAccountNumber: (() -> Void)? + + private let titleLabel: UILabel = { let textLabel = UILabel() textLabel.translatesAutoresizingMaskIntoConstraints = false textLabel.text = NSLocalizedString( @@ -119,63 +187,220 @@ class AccountTokenRow: UIView { return textLabel }() - private let valueButton: UIButton = { + private let accountNumberLabel: UILabel = { + let textLabel = UILabel() + textLabel.translatesAutoresizingMaskIntoConstraints = false + textLabel.font = UIFont.backport_monospacedSystemFont(ofSize: 17, weight: .regular) + textLabel.textColor = .white + return textLabel + }() + + private let showHideButton: UIButton = { let button = UIButton(type: .system) button.translatesAutoresizingMaskIntoConstraints = false - button.titleLabel?.font = UIFont.systemFont(ofSize: 17) - button.setTitleColor(.white, for: .normal) - button.contentHorizontalAlignment = .leading - button.contentEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 1) - button.accessibilityHint = NSLocalizedString( - "ACCOUNT_TOKEN_ACCESSIBILITY_HINT", - tableName: "Account", - value: "Tap to copy to pasteboard.", - comment: "" - ) + button.tintColor = .white + button.setContentHuggingPriority(.defaultHigh, for: .horizontal) return button }() + private let copyButton: UIButton = { + let button = UIButton(type: .system) + button.translatesAutoresizingMaskIntoConstraints = false + button.tintColor = .white + button.setContentHuggingPriority(.defaultHigh, for: .horizontal) + return button + }() + + private var revertCopyImageWorkItem: DispatchWorkItem? + override init(frame: CGRect) { super.init(frame: frame) - addSubview(textLabel) - addSubview(valueButton) + addSubview(titleLabel) + addSubview(accountNumberLabel) + addSubview(showHideButton) + addSubview(copyButton) NSLayoutConstraint.activate([ - textLabel.topAnchor.constraint(equalTo: topAnchor), - textLabel.leadingAnchor.constraint(equalTo: leadingAnchor), - textLabel.trailingAnchor.constraint(greaterThanOrEqualTo: trailingAnchor), + titleLabel.topAnchor.constraint(equalTo: topAnchor), + titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor), + titleLabel.trailingAnchor.constraint(greaterThanOrEqualTo: trailingAnchor), - valueButton.topAnchor.constraint(equalTo: textLabel.bottomAnchor, constant: 8), - valueButton.leadingAnchor.constraint(equalTo: leadingAnchor), - valueButton.trailingAnchor.constraint(equalTo: trailingAnchor), - valueButton.bottomAnchor.constraint(equalTo: bottomAnchor) + accountNumberLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 8), + accountNumberLabel.leadingAnchor.constraint(equalTo: leadingAnchor), + accountNumberLabel.trailingAnchor.constraint(equalTo: showHideButton.leadingAnchor), + accountNumberLabel.bottomAnchor.constraint(equalTo: bottomAnchor), + + showHideButton.heightAnchor.constraint(equalTo: accountNumberLabel.heightAnchor), + showHideButton.centerYAnchor.constraint(equalTo: accountNumberLabel.centerYAnchor), + showHideButton.leadingAnchor.constraint(equalTo: accountNumberLabel.trailingAnchor), + + copyButton.heightAnchor.constraint(equalTo: accountNumberLabel.heightAnchor), + copyButton.centerYAnchor.constraint(equalTo: accountNumberLabel.centerYAnchor), + copyButton.leadingAnchor.constraint(equalTo: showHideButton.trailingAnchor, constant: 24), + copyButton.trailingAnchor.constraint(equalTo: trailingAnchor), ]) - isAccessibilityElement = true - accessibilityLabel = textLabel.text + showHideButton.addTarget( + self, + action: #selector(didTapShowHideAccount), + for: .touchUpInside + ) - let actionName = NSLocalizedString( - "ACCOUNT_TOKEN_ACCESSIBILITY_ACTION_TITLE", - tableName: "Account", - value: "Copy account token to pasteboard", - comment: "" + copyButton.addTarget( + self, + action: #selector(didTapCopyAccountNumber), + for: .touchUpInside ) - accessibilityCustomActions = [UIAccessibilityCustomAction(name: actionName, target: self, selector: #selector(performAccessibilityAction))] - valueButton.addTarget(self, action: #selector(handleTap), for: .touchUpInside) + isAccessibilityElement = true + accessibilityLabel = titleLabel.text + + showCheckmark(false) + updateView() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - @objc private func handleTap() { - self.actionHandler?() + // MARK: - Private + + private func updateView() { + accountNumberLabel.text = displayAccountNumber ?? "" + showHideButton.setImage(showHideImage, for: .normal) + + accessibilityAttributedValue = _accessibilityAttributedValue + accessibilityCustomActions = _accessibilityCustomActions } - @objc private func performAccessibilityAction() { - self.actionHandler?() + private var displayAccountNumber: String? { + guard let accountNumber = accountNumber else { + return nil + } + + let formattedString = StringFormatter.formattedAccountNumber(from: accountNumber) + + if isObscured { + return String(formattedString.map { ch in + return ch == " " ? ch : "•" + }) + } else { + return formattedString + } + } + + private var showHideImage: UIImage? { + if isObscured { + return UIImage(named: "IconUnobscure") + } else { + return UIImage(named: "IconObscure") + } + } + + + private var _accessibilityAttributedValue: NSAttributedString? { + guard let accountNumber = accountNumber else { + return nil + } + + if isObscured { + return NSAttributedString( + string: NSLocalizedString( + "ACCOUNT_ACCESSIBILITY_OBSCURED", + tableName: "Account", + value: "Obscured", + comment: "" + ) + ) + } else { + var attributes: [NSAttributedString.Key: Any]? + if #available(iOS 13.0, *) { + attributes = [.accessibilitySpeechSpellOut: true] + } + + return NSAttributedString(string: accountNumber, attributes: attributes) + } + } + + private var _accessibilityCustomActions: [UIAccessibilityCustomAction]? { + guard accountNumber != nil else { return nil } + + return [ + UIAccessibilityCustomAction( + name: showHideAccessibilityActionName, + target: self, + selector: #selector(didTapShowHideAccount) + ), + UIAccessibilityCustomAction( + name: NSLocalizedString( + "ACCOUNT_ACCESSIBILITY_COPY_TO_PASTEBOARD", + tableName: "Account", + value: "Copy to pasteboard", + comment: "" + ), + target: self, + selector: #selector(didTapCopyAccountNumber) + ) + ] + } + + private var showHideAccessibilityActionName: String { + if isObscured { + return NSLocalizedString( + "ACCOUNT_ACCESSIBILITY_SHOW_ACCOUNT_NUMBER", + tableName: "Account", + value: "Show account number", + comment: "" + ) + } else { + return NSLocalizedString( + "ACCOUNT_ACCESSIBILITY_HIDE_ACCOUNT_NUMBER", + tableName: "Account", + value: "Hide account number", + comment: "" + ) + } + } + + private func showCheckmark(_ showCheckmark: Bool) { + if showCheckmark { + let tickIcon = UIImage(named: "IconTick") + + copyButton.setImage(tickIcon, for: .normal) + copyButton.tintColor = .successColor + } else { + let copyIcon = UIImage(named: "IconCopy") + + copyButton.setImage(copyIcon, for: .normal) + copyButton.tintColor = .white + } + } + + // MARK: - Actions + + @objc private func didTapShowHideAccount() { + isObscured.toggle() + updateView() + + UIAccessibility.post(notification: .layoutChanged, argument: nil) + } + + @objc private func didTapCopyAccountNumber() { + let delayedWorkItem = DispatchWorkItem { [weak self] in + self?.showCheckmark(false) + } + + revertCopyImageWorkItem?.cancel() + revertCopyImageWorkItem = delayedWorkItem + + showCheckmark(true) + copyAccountNumber?() + + DispatchQueue.main.asyncAfter( + deadline: .now() + .seconds(2), + execute: delayedWorkItem + ) } } @@ -206,7 +431,7 @@ class AccountExpiryRow: UIView { ) } - valueLabel.text = formattedDate + valueLabel.text = formattedDate ?? "" accessibilityValue = formattedDate valueLabel.textColor = .white diff --git a/ios/MullvadVPN/AccountInputGroupView.swift b/ios/MullvadVPN/AccountInputGroupView.swift index 7bc54d236b..6dfff6301d 100644 --- a/ios/MullvadVPN/AccountInputGroupView.swift +++ b/ios/MullvadVPN/AccountInputGroupView.swift @@ -374,11 +374,7 @@ class AccountInputGroupView: UIView { // MARK: - Private private static func accountNumberFont() -> UIFont { - if #available(iOS 13, *) { - return UIFont.monospacedSystemFont(ofSize: 20, weight: .regular) - } else { - return UIFont.systemFont(ofSize: 20) - } + return UIFont.backport_monospacedSystemFont(ofSize: 20, weight: .regular) } private func addTextFieldNotificationObservers() { diff --git a/ios/MullvadVPN/AccountViewController.swift b/ios/MullvadVPN/AccountViewController.swift index 9753d8aac9..f594e0a6c1 100644 --- a/ios/MullvadVPN/AccountViewController.swift +++ b/ios/MullvadVPN/AccountViewController.swift @@ -84,10 +84,8 @@ class AccountViewController: UIViewController, AppStorePaymentObserver, TunnelOb comment: "" ) - contentView.accountTokenRowView.value = TunnelManager.shared.accountNumber.map { string in - return StringFormatter.formattedAccountNumber(from: string) - } - contentView.accountTokenRowView.actionHandler = { [weak self] in + contentView.accountTokenRowView.accountNumber = TunnelManager.shared.accountNumber + contentView.accountTokenRowView.copyAccountNumber = { [weak self] in self?.copyAccountToken() } @@ -99,6 +97,7 @@ class AccountViewController: UIViewController, AppStorePaymentObserver, TunnelOb TunnelManager.shared.addObserver(self) updateAccountExpiry(expiryDate: TunnelManager.shared.accountExpiry) + updateDeviceName(TunnelManager.shared.device?.name) // Make sure to disable IAPs when payments are restricted if AppStorePaymentManager.canMakePayments { @@ -110,6 +109,10 @@ class AccountViewController: UIViewController, AppStorePaymentObserver, TunnelOb // MARK: - Private methods + private func updateDeviceName(_ deviceName: String?) { + contentView.accountDeviceRow.deviceName = deviceName + } + private func updateAccountExpiry(expiryDate: Date?) { contentView.accountExpiryRowView.value = expiryDate } @@ -322,7 +325,12 @@ class AccountViewController: UIViewController, AppStorePaymentObserver, TunnelOb } func tunnelManager(_ manager: TunnelManager, didUpdateTunnelSettings tunnelSettings: TunnelSettingsV2?) { - updateAccountExpiry(expiryDate: tunnelSettings?.account.expiry) + guard let tunnelSettings = tunnelSettings else { + return + } + + updateDeviceName(tunnelSettings.device.name) + updateAccountExpiry(expiryDate: tunnelSettings.account.expiry) } // MARK: - AppStorePaymentObserver @@ -377,24 +385,6 @@ class AccountViewController: UIViewController, AppStorePaymentObserver, TunnelOb private func copyAccountToken() { UIPasteboard.general.string = TunnelManager.shared.accountNumber - - contentView.accountTokenRowView.value = NSLocalizedString( - "COPIED_TO_PASTEBOARD_LABEL", - tableName: "Account", - value: "COPIED TO PASTEBOARD!", - comment: "" - ) - - let workItem = DispatchWorkItem { [weak self] in - guard let accountNumber = TunnelManager.shared.accountNumber else { return } - - self?.contentView.accountTokenRowView.value = StringFormatter.formattedAccountNumber(from: accountNumber) - } - - copyToPasteboardWork?.cancel() - copyToPasteboardWork = workItem - - DispatchQueue.main.asyncAfter(wallDeadline: .now() + .seconds(3), execute: workItem) } @objc private func doPurchase() { diff --git a/ios/MullvadVPN/Assets.xcassets/IconCopy.imageset/Contents.json b/ios/MullvadVPN/Assets.xcassets/IconCopy.imageset/Contents.json new file mode 100644 index 0000000000..761f256346 --- /dev/null +++ b/ios/MullvadVPN/Assets.xcassets/IconCopy.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "IconCopy.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/ios/MullvadVPN/Assets.xcassets/IconCopy.imageset/IconCopy.pdf b/ios/MullvadVPN/Assets.xcassets/IconCopy.imageset/IconCopy.pdf Binary files differnew file mode 100644 index 0000000000..47ec193d80 --- /dev/null +++ b/ios/MullvadVPN/Assets.xcassets/IconCopy.imageset/IconCopy.pdf diff --git a/ios/MullvadVPN/Assets.xcassets/IconObscure.imageset/Contents.json b/ios/MullvadVPN/Assets.xcassets/IconObscure.imageset/Contents.json new file mode 100644 index 0000000000..e3f7020b58 --- /dev/null +++ b/ios/MullvadVPN/Assets.xcassets/IconObscure.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "IconObscure.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/ios/MullvadVPN/Assets.xcassets/IconObscure.imageset/IconObscure.pdf b/ios/MullvadVPN/Assets.xcassets/IconObscure.imageset/IconObscure.pdf Binary files differnew file mode 100644 index 0000000000..813fa38764 --- /dev/null +++ b/ios/MullvadVPN/Assets.xcassets/IconObscure.imageset/IconObscure.pdf diff --git a/ios/MullvadVPN/Assets.xcassets/IconUnobscure.imageset/Contents.json b/ios/MullvadVPN/Assets.xcassets/IconUnobscure.imageset/Contents.json new file mode 100644 index 0000000000..c69f047004 --- /dev/null +++ b/ios/MullvadVPN/Assets.xcassets/IconUnobscure.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "IconUnobscure.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/ios/MullvadVPN/Assets.xcassets/IconUnobscure.imageset/IconUnobscure.pdf b/ios/MullvadVPN/Assets.xcassets/IconUnobscure.imageset/IconUnobscure.pdf Binary files differnew file mode 100644 index 0000000000..77452cf7b2 --- /dev/null +++ b/ios/MullvadVPN/Assets.xcassets/IconUnobscure.imageset/IconUnobscure.pdf diff --git a/ios/MullvadVPN/ProblemReportReviewViewController.swift b/ios/MullvadVPN/ProblemReportReviewViewController.swift index c8b2e8a6a9..19fc05a248 100644 --- a/ios/MullvadVPN/ProblemReportReviewViewController.swift +++ b/ios/MullvadVPN/ProblemReportReviewViewController.swift @@ -40,14 +40,10 @@ class ProblemReportReviewViewController: UIViewController { textView.translatesAutoresizingMaskIntoConstraints = false textView.text = reportString textView.isEditable = false - if #available(iOS 13.0, *) { - textView.font = UIFont.monospacedSystemFont( - ofSize: UIFont.systemFontSize, - weight: .regular - ) - } else { - textView.font = UIFont(name: "Courier", size: UIFont.systemFontSize) - } + textView.font = UIFont.backport_monospacedSystemFont( + ofSize: UIFont.systemFontSize, + weight: .regular + ) view.addSubview(textView) diff --git a/ios/MullvadVPN/UIFont+Monospaced.swift b/ios/MullvadVPN/UIFont+Monospaced.swift new file mode 100644 index 0000000000..5ce7bb633b --- /dev/null +++ b/ios/MullvadVPN/UIFont+Monospaced.swift @@ -0,0 +1,27 @@ +// +// UIFont+Monospaced.swift +// MullvadVPN +// +// Created by pronebird on 28/06/2022. +// Copyright © 2022 Mullvad VPN AB. All rights reserved. +// + +import UIKit + +extension UIFont { + class func backport_monospacedSystemFont(ofSize size: CGFloat, weight: UIFont.Weight) -> UIFont + { + if #available(iOS 13, *) { + return UIFont.monospacedSystemFont(ofSize: size, weight: weight) + } else { + let fontDescriptor = UIFontDescriptor(fontAttributes: [ + .name: "Menlo", + .traits: [ + UIFontDescriptor.TraitKey.weight: weight + ] + ]) + + return UIFont(descriptor: fontDescriptor, size: size) + } + } +} diff --git a/ios/convert-assets.rb b/ios/convert-assets.rb index 5d8be56277..5a26616e9e 100755 --- a/ios/convert-assets.rb +++ b/ios/convert-assets.rb @@ -32,7 +32,10 @@ GRAPHICAL_ASSETS = [ "location-marker-unsecure.svg", "logo-icon.svg", "logo-text.svg", - "icon-close-sml.svg" + "icon-close-sml.svg", + "icon-copy.svg", + "icon-obscure.svg", + "icon-unobscure.svg" ] # App icon sizes |
