From 7c35cc7b35c28140b0b6dc57dae55bebc4ca10b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20B=C3=B6ttger?= <49094811+leonboe1@users.noreply.github.com> Date: Mon, 29 Jun 2026 18:57:01 +0200 Subject: [PATCH] Improve fix instructions if other device is already running QuickDrop --- QuickDrop/Localizable.xcstrings | 233 +++++++++++++++++++++++ QuickDrop/Util/ErrorAlertHandler.swift | 23 ++- QuickDrop/ViewModel/ReceiveModel.swift | 4 +- QuickDrop/ViewModel/SendModel.swift | 2 +- QuickDropiOS/ShareViewModel.swift | 2 +- QuickDropiOS/View/FilePicker.swift | 2 + ShareExtension/ShareViewController.swift | 2 +- 7 files changed, 260 insertions(+), 8 deletions(-) diff --git a/QuickDrop/Localizable.xcstrings b/QuickDrop/Localizable.xcstrings index 670e663..04c0d28 100644 --- a/QuickDrop/Localizable.xcstrings +++ b/QuickDrop/Localizable.xcstrings @@ -15609,6 +15609,239 @@ } } }, + "Error.FixInstructionsAppInstalled" : { + "extractionState" : "manual", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إذا تكرر ذلك، يرجى إعادة تشغيل جهازك الآخر والمحاولة مرة أخرى." + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Si això passa repetidament, reinicia l'altre dispositiu i torna-ho a provar." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pokud se to opakuje, restartujte své druhé zařízení a zkuste to znovu." + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hvis dette sker gentagne gange, genstart venligst din anden enhed og prøv igen." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wenn das wiederholt passiert, starte bitte dein anderes Gerät neu und versuche es erneut." + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αν αυτό συμβαίνει επαναλαμβανόμενα, παρακαλώ επανεκκινήστε τη συσκευή σας και δοκιμάστε ξανά." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "If this happens repeatedly, please restart your other device and try again." + } + }, + "en-AU" : { + "stringUnit" : { + "state" : "translated", + "value" : "If this happens repeatedly, please restart your other device and try again." + } + }, + "en-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "If this happens repeatedly, please restart your other device and try again." + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "If this happens repeatedly, please restart your other device and try again." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Si esto ocurre repetidamente, reinicia tu otro dispositivo e inténtalo de nuevo." + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Si esto ocurre repetidamente, por favor reinicia tu otro dispositivo e inténtalo de nuevo." + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jos tämä toistuu, käynnistä toinen laitteesi uudelleen ja yritä uudelleen." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Si cela se produit à plusieurs reprises, veuillez redémarrer votre autre appareil et réessayer." + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Si cela se produit souvent, veuillez redémarrer votre autre appareil et réessayer." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אם זה קורה שוב ושוב, נא הפעל מחדש את המכשיר השני ונסה שוב." + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "अगर यह बार-बार होता है, तो कृपया अपने दूसरे डिवाइस को पुनः प्रारंभ करें और फिर से प्रयास करें।" + } + }, + "hr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ako se ovo ponavlja, ponovno pokrenite drugi uređaj i pokušajte ponovno." + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ha ez ismétlődően előfordul, indítsa újra a másik eszközt, és próbálja újra." + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jika ini terjadi berulang kali, silakan mulai ulang perangkat Anda yang lain dan coba lagi." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Se questo accade ripetutamente, riavvia il tuo altro dispositivo e riprova." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "この問題が繰り返し発生する場合は、他のデバイスを再起動してからもう一度お試しください。" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "이 문제가 반복된다면, 다른 기기를 재시작한 후 다시 시도해 보세요." + } + }, + "ms" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jika ini berlaku berulang kali, sila mulakan semula peranti anda yang lain dan cuba lagi." + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hvis dette skjer gjentatte ganger, start den andre enheten på nytt og prøv igjen." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Als dit herhaaldelijk gebeurt, start dan je andere apparaat opnieuw op en probeer het opnieuw." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jeśli to się powtarza, uruchom ponownie inne urządzenie i spróbuj ponownie." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Se isso acontecer repetidamente, reinicie seu outro dispositivo e tente novamente." + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Se isto acontecer repetidamente, reinicie o seu outro dispositivo e tente novamente." + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dacă se întâmplă frecvent, repornește celălalt dispozitiv și încearcă din nou." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Если это происходит часто, перезагрузите ваше другое устройство и попробуйте снова." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Om detta händer upprepade gånger, starta om din andra enhet och försök igen." + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "หากเกิดเหตุการณ์นี้บ่อยๆ กรุณารีสตาร์ทอุปกรณ์อีกเครื่องแล้วลองอีกครั้ง" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bu durum tekrar ederse, lütfen diğer cihazınızı yeniden başlatın ve tekrar deneyin." + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Якщо це трапляється часто, перезапустіть інший пристрій і спробуйте ще раз." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nếu điều này xảy ra nhiều lần, vui lòng khởi động lại thiết bị kia và thử lại." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "如果这种情况反复发生,请重启你的另一台设备,然后重试。" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "如果這種情況反覆發生,請重新啟動您的其他裝置,然後再試一次。" + } + } + } + }, "Error.NetworkDown" : { "extractionState" : "manual", "localizations" : { diff --git a/QuickDrop/Util/ErrorAlertHandler.swift b/QuickDrop/Util/ErrorAlertHandler.swift index 41dd65f..74bfbe9 100644 --- a/QuickDrop/Util/ErrorAlertHandler.swift +++ b/QuickDrop/Util/ErrorAlertHandler.swift @@ -30,13 +30,30 @@ class ErrorAlertHandler { private var androidAirDropModeAlertWindow: NSWindow? #endif - func showErrorAlert(for deviceName: String, error: Error) { + /// Resource key for the recoverable-error suffix ("if this keeps happening…") appended to most + /// transfer errors. + /// + /// `Error.FixInstructions` suggests downloading the QuickDrop Android app from Google Play, which + /// is pointless for a peer that already runs QuickDrop. For such a peer (`isQuickDropPeer == true`) + /// the `Error.FixInstructionsAppInstalled` variant — the same advice without the download + /// suggestion — is used instead. + static func fixInstructionsKey(isQuickDropPeer: Bool) -> String { + isQuickDropPeer ? "Error.FixInstructionsAppInstalled" : "Error.FixInstructions" + } + + /// Presents the transfer-error alert for `deviceName`. + /// + /// - Parameter isQuickDropPeer: whether the other device runs the QuickDrop app. When `true`, the + /// "download the app from Google Play" suggestion is omitted from the fix instructions (see + /// `fixInstructionsKey(isQuickDropPeer:)`). Defaults to `false` so callers without device context + /// keep the full hint. + func showErrorAlert(for deviceName: String, error: Error, isQuickDropPeer: Bool = false) { #if os(macOS) NSApp.activate(ignoringOtherApps: true) #endif - + var description = "" - let fixInstructions = " " + "Error.FixInstructions".localized() + let fixInstructions = " " + Self.fixInstructionsKey(isQuickDropPeer: isQuickDropPeer).localized() if let ne = (error as? NearbyError) { switch ne { case .inputOutput: diff --git a/QuickDrop/ViewModel/ReceiveModel.swift b/QuickDrop/ViewModel/ReceiveModel.swift index 78585ad..1e12c71 100644 --- a/QuickDrop/ViewModel/ReceiveModel.swift +++ b/QuickDrop/ViewModel/ReceiveModel.swift @@ -291,7 +291,7 @@ class ReceiveModel: ObservableObject, InboundAppDelegate { controlPlusScreen(false) errorVibration() - ErrorAlertHandler.shared.showErrorAlert(for: name, error: error) + ErrorAlertHandler.shared.showErrorAlert(for: name, error: error, isQuickDropPeer: device?.isQuickDropPeer ?? false) } } else { @@ -408,7 +408,7 @@ class ReceiveModel: ObservableObject, InboundAppDelegate { controlPlusScreen(false) errorVibration() - ErrorAlertHandler.shared.showErrorAlert(for: name, error: error) + ErrorAlertHandler.shared.showErrorAlert(for: name, error: error, isQuickDropPeer: device?.isQuickDropPeer ?? false) } return } diff --git a/QuickDrop/ViewModel/SendModel.swift b/QuickDrop/ViewModel/SendModel.swift index 7c8c375..b9265b3 100644 --- a/QuickDrop/ViewModel/SendModel.swift +++ b/QuickDrop/ViewModel/SendModel.swift @@ -63,7 +63,7 @@ class SendModel: ObservableObject, OutboundAppDelegate { if let name = selectedDevice?.name { errorVibration() - ErrorAlertHandler.shared.showErrorAlert(for: name, error: error) + ErrorAlertHandler.shared.showErrorAlert(for: name, error: error, isQuickDropPeer: selectedDevice?.isQuickDropPeer ?? false) } progressValue = nil diff --git a/QuickDropiOS/ShareViewModel.swift b/QuickDropiOS/ShareViewModel.swift index 23a685d..956ea3e 100644 --- a/QuickDropiOS/ShareViewModel.swift +++ b/QuickDropiOS/ShareViewModel.swift @@ -47,7 +47,7 @@ class SendModel: ObservableObject, OutboundAppDelegate { func connectionFailed(with error: any Error) { - ErrorAlertHandler.shared.showErrorAlert(for: selectedDevice?.name ?? "?", error: error) + ErrorAlertHandler.shared.showErrorAlert(for: selectedDevice?.name ?? "?", error: error, isQuickDropPeer: selectedDevice?.isQuickDropPeer ?? false) progressValue = nil selectedDevice = nil diff --git a/QuickDropiOS/View/FilePicker.swift b/QuickDropiOS/View/FilePicker.swift index 77fac62..152f39e 100644 --- a/QuickDropiOS/View/FilePicker.swift +++ b/QuickDropiOS/View/FilePicker.swift @@ -70,6 +70,8 @@ struct SendPickerButton: View { selectedPhotoItems = [] } catch { log("Could not fetch photo URLs: \(error)") + onResult(nil, nil) // let the caller dismiss the "preparing" it opened on `onPrepare` + selectedPhotoItems = [] } } } diff --git a/ShareExtension/ShareViewController.swift b/ShareExtension/ShareViewController.swift index bc455f1..3a05dac 100644 --- a/ShareExtension/ShareViewController.swift +++ b/ShareExtension/ShareViewController.swift @@ -228,7 +228,7 @@ class ShareViewController: NSViewController, OutboundAppDelegate { progressProgressBar?.doubleValue = 0 lastError = error - ErrorAlertHandler.shared.showErrorAlert(for: chosenDevice?.name ?? "", error: error) + ErrorAlertHandler.shared.showErrorAlert(for: chosenDevice?.name ?? "", error: error, isQuickDropPeer: chosenDevice?.isQuickDropPeer ?? false) self.extensionContext!.cancelRequest(withError: error) }