From 11063d0f887101aec84db07f6bd2ec111f0141ce Mon Sep 17 00:00:00 2001 From: Roopesh Chander Date: Sat, 6 Apr 2019 15:22:05 +0530 Subject: [PATCH] macOS: Tunnels list: Suppress alert buttons when removing tunnels is in progress Also refactor the deletion alert into a separate helper class Signed-off-by: Roopesh Chander --- WireGuard/WireGuard.xcodeproj/project.pbxproj | 4 ++ .../WireGuard/Base.lproj/Localizable.strings | 1 + .../View/DeleteTunnelsConfirmationAlert.swift | 36 +++++++++++++ .../TunnelsListTableViewController.swift | 53 +++++++------------ 4 files changed, 60 insertions(+), 34 deletions(-) create mode 100644 WireGuard/WireGuard/UI/macOS/View/DeleteTunnelsConfirmationAlert.swift diff --git a/WireGuard/WireGuard.xcodeproj/project.pbxproj b/WireGuard/WireGuard.xcodeproj/project.pbxproj index a7ceac9..98a18d2 100644 --- a/WireGuard/WireGuard.xcodeproj/project.pbxproj +++ b/WireGuard/WireGuard.xcodeproj/project.pbxproj @@ -50,6 +50,7 @@ 6BD5C97E220D1AE200784E08 /* key.c in Sources */ = {isa = PBXBuildFile; fileRef = 6BD5C979220D1AE100784E08 /* key.c */; }; 6F0F44C9222D55BB00B0FF04 /* TextCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F0F44C8222D55BB00B0FF04 /* TextCell.swift */; }; 6F0F44CB222D55FD00B0FF04 /* EditableTextCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F0F44CA222D55FD00B0FF04 /* EditableTextCell.swift */; }; + 6F1075642258AE9800D78929 /* DeleteTunnelsConfirmationAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1075632258AE9800D78929 /* DeleteTunnelsConfirmationAlert.swift */; }; 6F19D30422402B8700A126F2 /* ConfirmationAlertPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F19D30322402B8700A126F2 /* ConfirmationAlertPresenter.swift */; }; 6F4DD16B21DA558800690EAE /* TunnelListRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DD16A21DA558800690EAE /* TunnelListRow.swift */; }; 6F4DD16C21DA558F00690EAE /* NSTableView+Reuse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DD16721DA552B00690EAE /* NSTableView+Reuse.swift */; }; @@ -295,6 +296,7 @@ 6BD5C97A220D1AE200784E08 /* key.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = key.h; sourceTree = ""; }; 6F0F44C8222D55BB00B0FF04 /* TextCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextCell.swift; sourceTree = ""; }; 6F0F44CA222D55FD00B0FF04 /* EditableTextCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditableTextCell.swift; sourceTree = ""; }; + 6F1075632258AE9800D78929 /* DeleteTunnelsConfirmationAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteTunnelsConfirmationAlert.swift; sourceTree = ""; }; 6F19D30322402B8700A126F2 /* ConfirmationAlertPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationAlertPresenter.swift; sourceTree = ""; }; 6F4DD16721DA552B00690EAE /* NSTableView+Reuse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTableView+Reuse.swift"; sourceTree = ""; }; 6F4DD16A21DA558800690EAE /* TunnelListRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelListRow.swift; sourceTree = ""; }; @@ -483,6 +485,7 @@ 6F5EA59A223E58A8002B380A /* ButtonRow.swift */, 6FB17945222FD5960018AE71 /* OnDemandWiFiControls.swift */, 6FDB6D14224CB2CE00EE4BC3 /* LogViewCell.swift */, + 6F1075632258AE9800D78929 /* DeleteTunnelsConfirmationAlert.swift */, ); path = View; sourceTree = ""; @@ -1287,6 +1290,7 @@ 6FADE96C2254B8C300B838A4 /* UnusableTunnelDetailViewController.swift in Sources */, 6FFACD2021E4D8D500E9A2A5 /* ParseError+WireGuardAppError.swift in Sources */, 6FB1BDC021D50F0200A991BF /* NETunnelProviderProtocol+Extension.swift in Sources */, + 6F1075642258AE9800D78929 /* DeleteTunnelsConfirmationAlert.swift in Sources */, 6FBA101821D656000051C35F /* StatusMenu.swift in Sources */, 6F613D9B21DE33B8004B217A /* KeyValueRow.swift in Sources */, 6FB1BDC121D50F0200A991BF /* String+ArrayConversion.swift in Sources */, diff --git a/WireGuard/WireGuard/Base.lproj/Localizable.strings b/WireGuard/WireGuard/Base.lproj/Localizable.strings index 5e9b6d5..cd11045 100644 --- a/WireGuard/WireGuard/Base.lproj/Localizable.strings +++ b/WireGuard/WireGuard/Base.lproj/Localizable.strings @@ -314,6 +314,7 @@ "macDeleteTunnelConfirmationAlertInfo" = "You cannot undo this action."; "macDeleteTunnelConfirmationAlertButtonTitleDelete" = "Delete"; "macDeleteTunnelConfirmationAlertButtonTitleCancel" = "Cancel"; +"macDeleteTunnelConfirmationAlertButtonTitleDeleting" = "Deleting…"; "macButtonImportTunnels" = "Import tunnel(s) from file"; "macSheetButtonImport" = "Import"; diff --git a/WireGuard/WireGuard/UI/macOS/View/DeleteTunnelsConfirmationAlert.swift b/WireGuard/WireGuard/UI/macOS/View/DeleteTunnelsConfirmationAlert.swift new file mode 100644 index 0000000..e52ed89 --- /dev/null +++ b/WireGuard/WireGuard/UI/macOS/View/DeleteTunnelsConfirmationAlert.swift @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +// Copyright © 2018-2019 WireGuard LLC. All Rights Reserved. + +import Cocoa + +class DeleteTunnelsConfirmationAlert: NSAlert { + var alertDeleteButton: NSButton? + var alertCancelButton: NSButton? + + var onDeleteClicked: ((_ completionHandler: @escaping () -> Void) -> Void)? + + override init() { + super.init() + let alertDeleteButton = addButton(withTitle: tr("macDeleteTunnelConfirmationAlertButtonTitleDelete")) + alertDeleteButton.target = self + alertDeleteButton.action = #selector(removeTunnelAlertDeleteClicked) + self.alertDeleteButton = alertDeleteButton + self.alertCancelButton = addButton(withTitle: tr("macDeleteTunnelConfirmationAlertButtonTitleCancel")) + } + + @objc func removeTunnelAlertDeleteClicked() { + alertDeleteButton?.title = tr("macDeleteTunnelConfirmationAlertButtonTitleDeleting") + alertDeleteButton?.isEnabled = false + alertCancelButton?.isEnabled = false + if let onDeleteClicked = onDeleteClicked { + onDeleteClicked { [weak self] in + guard let self = self else { return } + self.window.sheetParent?.endSheet(self.window) + } + } + } + + func beginSheetModal(for sheetWindow: NSWindow) { + beginSheetModal(for: sheetWindow) { _ in } + } +} diff --git a/WireGuard/WireGuard/UI/macOS/ViewController/TunnelsListTableViewController.swift b/WireGuard/WireGuard/UI/macOS/ViewController/TunnelsListTableViewController.swift index 8918f79..40a2b9e 100644 --- a/WireGuard/WireGuard/UI/macOS/ViewController/TunnelsListTableViewController.swift +++ b/WireGuard/WireGuard/UI/macOS/ViewController/TunnelsListTableViewController.swift @@ -163,9 +163,15 @@ class TunnelsListTableViewController: NSViewController { @objc func handleRemoveTunnelAction() { guard let window = view.window else { return } + let selectedTunnelIndices = tableView.selectedRowIndexes.sorted().filter { $0 >= 0 && $0 < tunnelsManager.numberOfTunnels() } guard !selectedTunnelIndices.isEmpty else { return } - let alert = NSAlert() + var nextSelection = selectedTunnelIndices.last! + 1 + if nextSelection >= tunnelsManager.numberOfTunnels() { + nextSelection = max(selectedTunnelIndices.first! - 1, 0) + } + + let alert = DeleteTunnelsConfirmationAlert() if selectedTunnelIndices.count == 1 { let firstSelectedTunnel = tunnelsManager.tunnel(at: selectedTunnelIndices.first!) alert.messageText = tr(format: "macDeleteTunnelConfirmationAlertMessage (%@)", firstSelectedTunnel.name) @@ -173,41 +179,20 @@ class TunnelsListTableViewController: NSViewController { alert.messageText = tr(format: "macDeleteMultipleTunnelsConfirmationAlertMessage (%d)", selectedTunnelIndices.count) } alert.informativeText = tr("macDeleteTunnelConfirmationAlertInfo") - let alertDeleteButton = alert.addButton(withTitle: tr("macDeleteTunnelConfirmationAlertButtonTitleDelete")) - alertDeleteButton.target = self - alertDeleteButton.action = #selector(removeTunnelAlertDeleteClicked) - let alertCancelButton = alert.addButton(withTitle: tr("macDeleteTunnelConfirmationAlertButtonTitleCancel")) - alertCancelButton.target = self - alertCancelButton.action = #selector(removeTunnelAlertCancelClicked) - alert.beginSheetModal(for: window) { _ in } - } - - @objc func removeTunnelAlertDeleteClicked(_ sender: AnyObject) { - guard let alertWindow = (sender as? NSView)?.window, alertWindow.isSheet else { return } - let selectedTunnelIndices = tableView.selectedRowIndexes.sorted().filter { $0 >= 0 && $0 < tunnelsManager.numberOfTunnels() } - precondition(!selectedTunnelIndices.isEmpty) - var nextSelection = selectedTunnelIndices.last! + 1 - if nextSelection >= tunnelsManager.numberOfTunnels() { - nextSelection = max(selectedTunnelIndices.first! - 1, 0) - } - let selectedTunnels = selectedTunnelIndices.map { tunnelsManager.tunnel(at: $0) } - alertWindow.ignoresMouseEvents = true - self.selectTunnel(at: nextSelection) - isRemovingTunnels = true - tunnelsManager.removeMultiple(tunnels: selectedTunnels) { [weak self] error in + alert.onDeleteClicked = { [weak self] completion in guard let self = self else { return } - self.view.window?.endSheet(alertWindow) - self.isRemovingTunnels = false - if let error = error { - ErrorPresenter.showErrorAlert(error: error, from: self) - return - } + self.selectTunnel(at: nextSelection) + let selectedTunnels = selectedTunnelIndices.map { self.tunnelsManager.tunnel(at: $0) } + self.tunnelsManager.removeMultiple(tunnels: selectedTunnels) { [weak self] error in + guard let self = self else { return } + defer { completion() } + if let error = error { + ErrorPresenter.showErrorAlert(error: error, from: self) + return + } + } } - } - - @objc func removeTunnelAlertCancelClicked(_ sender: AnyObject) { - guard let alertWindow = (sender as? NSView)?.window, alertWindow.isSheet else { return } - view.window?.endSheet(alertWindow) + alert.beginSheetModal(for: window) } @objc func handleViewLogAction() {