From c012a5bbb617188c50444e15490caa732c13e685 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Sun, 10 May 2026 01:37:00 +0700 Subject: [PATCH] feat(ios): alert when active connection is deleted mid-session --- CHANGELOG.md | 1 + .../Coordinators/ConnectionCoordinator.swift | 4 ++-- TableProMobile/TableProMobile/Localizable.xcstrings | 6 ++++++ .../TableProMobile/Views/ConnectedView.swift | 13 ++++++++++++- .../TableProMobile/Views/ConnectionInfoView.swift | 2 -- 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e9ebbcec..4b64c0ab7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- iOS: alert when the active connection is deleted mid-session (for example via iCloud sync from another device), so a stale screen no longer fails silently on the next action - iOS: Face ID, Touch ID, or Optic ID lock with cold-launch protection and idle timeout (1, 5, 15, or 60 minutes), opt-in from Settings - iOS: Connection Info tab replaces the per-connection Settings tab, showing host, SSL, SSH tunnel, active database, and live connection status - MCP Setup sheet adds Zed alongside Claude Desktop, Claude Code, and Cursor with a one-paste `context_servers` snippet diff --git a/TableProMobile/TableProMobile/Coordinators/ConnectionCoordinator.swift b/TableProMobile/TableProMobile/Coordinators/ConnectionCoordinator.swift index b09503474..22d91dc74 100644 --- a/TableProMobile/TableProMobile/Coordinators/ConnectionCoordinator.swift +++ b/TableProMobile/TableProMobile/Coordinators/ConnectionCoordinator.swift @@ -33,7 +33,7 @@ final class ConnectionCoordinator { } } var pendingQuery: String? - var navigationPath = NavigationPath() + var tablesPath = NavigationPath() var showingEditSheet = false private(set) var queryHistory: [QueryHistoryItem] = [] @@ -281,7 +281,7 @@ final class ConnectionCoordinator { appState.pendingTableName = nil selectedTab = .tables Task { @MainActor in - navigationPath.append(table) + tablesPath.append(table) } } diff --git a/TableProMobile/TableProMobile/Localizable.xcstrings b/TableProMobile/TableProMobile/Localizable.xcstrings index 9807493d2..7f91ebb5c 100644 --- a/TableProMobile/TableProMobile/Localizable.xcstrings +++ b/TableProMobile/TableProMobile/Localizable.xcstrings @@ -831,6 +831,9 @@ } } } + }, + "Connection Deleted" : { + }, "Connection Failed" : { "localizations" : { @@ -3825,6 +3828,9 @@ } } } + }, + "This connection no longer exists. It may have been removed from another device." : { + }, "This database has no tables." : { "localizations" : { diff --git a/TableProMobile/TableProMobile/Views/ConnectedView.swift b/TableProMobile/TableProMobile/Views/ConnectedView.swift index 1133eb50a..9e01a379b 100644 --- a/TableProMobile/TableProMobile/Views/ConnectedView.swift +++ b/TableProMobile/TableProMobile/Views/ConnectedView.swift @@ -18,6 +18,7 @@ struct ConnectedView: View { @State private var coordinator: ConnectionCoordinator? @State private var hapticSuccess = false @State private var hapticError = false + @State private var showDeletedAlert = false var body: some View { Group { @@ -38,6 +39,16 @@ struct ConnectedView: View { } .navigationTitle(connection.name.isEmpty ? connection.host : connection.name) .navigationBarTitleDisplayMode(.inline) + .onChange(of: appState.connections) { _, newConnections in + if !newConnections.contains(where: { $0.id == connection.id }) { + showDeletedAlert = true + } + } + .alert(String(localized: "Connection Deleted"), isPresented: $showDeletedAlert) { + Button("OK", role: .cancel) { dismiss() } + } message: { + Text("This connection no longer exists. It may have been removed from another device.") + } .task { if let cached = cachedCoordinator { coordinator = cached @@ -88,7 +99,7 @@ struct ConnectedView: View { private func connectedContent(_ coordinator: ConnectionCoordinator) -> some View { @Bindable var coordinator = coordinator - return NavigationStack(path: $coordinator.navigationPath) { + return NavigationStack(path: $coordinator.tablesPath) { TabView(selection: $coordinator.selectedTab) { Tab("Tables", systemImage: "tablecells", value: .tables) { TableListView() diff --git a/TableProMobile/TableProMobile/Views/ConnectionInfoView.swift b/TableProMobile/TableProMobile/Views/ConnectionInfoView.swift index 0f17d79a3..f142f5acc 100644 --- a/TableProMobile/TableProMobile/Views/ConnectionInfoView.swift +++ b/TableProMobile/TableProMobile/Views/ConnectionInfoView.swift @@ -51,8 +51,6 @@ struct ConnectionInfoView: View { statsSection } - .navigationTitle("Info") - .navigationBarTitleDisplayMode(.inline) .sheet(isPresented: Binding( get: { coordinator.showingEditSheet }, set: { coordinator.showingEditSheet = $0 }