fix: auto-write config immediately after connect — no manual tap needed
Removes the two-phase flow where the user had to confirm beacon flashing and tap "Write Config". Now goes straight from connect → write → register in one shot. The dxsmartConnectedView UI is removed.
This commit is contained in:
parent
3c41ecb49d
commit
38600193b7
1 changed files with 17 additions and 95 deletions
|
|
@ -209,8 +209,8 @@ struct ScanView: View {
|
||||||
progressView(title: "Connecting…", message: statusMessage)
|
progressView(title: "Connecting…", message: statusMessage)
|
||||||
|
|
||||||
case .connected:
|
case .connected:
|
||||||
// DXSmart: beacon is flashing, show write button
|
// Legacy — auto-write skips this state now
|
||||||
dxsmartConnectedView
|
progressView(title: "Connected…", message: statusMessage)
|
||||||
|
|
||||||
case .writing:
|
case .writing:
|
||||||
progressView(title: "Writing Config…", message: statusMessage)
|
progressView(title: "Writing Config…", message: statusMessage)
|
||||||
|
|
@ -319,51 +319,7 @@ struct ScanView: View {
|
||||||
|
|
||||||
// MARK: - DXSmart Connected View
|
// MARK: - DXSmart Connected View
|
||||||
|
|
||||||
private var dxsmartConnectedView: some View {
|
// dxsmartConnectedView removed — auto-write skips the manual confirmation step
|
||||||
VStack(spacing: 24) {
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Image(systemName: "light.beacon.max")
|
|
||||||
.font(.system(size: 64))
|
|
||||||
.foregroundStyle(Color.payfritGreen)
|
|
||||||
.modifier(PulseEffectModifier())
|
|
||||||
|
|
||||||
Text("Connected — Beacon is Flashing")
|
|
||||||
.font(.title2.bold())
|
|
||||||
|
|
||||||
Text("Confirm the beacon LED is flashing, then tap Write Config to program it.\n\nThe beacon will timeout if you wait too long.")
|
|
||||||
.font(.subheadline)
|
|
||||||
.foregroundStyle(.secondary)
|
|
||||||
.multilineTextAlignment(.center)
|
|
||||||
.padding(.horizontal, 32)
|
|
||||||
|
|
||||||
Button {
|
|
||||||
Task { await writeConfigToConnectedBeacon() }
|
|
||||||
} label: {
|
|
||||||
HStack {
|
|
||||||
Image(systemName: "arrow.down.doc")
|
|
||||||
Text("Write Config")
|
|
||||||
}
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
}
|
|
||||||
.buttonStyle(.borderedProminent)
|
|
||||||
.tint(Color.payfritGreen)
|
|
||||||
.controlSize(.large)
|
|
||||||
.padding(.horizontal, 32)
|
|
||||||
|
|
||||||
Button("Cancel") {
|
|
||||||
cancelProvisioning()
|
|
||||||
}
|
|
||||||
.foregroundStyle(.secondary)
|
|
||||||
|
|
||||||
// Show diagnostic log
|
|
||||||
if !provisionLog.entries.isEmpty {
|
|
||||||
diagnosticLogView
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Progress / Success / Failed Views
|
// MARK: - Progress / Success / Failed Views
|
||||||
|
|
||||||
|
|
@ -614,6 +570,8 @@ struct ScanView: View {
|
||||||
|
|
||||||
// Create appropriate provisioner
|
// Create appropriate provisioner
|
||||||
let provisioner = makeProvisioner(for: beacon)
|
let provisioner = makeProvisioner(for: beacon)
|
||||||
|
pendingProvisioner = provisioner
|
||||||
|
pendingConfig = config
|
||||||
|
|
||||||
// Wire up real-time status updates from provisioner
|
// Wire up real-time status updates from provisioner
|
||||||
if let dxProvisioner = provisioner as? DXSmartProvisioner {
|
if let dxProvisioner = provisioner as? DXSmartProvisioner {
|
||||||
|
|
@ -640,14 +598,8 @@ struct ScanView: View {
|
||||||
|
|
||||||
provisionLog?.log("disconnect", "Unexpected disconnect: \(reason)", isError: true)
|
provisionLog?.log("disconnect", "Unexpected disconnect: \(reason)", isError: true)
|
||||||
|
|
||||||
// CP-28: disconnect during .connected is expected — beacon keeps
|
// For all active states, treat disconnect as failure
|
||||||
// flashing after BLE drops. We'll reconnect when user taps Write Config.
|
if self.provisioningState == .connecting ||
|
||||||
if self.provisioningState == .connected {
|
|
||||||
provisionLog?.log("disconnect", "CP-28 idle disconnect — beacon still flashing, ignoring")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// For all other active states, treat disconnect as failure
|
|
||||||
if self.provisioningState == .connecting || self.provisioningState == .connected ||
|
|
||||||
self.provisioningState == .writing || self.provisioningState == .verifying {
|
self.provisioningState == .writing || self.provisioningState == .verifying {
|
||||||
self.provisioningState = .failed
|
self.provisioningState = .failed
|
||||||
self.errorMessage = "Beacon disconnected: \(reason)"
|
self.errorMessage = "Beacon disconnected: \(reason)"
|
||||||
|
|
@ -659,48 +611,16 @@ struct ScanView: View {
|
||||||
try await provisioner.connect()
|
try await provisioner.connect()
|
||||||
provisionLog.log("connect", "Connected and authenticated successfully")
|
provisionLog.log("connect", "Connected and authenticated successfully")
|
||||||
|
|
||||||
// CP-28: stop at connected state, wait for user to confirm flashing
|
// Auto-fire write immediately — no pause needed
|
||||||
provisioningState = .connected
|
provisioningState = .writing
|
||||||
pendingConfig = config
|
writesCompleted = false
|
||||||
pendingProvisioner = provisioner
|
statusMessage = "Writing config to DX-Smart…"
|
||||||
|
provisionLog.log("write", "Auto-writing config (no user tap needed)…")
|
||||||
} catch {
|
|
||||||
provisionLog.log("error", "Provisioning failed after \(provisionLog.elapsed): \(error.localizedDescription)", isError: true)
|
|
||||||
provisioningState = .failed
|
|
||||||
errorMessage = error.localizedDescription
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store for DXSmart two-phase flow
|
|
||||||
@State private var pendingConfig: BeaconConfig?
|
|
||||||
@State private var pendingProvisioner: (any BeaconProvisioner)?
|
|
||||||
|
|
||||||
private func writeConfigToConnectedBeacon() async {
|
|
||||||
guard let config = pendingConfig,
|
|
||||||
let provisioner = pendingProvisioner,
|
|
||||||
let sp = selectedServicePoint,
|
|
||||||
let ns = namespace,
|
|
||||||
let token = appState.token else { return }
|
|
||||||
|
|
||||||
provisioningState = .writing
|
|
||||||
writesCompleted = false
|
|
||||||
statusMessage = "Writing config to DX-Smart…"
|
|
||||||
|
|
||||||
do {
|
|
||||||
// Reconnect if the beacon dropped BLE during the "confirm flashing" wait
|
|
||||||
if !provisioner.isConnected {
|
|
||||||
provisionLog.log("write", "Beacon disconnected while waiting — reconnecting…")
|
|
||||||
statusMessage = "Reconnecting to beacon…"
|
|
||||||
try await provisioner.connect()
|
|
||||||
provisionLog.log("write", "Reconnected — writing config…")
|
|
||||||
statusMessage = "Writing config to DX-Smart…"
|
|
||||||
}
|
|
||||||
|
|
||||||
try await provisioner.writeConfig(config)
|
try await provisioner.writeConfig(config)
|
||||||
writesCompleted = true
|
writesCompleted = true
|
||||||
|
|
||||||
// Give the beacon 200ms to process SaveConfig before dropping the BLE link.
|
// Give the beacon 200ms to process SaveConfig before dropping the BLE link.
|
||||||
// The beacon MCU needs the connection terminated to finalize its reboot/save cycle.
|
|
||||||
try? await Task.sleep(nanoseconds: 200_000_000)
|
try? await Task.sleep(nanoseconds: 200_000_000)
|
||||||
provisioner.disconnect()
|
provisioner.disconnect()
|
||||||
|
|
||||||
|
|
@ -719,14 +639,16 @@ struct ScanView: View {
|
||||||
statusMessage = "\(sp.name) — DX-Smart\nUUID: \(config.formattedUUID.prefix(13))…\nMajor: \(config.major) Minor: \(config.minor)"
|
statusMessage = "\(sp.name) — DX-Smart\nUUID: \(config.formattedUUID.prefix(13))…\nMajor: \(config.major) Minor: \(config.minor)"
|
||||||
|
|
||||||
} catch {
|
} catch {
|
||||||
|
provisionLog.log("error", "Provisioning failed after \(provisionLog.elapsed): \(error.localizedDescription)", isError: true)
|
||||||
provisioningState = .failed
|
provisioningState = .failed
|
||||||
errorMessage = error.localizedDescription
|
errorMessage = error.localizedDescription
|
||||||
}
|
}
|
||||||
|
|
||||||
pendingConfig = nil
|
|
||||||
pendingProvisioner = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Kept for cancel/reset and registerAnywayAfterFailure fallback
|
||||||
|
@State private var pendingConfig: BeaconConfig?
|
||||||
|
@State private var pendingProvisioner: (any BeaconProvisioner)?
|
||||||
|
|
||||||
private func registerAnywayAfterFailure() async {
|
private func registerAnywayAfterFailure() async {
|
||||||
guard let sp = selectedServicePoint,
|
guard let sp = selectedServicePoint,
|
||||||
let ns = namespace,
|
let ns = namespace,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue