Tunnel edit, Tunnel view model: UI for providing On-Demand activation options
Signed-off-by: Roopesh Chander <roop@roopc.net>
This commit is contained in:
parent
4b7094d652
commit
c5e8b05e8b
|
@ -372,6 +372,7 @@ class TunnelViewModel {
|
||||||
|
|
||||||
var interfaceData: InterfaceData
|
var interfaceData: InterfaceData
|
||||||
var peersData: [PeerData]
|
var peersData: [PeerData]
|
||||||
|
var activationType: ActivationType
|
||||||
|
|
||||||
init(tunnelConfiguration: TunnelConfiguration?) {
|
init(tunnelConfiguration: TunnelConfiguration?) {
|
||||||
let interfaceData: InterfaceData = InterfaceData()
|
let interfaceData: InterfaceData = InterfaceData()
|
||||||
|
@ -391,6 +392,20 @@ class TunnelViewModel {
|
||||||
}
|
}
|
||||||
self.interfaceData = interfaceData
|
self.interfaceData = interfaceData
|
||||||
self.peersData = peersData
|
self.peersData = peersData
|
||||||
|
self.activationType = tunnelConfiguration?.activationType ?? .activateManually
|
||||||
|
}
|
||||||
|
|
||||||
|
func activateOnDemandOptionText(for activationType: ActivationType) -> String {
|
||||||
|
switch (activationType) {
|
||||||
|
case .activateManually:
|
||||||
|
return ""
|
||||||
|
case .useOnDemandOverWifiAndCellular:
|
||||||
|
return "Over wifi and cellular"
|
||||||
|
case .useOnDemandOverWifiOnly:
|
||||||
|
return "Over wifi only"
|
||||||
|
case .useOnDemandOverCellularOnly:
|
||||||
|
return "Over cellular only"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func appendEmptyPeer() {
|
func appendEmptyPeer() {
|
||||||
|
@ -442,6 +457,7 @@ class TunnelViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
let tunnelConfiguration = TunnelConfiguration(interface: interfaceConfiguration, peers: peerConfigurations)
|
let tunnelConfiguration = TunnelConfiguration(interface: interfaceConfiguration, peers: peerConfigurations)
|
||||||
|
tunnelConfiguration.activationType = self.activationType
|
||||||
return .saved(tunnelConfiguration)
|
return .saved(tunnelConfiguration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,12 @@ class TunnelEditTableViewController: UITableViewController {
|
||||||
.deletePeer
|
.deletePeer
|
||||||
]
|
]
|
||||||
|
|
||||||
|
let activateOnDemandOptions: [ActivationType] = [
|
||||||
|
.useOnDemandOverWifiAndCellular,
|
||||||
|
.useOnDemandOverWifiOnly,
|
||||||
|
.useOnDemandOverCellularOnly
|
||||||
|
]
|
||||||
|
|
||||||
let tunnelsManager: TunnelsManager
|
let tunnelsManager: TunnelsManager
|
||||||
let tunnel: TunnelContainer?
|
let tunnel: TunnelContainer?
|
||||||
let tunnelViewModel: TunnelViewModel
|
let tunnelViewModel: TunnelViewModel
|
||||||
|
@ -58,12 +64,12 @@ class TunnelEditTableViewController: UITableViewController {
|
||||||
self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(cancelTapped))
|
self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(cancelTapped))
|
||||||
|
|
||||||
self.tableView.rowHeight = 44
|
self.tableView.rowHeight = 44
|
||||||
self.tableView.allowsSelection = false
|
|
||||||
|
|
||||||
self.tableView.register(TunnelEditTableViewKeyValueCell.self, forCellReuseIdentifier: TunnelEditTableViewKeyValueCell.id)
|
self.tableView.register(TunnelEditTableViewKeyValueCell.self, forCellReuseIdentifier: TunnelEditTableViewKeyValueCell.id)
|
||||||
self.tableView.register(TunnelEditTableViewReadOnlyKeyValueCell.self, forCellReuseIdentifier: TunnelEditTableViewReadOnlyKeyValueCell.id)
|
self.tableView.register(TunnelEditTableViewReadOnlyKeyValueCell.self, forCellReuseIdentifier: TunnelEditTableViewReadOnlyKeyValueCell.id)
|
||||||
self.tableView.register(TunnelEditTableViewButtonCell.self, forCellReuseIdentifier: TunnelEditTableViewButtonCell.id)
|
self.tableView.register(TunnelEditTableViewButtonCell.self, forCellReuseIdentifier: TunnelEditTableViewButtonCell.id)
|
||||||
self.tableView.register(TunnelEditTableViewSwitchCell.self, forCellReuseIdentifier: TunnelEditTableViewSwitchCell.id)
|
self.tableView.register(TunnelEditTableViewSwitchCell.self, forCellReuseIdentifier: TunnelEditTableViewSwitchCell.id)
|
||||||
|
self.tableView.register(TunnelEditTableViewSelectionListCell.self, forCellReuseIdentifier: TunnelEditTableViewSelectionListCell.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func saveTapped() {
|
@objc func saveTapped() {
|
||||||
|
@ -122,7 +128,7 @@ extension TunnelEditTableViewController {
|
||||||
let numberOfInterfaceSections = interfaceFieldsBySection.count
|
let numberOfInterfaceSections = interfaceFieldsBySection.count
|
||||||
let numberOfPeerSections = tunnelViewModel.peersData.count
|
let numberOfPeerSections = tunnelViewModel.peersData.count
|
||||||
|
|
||||||
return numberOfInterfaceSections + numberOfPeerSections + 1
|
return numberOfInterfaceSections + numberOfPeerSections + 1 /* Add Peer */ + 1 /* On-Demand */
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
|
@ -138,9 +144,16 @@ extension TunnelEditTableViewController {
|
||||||
let peerData = tunnelViewModel.peersData[peerIndex]
|
let peerData = tunnelViewModel.peersData[peerIndex]
|
||||||
let peerFieldsToShow = peerData.shouldAllowExcludePrivateIPsControl ? peerFields : peerFields.filter { $0 != .excludePrivateIPs }
|
let peerFieldsToShow = peerData.shouldAllowExcludePrivateIPsControl ? peerFields : peerFields.filter { $0 != .excludePrivateIPs }
|
||||||
return peerFieldsToShow.count
|
return peerFieldsToShow.count
|
||||||
} else {
|
} else if (section < (numberOfInterfaceSections + numberOfPeerSections + 1)) {
|
||||||
// Add peer
|
// Add peer
|
||||||
return 1
|
return 1
|
||||||
|
} else {
|
||||||
|
// On-Demand Rules
|
||||||
|
if (tunnelViewModel.activationType == .activateManually) {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
return 4
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,9 +167,12 @@ extension TunnelEditTableViewController {
|
||||||
} else if ((numberOfPeerSections > 0) && (section < (numberOfInterfaceSections + numberOfPeerSections))) {
|
} else if ((numberOfPeerSections > 0) && (section < (numberOfInterfaceSections + numberOfPeerSections))) {
|
||||||
// Peer
|
// Peer
|
||||||
return "Peer"
|
return "Peer"
|
||||||
} else {
|
} else if (section == (numberOfInterfaceSections + numberOfPeerSections)) {
|
||||||
// Add peer
|
// Add peer
|
||||||
return nil
|
return nil
|
||||||
|
} else {
|
||||||
|
assert(section == (numberOfInterfaceSections + numberOfPeerSections + 1))
|
||||||
|
return "On-Demand Activation"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,8 +360,7 @@ extension TunnelEditTableViewController {
|
||||||
}
|
}
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
} else {
|
} else if (section == (numberOfInterfaceSections + numberOfPeerSections)) {
|
||||||
assert(section == (numberOfInterfaceSections + numberOfPeerSections))
|
|
||||||
// Add peer
|
// Add peer
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: TunnelEditTableViewButtonCell.id, for: indexPath) as! TunnelEditTableViewButtonCell
|
let cell = tableView.dequeueReusableCell(withIdentifier: TunnelEditTableViewButtonCell.id, for: indexPath) as! TunnelEditTableViewButtonCell
|
||||||
cell.buttonText = "Add peer"
|
cell.buttonText = "Add peer"
|
||||||
|
@ -365,6 +380,32 @@ extension TunnelEditTableViewController {
|
||||||
}, completion: nil)
|
}, completion: nil)
|
||||||
}
|
}
|
||||||
return cell
|
return cell
|
||||||
|
} else {
|
||||||
|
assert(section == (numberOfInterfaceSections + numberOfPeerSections + 1))
|
||||||
|
if (row == 0) {
|
||||||
|
let cell = tableView.dequeueReusableCell(withIdentifier: TunnelEditTableViewSwitchCell.id, for: indexPath) as! TunnelEditTableViewSwitchCell
|
||||||
|
cell.message = "Activate on demand"
|
||||||
|
cell.isOn = (tunnelViewModel.activationType != .activateManually)
|
||||||
|
cell.onSwitchToggled = { [weak self] (isOn) in
|
||||||
|
guard let s = self else { return }
|
||||||
|
let indexPaths: [IndexPath] = (1 ..< 4).map { IndexPath(row: $0, section: section) }
|
||||||
|
if (isOn) {
|
||||||
|
s.tunnelViewModel.activationType = .useOnDemandOverWifiAndCellular
|
||||||
|
s.tableView.insertRows(at: indexPaths, with: .automatic)
|
||||||
|
} else {
|
||||||
|
s.tunnelViewModel.activationType = .activateManually
|
||||||
|
s.tableView.deleteRows(at: indexPaths, with: .automatic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cell
|
||||||
|
} else {
|
||||||
|
assert(row < 4)
|
||||||
|
let cell = tableView.dequeueReusableCell(withIdentifier: TunnelEditTableViewSelectionListCell.id, for: indexPath) as! TunnelEditTableViewSelectionListCell
|
||||||
|
let option = activateOnDemandOptions[row - 1]
|
||||||
|
cell.message = tunnelViewModel.activateOnDemandOptionText(for: option)
|
||||||
|
cell.isChecked = (tunnelViewModel.activationType == option)
|
||||||
|
return cell
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,6 +447,41 @@ extension TunnelEditTableViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: UITableViewDelegate
|
||||||
|
|
||||||
|
extension TunnelEditTableViewController {
|
||||||
|
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
|
||||||
|
let numberOfInterfaceSections = interfaceFieldsBySection.count
|
||||||
|
let numberOfPeerSections = tunnelViewModel.peersData.count
|
||||||
|
|
||||||
|
let section = indexPath.section
|
||||||
|
let row = indexPath.row
|
||||||
|
|
||||||
|
if (section == (numberOfInterfaceSections + numberOfPeerSections + 1)) {
|
||||||
|
return (row > 0) ? indexPath : nil
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||||
|
let numberOfInterfaceSections = interfaceFieldsBySection.count
|
||||||
|
let numberOfPeerSections = tunnelViewModel.peersData.count
|
||||||
|
|
||||||
|
let section = indexPath.section
|
||||||
|
let row = indexPath.row
|
||||||
|
|
||||||
|
assert(section == (numberOfInterfaceSections + numberOfPeerSections + 1))
|
||||||
|
assert(row > 0)
|
||||||
|
|
||||||
|
let option = activateOnDemandOptions[row - 1]
|
||||||
|
tunnelViewModel.activationType = option
|
||||||
|
|
||||||
|
let indexPaths: [IndexPath] = (1 ..< 4).map { IndexPath(row: $0, section: section) }
|
||||||
|
tableView.reloadRows(at: indexPaths, with: .automatic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class TunnelEditTableViewKeyValueCell: UITableViewCell {
|
class TunnelEditTableViewKeyValueCell: UITableViewCell {
|
||||||
static let id: String = "TunnelEditTableViewKeyValueCell"
|
static let id: String = "TunnelEditTableViewKeyValueCell"
|
||||||
var key: String {
|
var key: String {
|
||||||
|
@ -665,3 +741,30 @@ class TunnelEditTableViewSwitchCell: UITableViewCell {
|
||||||
isOn = false
|
isOn = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TunnelEditTableViewSelectionListCell: UITableViewCell {
|
||||||
|
static let id: String = "TunnelEditTableViewSelectionListCell"
|
||||||
|
var message: String {
|
||||||
|
get { return textLabel?.text ?? "" }
|
||||||
|
set(value) { textLabel!.text = value }
|
||||||
|
}
|
||||||
|
var isChecked: Bool {
|
||||||
|
didSet {
|
||||||
|
accessoryType = isChecked ? .checkmark : .none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||||
|
isChecked = false
|
||||||
|
super.init(style: .default, reuseIdentifier: reuseIdentifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func prepareForReuse() {
|
||||||
|
super.prepareForReuse()
|
||||||
|
message = ""
|
||||||
|
isChecked = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue