-
-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Issue #46: Added first implementation of first new scene for bridge s…
…election.
- Loading branch information
1 parent
2991c2a
commit 0fb6e2d
Showing
4 changed files
with
283 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
// | ||
// BridgesViewController.swift | ||
// Orbot | ||
// | ||
// Created by Benjamin Erhart on 10.01.23. | ||
// Copyright © 2023 Guardian Project. All rights reserved. | ||
// | ||
|
||
import UIKit | ||
import Eureka | ||
import IPtProxyUI | ||
|
||
class BridgesViewController: BaseFormViewController, BridgesConfDelegate { | ||
|
||
enum Option: String, CaseIterable { | ||
case direct = "transport_none" | ||
case snowflake = "transport_snowflake" | ||
case snowflakeAmp = "transport_snowflake_amp" | ||
case request = "request" | ||
case custom = "transport_custom" | ||
|
||
var localizedDescription: String { | ||
switch self { | ||
case .direct: | ||
return NSLocalizedString("Direct connection to Tor", comment: "") | ||
|
||
case .snowflake: | ||
return NSLocalizedString("Snowflake", comment: "") | ||
|
||
case .snowflakeAmp: | ||
return NSLocalizedString("Snowflake (AMP rendezvous)", comment: "") | ||
|
||
case .request: | ||
return NSLocalizedString("Get a bridge from Tor (Obfs4)", comment: "") | ||
|
||
case .custom: | ||
return NSLocalizedString("Custom bridge", comment: "") | ||
} | ||
} | ||
|
||
var longDescription: String { | ||
switch self { | ||
case .direct: | ||
return NSLocalizedString("The best way to connect to Tor. Use if Tor is not blocked.", comment: "") | ||
|
||
case .snowflake, .snowflakeAmp: | ||
return NSLocalizedString("Connects through Tor volunteers. Gets around some Tor blocking.", comment: "") | ||
|
||
case .request: | ||
return NSLocalizedString("Cloaks your traffic. Gets around some Tor blocking.", comment: "") | ||
|
||
case .custom: | ||
return NSLocalizedString("Most likely to keep you connected if Tor is severly blocked. Requires a bridge address from someone you trust.", comment: "") | ||
} | ||
} | ||
|
||
var isOn: Bool { | ||
switch self { | ||
case .direct: | ||
return Settings.transport == .none | ||
|
||
case .snowflake: | ||
return Settings.transport == .snowflake | ||
|
||
case .snowflakeAmp: | ||
return Settings.transport == .snowflakeAmp | ||
|
||
case .request: | ||
return false | ||
|
||
case .custom: | ||
return Settings.transport == .custom | ||
} | ||
} | ||
} | ||
|
||
var transport: IPtProxyUI.Transport { | ||
get { | ||
Settings.transport | ||
} | ||
set { | ||
Settings.transport = newValue | ||
} | ||
} | ||
|
||
var customBridges: [String]? { | ||
get { | ||
Settings.customBridges | ||
} | ||
set { | ||
Settings.customBridges = newValue | ||
} | ||
} | ||
|
||
private let section = SelectableSection<ListCheckRow<Option>>(nil, selectionType: .singleSelection(enableDeselection: false)) | ||
|
||
|
||
override func viewDidLoad() { | ||
super.viewDidLoad() | ||
|
||
tableView.separatorStyle = .none | ||
|
||
navigationItem.title = NSLocalizedString("Choose How to Connect", comment: "") | ||
|
||
section.onSelectSelectableRow = { [weak self] _, row in | ||
guard let self = self else { | ||
return | ||
} | ||
|
||
// Needed, otherwise rows don't get resized due to changing subtitle. | ||
self.tableView.reloadData() | ||
} | ||
|
||
form | ||
+++ section | ||
|
||
for option in Option.allCases { | ||
form.last! | ||
<<< ListCheckRow<Option>() { | ||
$0.cellStyle = .subtitle | ||
$0.title = option.localizedDescription | ||
|
||
$0.selectableValue = option | ||
$0.value = option.isOn ? $0.selectableValue : nil | ||
|
||
$0.cell.accessibilityIdentifier = option.rawValue | ||
$0.cell.detailTextLabel?.numberOfLines = 0 | ||
$0.cell.backgroundColor = .init(named: .colorBlack2) | ||
} | ||
.cellUpdate({ cell, row in | ||
cell.detailTextLabel?.text = row.value != nil ? row.selectableValue?.longDescription : nil | ||
}) | ||
} | ||
|
||
form | ||
+++ RoundedButtonRow() | ||
.cellUpdate({ [weak self] _, row in | ||
switch self?.section.selectedRow()?.value ?? .direct { | ||
case .request, .custom: | ||
row.title = NSLocalizedString("Next", comment: "") | ||
|
||
default: | ||
row.title = NSLocalizedString("Save", comment: "") | ||
} | ||
}) | ||
.onCellSelection({ [weak self] cell, row in | ||
switch self?.section.selectedRow()?.value ?? .direct { | ||
case .request: | ||
let vc = MoatViewController() | ||
vc.delegate = self | ||
self?.navigationController?.pushViewController(vc, animated: true) | ||
|
||
case .custom: | ||
let vc = CustomBridgesViewController() | ||
vc.delegate = self | ||
self?.navigationController?.pushViewController(vc, animated: true) | ||
|
||
default: | ||
self?.save() | ||
} | ||
}) | ||
} | ||
|
||
@objc | ||
func save() { | ||
Settings.smartConnect = false | ||
|
||
switch section.selectedRow()?.value ?? .direct { | ||
case .direct: | ||
Settings.transport = .none | ||
|
||
case .snowflake: | ||
Settings.transport = .snowflake | ||
|
||
case .snowflakeAmp: | ||
Settings.transport = .snowflakeAmp | ||
|
||
case .request, .custom: | ||
Settings.transport = .custom | ||
} | ||
|
||
navigationController?.dismiss(animated: true) | ||
|
||
VpnManager.shared.configChanged() | ||
|
||
NotificationCenter.default.post(name: .vpnStatusChanged, object: nil) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
// | ||
// RoundedButtonRow.swift | ||
// Orbot | ||
// | ||
// Created by Benjamin Erhart on 13.01.23. | ||
// Copyright © 2023 Guardian Project. All rights reserved. | ||
// | ||
|
||
import Eureka | ||
|
||
/** | ||
Eureka button row with rounded corners and leading and trailing padding using a `UIButton` for the actual functionality. | ||
*/ | ||
class RoundedButtonCell: ButtonCellOf<String> { | ||
|
||
private lazy var button: UIButton = { | ||
let button = UIButton(type: .system) | ||
|
||
button.translatesAutoresizingMaskIntoConstraints = false | ||
|
||
button.backgroundColor = .init(named: .colorAccent1) | ||
|
||
return button | ||
}() | ||
|
||
override func setup() { | ||
super.setup() | ||
|
||
contentView.addSubview(button) | ||
|
||
let row = row as? RoundedButtonRow | ||
|
||
let ltp = row?.leadingTrailingPadding ?? 16 | ||
let tbp = row?.topBottomPadding ?? 0 | ||
|
||
button.heightAnchor.constraint(equalToConstant: row?.height ?? 48).isActive = true | ||
button.topAnchor.constraint(equalTo: contentView.topAnchor, constant: tbp).isActive = true | ||
button.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: ltp).isActive = true | ||
button.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -ltp).isActive = true | ||
button.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -tbp).isActive = true | ||
|
||
button.layer.cornerRadius = row?.cornerRadius ?? 9 | ||
|
||
button.addTarget(self, action: #selector(tapped), for: .touchUpInside) | ||
|
||
backgroundColor = .init(named: .colorBlack2) | ||
} | ||
|
||
override func update() { | ||
button.setTitle(row.title) | ||
} | ||
|
||
@objc | ||
private func tapped() { | ||
row.didSelect() | ||
} | ||
} | ||
|
||
final class RoundedButtonRow: Row<RoundedButtonCell>, RowType { | ||
|
||
fileprivate var height: CGFloat = 48 | ||
|
||
fileprivate var cornerRadius: CGFloat = 9 | ||
|
||
fileprivate var leadingTrailingPadding: CGFloat = 16 | ||
|
||
fileprivate var topBottomPadding: CGFloat = 0 | ||
|
||
|
||
convenience init(tag: String?, height: CGFloat = 48, cornerRadius: CGFloat = 9, leadingTrailingPadding: CGFloat = 16, topBottomPadding: CGFloat = 0) { | ||
self.init(tag: tag) | ||
|
||
self.height = height | ||
self.cornerRadius = cornerRadius | ||
self.leadingTrailingPadding = leadingTrailingPadding | ||
self.topBottomPadding = topBottomPadding | ||
} | ||
|
||
required init(tag: String?) { | ||
super.init(tag: tag) | ||
|
||
displayValueFor = nil | ||
cellStyle = .default | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters