Skip to content

Commit

Permalink
Update API access methods functionality UI to conform with designs
Browse files Browse the repository at this point in the history
  • Loading branch information
Jon Petersson committed Jan 9, 2024
1 parent 0f6214d commit 3f1dd26
Show file tree
Hide file tree
Showing 52 changed files with 1,118 additions and 1,836 deletions.
116 changes: 26 additions & 90 deletions ios/MullvadVPN.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

42 changes: 19 additions & 23 deletions ios/MullvadVPN/AccessMethodRepository/AccessMethodRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,53 +34,49 @@ class AccessMethodRepository: AccessMethodRepositoryProtocol {
}

init() {
add([defaultDirectMethod, defaultBridgesMethod])
}

func add(_ method: PersistentAccessMethod) {
add([method])
}

func add(_ methods: [PersistentAccessMethod]) {
var storedMethods = fetchAll()

methods.forEach { method in
guard !storedMethods.contains(where: { $0.id == method.id }) else { return }
storedMethods.append(method)
[defaultDirectMethod, defaultBridgesMethod].forEach { method in
if !storedMethods.contains(where: { $0.id == method.id }) {
storedMethods.append(method)
}
}

do {
try writeApiAccessMethods(storedMethods)
} catch {
print("Could not add access method(s): \(methods) \nError: \(error)")
print("Could not update access methods: \(storedMethods) \nError: \(error)")
}
}

func update(_ method: PersistentAccessMethod) {
var methods = fetchAll()
func save(_ method: PersistentAccessMethod) {
var storedMethods = fetchAll()

guard let index = methods.firstIndex(where: { $0.id == method.id }) else { return }
methods[index] = method
if let index = storedMethods.firstIndex(where: { $0.id == method.id }) {
storedMethods[index] = method
} else {
storedMethods.append(method)
}

do {
try writeApiAccessMethods(methods)
try writeApiAccessMethods(storedMethods)
} catch {
print("Could not update access method: \(method) \nError: \(error)")
print("Could not update access methods: \(storedMethods) \nError: \(error)")
}
}

func delete(id: UUID) {
var methods = fetchAll()
guard let index = methods.firstIndex(where: { $0.id == id }) else { return }
var storedMethods = fetchAll()
guard let index = storedMethods.firstIndex(where: { $0.id == id }) else { return }

// Prevent removing methods that have static UUIDs and are always present.
let method = methods[index]
let method = storedMethods[index]
if !method.kind.isPermanent {
methods.remove(at: index)
storedMethods.remove(at: index)
}

do {
try writeApiAccessMethods(methods)
try writeApiAccessMethods(storedMethods)
} catch {
print("Could not delete access method with id: \(id) \nError: \(error)")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,10 @@ protocol AccessMethodRepositoryProtocol {
/// Publisher that propagates a snapshot of persistent store upon modifications.
var publisher: PassthroughSubject<[PersistentAccessMethod], Never> { get }

/// Add new access method.
/// Persist modified access method locating existing entry by id. Or, add new if id does
/// not exist.
/// - Parameter method: persistent access method model.
func add(_ method: PersistentAccessMethod)

/// Persist modified access method locating existing entry by id.
/// - Parameter method: persistent access method model.
func update(_ method: PersistentAccessMethod)
func save(_ method: PersistentAccessMethod)

/// Delete access method by id.
/// - Parameter id: an access method id.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class ProxyConfigurationTester: ProxyConfigurationTesterProtocol {
}

func cancel() {
cancellable?.cancel()
cancellable = nil
}
}
3 changes: 2 additions & 1 deletion ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,8 @@ final class ApplicationCoordinator: Coordinator, Presenting, RootContainerViewCo
presentTOS(animated: animated, completion: completion)

case .main:
presentMain(animated: animated, completion: completion)
// presentMain(animated: animated, completion: completion)
presentSettings(route: .apiAccess, animated: false, completion: completion)

case .welcome:
presentWelcome(animated: animated, completion: completion)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,19 @@

import UIKit

/// View controller used for presenting a detailed information on some topic using markdown in a scrollable text view.
/// View controller used for presenting a detailed information on some topic using a scrollable stack view.
class AboutViewController: UIViewController {
private let textView = UITextView()
private let markdown: String
private let scrollView = UIScrollView()
private let contentView = UIStackView()
private let header: String?
private let preamble: String?
private let body: [String]

init(header: String?, preamble: String?, body: [String]) {
self.header = header
self.preamble = preamble
self.body = body

init(markdown: String) {
self.markdown = markdown
super.init(nibName: nil, bundle: nil)
}

Expand All @@ -25,20 +31,62 @@ class AboutViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()

let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.paragraphSpacing = 16
view.backgroundColor = .secondaryColor
navigationController?.navigationBar.configureCustomAppeareance()

setUpContentView()

scrollView.addConstrainedSubviews([contentView]) {
contentView.pinEdgesToSuperview()
contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor)
}

view.addConstrainedSubviews([scrollView]) {
scrollView.pinEdgesToSuperview()
}
}

private func setUpContentView() {
contentView.axis = .vertical
contentView.spacing = 15
contentView.layoutMargins = UIMetrics.contentInsets
contentView.isLayoutMarginsRelativeArrangement = true

if let header {
let label = UILabel()

label.text = header
label.font = .systemFont(ofSize: 28, weight: .bold)
label.textColor = .white
label.numberOfLines = 0
label.textAlignment = .center

contentView.addArrangedSubview(label)
contentView.setCustomSpacing(32, after: label)
}

if let preamble {
let label = UILabel()

label.text = preamble
label.font = .systemFont(ofSize: 18)
label.textColor = .white
label.numberOfLines = 0
label.textAlignment = .center

contentView.addArrangedSubview(label)
contentView.setCustomSpacing(24, after: label)
}

let stylingOptions = MarkdownStylingOptions(
font: .systemFont(ofSize: 17),
paragraphStyle: paragraphStyle
)
for text in body {
let label = UILabel()

textView.attributedText = NSAttributedString(markdownString: markdown, options: stylingOptions)
textView.textContainerInset = UIMetrics.contentInsets
textView.isEditable = false
label.text = text
label.font = .systemFont(ofSize: 15)
label.textColor = .white
label.numberOfLines = 0

view.addConstrainedSubviews([textView]) {
textView.pinEdgesToSuperview()
contentView.addArrangedSubview(label)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@ import Combine
import Routing
import UIKit

class AddAccessMethodCoordinator: Coordinator, Presentable {
class AddAccessMethodCoordinator: Coordinator, Presentable, Presenting {
private let subject: CurrentValueSubject<AccessMethodViewModel, Never> = .init(AccessMethodViewModel())

var presentedViewController: UIViewController {
navigationController
}

let navigationController: UINavigationController
let accessMethodRepo: AccessMethodRepositoryProtocol
let proxyConfigurationTester: ProxyConfigurationTesterProtocol

var presentedViewController: UIViewController {
navigationController
}

init(
navigationController: UINavigationController,
accessMethodRepo: AccessMethodRepositoryProtocol,
Expand All @@ -32,38 +32,67 @@ class AddAccessMethodCoordinator: Coordinator, Presentable {
}

func start() {
let controller = AddAccessMethodViewController(
let controller = MethodSettingsViewController(
subject: subject,
interactor: AddAccessMethodInteractor(
interactor: EditAccessMethodInteractor(
subject: subject,
repo: accessMethodRepo,
proxyConfigurationTester: proxyConfigurationTester
)
),
alertPresenter: AlertPresenter(context: self)
)

setUpControllerNavigationItem(controller)
controller.delegate = self

navigationController.pushViewController(controller, animated: false)
}
}

extension AddAccessMethodCoordinator: AddAccessMethodViewControllerDelegate {
func controllerDidAdd(_ controller: AddAccessMethodViewController) {
dismiss(animated: true)
private func setUpControllerNavigationItem(_ controller: MethodSettingsViewController) {
controller.navigationItem.prompt = NSLocalizedString(
"METHOD_SETTINGS_NAVIGATION_ADD_PROMPT",
tableName: "APIAccess",
value: "The app will test the method before saving.",
comment: ""
)

controller.navigationItem.title = NSLocalizedString(
"METHOD_SETTINGS_NAVIGATION_ADD_TITLE",
tableName: "APIAccess",
value: "Add access method",
comment: ""
)

controller.saveBarButton.title = NSLocalizedString(
"METHOD_SETTINGS_NAVIGATION_ADD_BUTTON",
tableName: "APIAccess",
value: "Add",
comment: ""
)

controller.navigationItem.leftBarButtonItem = UIBarButtonItem(
systemItem: .cancel,
primaryAction: UIAction(handler: { [weak self] _ in
self?.dismiss(animated: true)
})
)
}
}

func controllerDidCancel(_ controller: AddAccessMethodViewController) {
extension AddAccessMethodCoordinator: MethodSettingsViewControllerDelegate {
func controllerDidSaveAccessMethod(_ controller: MethodSettingsViewController) {
dismiss(animated: true)
}

func controllerShouldShowProtocolPicker(_ controller: AddAccessMethodViewController) {
func controllerShouldShowProtocolPicker(_ controller: MethodSettingsViewController) {
let picker = AccessMethodProtocolPicker(navigationController: navigationController)

picker.present(currentValue: subject.value.method) { [weak self] newMethod in
self?.subject.value.method = newMethod
}
}

func controllerShouldShowShadowsocksCipherPicker(_ controller: AddAccessMethodViewController) {
func controllerShouldShowShadowsocksCipherPicker(_ controller: MethodSettingsViewController) {
let picker = ShadowsocksCipherPicker(navigationController: navigationController)

picker.present(currentValue: subject.value.shadowsocks.cipher) { [weak self] selectedCipher in
Expand Down

This file was deleted.

This file was deleted.

Loading

0 comments on commit 3f1dd26

Please sign in to comment.