mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-18 17:07:27 +00:00
iOS onboarding: prevent pairing flicker during auto-resume (#20310)
Merged via /review-pr -> /prepare-pr -> /merge-pr.
Prepared head SHA: 691808b747
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky
This commit is contained in:
@@ -14,6 +14,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- iOS/Onboarding: prevent pairing-status flicker during auto-resume by keeping resumed state transitions stable. (#20310) Thanks @mbelinky.
|
||||
- OpenClawKit/Protocol: preserve JSON boolean literals (`true`/`false`) when bridging through `AnyCodable` so Apple client RPC params no longer re-encode booleans as `1`/`0`. Thanks @mbelinky.
|
||||
- iOS/Onboarding: stabilize pairing and reconnect behavior by resetting stale pairing request state on manual retry, disconnecting both operator and node gateways on operator failure, and avoiding duplicate pairing loops from operator transport identity attachment. (#20056) Thanks @mbelinky.
|
||||
- Browser/Relay: reuse an already-running extension relay when the relay port is occupied by another OpenClaw process, while still failing on non-relay port collisions to avoid masking unrelated listeners. (#20035) Thanks @mbelinky.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import CoreImage
|
||||
import Combine
|
||||
import OpenClawKit
|
||||
import PhotosUI
|
||||
import SwiftUI
|
||||
@@ -68,6 +69,7 @@ struct OnboardingWizardView: View {
|
||||
@State private var scannerError: String?
|
||||
@State private var selectedPhoto: PhotosPickerItem?
|
||||
@State private var lastPairingAutoResumeAttemptAt: Date?
|
||||
private static let pairingAutoResumeTicker = Timer.publish(every: 2.0, on: .main, in: .common).autoconnect()
|
||||
|
||||
let allowSkip: Bool
|
||||
let onClose: () -> Void
|
||||
@@ -271,6 +273,7 @@ struct OnboardingWizardView: View {
|
||||
}
|
||||
.onChange(of: self.appModel.gatewayServerName) { _, newValue in
|
||||
guard newValue != nil else { return }
|
||||
self.showQRScanner = false
|
||||
self.statusLine = "Connected."
|
||||
if !self.didMarkCompleted, let selectedMode {
|
||||
OnboardingStateStore.markCompleted(mode: selectedMode)
|
||||
@@ -282,6 +285,9 @@ struct OnboardingWizardView: View {
|
||||
guard newValue == .active else { return }
|
||||
self.attemptAutomaticPairingResumeIfNeeded()
|
||||
}
|
||||
.onReceive(Self.pairingAutoResumeTicker) { _ in
|
||||
self.attemptAutomaticPairingResumeIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
@@ -685,7 +691,16 @@ struct OnboardingWizardView: View {
|
||||
Task { await self.retryLastAttempt() }
|
||||
}
|
||||
|
||||
private func resumeAfterPairingApprovalInBackground() {
|
||||
// Keep the pairing issue sticky to avoid visual flicker while we probe for approval.
|
||||
self.appModel.gatewayAutoReconnectEnabled = true
|
||||
self.appModel.gatewayPairingPaused = false
|
||||
self.appModel.gatewayPairingRequestId = nil
|
||||
Task { await self.retryLastAttempt(silent: true) }
|
||||
}
|
||||
|
||||
private func attemptAutomaticPairingResumeIfNeeded() {
|
||||
guard self.scenePhase == .active else { return }
|
||||
guard self.step == .auth else { return }
|
||||
guard self.issue.needsPairing else { return }
|
||||
guard self.connectingGatewayID == nil else { return }
|
||||
@@ -695,7 +710,7 @@ struct OnboardingWizardView: View {
|
||||
return
|
||||
}
|
||||
self.lastPairingAutoResumeAttemptAt = now
|
||||
self.resumeAfterPairingApproval()
|
||||
self.resumeAfterPairingApprovalInBackground()
|
||||
}
|
||||
|
||||
private func detectQRCode(from data: Data) -> String? {
|
||||
@@ -837,11 +852,13 @@ struct OnboardingWizardView: View {
|
||||
await self.gatewayController.connectManual(host: host, port: self.manualPort, useTLS: self.manualTLS)
|
||||
}
|
||||
|
||||
private func retryLastAttempt() async {
|
||||
self.connectingGatewayID = "retry"
|
||||
private func retryLastAttempt(silent: Bool = false) async {
|
||||
self.connectingGatewayID = silent ? "retry-auto" : "retry"
|
||||
// Keep current auth/pairing issue sticky while retrying to avoid Step 3 UI flip-flop.
|
||||
self.connectMessage = "Retrying…"
|
||||
self.statusLine = "Retrying last connection…"
|
||||
if !silent {
|
||||
self.connectMessage = "Retrying…"
|
||||
self.statusLine = "Retrying last connection…"
|
||||
}
|
||||
defer { self.connectingGatewayID = nil }
|
||||
await self.gatewayController.connectLastKnown()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user