Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- ER diagram with interactive layout, crow's foot notation, and PNG export (#186)
- Space key toggles FK preview popover (#648)
- Connection drag-to-reorder in iOS app with iCloud sync (#652)

### Fixed

Expand Down
10 changes: 10 additions & 0 deletions TableProMobile/TableProMobile/AppState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,16 @@ final class AppState {
didSet { UserDefaults.standard.set(hasCompletedOnboarding, forKey: "com.TablePro.hasCompletedOnboarding") }
}

func reorderConnections(_ reordered: [DatabaseConnection]) {
connections = reordered
storage.save(connections)
updateWidgetData()
for connection in reordered {
syncCoordinator.markDirty(connection.id)
}
syncCoordinator.scheduleSyncAfterChange()
}

func removeConnection(_ connection: DatabaseConnection) {
connections.removeAll { $0.id == connection.id }
try? connectionManager.deletePassword(for: connection.id)
Expand Down
58 changes: 57 additions & 1 deletion TableProMobile/TableProMobile/Views/ConnectionListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,17 @@ struct ConnectionListView: View {
@State private var showingTagManagement = false
@State private var filterTagId: UUID?
@State private var groupByGroup = false
@State private var editMode: EditMode = .inactive

private var displayedConnections: [DatabaseConnection] {
var result = appState.connections
if let filterTagId {
result = result.filter { $0.tagId == filterTagId }
}
return result.sorted { $0.name.localizedCaseInsensitiveCompare($1.name) == .orderedAscending }
return result.sorted {
if $0.sortOrder != $1.sortOrder { return $0.sortOrder < $1.sortOrder }
return $0.name.localizedCaseInsensitiveCompare($1.name) == .orderedAscending
}
}

private var isSyncing: Bool {
Expand All @@ -42,6 +46,11 @@ struct ConnectionListView: View {
.toolbar {
ToolbarItemGroup(placement: .topBarTrailing) {
filterMenu
if filterTagId == nil && !appState.connections.isEmpty {
Button(editMode == .active ? "Done" : "Edit") {
editMode = editMode == .active ? .inactive : .active
}
}
Button {
showingAddConnection = true
} label: {
Expand Down Expand Up @@ -71,6 +80,12 @@ struct ConnectionListView: View {
.onChange(of: appState.pendingConnectionId) { _, newId in
navigateToPendingConnection(newId)
}
.onChange(of: filterTagId) {
editMode = .inactive
}
.onChange(of: groupByGroup) {
editMode = .inactive
}
.onAppear {
navigateToPendingConnection(appState.pendingConnectionId)
}
Expand Down Expand Up @@ -132,6 +147,14 @@ struct ConnectionListView: View {
ForEach(displayedConnections) { connection in
connectionRow(connection)
}
.onMove { source, destination in
var items = displayedConnections
items.move(fromOffsets: source, toOffset: destination)
for index in items.indices {
items[index].sortOrder = index
}
appState.reorderConnections(items)
}
}
}
.listStyle(.insetGrouped)
Expand All @@ -144,6 +167,7 @@ struct ConnectionListView: View {
)
}
}
.environment(\.editMode, $editMode)
.refreshable {
await appState.syncCoordinator.sync(
localConnections: appState.connections,
Expand Down Expand Up @@ -219,6 +243,22 @@ struct ConnectionListView: View {
ForEach(groupConnections) { connection in
connectionRow(connection)
}
.onMove { source, destination in
var items = groupConnections
items.move(fromOffsets: source, toOffset: destination)
var all = appState.connections
for (i, item) in items.enumerated() {
if let idx = all.firstIndex(where: { $0.id == item.id }) {
all[idx].sortOrder = i
}
}
all.sort {
if $0.sortOrder != $1.sortOrder { return $0.sortOrder < $1.sortOrder }
return $0.name.localizedCaseInsensitiveCompare($1.name) == .orderedAscending
}
for index in all.indices { all[index].sortOrder = index }
appState.reorderConnections(all)
}
} header: {
HStack(spacing: 6) {
if group.color != .none {
Expand All @@ -241,6 +281,22 @@ struct ConnectionListView: View {
ForEach(ungrouped) { connection in
connectionRow(connection)
}
.onMove { source, destination in
var items = ungrouped
items.move(fromOffsets: source, toOffset: destination)
var all = appState.connections
for (i, item) in items.enumerated() {
if let idx = all.firstIndex(where: { $0.id == item.id }) {
all[idx].sortOrder = i
}
}
all.sort {
if $0.sortOrder != $1.sortOrder { return $0.sortOrder < $1.sortOrder }
return $0.name.localizedCaseInsensitiveCompare($1.name) == .orderedAscending
}
for index in all.indices { all[index].sortOrder = index }
appState.reorderConnections(all)
}
}
}
}
Expand Down
Loading