fix(profiles): connection-profile switch re-dials the WebSocket (#888)

Co-authored-by: M57 <hy4ri@users.noreply.github.com>
This commit is contained in:
M57andM57 authored and GitHub committed 2026-08-12 23:27:08 +03:00
1 parent 546bd17b18
commit 683a24becf
10 files changed
+183 -17

No files matched your search

@@ -576,6 +576,16 @@ class ChatViewModel(
resetSessionState(sessionId = null, title = "Hermes", isLoading = true)
}
}
// Same wipe when the CONNECTION profile changes (different server):
// without it, gateway.ready on the re-dialed socket tries to resume
// the OLD server's session on the NEW server and fails (split-brain
// after connection-profile switch, reproduced live 2026-08-12).
viewModelScope.launch {
ProfileSwitchCoordinator.connectionSwitched
.collect { _ ->
resetSessionState(sessionId = null, title = "Hermes", isLoading = true)
}
}
// Slash-command usage ranking (issue #865): mirror the local usage
// counts into state so the autocomplete can surface most-used
// commands first. Best-effort — the store never throws.
@@ -10,6 +10,7 @@ import com.m57.hermescontrol.data.remote.CleartextPolicy
import com.m57.hermescontrol.data.remote.NetworkResult
import com.m57.hermescontrol.data.remote.ServerEndpoint
import com.m57.hermescontrol.data.remote.safeApiCall
import com.m57.hermescontrol.data.session.ProfileSwitchCoordinator
import com.m57.hermescontrol.data.ws.HermesWsClient
import com.m57.hermescontrol.theme.ThemePreference
import com.m57.hermescontrol.theme.ThemePreset
@@ -103,9 +104,13 @@ class SettingsViewModel(
}
fun selectProfile(profileId: String?) {
AuthManager.setSelectedProfileId(profileId)
viewModelScope.launch(ioDispatcher) { loadSettings() }
ApiClient.rebuild()
viewModelScope.launch(ioDispatcher) {
// The coordinator re-homes EVERYTHING: selection + Retrofit +
// WebSocket re-dial, so chat doesn't stay glued to the previous
// server (issue: split-brain after connection-profile switch).
ProfileSwitchCoordinator.switchConnectionProfile(profileId)
loadSettings()
}
}
fun deleteProfile(profileId: String) {
@@ -114,14 +119,25 @@ class SettingsViewModel(
AuthManager.setProfileToken(profileId, null)
// Never leave selection null (issue #478): if the deleted profile was selected,
// fall back to the default profile instead of clearing selection.
if (AuthManager.getSelectedProfileId() == profileId) {
if (updatedProfiles.none { it.id == AuthManager.DEFAULT_PROFILE_ID }) {
AuthManager.ensureDefaultProfile()
val reselectDefault =
if (AuthManager.getSelectedProfileId() == profileId) {
if (updatedProfiles.none { it.id == AuthManager.DEFAULT_PROFILE_ID }) {
AuthManager.ensureDefaultProfile()
}
true
} else {
false
}
AuthManager.setSelectedProfileId(AuthManager.DEFAULT_PROFILE_ID)
viewModelScope.launch(ioDispatcher) {
if (reselectDefault) {
// Same re-home as a manual switch: the WebSocket must leave
// the deleted server too, or chat keeps its stale socket.
ProfileSwitchCoordinator.switchConnectionProfile(AuthManager.DEFAULT_PROFILE_ID)
} else {
ApiClient.rebuild()
}
loadSettings()
}
viewModelScope.launch(ioDispatcher) { loadSettings() }
ApiClient.rebuild()
}
// ── Profile add/edit dialog ──────────────────────────────────────────