mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
fix(ios): preserve active chat across foreground reconnects (#108241)
* fix(ios): preserve chat focus across reconnects * chore(ios): sync native i18n inventory * test(ios): strengthen reconnect ownership coverage Co-authored-by: NianJiuZst <180004567+NianJiuZst@users.noreply.github.com> --------- Co-authored-by: NianJiuZst <180004567+NianJiuZst@users.noreply.github.com> Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
co-authored by
NianJiuZst
Peter Steinberger
parent
48183146f5
commit
727f9e0a7e
@@ -22867,7 +22867,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 3813,
|
||||
"line": 3819,
|
||||
"path": "apps/ios/Sources/Model/NodeAppModel.swift",
|
||||
"source": "Approval needed",
|
||||
"surface": "apple",
|
||||
@@ -22875,7 +22875,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 3814,
|
||||
"line": 3820,
|
||||
"path": "apps/ios/Sources/Model/NodeAppModel.swift",
|
||||
"source": "Action required",
|
||||
"surface": "apple",
|
||||
@@ -22883,7 +22883,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 4491,
|
||||
"line": 4497,
|
||||
"path": "apps/ios/Sources/Model/NodeAppModel.swift",
|
||||
"source": "Connecting...",
|
||||
"surface": "apple",
|
||||
@@ -22891,7 +22891,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 4492,
|
||||
"line": 4498,
|
||||
"path": "apps/ios/Sources/Model/NodeAppModel.swift",
|
||||
"source": "Reconnecting...",
|
||||
"surface": "apple",
|
||||
@@ -22899,7 +22899,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "conditional-branch",
|
||||
"line": 4836,
|
||||
"line": 4842,
|
||||
"path": "apps/ios/Sources/Model/NodeAppModel.swift",
|
||||
"source": "Connected",
|
||||
"surface": "apple",
|
||||
@@ -22907,7 +22907,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "conditional-branch",
|
||||
"line": 4836,
|
||||
"line": 4842,
|
||||
"path": "apps/ios/Sources/Model/NodeAppModel.swift",
|
||||
"source": "Offline",
|
||||
"surface": "apple",
|
||||
@@ -22915,7 +22915,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "conditional-branch",
|
||||
"line": 5978,
|
||||
"line": 5984,
|
||||
"path": "apps/ios/Sources/Model/NodeAppModel.swift",
|
||||
"source": "No chat messages yet",
|
||||
"surface": "apple",
|
||||
@@ -22923,7 +22923,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "conditional-branch",
|
||||
"line": 6177,
|
||||
"line": 6183,
|
||||
"path": "apps/ios/Sources/Model/NodeAppModel.swift",
|
||||
"source": "Connecting…",
|
||||
"surface": "apple",
|
||||
@@ -22931,7 +22931,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "conditional-branch",
|
||||
"line": 6177,
|
||||
"line": 6183,
|
||||
"path": "apps/ios/Sources/Model/NodeAppModel.swift",
|
||||
"source": "Reconnecting…",
|
||||
"surface": "apple",
|
||||
@@ -22939,7 +22939,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "conditional-branch",
|
||||
"line": 8462,
|
||||
"line": 8468,
|
||||
"path": "apps/ios/Sources/Model/NodeAppModel.swift",
|
||||
"source": "Approval",
|
||||
"surface": "apple",
|
||||
@@ -22947,7 +22947,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "conditional-branch",
|
||||
"line": 8462,
|
||||
"line": 8468,
|
||||
"path": "apps/ios/Sources/Model/NodeAppModel.swift",
|
||||
"source": "This approval was already",
|
||||
"surface": "apple",
|
||||
@@ -22955,7 +22955,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "conditional-branch",
|
||||
"line": 8468,
|
||||
"line": 8474,
|
||||
"path": "apps/ios/Sources/Model/NodeAppModel.swift",
|
||||
"source": "This approval was already set to Always Allow.",
|
||||
"surface": "apple",
|
||||
@@ -22963,7 +22963,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "conditional-branch",
|
||||
"line": 8469,
|
||||
"line": 8475,
|
||||
"path": "apps/ios/Sources/Model/NodeAppModel.swift",
|
||||
"source": "Approval set to Always Allow.",
|
||||
"surface": "apple",
|
||||
@@ -22971,7 +22971,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "conditional-branch",
|
||||
"line": 9773,
|
||||
"line": 9779,
|
||||
"path": "apps/ios/Sources/Model/NodeAppModel.swift",
|
||||
"source": "\\(urlText.prefix(500))…",
|
||||
"surface": "apple",
|
||||
|
||||
@@ -3412,7 +3412,7 @@ extension NodeAppModel {
|
||||
nodeOptions: connectOptions)
|
||||
let previousGatewayStableID = self.activeGatewayConnectConfig?.effectiveStableID
|
||||
?? self.connectedGatewayID
|
||||
let preserveGatewayProblem = previousGatewayStableID.map {
|
||||
let isSameGatewayTarget = previousGatewayStableID.map {
|
||||
!$0.isEmpty && GatewayStableIdentifier.matches($0, effectiveStableID)
|
||||
} ?? false
|
||||
let targetChanged = previousGatewayStableID.map {
|
||||
@@ -3449,7 +3449,8 @@ extension NodeAppModel {
|
||||
self.activeGatewayConnectConfig = nextConfig
|
||||
prepareForGatewayConnect(
|
||||
stableID: effectiveStableID,
|
||||
preservingGatewayProblem: preserveGatewayProblem)
|
||||
preservingGatewayProblem: isSameGatewayTarget,
|
||||
preservingFocusedChatSession: isSameGatewayTarget)
|
||||
if operatorLoopRequired {
|
||||
startOperatorGatewayLoop(
|
||||
url: url,
|
||||
@@ -3693,7 +3694,8 @@ extension NodeAppModel {
|
||||
|
||||
private func prepareForGatewayConnect(
|
||||
stableID: String,
|
||||
preservingGatewayProblem: Bool = false)
|
||||
preservingGatewayProblem: Bool = false,
|
||||
preservingFocusedChatSession: Bool = false)
|
||||
{
|
||||
self.invalidateNodePushToTalkRoute()
|
||||
self.operatorTalkConnectionGeneration &+= 1
|
||||
@@ -3730,7 +3732,11 @@ extension NodeAppModel {
|
||||
self.gatewayDefaultAgentId = nil
|
||||
self.gatewayAgents = []
|
||||
self.selectedAgentId = GatewaySettingsStore.loadGatewaySelectedAgentId(stableID: stableID)
|
||||
self.focusedChatSessionKey = nil
|
||||
// Session keys are gateway-owned: transport reconnects keep the active chat,
|
||||
// while initial connects and target changes must not inherit another route.
|
||||
if !preservingFocusedChatSession {
|
||||
self.focusedChatSessionKey = nil
|
||||
}
|
||||
self.synchronizeTalkSessionKey()
|
||||
self.homeCanvasRevision &+= 1
|
||||
self.apnsLastRegisteredTokenHex = nil
|
||||
|
||||
@@ -500,6 +500,29 @@ private func waitForActiveGateway(stableID: String, appModel: NodeAppModel) asyn
|
||||
#expect(appModel.gatewayPairingRequestId == nil)
|
||||
}
|
||||
|
||||
@Test @MainActor func `stable gateway owner preserves focused chat across route changes`() throws {
|
||||
let appModel = NodeAppModel()
|
||||
defer { appModel.disconnectGateway() }
|
||||
let currentConfig = Self.makeGatewayConnectConfig()
|
||||
let focusedSessionKey = "agent:main:ios-focused"
|
||||
appModel.applyGatewayConnectConfig(currentConfig)
|
||||
appModel.focusChatSession(focusedSessionKey)
|
||||
|
||||
let replacementURL = try #require(URL(string: "wss://127.0.0.1:2"))
|
||||
let refreshedRoute = Self.makeGatewayConnectConfig(
|
||||
url: replacementURL,
|
||||
stableID: currentConfig.stableID)
|
||||
appModel.applyGatewayConnectConfig(refreshedRoute, forceReconnect: true)
|
||||
#expect(appModel.chatSessionKey == focusedSessionKey)
|
||||
|
||||
let replacementConfig = Self.makeGatewayConnectConfig(
|
||||
url: replacementURL,
|
||||
stableID: "manual|replacement.example.com|443")
|
||||
appModel.applyGatewayConnectConfig(replacementConfig, forceReconnect: true)
|
||||
|
||||
#expect(appModel.chatSessionKey != focusedSessionKey)
|
||||
}
|
||||
|
||||
@Test func `gateway connect config matches equivalent inputs`() {
|
||||
let lhs = Self.makeGatewayConnectConfig()
|
||||
let rhs = GatewayConnectConfig(
|
||||
@@ -2537,11 +2560,14 @@ private func waitForActiveGateway(stableID: String, appModel: NodeAppModel) asyn
|
||||
defer { appModel.disconnectGateway() }
|
||||
|
||||
let config = Self.makeGatewayConnectConfig()
|
||||
let focusedSessionKey = "agent:main:ios-foreground-focused"
|
||||
appModel.applyGatewayConnectConfig(config)
|
||||
appModel.focusChatSession(focusedSessionKey)
|
||||
await appModel._test_restartGatewaySessionsAfterForegroundStaleConnection()
|
||||
|
||||
#expect(appModel.gatewayStatusText == "Reconnecting…")
|
||||
#expect(appModel.activeGatewayConnectConfig?.hasSameConnectionInputs(as: config) == true)
|
||||
#expect(appModel.chatSessionKey == focusedSessionKey)
|
||||
#expect(appModel._test_hasGatewayLoopTasks().node)
|
||||
#expect(appModel._test_hasGatewayLoopTasks().operator)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user