From 30922aa0d6ad779696460d1b5f6b1ee6707c8ac9 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Sat, 11 Apr 2026 20:54:27 +0700 Subject: [PATCH 1/2] =?UTF-8?q?fix(ios):=20P3=20polish=20=E2=80=94=20font?= =?UTF-8?q?=20sizes,=20picker=20styles,=20clear=20confirmation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Views/ConnectionFormView.swift | 2 ++ .../Views/DataBrowserView.swift | 2 +- .../Views/QueryEditorView.swift | 20 +++++++++++++++---- .../TableProMobile/Views/RowDetailView.swift | 4 ++-- .../Views/TagManagementView.swift | 2 +- 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/TableProMobile/TableProMobile/Views/ConnectionFormView.swift b/TableProMobile/TableProMobile/Views/ConnectionFormView.swift index d380ad25..3d53243d 100644 --- a/TableProMobile/TableProMobile/Views/ConnectionFormView.swift +++ b/TableProMobile/TableProMobile/Views/ConnectionFormView.swift @@ -167,6 +167,7 @@ struct ConnectionFormView: View { Text(level.displayName).tag(level) } } + .pickerStyle(.menu) } if type == .sqlite { @@ -379,6 +380,7 @@ struct ConnectionFormView: View { Text("Password").tag(SSHConfiguration.SSHAuthMethod.password) Text("Private Key").tag(SSHConfiguration.SSHAuthMethod.privateKey) } + .pickerStyle(.segmented) } if sshAuthMethod == .password { diff --git a/TableProMobile/TableProMobile/Views/DataBrowserView.swift b/TableProMobile/TableProMobile/Views/DataBrowserView.swift index 7f4b65bf..4902ea29 100644 --- a/TableProMobile/TableProMobile/Views/DataBrowserView.swift +++ b/TableProMobile/TableProMobile/Views/DataBrowserView.swift @@ -888,7 +888,7 @@ private struct RowCard: View { ForEach(Array(detailPairs.enumerated()), id: \.offset) { _, pair in HStack(spacing: 6) { Text(pair.name) - .font(.caption2) + .font(.caption) .foregroundStyle(.tertiary) Text(verbatim: pair.value) .font(.caption) diff --git a/TableProMobile/TableProMobile/Views/QueryEditorView.swift b/TableProMobile/TableProMobile/Views/QueryEditorView.swift index c76df055..8b7a54da 100644 --- a/TableProMobile/TableProMobile/Views/QueryEditorView.swift +++ b/TableProMobile/TableProMobile/Views/QueryEditorView.swift @@ -31,6 +31,7 @@ struct QueryEditorView: View { @State private var showWriteConfirmation = false @State private var showWriteBlockedAlert = false @State private var pendingWriteQuery = "" + @State private var showClearConfirmation = false @State private var showShareSheet = false @State private var shareText = "" @State private var hapticSuccess = false @@ -63,6 +64,20 @@ struct QueryEditorView: View { ActivityViewController(items: [shareText]) } .sheet(isPresented: $showHistory) { historySheet } + .confirmationDialog( + String(localized: "Clear Query"), + isPresented: $showClearConfirmation, + titleVisibility: .visible + ) { + Button(String(localized: "Clear"), role: .destructive) { + query = "" + result = nil + appError = nil + executionTime = nil + } + } message: { + Text("Query text and results will be cleared.") + } } // MARK: - Editor @@ -290,10 +305,7 @@ struct QueryEditorView: View { Divider() Button(role: .destructive) { - query = "" - result = nil - appError = nil - executionTime = nil + showClearConfirmation = true } label: { Label("Clear", systemImage: "trash") } diff --git a/TableProMobile/TableProMobile/Views/RowDetailView.swift b/TableProMobile/TableProMobile/Views/RowDetailView.swift index 05671253..75223b62 100644 --- a/TableProMobile/TableProMobile/Views/RowDetailView.swift +++ b/TableProMobile/TableProMobile/Views/RowDetailView.swift @@ -247,9 +247,9 @@ struct RowDetailView: View { } label: { HStack(spacing: 4) { Image(systemName: "arrow.right.circle.fill") - .font(.caption2) + .font(.footnote) Text("\(fk.referencedTable).\(fk.referencedColumn)") - .font(.caption2) + .font(.footnote) } .foregroundStyle(.blue) } diff --git a/TableProMobile/TableProMobile/Views/TagManagementView.swift b/TableProMobile/TableProMobile/Views/TagManagementView.swift index 9dcc967a..98eb481d 100644 --- a/TableProMobile/TableProMobile/Views/TagManagementView.swift +++ b/TableProMobile/TableProMobile/Views/TagManagementView.swift @@ -31,7 +31,7 @@ struct TagManagementView: View { if tag.isPreset { Image(systemName: "lock.fill") - .font(.caption2) + .font(.caption) .foregroundStyle(.secondary) } From 1b2a6a455d49a7b9074dadacfe18de5e35d6f6d3 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Sat, 11 Apr 2026 20:54:20 +0700 Subject: [PATCH 2/2] =?UTF-8?q?feat(ios):=20P3=20polish=20=E2=80=94=20lead?= =?UTF-8?q?ing=20swipe,=20elapsed=20timer,=20reconnect=20indicator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TableProMobile/Views/ConnectedView.swift | 16 ++++++++++++++++ .../Views/ConnectionListView.swift | 8 ++++++++ .../TableProMobile/Views/QueryEditorView.swift | 18 +++++++++++++++--- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/TableProMobile/TableProMobile/Views/ConnectedView.swift b/TableProMobile/TableProMobile/Views/ConnectedView.swift index ebd2e436..6b834552 100644 --- a/TableProMobile/TableProMobile/Views/ConnectedView.swift +++ b/TableProMobile/TableProMobile/Views/ConnectedView.swift @@ -91,6 +91,22 @@ struct ConnectedView: View { } } .animation(.default, value: isSwitching) + .overlay(alignment: .top) { + if isReconnecting { + HStack(spacing: 6) { + ProgressView() + .controlSize(.mini) + Text(String(localized: "Reconnecting...")) + .font(.caption) + } + .padding(.horizontal, 12) + .padding(.vertical, 6) + .background(.regularMaterial, in: Capsule()) + .transition(.move(edge: .top).combined(with: .opacity)) + .padding(.top, 4) + } + } + .animation(.default, value: isReconnecting) } } .sensoryFeedback(.success, trigger: hapticSuccess) diff --git a/TableProMobile/TableProMobile/Views/ConnectionListView.swift b/TableProMobile/TableProMobile/Views/ConnectionListView.swift index b2f8a25c..2b7d8a3e 100644 --- a/TableProMobile/TableProMobile/Views/ConnectionListView.swift +++ b/TableProMobile/TableProMobile/Views/ConnectionListView.swift @@ -348,6 +348,14 @@ struct ConnectionListView: View { ConnectionRow(connection: connection, tag: appState.tag(for: connection.tagId)) } .hoverEffect() + .swipeActions(edge: .leading) { + Button { + editingConnection = connection + } label: { + Label("Edit", systemImage: "pencil") + } + .tint(.blue) + } .swipeActions(edge: .trailing, allowsFullSwipe: false) { Button { connectionToDelete = connection diff --git a/TableProMobile/TableProMobile/Views/QueryEditorView.swift b/TableProMobile/TableProMobile/Views/QueryEditorView.swift index 8b7a54da..32ddd5c4 100644 --- a/TableProMobile/TableProMobile/Views/QueryEditorView.swift +++ b/TableProMobile/TableProMobile/Views/QueryEditorView.swift @@ -23,6 +23,7 @@ struct QueryEditorView: View { @State private var isExecuting = false @State private var executionTime: TimeInterval? @State private var executeTask: Task? + @State private var executionStartTime: Date? @Binding var queryHistory: [QueryHistoryItem] let connectionId: UUID let historyStorage: QueryHistoryStorage @@ -87,9 +88,16 @@ struct QueryEditorView: View { SQLHighlightTextView(text: $query) .frame(minHeight: 80, maxHeight: result != nil || appError != nil ? 120 : 250) - if executionTime != nil || result != nil { + if isExecuting || executionTime != nil || result != nil { HStack { - if let time = executionTime { + if isExecuting, let startTime = executionStartTime { + TimelineView(.periodic(from: startTime, by: 0.1)) { context in + let elapsed = context.date.timeIntervalSince(startTime) + Label(String(format: "%.1fs", elapsed), systemImage: "clock") + .font(.caption2) + .foregroundStyle(.secondary) + } + } else if let time = executionTime { Label(String(format: "%.1fms", time * 1000), systemImage: "clock") .font(.caption2) .foregroundStyle(.secondary) @@ -410,7 +418,11 @@ struct QueryEditorView: View { UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) isExecuting = true - defer { isExecuting = false } + executionStartTime = Date() + defer { + isExecuting = false + executionStartTime = nil + } appError = nil result = nil