@@ -251,10 +251,23 @@ final class AppState {
251 @MainActor
251 @MainActor
252 func refreshStreams() async {
252 func refreshStreams() async {
253 guard let client else { return }
253 guard let client else { return }
254+ // Capture the connection this fetch belongs to. A disconnect during the
255+ // await would otherwise let the (now-stale) result overwrite the cleanup
256+ // disconnect() just performed (streams = [], client = nil, etc.), leaving
257+ // the disconnected UI showing the old server's streams and re-enabling
258+ // stream-scoped tabs. Mirrors the connID guard used in performConnect,
259+ // AlertsView.loadAlerts, UsersView.loadUsers, StreamDetailView.loadData,
260+ // and ServerInfoView.loadInfo.
261+ let connID = activeConnection?.id
254 isLoadingStreams = true
262 isLoadingStreams = true
255 streamLoadError = nil
263 streamLoadError = nil
257− streams = try await client.listStreams()
265+ let loaded = try await client.listStreams()
266+ // Drop results if the active connection changed while awaiting. Still
267+ // clear isLoadingStreams: this task set the spinner, so leaving it true
268+ // wedges the indicator and disables Refresh.
269+ guard connID == activeConnection?.id else { isLoadingStreams = false; return }
258 // Drop a selection that no longer exists server-side (e.g. the stream
271 // Drop a selection that no longer exists server-side (e.g. the stream
259 // was deleted from another client); otherwise the detail tabs keep
272 // was deleted from another client); otherwise the detail tabs keep
260 // querying a missing stream and surface 404s.
273 // querying a missing stream and surface 404s.
@@ -262,6 +275,7 @@ final class AppState {
262 selectedStream = nil
275 selectedStream = nil
264 } catch {
277 } catch {
278+ guard connID == activeConnection?.id else { isLoadingStreams = false; return }
265 let message = ParseableError.userFriendlyMessage(for: error)
279 let message = ParseableError.userFriendlyMessage(for: error)
266 streamLoadError = message
280 streamLoadError = message
267 self.errorMessage = "Failed to load streams: \(message)"
281 self.errorMessage = "Failed to load streams: \(message)"
@@ -356,8 +370,15 @@ final class AppState {
356 @MainActor
370 @MainActor
357 func refreshFilters() async {
371 func refreshFilters() async {
358 guard let client else { return }
372 guard let client else { return }
373+ // Capture the connection this fetch belongs to. A disconnect during the
374+ // await would otherwise let the (now-stale) result overwrite the cleanup
375+ // disconnect() performed, resurrecting the old server's filters in the
376+ // disconnected sidebar. Same guard pattern as refreshStreams.
377+ let connID = activeConnection?.id
359 isLoadingFilters = true
378 isLoadingFilters = true
360− filters = (try? await client.listFilters()) ?? []
379+ let loaded = (try? await client.listFilters()) ?? []
380+ guard connID == activeConnection?.id else { isLoadingFilters = false; return }
361 isLoadingFilters = false
382 isLoadingFilters = false