feat(ios): reconnect to last bridge

This commit is contained in:
Peter Steinberger
2025-12-14 00:23:34 +00:00
parent e6d522493b
commit f7076c38ea
7 changed files with 283 additions and 47 deletions

View File

@@ -12,15 +12,15 @@ extension ConnectStatusStore: @unchecked Sendable {}
struct SettingsTab: View {
@EnvironmentObject private var appModel: NodeAppModel
@EnvironmentObject private var voiceWake: VoiceWakeManager
@EnvironmentObject private var bridgeController: BridgeConnectionController
@Environment(\.dismiss) private var dismiss
@AppStorage("node.displayName") private var displayName: String = "iOS Node"
@AppStorage("node.instanceId") private var instanceId: String = UUID().uuidString
@AppStorage("voiceWake.enabled") private var voiceWakeEnabled: Bool = false
@AppStorage("camera.enabled") private var cameraEnabled: Bool = true
@AppStorage("bridge.preferredStableID") private var preferredBridgeStableID: String = ""
@StateObject private var discovery = BridgeDiscoveryModel()
@StateObject private var connectStatus = ConnectStatusStore()
@State private var connectingBridgeID: String?
@State private var didAutoConnect = false
@State private var localIPAddress: String?
var body: some View {
@@ -58,8 +58,15 @@ struct SettingsTab: View {
}
}
Section("Camera") {
Toggle("Allow Camera", isOn: self.$cameraEnabled)
Text("Allows the bridge to request photos or short video clips (foreground only).")
.font(.footnote)
.foregroundStyle(.secondary)
}
Section("Bridge") {
LabeledContent("Discovery", value: self.discovery.statusText)
LabeledContent("Discovery", value: self.bridgeController.discoveryStatusText)
LabeledContent("Status", value: self.appModel.bridgeStatusText)
if let serverName = self.appModel.bridgeServerName {
LabeledContent("Server", value: serverName)
@@ -120,31 +127,12 @@ struct SettingsTab: View {
}
}
.onAppear {
self.discovery.start()
self.localIPAddress = Self.primaryIPv4Address()
}
.onDisappear { self.discovery.stop() }
.onChange(of: self.discovery.bridges) { _, newValue in
if self.didAutoConnect { return }
if self.appModel.bridgeServerName != nil { return }
let existing = KeychainStore.loadString(
service: "com.steipete.clawdis.bridge",
account: self.keychainAccount())
guard let existing, !existing.isEmpty else { return }
guard let target = self.pickAutoConnectBridge(from: newValue) else { return }
self.didAutoConnect = true
self.preferredBridgeStableID = target.stableID
self.appModel.connectToBridge(
endpoint: target.endpoint,
hello: BridgeHello(
nodeId: self.instanceId,
displayName: self.displayName,
token: existing,
platform: self.platformString(),
version: self.appVersion()))
self.connectStatus.text = nil
.onChange(of: self.preferredBridgeStableID) { _, newValue in
let trimmed = newValue.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty else { return }
BridgeSettingsStore.savePreferredBridgeStableID(trimmed)
}
.onChange(of: self.appModel.bridgeServerName) { _, _ in
self.connectStatus.text = nil
@@ -154,12 +142,12 @@ struct SettingsTab: View {
@ViewBuilder
private func bridgeList(showing: BridgeListMode) -> some View {
if self.discovery.bridges.isEmpty {
if self.bridgeController.bridges.isEmpty {
Text("No bridges found yet.")
.foregroundStyle(.secondary)
} else {
let connectedID = self.appModel.connectedBridgeID
let rows = self.discovery.bridges.filter { bridge in
let rows = self.bridgeController.bridges.filter { bridge in
let isConnected = bridge.stableID == connectedID
switch showing {
case .all:
@@ -218,6 +206,7 @@ struct SettingsTab: View {
private func connect(_ bridge: BridgeDiscoveryModel.DiscoveredBridge) async {
self.connectingBridgeID = bridge.id
self.preferredBridgeStableID = bridge.stableID
BridgeSettingsStore.savePreferredBridgeStableID(bridge.stableID)
defer { self.connectingBridgeID = nil }
do {
@@ -265,16 +254,6 @@ struct SettingsTab: View {
}
}
private func pickAutoConnectBridge(from bridges: [BridgeDiscoveryModel.DiscoveredBridge]) -> BridgeDiscoveryModel
.DiscoveredBridge? {
if !self.preferredBridgeStableID.isEmpty,
let match = bridges.first(where: { $0.stableID == self.preferredBridgeStableID })
{
return match
}
return bridges.first
}
private static func primaryIPv4Address() -> String? {
var addrList: UnsafeMutablePointer<ifaddrs>?
guard getifaddrs(&addrList) == 0, let first = addrList else { return nil }