Merge c4d727a2b810079c43cd3c72d243be94df63b86a into 9fb78453e088cd7b553d7779faa0de5c83708e70
This commit is contained in:
commit
fb8a5269b4
@ -37,6 +37,9 @@ final class GatewayConnectionController {
|
|||||||
private(set) var pendingTrustPrompt: TrustPrompt?
|
private(set) var pendingTrustPrompt: TrustPrompt?
|
||||||
|
|
||||||
private let discovery = GatewayDiscoveryModel()
|
private let discovery = GatewayDiscoveryModel()
|
||||||
|
/// Reused instance — avoids creating CLLocationManager on the main thread
|
||||||
|
/// each time currentPermissions() is called, which triggers a UI-thread warning.
|
||||||
|
private let locationManager = CLLocationManager()
|
||||||
private weak var appModel: NodeAppModel?
|
private weak var appModel: NodeAppModel?
|
||||||
private var didAutoConnect = false
|
private var didAutoConnect = false
|
||||||
private var pendingServiceResolvers: [String: GatewayServiceResolver] = [:]
|
private var pendingServiceResolvers: [String: GatewayServiceResolver] = [:]
|
||||||
@ -228,15 +231,25 @@ final class GatewayConnectionController {
|
|||||||
guard let cfg = appModel.activeGatewayConnectConfig else { return }
|
guard let cfg = appModel.activeGatewayConnectConfig else { return }
|
||||||
guard appModel.gatewayAutoReconnectEnabled else { return }
|
guard appModel.gatewayAutoReconnectEnabled else { return }
|
||||||
|
|
||||||
let refreshedConfig = GatewayConnectConfig(
|
Task { [weak self, weak appModel] in
|
||||||
url: cfg.url,
|
guard let self, let appModel else { return }
|
||||||
stableID: cfg.stableID,
|
let refreshedConfig = GatewayConnectConfig(
|
||||||
tls: cfg.tls,
|
url: cfg.url,
|
||||||
token: cfg.token,
|
stableID: cfg.stableID,
|
||||||
bootstrapToken: cfg.bootstrapToken,
|
tls: cfg.tls,
|
||||||
password: cfg.password,
|
token: cfg.token,
|
||||||
nodeOptions: self.makeConnectOptions(stableID: cfg.stableID))
|
bootstrapToken: cfg.bootstrapToken,
|
||||||
appModel.applyGatewayConnectConfig(refreshedConfig)
|
password: cfg.password,
|
||||||
|
nodeOptions: await self.makeConnectOptions(stableID: cfg.stableID))
|
||||||
|
|
||||||
|
guard appModel.gatewayAutoReconnectEnabled,
|
||||||
|
let latestConfig = appModel.activeGatewayConnectConfig,
|
||||||
|
latestConfig.effectiveStableID == cfg.effectiveStableID,
|
||||||
|
latestConfig.url == cfg.url
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
appModel.applyGatewayConnectConfig(refreshedConfig)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func clearPendingTrustPrompt() {
|
func clearPendingTrustPrompt() {
|
||||||
@ -462,10 +475,10 @@ final class GatewayConnectionController {
|
|||||||
password: String?)
|
password: String?)
|
||||||
{
|
{
|
||||||
guard let appModel else { return }
|
guard let appModel else { return }
|
||||||
let connectOptions = self.makeConnectOptions(stableID: gatewayStableID)
|
|
||||||
|
|
||||||
Task { [weak appModel] in
|
Task { [weak self, weak appModel] in
|
||||||
guard let appModel else { return }
|
guard let self, let appModel else { return }
|
||||||
|
let connectOptions = await self.makeConnectOptions(stableID: gatewayStableID)
|
||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
appModel.gatewayStatusText = "Connecting…"
|
appModel.gatewayStatusText = "Connecting…"
|
||||||
}
|
}
|
||||||
@ -741,7 +754,7 @@ final class GatewayConnectionController {
|
|||||||
"manual|\(host.lowercased())|\(port)"
|
"manual|\(host.lowercased())|\(port)"
|
||||||
}
|
}
|
||||||
|
|
||||||
private func makeConnectOptions(stableID: String?) -> GatewayConnectOptions {
|
private func makeConnectOptions(stableID: String?) async -> GatewayConnectOptions {
|
||||||
let defaults = UserDefaults.standard
|
let defaults = UserDefaults.standard
|
||||||
let displayName = self.resolvedDisplayName(defaults: defaults)
|
let displayName = self.resolvedDisplayName(defaults: defaults)
|
||||||
let resolvedClientId = self.resolvedClientId(defaults: defaults, stableID: stableID)
|
let resolvedClientId = self.resolvedClientId(defaults: defaults, stableID: stableID)
|
||||||
@ -751,7 +764,7 @@ final class GatewayConnectionController {
|
|||||||
scopes: [],
|
scopes: [],
|
||||||
caps: self.currentCaps(),
|
caps: self.currentCaps(),
|
||||||
commands: self.currentCommands(),
|
commands: self.currentCommands(),
|
||||||
permissions: self.currentPermissions(),
|
permissions: await self.currentPermissions(),
|
||||||
clientId: resolvedClientId,
|
clientId: resolvedClientId,
|
||||||
clientMode: "node",
|
clientMode: "node",
|
||||||
clientDisplayName: displayName)
|
clientDisplayName: displayName)
|
||||||
@ -887,13 +900,15 @@ final class GatewayConnectionController {
|
|||||||
return commands
|
return commands
|
||||||
}
|
}
|
||||||
|
|
||||||
private func currentPermissions() -> [String: Bool] {
|
private func currentPermissions() async -> [String: Bool] {
|
||||||
|
let speechRecognitionStatus = await Self.currentSpeechRecognitionStatus()
|
||||||
|
|
||||||
var permissions: [String: Bool] = [:]
|
var permissions: [String: Bool] = [:]
|
||||||
permissions["camera"] = AVCaptureDevice.authorizationStatus(for: .video) == .authorized
|
permissions["camera"] = AVCaptureDevice.authorizationStatus(for: .video) == .authorized
|
||||||
permissions["microphone"] = AVCaptureDevice.authorizationStatus(for: .audio) == .authorized
|
permissions["microphone"] = AVCaptureDevice.authorizationStatus(for: .audio) == .authorized
|
||||||
permissions["speechRecognition"] = SFSpeechRecognizer.authorizationStatus() == .authorized
|
permissions["speechRecognition"] = speechRecognitionStatus == .authorized
|
||||||
permissions["location"] = Self.isLocationAuthorized(
|
permissions["location"] = Self.isLocationAuthorized(
|
||||||
status: CLLocationManager().authorizationStatus)
|
status: self.locationManager.authorizationStatus)
|
||||||
&& CLLocationManager.locationServicesEnabled()
|
&& CLLocationManager.locationServicesEnabled()
|
||||||
permissions["screenRecording"] = RPScreenRecorder.shared().isAvailable
|
permissions["screenRecording"] = RPScreenRecorder.shared().isAvailable
|
||||||
|
|
||||||
@ -921,6 +936,14 @@ final class GatewayConnectionController {
|
|||||||
return permissions
|
return permissions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private nonisolated static func currentSpeechRecognitionStatus() async
|
||||||
|
-> SFSpeechRecognizerAuthorizationStatus
|
||||||
|
{
|
||||||
|
await Task.detached(priority: .utility) {
|
||||||
|
SFSpeechRecognizer.authorizationStatus()
|
||||||
|
}.value
|
||||||
|
}
|
||||||
|
|
||||||
private static func isLocationAuthorized(status: CLAuthorizationStatus) -> Bool {
|
private static func isLocationAuthorized(status: CLAuthorizationStatus) -> Bool {
|
||||||
switch status {
|
switch status {
|
||||||
case .authorizedAlways, .authorizedWhenInUse:
|
case .authorizedAlways, .authorizedWhenInUse:
|
||||||
@ -953,8 +976,8 @@ extension GatewayConnectionController {
|
|||||||
self.currentCommands()
|
self.currentCommands()
|
||||||
}
|
}
|
||||||
|
|
||||||
func _test_currentPermissions() -> [String: Bool] {
|
func _test_currentPermissions() async -> [String: Bool] {
|
||||||
self.currentPermissions()
|
await self.currentPermissions()
|
||||||
}
|
}
|
||||||
|
|
||||||
func _test_platformString() -> String {
|
func _test_platformString() -> String {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user