Add camera QR pairing, Bonjour discovery and pinned address recovery
This commit is contained in:
@@ -10,6 +10,8 @@ final class PlayerStore: ObservableObject {
|
||||
@Published var connected = false
|
||||
@Published var busy = false
|
||||
@Published var pairing = false
|
||||
@Published var incomingInvitation: String?
|
||||
let discovery = DiscoveryStore()
|
||||
private(set) var receivedAt = Date()
|
||||
private var client: BridgeClient?
|
||||
private var runner: Task<Void, Never>?
|
||||
@@ -43,7 +45,16 @@ final class PlayerStore: ObservableObject {
|
||||
|
||||
func setActive(_ active: Bool) {
|
||||
self.active = active
|
||||
if active { resume() } else { stop(); status = "Соединение приостановлено" }
|
||||
if active { discovery.start(); resume() }
|
||||
else { discovery.stop(); stop(); status = "Соединение приостановлено" }
|
||||
}
|
||||
|
||||
func importInvitation(_ text: String) {
|
||||
guard connection == nil, !pairing else {
|
||||
error = "Для подключения другого ПК сначала выберите «Забыть компьютер»."
|
||||
return
|
||||
}
|
||||
incomingInvitation = text
|
||||
}
|
||||
|
||||
func resume() {
|
||||
@@ -52,15 +63,26 @@ final class PlayerStore: ObservableObject {
|
||||
let current = generation
|
||||
runner = Task { [weak self] in
|
||||
var delay: UInt64 = 1
|
||||
var attempt = 0
|
||||
while !Task.isCancelled {
|
||||
guard let self, self.generation == current else { return }
|
||||
let client = BridgeClient(connection)
|
||||
let saved = self.connection ?? connection
|
||||
let alternatives = self.discovery.computers.compactMap { $0.reconnect(saved) }
|
||||
.filter { $0.endpoint != saved.endpoint }
|
||||
let candidates = [saved] + alternatives
|
||||
let candidate = candidates[attempt % candidates.count]
|
||||
let client = BridgeClient(candidate)
|
||||
self.client = client
|
||||
self.status = "Подключение к ПК…"
|
||||
do {
|
||||
let initial = try await client.state()
|
||||
try Task.checkCancellation()
|
||||
guard self.generation == current else { client.close(); return }
|
||||
// Persist a discovered address only after pinned, authenticated HTTPS succeeds.
|
||||
if candidate.endpoint != saved.endpoint {
|
||||
try KeychainStore.save(candidate)
|
||||
self.connection = candidate
|
||||
}
|
||||
self.accept(initial, client: client)
|
||||
client.openEvents()
|
||||
// Abort a silent/stalled stream even if the OS has not detected a lost Wi-Fi link.
|
||||
@@ -94,6 +116,7 @@ final class PlayerStore: ObservableObject {
|
||||
}
|
||||
self.status = "ПК недоступен. Повтор через \(delay) с"
|
||||
self.error = error.localizedDescription
|
||||
attempt += 1
|
||||
}
|
||||
do { try await Task.sleep(nanoseconds: delay * 1_000_000_000) } catch { return }
|
||||
delay = min(delay * 2, 16)
|
||||
|
||||
Reference in New Issue
Block a user