commit e82df68743c94eb5bd70cf004b9d1f900233e89c
parent ebe07dff1a78256cf65cd8099458bfb6661f8fa4
Author: Michael Camilleri <[email protected]>
Date: Thu, 19 Feb 2026 05:18:39 +0900
Use enum-based drag state
Co-Authored-By: Codex GPT 5.3 <[email protected]>
Diffstat:
4 files changed, 52 insertions(+), 28 deletions(-)
diff --git a/Listless/Extensions/TaskListView+Logic.swift b/Listless/Extensions/TaskListView+Logic.swift
@@ -10,7 +10,7 @@ extension TaskListView {
}
var displayActiveTasks: [TaskItem] {
- guard let visualOrder = visualOrder else {
+ guard let visualOrder else {
return activeTasks
}
@@ -35,6 +35,20 @@ extension TaskListView {
return nil
}
+ var draggedTaskID: UUID? {
+ if case .dragging(let id, _) = dragState {
+ return id
+ }
+ return nil
+ }
+
+ var visualOrder: [UUID]? {
+ if case .dragging(_, let order) = dragState {
+ return order
+ }
+ return nil
+ }
+
func presentStoreError(_ error: Error) {
syncMonitor.ingest(error: error)
}
@@ -47,8 +61,7 @@ extension TaskListView {
// MARK: - Task Creation
func createNewTaskAtTop() -> UUID {
- draggedTaskID = nil
- visualOrder = nil
+ clearDragState()
do {
let task = try store.createTask(title: "", atBeginning: true)
pendingFocus = .task(task.id)
@@ -62,8 +75,7 @@ extension TaskListView {
}
func createNewTask() {
- draggedTaskID = nil
- visualOrder = nil
+ clearDragState()
do {
let task = try store.createTask(title: "")
pendingFocus = .task(task.id)
@@ -332,9 +344,8 @@ extension TaskListView {
// MARK: - Drag and Drop
func startDrag(taskID: UUID) {
- guard draggedTaskID == nil else { return }
- draggedTaskID = taskID
- visualOrder = activeTasks.map(\.id)
+ guard case .idle = dragState else { return }
+ dragState = .dragging(id: taskID, order: activeTasks.map(\.id))
didStartDrag()
}
@@ -348,9 +359,9 @@ extension TaskListView {
newOrder.insert(draggedID, at: targetIndex)
}
- if newOrder != visualOrder {
+ if newOrder != order {
withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) {
- visualOrder = newOrder
+ setDragOrder(newOrder)
}
}
}
@@ -365,9 +376,9 @@ extension TaskListView {
newOrder.insert(draggedID, at: targetIndex + 1)
}
- if newOrder != visualOrder {
+ if newOrder != order {
withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) {
- visualOrder = newOrder
+ setDragOrder(newOrder)
}
}
}
@@ -396,9 +407,9 @@ extension TaskListView {
var newOrder = order.filter { $0 != draggedID }
newOrder.append(draggedID)
- if newOrder != visualOrder {
+ if newOrder != order {
withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) {
- visualOrder = newOrder
+ setDragOrder(newOrder)
}
}
}
@@ -409,8 +420,7 @@ extension TaskListView {
let order = visualOrder,
let finalIndex = order.firstIndex(of: droppedUUID)
else {
- draggedTaskID = nil
- visualOrder = nil
+ clearDragState()
return false
}
@@ -419,9 +429,17 @@ extension TaskListView {
} catch {
presentStoreError(error)
}
- draggedTaskID = nil
- visualOrder = nil
+ clearDragState()
return true
}
+
+ func setDragOrder(_ order: [UUID]) {
+ guard case .dragging(let id, _) = dragState else { return }
+ dragState = .dragging(id: id, order: order)
+ }
+
+ func clearDragState() {
+ dragState = .idle
+ }
}
diff --git a/ListlessMac/Views/TaskListView.swift b/ListlessMac/Views/TaskListView.swift
@@ -6,6 +6,11 @@ struct TaskListView: View {
case scrollView
}
+ enum DragState: Equatable {
+ case idle
+ case dragging(id: UUID, order: [UUID])
+ }
+
@Environment(\.undoManager) var undoManager
@Environment(\.managedObjectContext) var managedObjectContext
@@ -22,8 +27,7 @@ struct TaskListView: View {
@FocusState var focusedField: FocusField?
@State var selectedTaskID: UUID?
@State private var refreshID = UUID()
- @State var draggedTaskID: UUID?
- @State var visualOrder: [UUID]?
+ @State var dragState: DragState = .idle
@State var pendingFocus: FocusField?
@State var pullOffset: CGFloat = 0
diff --git a/ListlessiOS/Extensions/TaskListView+Drag.swift b/ListlessiOS/Extensions/TaskListView+Drag.swift
@@ -13,7 +13,7 @@ extension TaskListView {
if currentIndex < order.count - 1 && point.y > draggedFrame.maxY + threshold {
order.swapAt(currentIndex, currentIndex + 1)
withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) {
- visualOrder = order
+ setDragOrder(order)
}
return
}
@@ -22,7 +22,7 @@ extension TaskListView {
if currentIndex > 0 && point.y < draggedFrame.minY - threshold {
order.swapAt(currentIndex, currentIndex - 1)
withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) {
- visualOrder = order
+ setDragOrder(order)
}
}
}
@@ -31,8 +31,7 @@ extension TaskListView {
guard let draggedID = draggedTaskID,
let order = visualOrder,
let finalIndex = order.firstIndex(of: draggedID) else {
- draggedTaskID = nil
- visualOrder = nil
+ clearDragState()
isDragging = false
return
}
@@ -41,8 +40,7 @@ extension TaskListView {
} catch {
presentStoreError(error)
}
- draggedTaskID = nil
- visualOrder = nil
+ clearDragState()
isDragging = false
}
}
diff --git a/ListlessiOS/Views/TaskListView.swift b/ListlessiOS/Views/TaskListView.swift
@@ -6,6 +6,11 @@ struct TaskListView: View {
case scrollView
}
+ enum DragState: Equatable {
+ case idle
+ case dragging(id: UUID, order: [UUID])
+ }
+
@Environment(\.undoManager) var undoManager
@Environment(\.managedObjectContext) var managedObjectContext
@@ -22,8 +27,7 @@ struct TaskListView: View {
@FocusState var focusedField: FocusField?
@State var selectedTaskID: UUID?
@State private var refreshID = UUID()
- @State var draggedTaskID: UUID?
- @State var visualOrder: [UUID]?
+ @State var dragState: DragState = .idle
@State var pendingFocus: FocusField?
@State var pullToCreate = PullToCreateState()
@State var pullUpOffset: CGFloat = 0