diff --git a/NearbyShare/Connection/NearbyConnectionManager.swift b/NearbyShare/Connection/NearbyConnectionManager.swift index d7ae9b4..dcdd3b3 100644 --- a/NearbyShare/Connection/NearbyConnectionManager.swift +++ b/NearbyShare/Connection/NearbyConnectionManager.swift @@ -15,8 +15,9 @@ import Network import System import SwiftECC import CryptoKit +import SwiftUI -public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearbyConnectionDelegate, OutboundNearbyConnectionDelegate { +public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearbyConnectionDelegate, OutboundNearbyConnectionDelegate, ObservableObject { #if !EXTENSION private let sleepManager = SleepManager.shared @@ -39,6 +40,8 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb public var mainAppDelegate: (any InboundAppDelegate)? public static let shared = NearbyConnectionManager() + @Published var attachments: AttachmentDetails? = nil + override init() { #if os(macOS) diff --git a/QuickDrop.xcodeproj/project.pbxproj b/QuickDrop.xcodeproj/project.pbxproj index 96277a9..64801d3 100644 --- a/QuickDrop.xcodeproj/project.pbxproj +++ b/QuickDrop.xcodeproj/project.pbxproj @@ -83,6 +83,7 @@ EB704A832E4B10D4008E608F /* DistributionDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB704A812E4A7BAE008E608F /* DistributionDetector.swift */; }; EB704A842E4B10D4008E608F /* DistributionDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB704A812E4A7BAE008E608F /* DistributionDetector.swift */; }; EB7654782E525ACC006B4AA3 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = EB7654772E525ACC006B4AA3 /* MainMenu.xib */; }; + EB76547D2E526164006B4AA3 /* ZIPFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = EB76547C2E526164006B4AA3 /* ZIPFoundation */; }; EB7CD1452D6FB0C300CB0CB5 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB7CD1442D6FB0C100CB0CB5 /* Logger.swift */; }; EB7CD1462D6FB0E800CB0CB5 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB7CD1442D6FB0C100CB0CB5 /* Logger.swift */; }; EB7F54122D78E96000989564 /* TutorialView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB7F54112D78E95C00989564 /* TutorialView.swift */; }; @@ -346,6 +347,7 @@ EB5C37D82E377ACF0052EFD7 /* SwiftECC in Frameworks */, EB5C37D62E377ACF0052EFD7 /* SwiftProtobuf in Frameworks */, EB9925852E45EF0900357055 /* QRCode in Frameworks */, + EB76547D2E526164006B4AA3 /* ZIPFoundation in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1528,6 +1530,14 @@ minimumVersion = 2.1.0; }; }; + EB7654792E526155006B4AA3 /* XCRemoteSwiftPackageReference "ZIPFoundation" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/weichsel/ZIPFoundation.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.9.19; + }; + }; EB886E562E4E66420052B7D9 /* XCRemoteSwiftPackageReference "LUI" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/leonboe1/LUI.git"; @@ -1590,6 +1600,11 @@ package = 69DA9A3429E1994C00A442DA /* XCRemoteSwiftPackageReference "SwiftECC" */; productName = SwiftECC; }; + EB76547C2E526164006B4AA3 /* ZIPFoundation */ = { + isa = XCSwiftPackageProductDependency; + package = EB7654792E526155006B4AA3 /* XCRemoteSwiftPackageReference "ZIPFoundation" */; + productName = ZIPFoundation; + }; EB886E572E4E66420052B7D9 /* LUI */ = { isa = XCSwiftPackageProductDependency; package = EB886E562E4E66420052B7D9 /* XCRemoteSwiftPackageReference "LUI" */; diff --git a/QuickDrop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/QuickDrop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 799e8bd..bc436ff 100644 --- a/QuickDrop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/QuickDrop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "6596056df2297c97fbb8a90f8b23b4feec22b8d0fbdb59f154e1c543f10c631c", + "originHash" : "212cf044c2b0bab89578ca93a3666a656e0c44fb54a69f813c4cc4fae07827d7", "pins" : [ { "identity" : "applewatchscreensize", diff --git a/QuickDrop/Localizable.xcstrings b/QuickDrop/Localizable.xcstrings index 1bc6f85..ea12896 100644 --- a/QuickDrop/Localizable.xcstrings +++ b/QuickDrop/Localizable.xcstrings @@ -10585,6 +10585,113 @@ } } }, + "YouAreSending" : { + "extractionState" : "manual", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "أنت ترسل" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Du sendest" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "You’re sending" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Estás enviando" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vous envoyez" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Küldés alatt" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stai inviando" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "送信中です" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "보내는 중입니다" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Je bent aan het verzenden" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Você está enviando" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Está a enviar" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trimiteți" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Вы отправляете" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "คุณกำลังส่ง" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ви надсилаєте" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "您正在发送" + } + } + } + }, "YouAreVisibleAs" : { "extractionState" : "manual", "localizations" : { diff --git a/QuickDropiOS/ContentView.swift b/QuickDropiOS/ContentView.swift index 21bcc26..9d32e98 100644 --- a/QuickDropiOS/ContentView.swift +++ b/QuickDropiOS/ContentView.swift @@ -10,11 +10,9 @@ import LUI struct ContentView: View { - var attachments: AttachmentDetails? = nil - var body: some View { AppRootView(isPlus: .constant(true), phoneView: { - DeviceListView(attachments: attachments) + DeviceListView() }, settingsView: { EmptyView() }) @@ -23,29 +21,41 @@ struct ContentView: View { struct DeviceListView: View { - var attachments: AttachmentDetails? = nil - @StateObject var sendModel = SendModel() #if !EXTENSION @StateObject var receiveModel = ReceiveModel() #endif + @ObservedObject var nearbyConnectionManager = NearbyConnectionManager.shared + var body: some View { NavigationSubView(header: "QuickDrop ") { - // "You're visible as" VStack(alignment: .leading, spacing: 8) { - FooterView(text: "YouAreVisibleAs") - Text(NearbyConnectionManager.shared.deviceInfo.name ?? "Unknown".localized()) - .fontWeight(.bold) - .padding(.horizontal, 16) - .padding(.vertical, 6) - .background(Color.defaultForegroundColor) - .cornerRadius(24) - .padding(.horizontal) + let attachments = nearbyConnectionManager.attachments + + FooterView(text: attachments == nil ? "YouAreVisibleAs".localized() : "YouAreSending".localized()) + + HStack { + + if let image = attachments?.previewImage { + image + .resizable() + .scaledToFill() + .frame(width: 10, height: 10) + } + + Text(attachments?.shortDescription ?? NearbyConnectionManager.shared.deviceInfo.name ?? "Unknown".localized()) + .fontWeight(.bold) + } + .padding(.horizontal, 16) + .padding(.vertical, 6) + .background(Color.defaultForegroundColor) + .cornerRadius(24) + .padding(.horizontal) } .padding(.top, 8) .frame(maxWidth: .infinity, alignment: .leading) @@ -72,7 +82,7 @@ struct DeviceListView: View { let isSelected = sendModel.selectedDevice == device ZStack { - if let attachments = attachments { + if let attachments = NearbyConnectionManager.shared.attachments { LUIButton { sendModel.urls = attachments.urls sendModel.textToSend = attachments.textToSend diff --git a/QuickDropiOS/ReceiveModel.swift b/QuickDropiOS/ReceiveModel.swift index 625735b..ffc66c8 100644 --- a/QuickDropiOS/ReceiveModel.swift +++ b/QuickDropiOS/ReceiveModel.swift @@ -138,10 +138,19 @@ class ReceiveModel: ObservableObject, InboundAppDelegate { func finish() { if let error = error { + + #if os(iOS) + errorVibration() + #endif + controlPlusScreen(false) ErrorAlertHandler.shared.showErrorAlert(for: device.name ?? "Android", error: error) } else { + #if os(iOS) + doubleVibration() + #endif + if wasPlainTextTransfer { showCopiedToClipboardAlert() } @@ -165,9 +174,6 @@ class ReceiveModel: ObservableObject, InboundAppDelegate { DispatchQueue.main.async { BezelNotification.show(messageText: "InsertedIntoClipboard".localized(), icon: .clipboard) } - #else - // iOS - doubleVibration() #endif } diff --git a/QuickDropiOS/SendModel.swift b/QuickDropiOS/SendModel.swift index dfd7ecc..e7d7c00 100644 --- a/QuickDropiOS/SendModel.swift +++ b/QuickDropiOS/SendModel.swift @@ -6,6 +6,9 @@ // import SwiftUI +#if os(iOS) +import LUI +#endif class SendModel: ObservableObject, OutboundAppDelegate { @@ -47,6 +50,10 @@ class SendModel: ObservableObject, OutboundAppDelegate { func connectionFailed(with error: any Error) { + #if os(iOS) + errorVibration() + #endif + ErrorAlertHandler.shared.showErrorAlert(for: selectedDevice?.name ?? "?", error: error) progressValue = nil @@ -62,9 +69,16 @@ class SendModel: ObservableObject, OutboundAppDelegate { } func transferFinished() { + + #if os(iOS) + doubleVibration() + #endif + progressValue = 0 selectedDevice = nil progressState = "TransferFinished".localized() + + NearbyConnectionManager.shared.attachments?.closeView?() } func startTransferWithQrCode(device: RemoteDeviceInfo) { diff --git a/iOSShareExtension/ShareViewController.swift b/iOSShareExtension/ShareViewController.swift index 27dcac6..c1eadd8 100644 --- a/iOSShareExtension/ShareViewController.swift +++ b/iOSShareExtension/ShareViewController.swift @@ -22,26 +22,30 @@ class ShareViewController: UIViewController { self?.extensionContext?.completeRequest(returningItems: nil, completionHandler: nil) } - // Replace with your app’s SwiftUI entry view - let contentView = ContentView(attachments: attachmentDetails) - - // Embed SwiftUI inside the extension’s controller - let hostingController = UIHostingController(rootView: contentView) - - addChild(hostingController) - hostingController.view.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(hostingController.view) - - // Pin to edges - NSLayoutConstraint.activate([ - hostingController.view.topAnchor.constraint(equalTo: view.topAnchor), - hostingController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor), - hostingController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), - hostingController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor) - ]) - - hostingController.didMove(toParent: self) + DispatchQueue.main.async { + NearbyConnectionManager.shared.attachments = attachmentDetails + } }) + + // Replace with your app’s SwiftUI entry view + let contentView = ContentView() + + // Embed SwiftUI inside the extension’s controller + let hostingController = UIHostingController(rootView: contentView) + + addChild(hostingController) + hostingController.view.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(hostingController.view) + + // Pin to edges + NSLayoutConstraint.activate([ + hostingController.view.topAnchor.constraint(equalTo: view.topAnchor), + hostingController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor), + hostingController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), + hostingController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor) + ]) + + hostingController.didMove(toParent: self) } }