mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 12:56:01 -06:00
fix(ios): stop repeated gateway errors from wobbling (#110493)
* fix(ios): keep gateway problem toast stationary * chore(ios): sync native i18n inventory
This commit is contained in:
committed by
GitHub
parent
e77140ab8e
commit
af0d189774
@@ -24483,7 +24483,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-call",
|
||||
"line": 179,
|
||||
"line": 174,
|
||||
"path": "apps/ios/Sources/RootTabs.swift",
|
||||
"source": "Talk",
|
||||
"surface": "apple",
|
||||
@@ -24491,7 +24491,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-call",
|
||||
"line": 192,
|
||||
"line": 187,
|
||||
"path": "apps/ios/Sources/RootTabs.swift",
|
||||
"source": "Control",
|
||||
"surface": "apple",
|
||||
@@ -24499,7 +24499,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-call",
|
||||
"line": 201,
|
||||
"line": 196,
|
||||
"path": "apps/ios/Sources/RootTabs.swift",
|
||||
"source": "Agent",
|
||||
"surface": "apple",
|
||||
@@ -24507,7 +24507,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-call",
|
||||
"line": 214,
|
||||
"line": 209,
|
||||
"path": "apps/ios/Sources/RootTabs.swift",
|
||||
"source": "Settings",
|
||||
"surface": "apple",
|
||||
@@ -24515,7 +24515,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-call",
|
||||
"line": 313,
|
||||
"line": 308,
|
||||
"path": "apps/ios/Sources/RootTabs.swift",
|
||||
"source": "OpenClaw",
|
||||
"surface": "apple",
|
||||
@@ -24523,7 +24523,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 345,
|
||||
"line": 340,
|
||||
"path": "apps/ios/Sources/RootTabs.swift",
|
||||
"source": "OpenClaw %@",
|
||||
"surface": "apple",
|
||||
@@ -24531,7 +24531,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 352,
|
||||
"line": 347,
|
||||
"path": "apps/ios/Sources/RootTabs.swift",
|
||||
"source": "Online",
|
||||
"surface": "apple",
|
||||
@@ -24539,7 +24539,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 354,
|
||||
"line": 349,
|
||||
"path": "apps/ios/Sources/RootTabs.swift",
|
||||
"source": "Connecting",
|
||||
"surface": "apple",
|
||||
@@ -24547,7 +24547,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 356,
|
||||
"line": 351,
|
||||
"path": "apps/ios/Sources/RootTabs.swift",
|
||||
"source": "Needs attention",
|
||||
"surface": "apple",
|
||||
@@ -24555,7 +24555,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 358,
|
||||
"line": 353,
|
||||
"path": "apps/ios/Sources/RootTabs.swift",
|
||||
"source": "Offline",
|
||||
"surface": "apple",
|
||||
@@ -24563,7 +24563,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-named-argument",
|
||||
"line": 437,
|
||||
"line": 432,
|
||||
"path": "apps/ios/Sources/RootTabs.swift",
|
||||
"source": "Chat",
|
||||
"surface": "apple",
|
||||
@@ -24571,7 +24571,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-named-argument",
|
||||
"line": 450,
|
||||
"line": 445,
|
||||
"path": "apps/ios/Sources/RootTabs.swift",
|
||||
"source": "Overview",
|
||||
"surface": "apple",
|
||||
@@ -24579,7 +24579,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-named-argument",
|
||||
"line": 474,
|
||||
"line": 469,
|
||||
"path": "apps/ios/Sources/RootTabs.swift",
|
||||
"source": "Agents",
|
||||
"surface": "apple",
|
||||
@@ -24587,7 +24587,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-named-argument",
|
||||
"line": 481,
|
||||
"line": 476,
|
||||
"path": "apps/ios/Sources/RootTabs.swift",
|
||||
"source": "Instances",
|
||||
"surface": "apple",
|
||||
@@ -24595,7 +24595,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-named-argument",
|
||||
"line": 492,
|
||||
"line": 487,
|
||||
"path": "apps/ios/Sources/RootTabs.swift",
|
||||
"source": "Files",
|
||||
"surface": "apple",
|
||||
@@ -24603,7 +24603,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-named-argument",
|
||||
"line": 499,
|
||||
"line": 494,
|
||||
"path": "apps/ios/Sources/RootTabs.swift",
|
||||
"source": "Dreaming",
|
||||
"surface": "apple",
|
||||
@@ -24611,7 +24611,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-named-argument",
|
||||
"line": 506,
|
||||
"line": 501,
|
||||
"path": "apps/ios/Sources/RootTabs.swift",
|
||||
"source": "Usage",
|
||||
"surface": "apple",
|
||||
@@ -24619,7 +24619,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-named-argument",
|
||||
"line": 513,
|
||||
"line": 508,
|
||||
"path": "apps/ios/Sources/RootTabs.swift",
|
||||
"source": "Automations",
|
||||
"surface": "apple",
|
||||
@@ -24627,7 +24627,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 625,
|
||||
"line": 620,
|
||||
"path": "apps/ios/Sources/RootTabs.swift",
|
||||
"source": "Show Sidebar",
|
||||
"surface": "apple",
|
||||
@@ -24635,7 +24635,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 636,
|
||||
"line": 631,
|
||||
"path": "apps/ios/Sources/RootTabs.swift",
|
||||
"source": "Back to %@",
|
||||
"surface": "apple",
|
||||
@@ -24643,7 +24643,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-modifier",
|
||||
"line": 661,
|
||||
"line": 656,
|
||||
"path": "apps/ios/Sources/RootTabs.swift",
|
||||
"source": "Hide Sidebar",
|
||||
"surface": "apple",
|
||||
@@ -24651,7 +24651,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-modifier",
|
||||
"line": 788,
|
||||
"line": 774,
|
||||
"path": "apps/ios/Sources/RootTabs.swift",
|
||||
"source": "Close canvas",
|
||||
"surface": "apple",
|
||||
@@ -24659,7 +24659,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "conditional-branch",
|
||||
"line": 1042,
|
||||
"line": 1026,
|
||||
"path": "apps/ios/Sources/RootTabs.swift",
|
||||
"source": "Gateway needs attention",
|
||||
"surface": "apple",
|
||||
@@ -24667,7 +24667,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "conditional-branch",
|
||||
"line": 1042,
|
||||
"line": 1026,
|
||||
"path": "apps/ios/Sources/RootTabs.swift",
|
||||
"source": "OpenClaw iOS",
|
||||
"surface": "apple",
|
||||
@@ -24675,7 +24675,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "conditional-branch",
|
||||
"line": 1078,
|
||||
"line": 1062,
|
||||
"path": "apps/ios/Sources/RootTabs.swift",
|
||||
"source": "Available",
|
||||
"surface": "apple",
|
||||
@@ -24683,7 +24683,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "conditional-branch",
|
||||
"line": 1078,
|
||||
"line": 1062,
|
||||
"path": "apps/ios/Sources/RootTabs.swift",
|
||||
"source": "Gateway default",
|
||||
"surface": "apple",
|
||||
@@ -24691,7 +24691,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "conditional-branch",
|
||||
"line": 1078,
|
||||
"line": 1062,
|
||||
"path": "apps/ios/Sources/RootTabs.swift",
|
||||
"source": "Routed on this phone",
|
||||
"surface": "apple",
|
||||
|
||||
@@ -428,7 +428,7 @@ final class NodeAppModel {
|
||||
var gatewayPairingPaused: Bool = false
|
||||
var gatewayPairingRequestId: String?
|
||||
// Bumped on every non-nil assignment, including re-reports of an equal problem;
|
||||
// value equality alone cannot tell the UI to re-surface or shake the toast.
|
||||
// value equality alone cannot tell the UI to re-surface a dismissed toast.
|
||||
private(set) var gatewayProblemReportCount = 0
|
||||
private(set) var lastGatewayProblem: GatewayConnectionProblem? {
|
||||
didSet { if self.lastGatewayProblem != nil { self.gatewayProblemReportCount &+= 1 } }
|
||||
|
||||
@@ -40,13 +40,8 @@ struct RootTabs: View {
|
||||
@State private var presentedSheet: PresentedSheet?
|
||||
@State private var showGatewayProblemDetails: Bool = false
|
||||
@State private var gatewayToastDragOffset: CGFloat = 0
|
||||
// Swipe-up hides the toast only until the next problem report; every report
|
||||
// (even an equal problem) must re-surface it or shake the visible toast.
|
||||
// Swipe-up hides the toast only until the next problem report.
|
||||
@State private var isGatewayToastSwipeDismissed: Bool = false
|
||||
@State private var gatewayToastShake: CGFloat = 0
|
||||
// Mirror of the problem at the last handled report, used to tell a first
|
||||
// appearance (animate in) from a re-report while visible (shake).
|
||||
@State private var lastReportedGatewayProblem: GatewayConnectionProblem?
|
||||
@State private var showOnboarding: Bool = false
|
||||
@State private var onboardingAllowSkip: Bool = true
|
||||
@State private var didEvaluateOnboarding: Bool = false
|
||||
@@ -731,7 +726,6 @@ struct RootTabs: View {
|
||||
.padding(.horizontal, 12)
|
||||
.safeAreaPadding(.top, 10)
|
||||
.offset(y: min(self.gatewayToastDragOffset, 0))
|
||||
.modifier(GatewayToastShakeEffect(animatableData: self.gatewayToastShake))
|
||||
.gesture(self.gatewayToastSwipeGesture)
|
||||
// A drag cancelled by toast removal never fires onEnded; clear the
|
||||
// offset so the next toast doesn't render shifted up.
|
||||
@@ -756,16 +750,8 @@ struct RootTabs: View {
|
||||
}
|
||||
|
||||
private func handleGatewayProblemReport() {
|
||||
let toastWasVisible = self.lastReportedGatewayProblem != nil && !self.isGatewayToastSwipeDismissed
|
||||
self.lastReportedGatewayProblem = self.appModel.lastGatewayProblem
|
||||
if self.isGatewayToastSwipeDismissed {
|
||||
self.isGatewayToastSwipeDismissed = false
|
||||
return
|
||||
}
|
||||
guard toastWasVisible, self.activeGatewayProblemToast != nil else { return }
|
||||
withAnimation(self.reduceMotion ? nil : .linear(duration: 0.4)) {
|
||||
self.gatewayToastShake += 1
|
||||
}
|
||||
guard self.isGatewayToastSwipeDismissed else { return }
|
||||
self.isGatewayToastSwipeDismissed = false
|
||||
}
|
||||
|
||||
private var canvasPresentationOverlay: some View {
|
||||
@@ -824,7 +810,6 @@ struct RootTabs: View {
|
||||
private func rootAppearLifecycle(_ content: some View) -> some View {
|
||||
content
|
||||
.onAppear { self.updateIdleTimer() }
|
||||
.onAppear { self.lastReportedGatewayProblem = self.appModel.lastGatewayProblem }
|
||||
.onAppear { self.updateCanvasState() }
|
||||
.onAppear { self.evaluateOnboardingPresentation(force: false) }
|
||||
.onAppear { self.maybeAutoOpenSettings() }
|
||||
@@ -857,7 +842,6 @@ struct RootTabs: View {
|
||||
.onChange(of: self.appModel.lastGatewayProblem) { _, newValue in
|
||||
if newValue == nil {
|
||||
self.isGatewayToastSwipeDismissed = false
|
||||
self.lastReportedGatewayProblem = nil
|
||||
}
|
||||
}
|
||||
.onChange(of: self.appModel.gatewayProblemReportCount) { _, _ in
|
||||
@@ -1459,16 +1443,6 @@ private struct RootTabsHomeCanvasAgentCard: Codable {
|
||||
var isActive: Bool
|
||||
}
|
||||
|
||||
/// Horizontal shake for re-reported gateway problems: three oscillations that
|
||||
/// settle back to identity at integer trigger values.
|
||||
private struct GatewayToastShakeEffect: GeometryEffect {
|
||||
var animatableData: CGFloat
|
||||
|
||||
func effectValue(size _: CGSize) -> ProjectionTransform {
|
||||
ProjectionTransform(CGAffineTransform(translationX: 7 * sin(self.animatableData * 6 * .pi), y: 0))
|
||||
}
|
||||
}
|
||||
|
||||
private struct RootCameraFlashOverlay: View {
|
||||
var nonce: Int
|
||||
|
||||
|
||||
@@ -99,10 +99,11 @@ extension RootTabsSourceGuardTests {
|
||||
// root's only remediation surface must not depend on aggregate status.
|
||||
#expect(activeProblemToast.contains("appModel.lastGatewayProblem"))
|
||||
#expect(!activeProblemToast.contains("gatewayStatus"))
|
||||
// Every problem report re-surfaces a swiped-away toast or shakes the
|
||||
// visible one; value equality alone must not keep the toast hidden.
|
||||
// Every problem report re-surfaces a swiped-away toast. Visible problem
|
||||
// banners stay stationary when reconnects re-report the same failure.
|
||||
#expect(rootSource.contains("self.appModel.gatewayProblemReportCount"))
|
||||
#expect(rootSource.contains("GatewayToastShakeEffect"))
|
||||
#expect(rootSource.contains("guard self.isGatewayToastSwipeDismissed else { return }"))
|
||||
#expect(!rootSource.contains("GatewayToastShakeEffect"))
|
||||
|
||||
#expect(actionsSource.contains("await self.gatewayController.connectActiveGateway()"))
|
||||
#expect(actionsSource.contains("self.gatewayController.refreshActiveGatewayRegistrationFromSettings()"))
|
||||
|
||||
Reference in New Issue
Block a user