Tunnel detail: UI for activating and deactivating a tunnel
This commit is contained in:
parent
59133f5467
commit
c1cfed7739
|
@ -39,6 +39,7 @@ class TunnelDetailTableViewController: UITableViewController {
|
|||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .edit, target: self, action: #selector(editTapped))
|
||||
|
||||
self.tableView.rowHeight = 44
|
||||
self.tableView.register(TunnelDetailTableViewStatusCell.self, forCellReuseIdentifier: TunnelDetailTableViewStatusCell.id)
|
||||
self.tableView.register(TunnelDetailTableViewKeyValueCell.self, forCellReuseIdentifier: TunnelDetailTableViewKeyValueCell.id)
|
||||
self.tableView.register(TunnelDetailTableViewButtonCell.self, forCellReuseIdentifier: TunnelDetailTableViewButtonCell.id)
|
||||
}
|
||||
|
@ -50,6 +51,14 @@ class TunnelDetailTableViewController: UITableViewController {
|
|||
editNC.modalPresentationStyle = .formSheet
|
||||
present(editNC, animated: true)
|
||||
}
|
||||
|
||||
func showErrorAlert(title: String, message: String) {
|
||||
let okAction = UIAlertAction(title: "Ok", style: .default)
|
||||
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||
alert.addAction(okAction)
|
||||
|
||||
self.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: TunnelEditTableViewControllerDelegate
|
||||
|
@ -75,7 +84,7 @@ extension TunnelDetailTableViewController {
|
|||
let numberOfPeerSections = peerFieldsBySection.count
|
||||
let numberOfPeers = tunnelViewModel.peersData.count
|
||||
|
||||
return numberOfInterfaceSections + (numberOfPeers * numberOfPeerSections) + 1
|
||||
return 1 + numberOfInterfaceSections + (numberOfPeers * numberOfPeerSections) + 1
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
|
@ -86,10 +95,13 @@ extension TunnelDetailTableViewController {
|
|||
let numberOfPeerSections = peerFieldsBySection.count
|
||||
let numberOfPeers = tunnelViewModel.peersData.count
|
||||
|
||||
if (section < numberOfInterfaceSections) {
|
||||
if (section == 0) {
|
||||
// Status
|
||||
return 1
|
||||
} else if (section < (1 + numberOfInterfaceSections)) {
|
||||
// Interface
|
||||
return interfaceData.filterFieldsWithValueOrControl(interfaceFields: interfaceFieldsBySection[section]).count
|
||||
} else if ((numberOfPeers > 0) && (section < (numberOfInterfaceSections + numberOfPeers * numberOfPeerSections))) {
|
||||
return interfaceData.filterFieldsWithValueOrControl(interfaceFields: interfaceFieldsBySection[section - 1]).count
|
||||
} else if ((numberOfPeers > 0) && (section < (1 + numberOfInterfaceSections + numberOfPeers * numberOfPeerSections))) {
|
||||
// Peer
|
||||
let peerIndex = Int((section - numberOfInterfaceSections) / numberOfPeerSections)
|
||||
let peerData = tunnelViewModel.peersData[peerIndex]
|
||||
|
@ -109,13 +121,16 @@ extension TunnelDetailTableViewController {
|
|||
let numberOfPeerSections = peerFieldsBySection.count
|
||||
let numberOfPeers = tunnelViewModel.peersData.count
|
||||
|
||||
if (section < numberOfInterfaceSections) {
|
||||
if (section == 0) {
|
||||
// Status
|
||||
return "Status"
|
||||
} else if (section < 1 + numberOfInterfaceSections) {
|
||||
// Interface
|
||||
return (section == 0) ? "Interface" : nil
|
||||
} else if ((numberOfPeers > 0) && (section < (numberOfInterfaceSections + numberOfPeers * numberOfPeerSections))) {
|
||||
return (section == 1) ? "Interface" : nil
|
||||
} else if ((numberOfPeers > 0) && (section < (1 + numberOfInterfaceSections + numberOfPeers * numberOfPeerSections))) {
|
||||
// Peer
|
||||
let fieldIndex = (section - numberOfInterfaceSections) % numberOfPeerSections
|
||||
return (fieldIndex == 0) ? "Peer" : nil
|
||||
let peerSectionIndex = (section - 1 - numberOfInterfaceSections) % numberOfPeerSections
|
||||
return (peerSectionIndex == 0) ? "Peer" : nil
|
||||
} else {
|
||||
// Add peer
|
||||
return nil
|
||||
|
@ -133,9 +148,41 @@ extension TunnelDetailTableViewController {
|
|||
let section = indexPath.section
|
||||
let row = indexPath.row
|
||||
|
||||
if (section < numberOfInterfaceSections) {
|
||||
if (section == 0) {
|
||||
// Status
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: TunnelDetailTableViewStatusCell.id, for: indexPath) as! TunnelDetailTableViewStatusCell
|
||||
cell.tunnel = self.tunnel
|
||||
cell.onSwitchToggled = { [weak self] isOn in
|
||||
cell.isSwitchInteractionEnabled = false
|
||||
guard let s = self else { return }
|
||||
if (isOn) {
|
||||
s.tunnelsManager.activate(tunnel: s.tunnel) { [weak s] isActivated in
|
||||
if (!isActivated) {
|
||||
DispatchQueue.main.async {
|
||||
cell.setSwitchStatus(isOn: false)
|
||||
}
|
||||
s?.showErrorAlert(title: "Could not activate",
|
||||
message: "Could not activate the tunnel because of an internal error")
|
||||
}
|
||||
cell.isSwitchInteractionEnabled = true
|
||||
}
|
||||
} else {
|
||||
s.tunnelsManager.deactivate(tunnel: s.tunnel) { [weak s] isDeactivated in
|
||||
cell.isSwitchInteractionEnabled = true
|
||||
if (!isDeactivated) {
|
||||
DispatchQueue.main.async {
|
||||
cell.setSwitchStatus(isOn: true)
|
||||
}
|
||||
s?.showErrorAlert(title: "Could not deactivate",
|
||||
message: "Could not deactivate the tunnel because of an internal error")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return cell
|
||||
} else if (section < 1 + numberOfInterfaceSections) {
|
||||
// Interface
|
||||
let field = interfaceData.filterFieldsWithValueOrControl(interfaceFields: interfaceFieldsBySection[section])[row]
|
||||
let field = interfaceData.filterFieldsWithValueOrControl(interfaceFields: interfaceFieldsBySection[section - 1])[row]
|
||||
if (field == .copyPublicKey) {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: TunnelDetailTableViewButtonCell.id, for: indexPath) as! TunnelDetailTableViewButtonCell
|
||||
cell.buttonText = field.rawValue
|
||||
|
@ -155,10 +202,10 @@ extension TunnelDetailTableViewController {
|
|||
}
|
||||
return cell
|
||||
}
|
||||
} else if ((numberOfPeers > 0) && (section < (numberOfInterfaceSections + numberOfPeers * numberOfPeerSections))) {
|
||||
} else if ((numberOfPeers > 0) && (section < (1 + numberOfInterfaceSections + numberOfPeers * numberOfPeerSections))) {
|
||||
// Peer
|
||||
let peerIndex = Int((section - numberOfInterfaceSections) / numberOfPeerSections)
|
||||
let peerSectionIndex = (section - numberOfInterfaceSections) % numberOfPeerSections
|
||||
let peerIndex = Int((section - 1 - numberOfInterfaceSections) / numberOfPeerSections)
|
||||
let peerSectionIndex = (section - 1 - numberOfInterfaceSections) % numberOfPeerSections
|
||||
let peerData = tunnelViewModel.peersData[peerIndex]
|
||||
let field = peerData.filterFieldsWithValueOrControl(peerFields: peerFieldsBySection[peerSectionIndex])[row]
|
||||
|
||||
|
@ -174,7 +221,7 @@ extension TunnelDetailTableViewController {
|
|||
|
||||
return cell
|
||||
} else {
|
||||
assert(section == (numberOfInterfaceSections + numberOfPeers * numberOfPeerSections))
|
||||
assert(section == (1 + numberOfInterfaceSections + numberOfPeers * numberOfPeerSections))
|
||||
// Delete configuration
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: TunnelDetailTableViewButtonCell.id, for: indexPath) as! TunnelDetailTableViewButtonCell
|
||||
cell.buttonText = "Delete tunnel"
|
||||
|
@ -186,6 +233,88 @@ extension TunnelDetailTableViewController {
|
|||
}
|
||||
}
|
||||
|
||||
class TunnelDetailTableViewStatusCell: UITableViewCell {
|
||||
static let id: String = "TunnelDetailTableViewStatusCell"
|
||||
|
||||
var tunnel: TunnelContainer? {
|
||||
didSet(value) {
|
||||
update(from: tunnel?.status)
|
||||
statusObservervationToken = tunnel?.observe(\.status) { [weak self] (tunnel, _) in
|
||||
self?.update(from: tunnel.status)
|
||||
}
|
||||
}
|
||||
}
|
||||
var isSwitchInteractionEnabled: Bool {
|
||||
get { return statusSwitch.isUserInteractionEnabled }
|
||||
set(value) { statusSwitch.isUserInteractionEnabled = value }
|
||||
}
|
||||
var onSwitchToggled: ((Bool) -> Void)? = nil
|
||||
private var isOnSwitchToggledHandlerEnabled: Bool = true
|
||||
|
||||
func setSwitchStatus(isOn: Bool) {
|
||||
statusSwitch.isOn = isOn
|
||||
}
|
||||
|
||||
let statusSwitch: UISwitch
|
||||
private var statusObservervationToken: AnyObject? = nil
|
||||
|
||||
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
|
||||
statusSwitch = UISwitch()
|
||||
super.init(style: .default, reuseIdentifier: TunnelDetailTableViewKeyValueCell.id)
|
||||
accessoryView = statusSwitch
|
||||
|
||||
statusSwitch.addTarget(self, action: #selector(switchToggled), for: .valueChanged)
|
||||
}
|
||||
|
||||
@objc func switchToggled() {
|
||||
if (isOnSwitchToggledHandlerEnabled) {
|
||||
onSwitchToggled?(statusSwitch.isOn)
|
||||
}
|
||||
}
|
||||
|
||||
private func update(from status: TunnelStatus?) {
|
||||
guard let status = status else {
|
||||
reset()
|
||||
return
|
||||
}
|
||||
let text: String
|
||||
switch (status) {
|
||||
case .inactive:
|
||||
text = "Inactive"
|
||||
case .activating:
|
||||
text = "Activating"
|
||||
case .active:
|
||||
text = "Active"
|
||||
case .deactivating:
|
||||
text = "Deactivating"
|
||||
case .reasserting:
|
||||
text = "Reactivating"
|
||||
case .waitingForOtherDeactivation:
|
||||
text = "Waiting"
|
||||
case .resolvingEndpointDomains:
|
||||
text = "Resolving domains"
|
||||
}
|
||||
textLabel?.text = text
|
||||
setSwitchStatus(isOn: !(status == .deactivating || status == .inactive))
|
||||
textLabel?.textColor = (status == .active || status == .inactive) ? UIColor.black : UIColor.gray
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func reset() {
|
||||
textLabel?.text = "Invalid"
|
||||
statusSwitch.isOn = false
|
||||
textLabel?.textColor = UIColor.gray
|
||||
statusSwitch.isUserInteractionEnabled = false
|
||||
}
|
||||
|
||||
override func prepareForReuse() {
|
||||
reset()
|
||||
}
|
||||
}
|
||||
|
||||
class TunnelDetailTableViewKeyValueCell: UITableViewCell {
|
||||
static let id: String = "TunnelDetailTableViewKeyValueCell"
|
||||
var key: String {
|
||||
|
|
Loading…
Reference in New Issue