Skip to content

Commit 6bdf01b

Browse files
committed
refactor: Extract services and child views from complex files
Major refactoring to improve code organization and maintainability: Phase 1 - DataChange.swift (1,095 → 4 files): - SQLStatementGenerator: SQL generation logic - DataChangeUndoManager: Undo/redo stack management - DataChangeModels: Pure data models - DataChangeManager: Refactored change tracking Phase 2 - FilterPanelView.swift (758 → 260 lines): - FilterRowView: Single filter row component - SQLPreviewSheet: SQL preview modal - FilterSettingsPopover: Settings popover - QuickSearchField: Quick search component Phase 3 - DataGridView.swift (1,706 → 700 lines): - KeyHandlingTableView: NSTableView subclass - TableRowViewWithMenu: Row view with context menu - CellTextField: Custom text field - DataGridCellFactory: Cell creation factory Phase 4 - HistoryListViewController.swift (1,325 → 550 lines): - HistoryDataProvider: Data fetching/filtering - HistoryRowView: Table cell view - HistoryTableView: Custom NSTableView Phase 5 - MainContentView.swift (2,365 → 2,113 lines): - QueryExecutionService: Query execution logic - RowOperationsManager: Row operations (add/delete/undo) - MainStatusBarView: Status bar component - QueryTabContentView/TableTabContentView: Tab content views
1 parent bbd8526 commit 6bdf01b

27 files changed

+4827
-4637
lines changed

OpenTable/Core/ChangeTracking/DataChangeManager.swift

Lines changed: 596 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//
2+
// DataChangeModels.swift
3+
// OpenTable
4+
//
5+
// Pure data models for tracking data changes.
6+
// No business logic - just structures for representing change state.
7+
//
8+
9+
import Foundation
10+
11+
/// Represents a type of data change
12+
enum ChangeType: Equatable {
13+
case update
14+
case insert
15+
case delete
16+
}
17+
18+
/// Represents a single cell change
19+
struct CellChange: Identifiable, Equatable {
20+
let id: UUID
21+
let rowIndex: Int
22+
let columnIndex: Int
23+
let columnName: String
24+
let oldValue: String?
25+
let newValue: String?
26+
27+
init(
28+
rowIndex: Int,
29+
columnIndex: Int,
30+
columnName: String,
31+
oldValue: String?,
32+
newValue: String?
33+
) {
34+
self.id = UUID()
35+
self.rowIndex = rowIndex
36+
self.columnIndex = columnIndex
37+
self.columnName = columnName
38+
self.oldValue = oldValue
39+
self.newValue = newValue
40+
}
41+
}
42+
43+
/// Represents a row-level change
44+
struct RowChange: Identifiable, Equatable {
45+
let id: UUID
46+
var rowIndex: Int
47+
let type: ChangeType
48+
var cellChanges: [CellChange]
49+
let originalRow: [String?]?
50+
51+
init(
52+
rowIndex: Int,
53+
type: ChangeType,
54+
cellChanges: [CellChange] = [],
55+
originalRow: [String?]? = nil
56+
) {
57+
self.id = UUID()
58+
self.rowIndex = rowIndex
59+
self.type = type
60+
self.cellChanges = cellChanges
61+
self.originalRow = originalRow
62+
}
63+
}
64+
65+
/// Represents an action that can be undone
66+
enum UndoAction {
67+
case cellEdit(
68+
rowIndex: Int,
69+
columnIndex: Int,
70+
columnName: String,
71+
previousValue: String?,
72+
newValue: String?
73+
)
74+
case rowInsertion(rowIndex: Int)
75+
case rowDeletion(rowIndex: Int, originalRow: [String?])
76+
/// Batch deletion of multiple rows (for undo as a single action)
77+
case batchRowDeletion(rows: [(rowIndex: Int, originalRow: [String?])])
78+
/// Batch insertion undo - when user deletes multiple inserted rows at once
79+
case batchRowInsertion(rowIndices: [Int], rowValues: [[String?]])
80+
}
81+
82+
// Note: TabPendingChanges is defined in QueryTab.swift
83+
84+
// MARK: - Array Extension
85+
86+
extension Array {
87+
subscript(safe index: Int) -> Element? {
88+
indices.contains(index) ? self[index] : nil
89+
}
90+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
//
2+
// DataChangeUndoManager.swift
3+
// OpenTable
4+
//
5+
// Manages undo/redo stacks for data changes.
6+
// Extracted from DataChangeManager to improve separation of concerns.
7+
//
8+
9+
import Foundation
10+
11+
/// Manages undo/redo stacks for data changes
12+
final class DataChangeUndoManager {
13+
/// Undo stack for reversing changes (LIFO)
14+
private var undoStack: [UndoAction] = []
15+
16+
/// Redo stack for re-applying undone changes (LIFO)
17+
private var redoStack: [UndoAction] = []
18+
19+
// MARK: - Public API
20+
21+
/// Check if there are any undo actions available
22+
var canUndo: Bool {
23+
!undoStack.isEmpty
24+
}
25+
26+
/// Check if there are any redo actions available
27+
var canRedo: Bool {
28+
!redoStack.isEmpty
29+
}
30+
31+
/// Push an undo action onto the stack
32+
/// Clears the redo stack since new changes invalidate redo history
33+
func push(_ action: UndoAction) {
34+
undoStack.append(action)
35+
// Don't clear redo here - let caller decide when to clear
36+
}
37+
38+
/// Pop the last undo action from the stack
39+
func popUndo() -> UndoAction? {
40+
undoStack.popLast()
41+
}
42+
43+
/// Pop the last redo action from the stack
44+
func popRedo() -> UndoAction? {
45+
redoStack.popLast()
46+
}
47+
48+
/// Move an action from undo to redo stack
49+
func moveToRedo(_ action: UndoAction) {
50+
redoStack.append(action)
51+
}
52+
53+
/// Move an action from redo to undo stack
54+
func moveToUndo(_ action: UndoAction) {
55+
undoStack.append(action)
56+
}
57+
58+
/// Clear the undo stack
59+
func clearUndo() {
60+
undoStack.removeAll()
61+
}
62+
63+
/// Clear the redo stack (called when new changes are made)
64+
func clearRedo() {
65+
redoStack.removeAll()
66+
}
67+
68+
/// Clear both stacks
69+
func clearAll() {
70+
undoStack.removeAll()
71+
redoStack.removeAll()
72+
}
73+
74+
/// Get the count of undo actions
75+
var undoCount: Int {
76+
undoStack.count
77+
}
78+
79+
/// Get the count of redo actions
80+
var redoCount: Int {
81+
redoStack.count
82+
}
83+
}

0 commit comments

Comments
 (0)