From 9a3f6c81c7c87c7c0b15db51dded41f1cb7d4063 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20B=C3=B6ttger?= Date: Fri, 15 Aug 2025 11:53:45 +0200 Subject: [PATCH] Add alert handler for outgoing connections to iOS app --- QuickDrop.xcodeproj/project.pbxproj | 2 ++ QuickDrop/Util/ErrorAlertHandler.swift | 49 ++++++++++++++++++++------ QuickDropiOS/Model.swift | 29 ++++++++++----- 3 files changed, 61 insertions(+), 19 deletions(-) diff --git a/QuickDrop.xcodeproj/project.pbxproj b/QuickDrop.xcodeproj/project.pbxproj index 4bd3331..c3a180e 100644 --- a/QuickDrop.xcodeproj/project.pbxproj +++ b/QuickDrop.xcodeproj/project.pbxproj @@ -97,6 +97,7 @@ EB886E5E2E4F13EB0052B7D9 /* Zip.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBCC27F82D80C9F70079C478 /* Zip.swift */; }; EB886E672E4F2F800052B7D9 /* License.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB886E662E4F2F7C0052B7D9 /* License.swift */; }; EB886E682E4F2F800052B7D9 /* License.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB886E662E4F2F7C0052B7D9 /* License.swift */; }; + EB886E6A2E4F37B90052B7D9 /* ErrorAlertHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBC05C5A2E33936C00EECC18 /* ErrorAlertHandler.swift */; }; EB96ED8B2D7F7CF7004B3FF1 /* SmallSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB96ED8A2D7F7CF4004B3FF1 /* SmallSheetView.swift */; }; EB96ED8C2D7F7E7B004B3FF1 /* SmallSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB96ED8A2D7F7CF4004B3FF1 /* SmallSheetView.swift */; }; EB96ED8D2D7F7EA3004B3FF1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 69DA9A1329E0BF5200A442DA /* Assets.xcassets */; }; @@ -775,6 +776,7 @@ EB5C37FC2E377CE70052EFD7 /* OutboundNearbyConnection.swift in Sources */, EB5C37FD2E377CE70052EFD7 /* Logger.swift in Sources */, EB5C37FE2E377CE70052EFD7 /* offline_wire_formats.pb.swift in Sources */, + EB886E6A2E4F37B90052B7D9 /* ErrorAlertHandler.swift in Sources */, EB886E5E2E4F13EB0052B7D9 /* Zip.swift in Sources */, EB5C383D2E38C2D90052EFD7 /* AudioManager.swift in Sources */, EB5C38402E38C49D0052EFD7 /* LaunchAtLogin.swift in Sources */, diff --git a/QuickDrop/Util/ErrorAlertHandler.swift b/QuickDrop/Util/ErrorAlertHandler.swift index 0bd0107..30b40a8 100644 --- a/QuickDrop/Util/ErrorAlertHandler.swift +++ b/QuickDrop/Util/ErrorAlertHandler.swift @@ -6,25 +6,34 @@ // import AudioToolbox -import BezelNotification -import Cocoa import Network import StoreKit import SwiftUI import UserNotifications +#if os(macOS) +import BezelNotification +import Cocoa +#else +import LUI +#endif + class ErrorAlertHandler { private init() {} static let shared = ErrorAlertHandler() private var isAlertShown = false + #if os(macOS) private var firewallAlertWindow: NSWindow? private var apIsolationAlertWindow: NSWindow? private var networkFilterAlertWindow: NSWindow? + #endif func showErrorAlert(for deviceName: String, error: Error) { + #if os(macOS) NSApp.activate(ignoringOtherApps: true) + #endif var description = "" let fixInstructions = " " + "Error.FixInstructions".localized() @@ -36,11 +45,19 @@ class ErrorAlertHandler { case .protocolError(errorMessage: let errorMessage): description = errorMessage + fixInstructions case .packetFilterError: + #if os(macOS) openAlert(type: .NetworkFilter) return + #else + description = error.localizedDescription + #endif case .firewallError: + #if os(macOS) openAlert(type: .Firewall) return + #else + description = error.localizedDescription + #endif case .requiredFieldMissing(errorMessage: let errorMessage): description = errorMessage + fixInstructions case .ukey2: @@ -57,13 +74,6 @@ class ErrorAlertHandler { description = error.localizedDescription } - let alert = NSAlert() - alert.alertStyle = .critical - alert.messageText = String(format: "TransferError".localized(), arguments: [deviceName]) - alert.informativeText = description - alert.addButton(withTitle: "InformDeveloper".localized()) - alert.addButton(withTitle: "CloseAlert".localized()) - // Prevent multiple alerts at the same time if self.isAlertShown { log("Skipping alert for error \(error.localizedDescription) because one is already shown") @@ -71,10 +81,20 @@ class ErrorAlertHandler { } else { AudioManager.playErrorSound() - self.isAlertShown = true - log("Showing alert with message: \"\(alert.messageText)\" and description: \"\(alert.informativeText)\"") + + let title = String(format: "TransferError".localized(), arguments: [deviceName]) + log("Showing alert with title: \"\(title)\" and description: \"\(description)\"") log("Unsuccessful transmission. Already successful transmissions: \(transmissionCount())") + #if os(macOS) + self.isAlertShown = true + let alert = NSAlert() + alert.alertStyle = .critical + alert.messageText = title + alert.informativeText = description + alert.addButton(withTitle: "InformDeveloper".localized()) + alert.addButton(withTitle: "CloseAlert".localized()) + let result = alert.runModal() self.isAlertShown = false @@ -82,10 +102,14 @@ class ErrorAlertHandler { log("Sending logging string") sendLoggingString() } + #else + showAlert(title: title, message: description) + #endif } } + #if os(macOS) func openAlert(type: AlertType) { log("Opening Alert for \(type)") AudioManager.playErrorSound() @@ -130,9 +154,11 @@ class ErrorAlertHandler { self.apIsolationAlertWindow = nil } } + #endif } +#if os(macOS) func sendLoggingString() { // If we're in an extension, redirect to the main app if Bundle.main.bundlePath.hasSuffix(".appex") { @@ -195,6 +221,7 @@ func copyToClipboard(_ text: String) { pasteboard.clearContents() pasteboard.setString(text, forType: .string) } +#endif enum AlertType: String { diff --git a/QuickDropiOS/Model.swift b/QuickDropiOS/Model.swift index 0e113c7..101e980 100644 --- a/QuickDropiOS/Model.swift +++ b/QuickDropiOS/Model.swift @@ -11,7 +11,6 @@ class ShareViewModel: ObservableObject, ShareExtensionDelegate { @Published var foundDevices: [RemoteDeviceInfo] = [] @Published var selectedDevice: RemoteDeviceInfo? - @Published var lastError: Error? @Published var progressState: String? = nil @Published var progressValue: Double? = nil @@ -25,6 +24,7 @@ class ShareViewModel: ObservableObject, ShareExtensionDelegate { init() { NearbyConnectionManager.shared.startDeviceDiscovery() + NearbyConnectionManager.shared.becomeVisible() NearbyConnectionManager.shared.addShareExtensionDelegate(self) } @@ -47,10 +47,11 @@ class ShareViewModel: ObservableObject, ShareExtensionDelegate { } func connectionFailed(with error: any Error) { - progressValue = nil - lastError = error - //ErrorAlertHandler.shared.showErrorAlert(for: chosenDevice?.name ?? "", error: error) + ErrorAlertHandler.shared.showErrorAlert(for: selectedDevice?.name ?? "?", error: error) + + progressValue = nil + selectedDevice = nil } func transferAccepted() { @@ -62,6 +63,8 @@ class ShareViewModel: ObservableObject, ShareExtensionDelegate { } func transferFinished() { + progressValue = 0 + selectedDevice = nil progressState = "TransferFinished".localized() } @@ -70,9 +73,19 @@ class ShareViewModel: ObservableObject, ShareExtensionDelegate { } func selectDevice(device: RemoteDeviceInfo) { - progressValue = 0 - progressState = "Connecting".localized() - selectedDevice = device - NearbyConnectionManager.shared.startOutgoingTransfer(deviceID: device.id!, delegate: self, urls: urls, textToSend: textToSend) + + // already selected, cancel transfer + if device == selectedDevice { + progressValue = 0 + progressState = nil + selectedDevice = nil + NearbyConnectionManager.shared.cancelOutgoingTransfer(id: device.id!) + } + else { + progressValue = 0 + progressState = "Connecting".localized() + selectedDevice = device + NearbyConnectionManager.shared.startOutgoingTransfer(deviceID: device.id!, delegate: self, urls: urls, textToSend: textToSend) + } } }