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:
Roopesh Chander 2018-11-10 18:50:09 +05:30
parent 4b7094d652
commit c5e8b05e8b
2 changed files with 125 additions and 6 deletions

View File

@ -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)
} }
} }

View File

@ -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
}
}