fix: switch WebSocket to Tailscale IP, add configurable Orin URL (Issue #709)
- Default URL updated from ws://192.168.86.158:9090 (LAN) to ws://100.64.0.2:9090 (Tailscale) - URL persisted in UserDefaults under key "orinURL" — survives app restarts - WebSocketClient.url is now mutable so it can be updated without recreation - SensorManager.updateURL(_:) applies a new URL when not streaming - ContentView: editable text field for Orin address with Apply button, disabled while streaming - Connection banner shows the active URL instead of hardcoded string Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
bd36370e5a
commit
1e7a512c89
@ -2,11 +2,15 @@ import SwiftUI
|
||||
|
||||
struct ContentView: View {
|
||||
@EnvironmentObject var sensor: SensorManager
|
||||
@AppStorage("orinURL") private var orinURL: String = SensorManager.defaultOrinURL
|
||||
@State private var editingURL: String = ""
|
||||
@FocusState private var urlFieldFocused: Bool
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
VStack(spacing: 24) {
|
||||
connectionBanner
|
||||
orinURLField
|
||||
Divider()
|
||||
sensorRatesGrid
|
||||
Divider()
|
||||
@ -20,11 +24,41 @@ struct ContentView: View {
|
||||
}
|
||||
.padding()
|
||||
.navigationTitle("Sul-Tee")
|
||||
.onAppear { editingURL = orinURL }
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Subviews
|
||||
|
||||
private var orinURLField: some View {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text("Orin WebSocket URL")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
HStack {
|
||||
TextField("ws://host:port", text: $editingURL)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.keyboardType(.URL)
|
||||
.autocorrectionDisabled()
|
||||
.textInputAutocapitalization(.never)
|
||||
.focused($urlFieldFocused)
|
||||
.disabled(sensor.isStreaming)
|
||||
.onSubmit { applyURL() }
|
||||
if !sensor.isStreaming {
|
||||
Button("Apply") { applyURL() }
|
||||
.buttonStyle(.borderedProminent)
|
||||
.controlSize(.small)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func applyURL() {
|
||||
urlFieldFocused = false
|
||||
orinURL = editingURL
|
||||
sensor.updateURL(editingURL)
|
||||
}
|
||||
|
||||
private var connectionBanner: some View {
|
||||
HStack(spacing: 12) {
|
||||
Circle()
|
||||
@ -102,7 +136,7 @@ struct ContentView: View {
|
||||
|
||||
private var wsLabel: String {
|
||||
switch sensor.wsState {
|
||||
case .connected: return "Connected — ws://192.168.86.158:9090"
|
||||
case .connected: return "Connected — \(orinURL)"
|
||||
case .connecting: return "Connecting…"
|
||||
case .disconnected: return "Disconnected"
|
||||
}
|
||||
|
||||
@ -18,7 +18,16 @@ final class SensorManager: NSObject, ObservableObject {
|
||||
|
||||
// MARK: - Dependencies
|
||||
|
||||
private let ws = WebSocketClient(url: URL(string: "ws://192.168.86.158:9090")!)
|
||||
static let defaultOrinURL = "ws://100.64.0.2:9090"
|
||||
private static let orinURLKey = "orinURL"
|
||||
|
||||
private(set) var ws: WebSocketClient
|
||||
|
||||
/// Current Orin WebSocket URL string (persisted in UserDefaults).
|
||||
var orinURLString: String {
|
||||
get { UserDefaults.standard.string(forKey: Self.orinURLKey) ?? Self.defaultOrinURL }
|
||||
set { UserDefaults.standard.set(newValue, forKey: Self.orinURLKey) }
|
||||
}
|
||||
private let locationManager = CLLocationManager()
|
||||
private let motionManager = CMMotionManager()
|
||||
private let altimeter = CMAltimeter()
|
||||
@ -35,6 +44,8 @@ final class SensorManager: NSObject, ObservableObject {
|
||||
// MARK: - Init
|
||||
|
||||
override init() {
|
||||
let urlString = UserDefaults.standard.string(forKey: Self.orinURLKey) ?? Self.defaultOrinURL
|
||||
self.ws = WebSocketClient(url: URL(string: urlString) ?? URL(string: Self.defaultOrinURL)!)
|
||||
super.init()
|
||||
locationManager.delegate = self
|
||||
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
|
||||
@ -69,6 +80,16 @@ final class SensorManager: NSObject, ObservableObject {
|
||||
rateTimer = nil
|
||||
}
|
||||
|
||||
/// Call when the user edits the Orin URL. Persists the value and updates
|
||||
/// the client URL; takes effect on the next connect().
|
||||
func updateURL(_ urlString: String) {
|
||||
guard !isStreaming else { return }
|
||||
orinURLString = urlString
|
||||
if let url = URL(string: urlString), url.scheme?.hasPrefix("ws") == true {
|
||||
ws.url = url
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Sensor start / stop
|
||||
|
||||
private func requestPermissionsAndStartSensors() {
|
||||
|
||||
@ -11,7 +11,7 @@ final class WebSocketClient: NSObject, ObservableObject {
|
||||
|
||||
@Published var state: ConnectionState = .disconnected
|
||||
|
||||
private let url: URL
|
||||
var url: URL
|
||||
private var session: URLSession!
|
||||
private var task: URLSessionWebSocketTask?
|
||||
private var shouldRun = false
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user