mirror of
https://github.com/leonboe1/QuickDrop.git
synced 2026-11-11 02:51:11 +00:00
Prefer fixed port if possible
This commit is contained in:
1 parent
b546370e87
commit
cce84e9460
7 files changed
+133
-41
No files matched your search
@@ -21,6 +21,8 @@ import LUI
|
||||
|
||||
public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearbyConnectionDelegate, OutboundNearbyConnectionDelegate, ObservableObject {
|
||||
|
||||
// MARK: Private Properties
|
||||
|
||||
#if !EXTENSION
|
||||
private let sleepManager = SleepManager.shared
|
||||
#endif
|
||||
@@ -32,7 +34,7 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb
|
||||
private var inboundAppDelegates: [InboundAppDelegate] = []
|
||||
private var outgoingTransfers: [String: OutgoingTransferInfo] = [:]
|
||||
private var startedDeviceDiscovery = false
|
||||
private var startedAdvertising = false
|
||||
private var startedAdvertising = false { didSet { informAboutStatus() }}
|
||||
private var browsers: [NWBrowser] = []
|
||||
private let serviceTypes = ["_FC9F5ED42C8A._tcp."]
|
||||
private var qrCodePrivateKey: ECPrivateKey?
|
||||
@@ -42,24 +44,31 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb
|
||||
private let connectionMonitorQueue = DispatchQueue(label: "NetworkConnectionMonitorQueue")
|
||||
private var securityScopeUrl: URL?
|
||||
private static let customDeviceNameKey = "com.leonboettger.quickdrop.deviceName"
|
||||
private var defaultPort: NWEndpoint.Port = 50362
|
||||
|
||||
public let endpointID: [UInt8] = generateEndpointID()
|
||||
|
||||
// MARK: Shared Instance
|
||||
|
||||
public static let shared = NearbyConnectionManager()
|
||||
|
||||
|
||||
// MARK: Published State
|
||||
|
||||
@Published var attachments: AttachmentDetails? = nil
|
||||
@Published var hasLocalNetworkPermission = true
|
||||
@Published var isConnectedToLocalNetwork = true
|
||||
@Published var isConnectedToLocalNetwork = true { didSet { informAboutStatus() }}
|
||||
@Published var deviceInfo: EndpointInfo
|
||||
|
||||
public var connectionUpdateCallback: (Bool) -> Void = { _ in } {
|
||||
didSet {
|
||||
connectionUpdateCallback(isConnectedToLocalNetwork)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Public Properties
|
||||
|
||||
public var endpointID: [UInt8] = generateEndpointID(forceRegeneration: false)
|
||||
public var connectionUpdateCallback: (Bool) -> Void = { _ in } { didSet { informAboutStatus() }}
|
||||
public var changedDeviceNameCallback: () -> Void = { }
|
||||
|
||||
|
||||
// MARK: Initializers
|
||||
|
||||
override init() {
|
||||
self.deviceInfo = Self.getEndpointInfo()
|
||||
tcpListener = try! NWListener(using: NWParameters(tls: .none))
|
||||
@@ -83,7 +92,6 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb
|
||||
log("[NearbyConnectionManager] Local network lost.")
|
||||
}
|
||||
self.isConnectedToLocalNetwork = isConnected
|
||||
self.connectionUpdateCallback(isConnected)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,6 +121,8 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deinitializer
|
||||
|
||||
deinit {
|
||||
self.stopAccessingSaveDirectory()
|
||||
}
|
||||
@@ -161,22 +171,73 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb
|
||||
}
|
||||
|
||||
|
||||
public func becomeVisible() {
|
||||
public func becomeVisible(randomPort: Bool = false) {
|
||||
if startedAdvertising {
|
||||
log("[NearbyConnectionManager] Already advertising, skipping")
|
||||
return
|
||||
}
|
||||
|
||||
log("[NearbyConnectionManager] Becoming visible")
|
||||
let parameters = NWParameters(tls: .none)
|
||||
|
||||
tcpListener = try! NWListener(using: NWParameters(tls: .none))
|
||||
startedAdvertising = true
|
||||
|
||||
tcpListener.stateUpdateHandler = { (state: NWListener.State) in
|
||||
if case .ready = state {
|
||||
self.initMDNS()
|
||||
do {
|
||||
if randomPort {
|
||||
self.tcpListener = try NWListener(using: parameters)
|
||||
} else {
|
||||
self.tcpListener = try NWListener(using: parameters, on: defaultPort)
|
||||
}
|
||||
}
|
||||
catch {
|
||||
log("[NearbyConnectionManager] Error starting TCP listener: \(error). Trying with random port.")
|
||||
|
||||
runAfter(seconds: 1) {
|
||||
self.becomeVisible(randomPort: true)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
startedAdvertising = true
|
||||
|
||||
tcpListener.stateUpdateHandler = { state in
|
||||
switch state {
|
||||
case .ready:
|
||||
log("[NearbyConnectionManager] Listener ready")
|
||||
self.initMDNS(forceIDRegeneration: randomPort)
|
||||
|
||||
if let port = self.tcpListener.port, port != self.defaultPort {
|
||||
log("[NearbyConnectionManager] Updated default port to \(port)")
|
||||
self.defaultPort = port
|
||||
}
|
||||
|
||||
case .failed(let error):
|
||||
log("[NearbyConnectionManager] Listener failed: \(error)")
|
||||
self.becomeInvisible()
|
||||
|
||||
runAfter(seconds: 1) {
|
||||
if case .posix(let posixError) = error,
|
||||
posixError == .EADDRINUSE {
|
||||
self.becomeVisible(randomPort: true)
|
||||
}
|
||||
else {
|
||||
self.becomeVisible()
|
||||
}
|
||||
}
|
||||
|
||||
case .cancelled:
|
||||
log("[NearbyConnectionManager] Listener cancelled")
|
||||
|
||||
case .setup:
|
||||
log("[NearbyConnectionManager] Listener setup")
|
||||
|
||||
case .waiting(let state):
|
||||
log("[NearbyConnectionManager] Listener waiting: \(state)")
|
||||
|
||||
@unknown default:
|
||||
log("[NearbyConnectionManager] Listener unknown state")
|
||||
}
|
||||
}
|
||||
|
||||
tcpListener.newConnectionHandler = { (connection: NWConnection) in
|
||||
let id = UUID().uuidString
|
||||
let conn = InboundNearbyConnection(connection: connection, id: id)
|
||||
@@ -184,6 +245,7 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb
|
||||
conn.delegate = self
|
||||
conn.start()
|
||||
}
|
||||
|
||||
tcpListener.start(queue: .global(qos: .utility))
|
||||
}
|
||||
|
||||
@@ -202,11 +264,13 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb
|
||||
}
|
||||
|
||||
|
||||
private static func generateEndpointID() -> [UInt8] {
|
||||
private static func generateEndpointID(forceRegeneration: Bool) -> [UInt8] {
|
||||
|
||||
// Try to retrieve from UserDefaults
|
||||
if let savedString = Settings.shared.endpointID,
|
||||
if !forceRegeneration,
|
||||
let savedString = Settings.shared.endpointID,
|
||||
let savedData = savedString.data(using: .utf8) {
|
||||
log("[NearbyConnectionManager] Using cached endpoint ID: \(savedString)")
|
||||
return [UInt8](savedData)
|
||||
}
|
||||
|
||||
@@ -220,12 +284,16 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb
|
||||
// Save to UserDefaults as String
|
||||
let idString = String(bytes: id, encoding: .utf8) ?? ""
|
||||
Settings.shared.endpointID = idString
|
||||
log("[NearbyConnectionManager] Storing new endpoint ID: \(idString) ======================================")
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
|
||||
private func initMDNS() {
|
||||
private func initMDNS(forceIDRegeneration: Bool) {
|
||||
|
||||
self.endpointID = Self.generateEndpointID(forceRegeneration: forceIDRegeneration)
|
||||
|
||||
let nameBytes: [UInt8] = [
|
||||
0x23, // PCP
|
||||
endpointID[0], endpointID[1], endpointID[2], endpointID[3],
|
||||
@@ -721,6 +789,14 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb
|
||||
}
|
||||
|
||||
|
||||
// MARK: Callbacks
|
||||
|
||||
func informAboutStatus() {
|
||||
DispatchQueue.main.async {
|
||||
self.connectionUpdateCallback(self.isConnectedToLocalNetwork && self.startedAdvertising)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -- MARK: - Internal Data Model
|
||||
|
||||
|
||||
@@ -1719,8 +1719,8 @@
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
SHARED_BUILD = 108;
|
||||
SHARED_VERSION = 2.10.4;
|
||||
SHARED_BUILD = 111;
|
||||
SHARED_VERSION = 2.10.6;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
@@ -1782,8 +1782,8 @@
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = macosx;
|
||||
SHARED_BUILD = 108;
|
||||
SHARED_VERSION = 2.10.4;
|
||||
SHARED_BUILD = 111;
|
||||
SHARED_VERSION = 2.10.6;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
"location" : "https://github.com/leonboe1/LUI.git",
|
||||
"state" : {
|
||||
"branch" : "main",
|
||||
"revision" : "170752316e3b728bd06f8fa4073fe18e9a99a5cd"
|
||||
"revision" : "c26d4a19b175cf725deb00b412cf2f8c6df95826"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -337,7 +337,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
|
||||
}
|
||||
}), hasSheet: false)
|
||||
|
||||
let plusViewWidth: CGFloat = 550
|
||||
let plusViewWidth: CGFloat = 750
|
||||
let plusViewHeight: CGFloat = 750
|
||||
|
||||
// Create an NSWindow to host the SwiftUI view
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
// Created by Leon Böttger on 05.10.25.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import AppKit
|
||||
import LUI
|
||||
import SwiftUI
|
||||
|
||||
let toastViewSize = CGSize(width: 400, height: 120)
|
||||
|
||||
@@ -32,14 +33,12 @@ struct QuickDropToastView: View {
|
||||
let isDone = receiveModel.toastActions != nil
|
||||
|
||||
VStack(spacing: 12) {
|
||||
HStack(spacing: 12) {
|
||||
ZStack(alignment: .bottomLeading) {
|
||||
Image(.quickDropIcon)
|
||||
.resizable()
|
||||
.scaledToFill()
|
||||
.frame(width: 44, height: 44)
|
||||
.clipShape(Circle())
|
||||
}
|
||||
HStack(spacing: 10) {
|
||||
|
||||
let iconSize = 39.0
|
||||
|
||||
AppIconView(hasPlusIcon: false, size: iconSize)
|
||||
.frame(width: iconSize, height: iconSize)
|
||||
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(String("QuickDrop"))
|
||||
@@ -118,7 +117,7 @@ struct QuickDropToastView: View {
|
||||
.padding(16)
|
||||
.background(
|
||||
VisualEffectView(material: .hudWindow)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
|
||||
.clipShape(RoundedRectangle(cornerRadius: 25, style: .continuous))
|
||||
)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 16, style: .continuous)
|
||||
@@ -143,6 +142,10 @@ fileprivate struct QuickDropToastViewButton: View {
|
||||
Color.gray.opacity(0.15)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 8, style: .continuous))
|
||||
)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 8, style: .continuous)
|
||||
.stroke(Color.black.opacity(0.04), lineWidth: 1)
|
||||
)
|
||||
}
|
||||
.buttonStyle(.plain) // prevents blue macOS button look
|
||||
}
|
||||
@@ -203,7 +206,7 @@ struct CapsuleProgress: View {
|
||||
struct QuickDropToastView_Previews: PreviewProvider {
|
||||
struct Demo: View {
|
||||
@State var model = ReceiveModel(controlPlusScreen: { _ in })
|
||||
@State var done = false
|
||||
@State var done = true
|
||||
|
||||
var body: some View {
|
||||
QuickDropToastView(
|
||||
@@ -215,7 +218,8 @@ struct QuickDropToastView_Previews: PreviewProvider {
|
||||
.padding(100)
|
||||
.background(Color.black.opacity(0.2))
|
||||
.onAppear {
|
||||
model.progress = 0.01
|
||||
model.progress = 1
|
||||
model.toastActions = .init(openFilesAction: {}, importPhotosAction: {}, closeToastAction: {})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,8 +355,8 @@ class ReceiveModel: ObservableObject, InboundAppDelegate {
|
||||
let frame = screen.frame
|
||||
let visible = screen.visibleFrame
|
||||
|
||||
let marginX: CGFloat = 24
|
||||
let marginFromTop: CGFloat = 30
|
||||
let marginX: CGFloat = 20
|
||||
let marginFromTop: CGFloat = 20
|
||||
|
||||
// If the menu bar is auto-hidden, visible.maxY == frame.maxY; subtract the status bar thickness to stay clear
|
||||
let menuBarHidden = abs(frame.maxY - visible.maxY) < 0.5
|
||||
|
||||
@@ -10,7 +10,19 @@
|
||||
"layers" : [
|
||||
{
|
||||
"image-name" : "QuickDropArrows.png",
|
||||
"name" : "QuickDropArrows"
|
||||
"name" : "QuickDropArrows",
|
||||
"position-specializations" : [
|
||||
{
|
||||
"idiom" : "square",
|
||||
"value" : {
|
||||
"scale" : 1,
|
||||
"translation-in-points" : [
|
||||
0,
|
||||
-5
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"image-name" : "QuickDropCircle.png",
|
||||
@@ -34,4 +46,4 @@
|
||||
],
|
||||
"squares" : "shared"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user