diff --git a/WireGuard/WireGuard/Base.lproj/Localizable.strings b/WireGuard/WireGuard/Base.lproj/Localizable.strings index 7652b34..68a81eb 100644 --- a/WireGuard/WireGuard/Base.lproj/Localizable.strings +++ b/WireGuard/WireGuard/Base.lproj/Localizable.strings @@ -57,6 +57,7 @@ "tunnelInterfaceListenPort" = "Listen port"; "tunnelInterfaceMTU" = "MTU"; "tunnelInterfaceDNS" = "DNS servers"; +"tunnelInterfaceStatus" = "Status"; "tunnelSectionTitlePeer" = "Peer"; diff --git a/WireGuard/WireGuard/UI/TunnelViewModel.swift b/WireGuard/WireGuard/UI/TunnelViewModel.swift index f57eed0..20620fc 100644 --- a/WireGuard/WireGuard/UI/TunnelViewModel.swift +++ b/WireGuard/WireGuard/UI/TunnelViewModel.swift @@ -14,6 +14,7 @@ class TunnelViewModel { case listenPort case mtu case dns + case status var localizedUIString: String { switch self { @@ -25,6 +26,7 @@ class TunnelViewModel { case .listenPort: return tr("tunnelInterfaceListenPort") case .mtu: return tr("tunnelInterfaceMTU") case .dns: return tr("tunnelInterfaceDNS") + case .status: return tr("tunnelInterfaceStatus") } } } diff --git a/WireGuard/WireGuard/UI/macOS/ViewController/TunnelDetailTableViewController.swift b/WireGuard/WireGuard/UI/macOS/ViewController/TunnelDetailTableViewController.swift index b6dff65..82f7706 100644 --- a/WireGuard/WireGuard/UI/macOS/ViewController/TunnelDetailTableViewController.swift +++ b/WireGuard/WireGuard/UI/macOS/ViewController/TunnelDetailTableViewController.swift @@ -31,7 +31,7 @@ class TunnelDetailTableViewController: NSViewController { } static let interfaceFields: [TunnelViewModel.InterfaceField] = [ - .name, .publicKey, .addresses, + .name, .status, .publicKey, .addresses, .listenPort, .mtu, .dns ] @@ -175,7 +175,9 @@ class TunnelDetailTableViewController: NSViewController { var interfaceSection = [(isVisible: Bool, modelRow: TableViewModelRow)]() for field in TunnelDetailTableViewController.interfaceFields { - interfaceSection.append((isVisible: !tunnelViewModel.interfaceData[field].isEmpty, modelRow: .interfaceFieldRow(field))) + let isStatus = field == .status + let isEmpty = tunnelViewModel.interfaceData[field].isEmpty + interfaceSection.append((isVisible: isStatus || !isEmpty, modelRow: .interfaceFieldRow(field))) } interfaceSection.append((isVisible: true, modelRow: .spacerRow)) modelRowsBySection.append(interfaceSection) @@ -398,12 +400,16 @@ extension TunnelDetailTableViewController: NSTableViewDelegate { let modelRow = tableViewModelRows[row] switch modelRow { case .interfaceFieldRow(let field): - let cell: KeyValueRow = tableView.dequeueReusableCell() - let localizedKeyString = modelRow.isTitleRow() ? modelRow.localizedSectionKeyString() : field.localizedUIString - cell.key = tr(format: "macFieldKey (%@)", localizedKeyString) - cell.value = tunnelViewModel.interfaceData[field] - cell.isKeyInBold = modelRow.isTitleRow() - return cell + if field == .status { + return statusCell() + } else { + let cell: KeyValueRow = tableView.dequeueReusableCell() + let localizedKeyString = modelRow.isTitleRow() ? modelRow.localizedSectionKeyString() : field.localizedUIString + cell.key = tr(format: "macFieldKey (%@)", localizedKeyString) + cell.value = tunnelViewModel.interfaceData[field] + cell.isKeyInBold = modelRow.isTitleRow() + return cell + } case .peerFieldRow(let peerData, let field): let cell: KeyValueRow = tableView.dequeueReusableCell() let localizedKeyString = modelRow.isTitleRow() ? modelRow.localizedSectionKeyString() : field.localizedUIString @@ -427,6 +433,50 @@ extension TunnelDetailTableViewController: NSTableViewDelegate { return cell } } + + func statusCell() -> NSView { + let cell: KeyValueImageRow = tableView.dequeueReusableCell() + cell.key = tr(format: "macFieldKey (%@)", tr("tunnelInterfaceStatus")) + cell.value = TunnelDetailTableViewController.localizedStatusDescription(forStatus: tunnel.status) + cell.valueImage = TunnelDetailTableViewController.image(forStatus: tunnel.status) + cell.observationToken = tunnel.observe(\.status) { [weak cell] tunnel, _ in + guard let cell = cell else { return } + cell.value = TunnelDetailTableViewController.localizedStatusDescription(forStatus: tunnel.status) + cell.valueImage = TunnelDetailTableViewController.image(forStatus: tunnel.status) + } + return cell + } + + private static func localizedStatusDescription(forStatus status: TunnelStatus) -> String { + switch status { + case .inactive: + return tr("tunnelStatusInactive") + case .activating: + return tr("tunnelStatusActivating") + case .active: + return tr("tunnelStatusActive") + case .deactivating: + return tr("tunnelStatusDeactivating") + case .reasserting: + return tr("tunnelStatusReasserting") + case .restarting: + return tr("tunnelStatusRestarting") + case .waiting: + return tr("tunnelStatusWaiting") + } + } + + private static func image(forStatus status: TunnelStatus?) -> NSImage? { + guard let status = status else { return nil } + switch status { + case .active, .restarting, .reasserting: + return NSImage(named: NSImage.statusAvailableName) + case .activating, .waiting, .deactivating: + return NSImage(named: NSImage.statusPartiallyAvailableName) + case .inactive: + return NSImage(named: NSImage.statusNoneName) + } + } } extension TunnelDetailTableViewController: TunnelEditViewControllerDelegate {