@@ -96,16 +96,18 @@ final class AppState {
96 // MARK: - Connection Management
96 // MARK: - Connection Management
98 @MainActor
98 @MainActor
99− func connect(to connection: ServerConnection) async {
100+ func connect(to connection: ServerConnection) async -> Bool {
100− guard !isConnecting else { return }
101+ guard !isConnecting else { return false }
102 if !isNetworkAvailable {
103 if !isNetworkAvailable {
103 self.errorMessage = "No internet connection. Check your network and try again."
104 self.errorMessage = "No internet connection. Check your network and try again."
104 self.showError = true
105 self.showError = true
105− return
106+ return false
108 isConnecting = true
109 isConnecting = true
110+ defer { isConnecting = false }
109 errorMessage = nil
111 errorMessage = nil
111 // When switching to a different server, clear stream-specific state
113 // When switching to a different server, clear stream-specific state
@@ -126,6 +128,11 @@ final class AppState {
126 // Test connection — throws on non-200 or network failure
128 // Test connection — throws on non-200 or network failure
127 try await newClient.checkHealth()
129 try await newClient.checkHealth()
131+ // If the task was cancelled while awaiting (e.g. user clicked
132+ // Disconnect during auto-reconnect), bail before installing the
133+ // client so we don't resurrect a connection disconnect() just cleared.
134+ if Task.isCancelled { return false }
129 self.client = newClient
136 self.client = newClient
130 self.activeConnection = connection
137 self.activeConnection = connection
131 self.isConnected = true
138 self.isConnected = true
@@ -137,15 +144,30 @@ final class AppState {
137 async let streamList = newClient.listStreams()
144 async let streamList = newClient.listStreams()
138 async let filterList = newClient.listFilters()
145 async let filterList = newClient.listFilters()
140− self.serverAbout = try? await about
147+ let aboutResult = try? await about
141− self.filters = (try? await filterList) ?? []
148+ let filterResult = (try? await filterList) ?? []
149+ let streamResult: Result<[LogStream], Error>
143− self.streams = try await streamList
151+ streamResult = .success(try await streamList)
144− self.streamLoadError = nil
145 } catch {
152 } catch {
153+ streamResult = .failure(error)
156+ // Re-check after the loads above: a disconnect could have landed
157+ // while awaiting, in which case committing these would show stale data.
158+ if Task.isCancelled { return false }
160+ self.serverAbout = aboutResult
161+ self.filters = filterResult
162+ switch streamResult {
163+ case .success(let list):
165+ self.streamLoadError = nil
166+ case .failure(let error):
146 self.streams = []
167 self.streams = []
147 self.streamLoadError = ParseableError.userFriendlyMessage(for: error)
168 self.streamLoadError = ParseableError.userFriendlyMessage(for: error)
149 } catch {
171 } catch {
150 // If the task was cancelled (e.g. user clicked Disconnect during
172 // If the task was cancelled (e.g. user clicked Disconnect during
151 // auto-reconnect), skip the error alert — disconnect() already
173 // auto-reconnect), skip the error alert — disconnect() already
@@ -164,9 +186,8 @@ final class AppState {
164 self.serverAbout = nil
186 self.serverAbout = nil
165 self.filters = []
187 self.filters = []
169− isConnecting = false
172 @MainActor
193 @MainActor