Skip to content

Commit 461cd8f

Browse files
committed
fix: polish SQL favorites — accessibility, delete confirmation, localization, edge cases
1 parent d9f17d1 commit 461cd8f

4 files changed

Lines changed: 67 additions & 14 deletions

File tree

TablePro/Views/Main/Extensions/MainContentCoordinator+Favorites.swift

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,15 @@
66
import Foundation
77

88
extension MainContentCoordinator {
9-
/// Insert a favorite's query into the current editor tab
9+
/// Insert a favorite's query into the current editor tab.
10+
/// If the current tab is not a query tab, opens a new query tab instead.
1011
func insertFavorite(_ favorite: SQLFavorite) {
11-
guard let tabIndex = tabManager.selectedTabIndex else { return }
12-
tabManager.tabs[tabIndex].query = favorite.query
12+
if let tabIndex = tabManager.selectedTabIndex,
13+
tabManager.tabs[tabIndex].tabType == .query {
14+
tabManager.tabs[tabIndex].query = favorite.query
15+
} else {
16+
runFavoriteInNewTab(favorite)
17+
}
1318
}
1419

1520
/// Open a favorite's query in a new tab

TablePro/Views/Sidebar/FavoriteEditDialog.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ struct FavoriteEditDialog: View {
2020
@State private var keyword: String = ""
2121
@State private var isGlobal: Bool = true
2222
@State private var keywordError: String?
23+
@State private var isKeywordWarning = false
2324
@State private var isSaving = false
2425

2526
private var isEditing: Bool { favorite != nil }
@@ -57,7 +58,7 @@ struct FavoriteEditDialog: View {
5758
if let error = keywordError {
5859
LabeledContent {} label: {
5960
Text(error)
60-
.foregroundStyle(error.hasPrefix("Warning") ? .orange : .red)
61+
.foregroundStyle(isKeywordWarning ? .orange : .red)
6162
.font(.callout)
6263
}
6364
}
@@ -78,6 +79,7 @@ struct FavoriteEditDialog: View {
7879

7980
if !forceGlobal {
8081
Toggle("Global:", isOn: $isGlobal)
82+
.help(String(localized: "When enabled, this favorite is visible in all connections"))
8183
}
8284
}
8385
.formStyle(.columns)
@@ -105,7 +107,7 @@ struct FavoriteEditDialog: View {
105107
keyword = fav.keyword ?? ""
106108
isGlobal = forceGlobal || fav.connectionId == nil
107109
} else {
108-
isGlobal = forceGlobal || true
110+
isGlobal = true
109111
if let q = initialQuery {
110112
query = q
111113
}
@@ -122,6 +124,7 @@ struct FavoriteEditDialog: View {
122124
return
123125
}
124126
if trimmed.contains(" ") {
127+
isKeywordWarning = false
125128
keywordError = String(localized: "Keyword cannot contain spaces")
126129
return
127130
}
@@ -133,6 +136,7 @@ struct FavoriteEditDialog: View {
133136
excludingFavoriteId: favorite?.id
134137
)
135138
if !available {
139+
isKeywordWarning = false
136140
keywordError = String(localized: "This keyword is already in use")
137141
} else {
138142
let sqlKeywords: Set<String> = [
@@ -143,10 +147,12 @@ struct FavoriteEditDialog: View {
143147
"true", "false", "case", "when", "then", "else", "end"
144148
]
145149
if sqlKeywords.contains(trimmed.lowercased()) {
150+
isKeywordWarning = true
146151
keywordError = String(
147-
localized: "Warning: this shadows the SQL keyword '\(trimmed.uppercased())'"
152+
localized: "Shadows the SQL keyword '\(trimmed.uppercased())'"
148153
)
149154
} else {
155+
isKeywordWarning = false
150156
keywordError = nil
151157
}
152158
}

TablePro/Views/Sidebar/FavoriteRowView.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ struct FavoriteRowView: View {
1414
Image(systemName: "star.fill")
1515
.font(.system(size: 10))
1616
.foregroundStyle(.yellow)
17+
.accessibilityHidden(true)
1718

1819
Text(favorite.name)
1920
.lineLimit(1)
@@ -30,7 +31,17 @@ struct FavoriteRowView: View {
3031
Capsule()
3132
.fill(Color(nsColor: .quaternaryLabelColor))
3233
)
34+
.accessibilityHidden(true)
3335
}
3436
}
37+
.accessibilityElement(children: .combine)
38+
.accessibilityLabel(accessibilityDescription)
39+
}
40+
41+
private var accessibilityDescription: String {
42+
if let keyword = favorite.keyword, !keyword.isEmpty {
43+
return "\(favorite.name), \(String(localized: "keyword: \(keyword)"))"
44+
}
45+
return favorite.name
3546
}
3647
}

TablePro/Views/Sidebar/FavoritesTabView.swift

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import SwiftUI
1010
/// Full-tab favorites view with folder hierarchy and bottom toolbar
1111
struct FavoritesTabView: View {
1212
@State private var viewModel: FavoritesSidebarViewModel
13+
@State private var folderToDelete: SQLFavoriteFolder?
14+
@State private var showDeleteFolderAlert = false
1315
let searchText: String
1416
private weak var coordinator: MainContentCoordinator?
1517

@@ -48,6 +50,18 @@ struct FavoritesTabView: View {
4850
folderId: viewModel.editingFolderId
4951
)
5052
}
53+
.alert(
54+
String(localized: "Delete Folder?"),
55+
isPresented: $showDeleteFolderAlert,
56+
presenting: folderToDelete
57+
) { folder in
58+
Button(String(localized: "Cancel"), role: .cancel) {}
59+
Button(String(localized: "Delete"), role: .destructive) {
60+
viewModel.deleteFolder(folder)
61+
}
62+
} message: { folder in
63+
Text("The folder \"\(folder.name)\" will be deleted. Items inside will be moved to the parent level.")
64+
}
5165
}
5266

5367
// MARK: - List
@@ -58,7 +72,11 @@ struct FavoritesTabView: View {
5872
FavoriteTreeItemRow(
5973
item: item,
6074
viewModel: viewModel,
61-
coordinator: coordinator
75+
coordinator: coordinator,
76+
onDeleteFolder: { folder in
77+
folderToDelete = folder
78+
showDeleteFolderAlert = true
79+
}
6280
)
6381
}
6482
}
@@ -82,6 +100,15 @@ struct FavoritesTabView: View {
82100
.font(.system(size: 11))
83101
.foregroundStyle(Color(nsColor: .tertiaryLabelColor))
84102
.multilineTextAlignment(.center)
103+
104+
Button {
105+
viewModel.createFavorite()
106+
} label: {
107+
Label(String(localized: "New Favorite"), systemImage: "plus")
108+
.font(.system(size: 12))
109+
}
110+
.buttonStyle(.borderless)
111+
.padding(.top, 4)
85112
}
86113
.frame(maxWidth: .infinity, maxHeight: .infinity)
87114
}
@@ -134,6 +161,7 @@ struct FavoriteTreeItemRow: View {
134161
let item: FavoriteTreeItem
135162
let viewModel: FavoritesSidebarViewModel
136163
weak var coordinator: MainContentCoordinator?
164+
var onDeleteFolder: ((SQLFavoriteFolder) -> Void)?
137165
@FocusState private var isRenameFocused: Bool
138166

139167
var body: some View {
@@ -167,7 +195,8 @@ struct FavoriteTreeItemRow: View {
167195
FavoriteTreeItemRow(
168196
item: child,
169197
viewModel: viewModel,
170-
coordinator: coordinator
198+
coordinator: coordinator,
199+
onDeleteFolder: onDeleteFolder
171200
)
172201
}
173202
} label: {
@@ -198,7 +227,8 @@ struct FavoriteTreeItemRow: View {
198227
.contextMenu {
199228
FolderContextMenu(
200229
folder: folder,
201-
viewModel: viewModel
230+
viewModel: viewModel,
231+
onDelete: onDeleteFolder ?? { _ in }
202232
)
203233
}
204234
}
@@ -215,7 +245,7 @@ private struct FavoriteItemContextMenu: View {
215245
weak var coordinator: MainContentCoordinator?
216246

217247
var body: some View {
218-
Button("Edit...") {
248+
Button(String(localized: "Edit...")) {
219249
viewModel.editFavorite(favorite)
220250
}
221251

@@ -251,24 +281,25 @@ private struct FavoriteItemContextMenu: View {
251281
private struct FolderContextMenu: View {
252282
let folder: SQLFavoriteFolder
253283
let viewModel: FavoritesSidebarViewModel
284+
var onDelete: (SQLFavoriteFolder) -> Void
254285

255286
var body: some View {
256-
Button("Rename") {
287+
Button(String(localized: "Rename")) {
257288
viewModel.startRenameFolder(folder)
258289
}
259290

260-
Button("New Favorite...") {
291+
Button(String(localized: "New Favorite...")) {
261292
viewModel.createFavorite(folderId: folder.id)
262293
}
263294

264-
Button("New Subfolder") {
295+
Button(String(localized: "New Subfolder")) {
265296
viewModel.createFolder(parentId: folder.id)
266297
}
267298

268299
Divider()
269300

270301
Button(role: .destructive) {
271-
viewModel.deleteFolder(folder)
302+
onDelete(folder)
272303
} label: {
273304
Label(String(localized: "Delete Folder"), systemImage: "trash")
274305
}

0 commit comments

Comments
 (0)