commit f7fd505561ba27161bed9b87b0021ec7ae1199c7
parent b6947ab9c1df71ddd081901fa2f227f32cbd615a
Author: Michael Camilleri <[email protected]>
Date: Fri, 13 Mar 2026 08:20:22 +0900
Remove unnecessary shims
Diffstat:
10 files changed, 106 insertions(+), 183 deletions(-)
diff --git a/Listless/Extensions/TaskListView+Logic.swift b/Listless/Extensions/TaskListView+Logic.swift
@@ -98,17 +98,17 @@ extension TaskListViewProtocol {
let taskID = draftTaskID(for: placement)
draftTaskTitle = ""
draftTaskPlacement = placement
- pendingFocus = .task(taskID)
+ fState.pendingFocus = .task(taskID)
focusedField = .task(taskID)
- selectedTaskID = taskID
+ fState.selectedTaskID = taskID
}
func beginDraftTaskEditing(_ placement: DraftTaskPlacement) {
guard draftTaskPlacement == placement else { return }
let taskID = draftTaskID(for: placement)
- selectedTaskID = taskID
- if case .task(let id) = pendingFocus, id == taskID {
- pendingFocus = nil
+ fState.selectedTaskID = taskID
+ if case .task(let id) = fState.pendingFocus, id == taskID {
+ fState.pendingFocus = nil
}
}
@@ -117,17 +117,17 @@ extension TaskListViewProtocol {
let taskID = draftTaskID(for: placement)
let title = draftTaskTitle.trimmingCharacters(in: .whitespacesAndNewlines)
- // Clear pendingFocus before clearDraftTaskUI so that the iOS
+ // Clear fState.pendingFocus before clearDraftTaskUI so that the iOS
// onChange(of: focusedFieldBinding) nil-redirect doesn't re-focus
- // the draft row via a stale pendingFocus value.
- if case .task(let id) = pendingFocus, id == taskID {
- pendingFocus = nil
+ // the draft row via a stale fState.pendingFocus value.
+ if case .task(let id) = fState.pendingFocus, id == taskID {
+ fState.pendingFocus = nil
}
clearDraftTaskUI(at: placement, hasTitle: !title.isEmpty)
- if selectedTaskID == taskID {
- selectedTaskID = nil
+ if fState.selectedTaskID == taskID {
+ fState.selectedTaskID = nil
}
guard !title.isEmpty else { return }
@@ -141,7 +141,7 @@ extension TaskListViewProtocol {
}
try store.save()
if placement == .append {
- selectedTaskID = task.id
+ fState.selectedTaskID = task.id
}
} catch {
presentStoreError(error)
@@ -158,7 +158,7 @@ extension TaskListViewProtocol {
let sortOrder = try sortOrderAfter(taskID: afterTaskID)
let newTask = try store.createTask(title: title, sortOrder: sortOrder)
try store.save()
- selectedTaskID = newTask.id
+ fState.selectedTaskID = newTask.id
focusedField = .scrollView
} catch {
presentStoreError(error)
@@ -191,12 +191,12 @@ extension TaskListViewProtocol {
func handleBackgroundTap() {
let isTaskFocused = if case .task = focusedField { true } else { false }
- if isTaskFocused || selectedTaskID != nil {
- pendingFocus = nil
+ if isTaskFocused || fState.selectedTaskID != nil {
+ fState.pendingFocus = nil
if draftTaskPlacement != nil {
commitDraftTask()
}
- selectedTaskID = nil
+ fState.selectedTaskID = nil
focusedField = nil
} else {
revealDraftTask(at: .append)
@@ -224,7 +224,7 @@ extension TaskListViewProtocol {
}
private func deleteIfEmpty(taskID: UUID) {
- if case .task(let pendingTaskID) = pendingFocus, pendingTaskID == taskID {
+ if case .task(let pendingTaskID) = fState.pendingFocus, pendingTaskID == taskID {
return
}
@@ -272,15 +272,15 @@ extension TaskListViewProtocol {
}
func selectTask(_ taskID: UUID) {
- selectedTaskID = taskID
+ fState.selectedTaskID = taskID
}
func deleteTask(_ task: TaskItem) {
let taskID = task.id
do {
try store.delete(taskID: taskID)
- if selectedTaskID == taskID {
- selectedTaskID = nil
+ if fState.selectedTaskID == taskID {
+ fState.selectedTaskID = nil
}
} catch {
presentStoreError(error)
@@ -304,8 +304,8 @@ extension TaskListViewProtocol {
return .ignored
}
- guard let currentID = selectedTaskID else {
- selectedTaskID = activeTasks.last?.id
+ guard let currentID = fState.selectedTaskID else {
+ fState.selectedTaskID = activeTasks.last?.id
return .handled
}
@@ -315,7 +315,7 @@ extension TaskListViewProtocol {
}
if currentIndex > 0 {
- selectedTaskID = displayOrder[currentIndex - 1].id
+ fState.selectedTaskID = displayOrder[currentIndex - 1].id
}
return .handled
}
@@ -325,8 +325,8 @@ extension TaskListViewProtocol {
return .ignored
}
- guard let currentID = selectedTaskID else {
- selectedTaskID = activeTasks.first?.id ?? completedTasks.first?.id
+ guard let currentID = fState.selectedTaskID else {
+ fState.selectedTaskID = activeTasks.first?.id ?? completedTasks.first?.id
return .handled
}
@@ -336,14 +336,14 @@ extension TaskListViewProtocol {
}
if currentIndex < displayOrder.count - 1 {
- selectedTaskID = displayOrder[currentIndex + 1].id
+ fState.selectedTaskID = displayOrder[currentIndex + 1].id
}
return .handled
}
func toggleSelectedTask() -> KeyPress.Result {
guard focusedField == .scrollView else { return .ignored }
- guard let currentID = selectedTaskID else { return .handled }
+ guard let currentID = fState.selectedTaskID else { return .handled }
guard let task = allTasksInDisplayOrder.first(where: { $0.id == currentID }) else {
return .handled
}
@@ -353,7 +353,7 @@ extension TaskListViewProtocol {
func focusSelectedTask() -> KeyPress.Result {
guard focusedField == .scrollView else { return .ignored }
- guard let currentID = selectedTaskID else { return .handled }
+ guard let currentID = fState.selectedTaskID else { return .handled }
guard let task = allTasksInDisplayOrder.first(where: { $0.id == currentID }) else {
return .handled
}
@@ -366,7 +366,7 @@ extension TaskListViewProtocol {
guard focusedField == .scrollView else {
return .ignored
}
- guard let currentID = selectedTaskID else {
+ guard let currentID = fState.selectedTaskID else {
return .handled
}
guard let task = allTasksInDisplayOrder.first(where: { $0.id == currentID }) else {
@@ -378,7 +378,7 @@ extension TaskListViewProtocol {
func moveSelectedTaskUp() {
guard focusedField == .scrollView else { return }
- guard let currentID = selectedTaskID else { return }
+ guard let currentID = fState.selectedTaskID else { return }
guard let currentIndex = activeTasks.firstIndex(where: { $0.id == currentID }) else { return }
guard currentIndex > 0 else { return }
@@ -391,7 +391,7 @@ extension TaskListViewProtocol {
func moveSelectedTaskDown() {
guard focusedField == .scrollView else { return }
- guard let currentID = selectedTaskID else { return }
+ guard let currentID = fState.selectedTaskID else { return }
guard let currentIndex = activeTasks.firstIndex(where: { $0.id == currentID }) else { return }
guard currentIndex < activeTasks.count - 1 else { return }
@@ -404,7 +404,7 @@ extension TaskListViewProtocol {
func markSelectedTaskCompleted() {
guard focusedField == .scrollView else { return }
- guard let currentID = selectedTaskID else { return }
+ guard let currentID = fState.selectedTaskID else { return }
guard let task = allTasksInDisplayOrder.first(where: { $0.id == currentID }) else { return }
toggleCompletion(task)
}
@@ -416,9 +416,9 @@ extension TaskListViewProtocol {
}
func startEditing(_ taskID: UUID) {
- selectedTaskID = taskID
+ fState.selectedTaskID = taskID
focusedField = .task(taskID)
- pendingFocus = nil
+ fState.pendingFocus = nil
}
func endEditing(_ taskID: UUID, shouldCreateNewTask: Bool) {
@@ -437,7 +437,7 @@ extension TaskListViewProtocol {
let willBeDeleted = shouldDeleteIfEmpty(taskID: taskID)
if willBeDeleted {
- selectedTaskID = nil
+ fState.selectedTaskID = nil
deleteIfEmpty(taskID: taskID)
} else if wasLastActiveTask && shouldCreateNewTask {
revealDraftTask(at: .append)
diff --git a/Listless/Helpers/TaskListTypes.swift b/Listless/Helpers/TaskListTypes.swift
@@ -15,5 +15,11 @@ enum DraftTaskPlacement: Equatable {
case append
}
+struct FocusStateData {
+ var focusedField: FocusField?
+ var selectedTaskID: UUID?
+ var pendingFocus: FocusField?
+}
+
let draftPrependRowID = UUID()
let draftAppendRowID = UUID()
diff --git a/Listless/Helpers/TaskListViewProtocol.swift b/Listless/Helpers/TaskListViewProtocol.swift
@@ -8,8 +8,7 @@ protocol TaskListViewProtocol {
var syncMonitor: CloudKitSyncMonitor { get }
var managedObjectContext: NSManagedObjectContext { get }
var focusedField: FocusField? { get nonmutating set }
- var selectedTaskID: UUID? { get nonmutating set }
- var pendingFocus: FocusField? { get nonmutating set }
+ var fState: FocusStateData { get nonmutating set }
var dragState: DragState { get nonmutating set }
var draftTaskPlacement: DraftTaskPlacement? { get nonmutating set }
var draftTaskTitle: String { get nonmutating set }
diff --git a/ListlessMac/Extensions/TaskListView+Toolbar.swift b/ListlessMac/Extensions/TaskListView+Toolbar.swift
@@ -33,7 +33,7 @@ extension TaskListView {
.help("Create a new item")
Button {
- if let currentID = selectedTaskID,
+ if let currentID = fState.selectedTaskID,
let task = allTasksInDisplayOrder.first(where: { $0.id == currentID })
{
deleteTask(task)
@@ -41,7 +41,7 @@ extension TaskListView {
} label: {
Label("Delete", systemImage: "trash")
}
- .disabled(selectedTaskID == nil || focusedField != .scrollView)
+ .disabled(fState.selectedTaskID == nil || focusedField != .scrollView)
.help("Delete selected item")
Divider()
diff --git a/ListlessMac/Views/TaskListView.swift b/ListlessMac/Views/TaskListView.swift
@@ -2,12 +2,6 @@ import SwiftUI
import UniformTypeIdentifiers
struct TaskListView: View, TaskListViewProtocol {
- struct FocusStateData {
- var focusedField: FocusField?
- var selectedTaskID: UUID?
- var pendingFocus: FocusField?
- }
-
struct InteractionStateData {
var dragState: DragState = .idle
var liftedTaskID: UUID?
@@ -15,10 +9,6 @@ struct TaskListView: View, TaskListViewProtocol {
var draftTaskTitle: String = ""
}
- struct TaskStateData {
- var refreshID = UUID()
- }
-
@Environment(\.undoManager) var undoManager
@Environment(\.managedObjectContext) var managedObjectContext
@@ -31,9 +21,8 @@ struct TaskListView: View, TaskListViewProtocol {
)
var tasks: FetchedResults<TaskItem>
@FocusState private var focusedFieldBinding: FocusField?
- @State private var fState = FocusStateData()
+ @State var fState = FocusStateData()
@State private var iState = InteractionStateData()
- @State private var tState = TaskStateData()
var focusedField: FocusField? {
get { fState.focusedField }
@@ -43,26 +32,11 @@ struct TaskListView: View, TaskListViewProtocol {
}
}
- var selectedTaskID: UUID? {
- get { fState.selectedTaskID }
- nonmutating set { fState.selectedTaskID = newValue }
- }
-
- var pendingFocus: FocusField? {
- get { fState.pendingFocus }
- nonmutating set { fState.pendingFocus = newValue }
- }
-
var dragState: DragState {
get { iState.dragState }
nonmutating set { iState.dragState = newValue }
}
- var liftedTaskID: UUID? {
- get { iState.liftedTaskID }
- nonmutating set { iState.liftedTaskID = newValue }
- }
-
var draftTaskPlacement: DraftTaskPlacement? {
get { iState.draftTaskPlacement }
nonmutating set { iState.draftTaskPlacement = newValue }
@@ -73,30 +47,25 @@ struct TaskListView: View, TaskListViewProtocol {
nonmutating set { iState.draftTaskTitle = newValue }
}
- var refreshID: UUID {
- get { tState.refreshID }
- nonmutating set { tState.refreshID = newValue }
- }
-
var vStackSpacing: CGFloat { 0 }
var isCompletelyEmpty: Bool { activeTasks.isEmpty && completedTasks.isEmpty }
var selectedIndex: Int? {
- guard let currentID = selectedTaskID else { return nil }
+ guard let currentID = fState.selectedTaskID else { return nil }
return activeTasks.firstIndex(where: { $0.id == currentID })
}
var canDeleteSelectionFromList: Bool {
- selectedTaskID != nil && focusedField == .scrollView
+ fState.selectedTaskID != nil && focusedField == .scrollView
}
var canMarkSelectionCompleted: Bool {
guard focusedField == .scrollView else { return false }
- guard let currentID = selectedTaskID else { return false }
+ guard let currentID = fState.selectedTaskID else { return false }
return allTasksInDisplayOrder.contains(where: { $0.id == currentID })
}
var markCompletedMenuTitle: String {
- completedTasks.contains(where: { $0.id == selectedTaskID })
+ completedTasks.contains(where: { $0.id == fState.selectedTaskID })
? "Mark as Incomplete" : "Mark as Complete"
}
@@ -122,7 +91,7 @@ struct TaskListView: View, TaskListViewProtocol {
var menuCoordinatorTrigger: MenuState {
MenuState(
- selectedTaskID: selectedTaskID,
+ selectedTaskID: fState.selectedTaskID,
isScrollViewFocused: focusedField == .scrollView,
activeTaskCount: activeTasks.count,
completedTaskCount: completedTasks.count,
@@ -134,14 +103,14 @@ struct TaskListView: View, TaskListViewProtocol {
let coord = menuCoordinator
coord.newTask = { createNewTask(); focusedField = nil }
coord.copySelectedTask = {
- guard let taskID = selectedTaskID,
+ guard let taskID = fState.selectedTaskID,
let task = tasks.first(where: { $0.id == taskID }) else { return }
let pasteboard = NSPasteboard.general
pasteboard.clearContents()
pasteboard.setString(task.title, forType: .string)
}
coord.cutSelectedTask = {
- guard let taskID = selectedTaskID,
+ guard let taskID = fState.selectedTaskID,
let task = tasks.first(where: { $0.id == taskID }) else { return }
let pasteboard = NSPasteboard.general
pasteboard.clearContents()
@@ -149,7 +118,7 @@ struct TaskListView: View, TaskListViewProtocol {
deleteTask(task)
}
coord.pasteAfterSelectedTask = {
- guard let taskID = selectedTaskID,
+ guard let taskID = fState.selectedTaskID,
let string = NSPasteboard.general.string(forType: .string) else { return }
createTask(title: string, afterTaskID: taskID)
}
@@ -159,8 +128,8 @@ struct TaskListView: View, TaskListViewProtocol {
coord.markSelectedTaskCompleted = { markSelectedTaskCompleted() }
coord.clearCompletedTasks = { clearCompletedTasks() }
let inNavMode = focusedField == .scrollView
- coord.canCopySelectedTask = selectedTaskID != nil && inNavMode
- coord.canCutSelectedTask = selectedTaskID != nil && inNavMode
+ coord.canCopySelectedTask = fState.selectedTaskID != nil && inNavMode
+ coord.canCutSelectedTask = fState.selectedTaskID != nil && inNavMode
coord.canPasteAfterSelectedTask = selectedIndex != nil && inNavMode
coord.canDeleteSelectedTask = canDeleteSelectionFromList
coord.canMoveSelectedTaskUp = canMoveSelectionUp
@@ -177,7 +146,7 @@ struct TaskListView: View, TaskListViewProtocol {
}
func isRowLifted(_ taskID: UUID) -> Bool {
- liftedTaskID == taskID || draggedTaskID == taskID
+ iState.liftedTaskID == taskID || draggedTaskID == taskID
}
func clearDraftTaskUI(at placement: DraftTaskPlacement, hasTitle _: Bool) {
@@ -185,8 +154,8 @@ struct TaskListView: View, TaskListViewProtocol {
draftTaskPlacement = nil
}
draftTaskTitle = ""
- if selectedTaskID == draftTaskID(for: placement) {
- selectedTaskID = nil
+ if fState.selectedTaskID == draftTaskID(for: placement) {
+ fState.selectedTaskID = nil
}
focusedField = nil
}
@@ -203,7 +172,7 @@ struct TaskListView: View, TaskListViewProtocol {
taskID: taskID,
index: index,
totalTasks: displayActiveTasks.count,
- isSelected: selectedTaskID == taskID,
+ isSelected: fState.selectedTaskID == taskID,
focusedField: $focusedFieldBinding,
onToggle: { toggleCompletion($0) },
onTitleChange: { updateTitle($0, $1) },
@@ -217,11 +186,11 @@ struct TaskListView: View, TaskListViewProtocol {
isActive: !task.isCompleted,
taskID: task.id,
onDragStart: {
- liftedTaskID = nil
+ iState.liftedTaskID = nil
startDrag(taskID: task.id)
},
- onLift: { liftedTaskID = task.id },
- onLiftEnd: { if liftedTaskID == task.id { liftedTaskID = nil } }
+ onLift: { iState.liftedTaskID = task.id },
+ onLiftEnd: { if iState.liftedTaskID == task.id { iState.liftedTaskID = nil } }
)
.scaleEffect(isRowLifted(taskID) ? 1.03 : 1.0)
.shadow(
@@ -279,7 +248,7 @@ struct TaskListView: View, TaskListViewProtocol {
let accentColor = cachedTaskColor(
forIndex: index, total: total
)
- let isSelected = selectedTaskID == draftAppendRowID
+ let isSelected = fState.selectedTaskID == draftAppendRowID
HStack(alignment: .firstTextBaseline, spacing: 12) {
Image(systemName: "circle")
.foregroundStyle(.primary)
@@ -341,7 +310,7 @@ struct TaskListView: View, TaskListViewProtocol {
TaskRowView(
task: task,
taskID: taskID,
- isSelected: selectedTaskID == taskID,
+ isSelected: fState.selectedTaskID == taskID,
focusedField: $focusedFieldBinding,
onToggle: { toggleCompletion($0) },
onTitleChange: { updateTitle($0, $1) },
@@ -399,10 +368,10 @@ struct TaskListView: View, TaskListViewProtocol {
handleFocusChange(from: oldValue, to: newValue)
if newValue == nil {
- if let pending = pendingFocus {
+ if let pending = fState.pendingFocus {
focusedFieldBinding = pending
fState.focusedField = pending
- pendingFocus = nil
+ fState.pendingFocus = nil
} else {
focusedFieldBinding = .scrollView
fState.focusedField = .scrollView
diff --git a/ListlessiOS/Extensions/TaskListView+Drag.swift b/ListlessiOS/Extensions/TaskListView+Drag.swift
@@ -5,7 +5,7 @@ extension TaskListView {
guard let draggedID = draggedTaskID,
var order = visualOrder,
let currentIndex = order.firstIndex(of: draggedID),
- let draggedFrame = rowFrames[draggedID] else { return }
+ let draggedFrame = iState.rowFrames[draggedID] else { return }
let threshold = draggedFrame.height * 0.2
@@ -32,17 +32,17 @@ extension TaskListView {
let order = visualOrder,
let finalIndex = order.firstIndex(of: draggedID) else {
clearDragState()
- isDragging = false
+ iState.isDragging = false
return
}
do {
try store.moveTask(taskID: draggedID, toIndex: finalIndex)
clearDragState()
- isDragging = false
+ iState.isDragging = false
} catch {
withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) {
clearDragState()
- isDragging = false
+ iState.isDragging = false
}
presentStoreError(error)
}
diff --git a/ListlessiOS/Extensions/TaskListView+NavigationHeader.swift b/ListlessiOS/Extensions/TaskListView+NavigationHeader.swift
@@ -33,7 +33,7 @@ extension TaskListView {
.padding(.bottom, 8)
.contentShape(Rectangle())
.onTapGesture {
- selectedTaskID = nil
+ fState.selectedTaskID = nil
focusedField = nil
}
}
diff --git a/ListlessiOS/Extensions/TaskListView+PullToClear.swift b/ListlessiOS/Extensions/TaskListView+PullToClear.swift
@@ -2,8 +2,8 @@ import SwiftUI
extension TaskListView {
@ViewBuilder var pullToClearIndicatorRow: some View {
- if pullUpOffset > 0 && !completedTasks.isEmpty {
- PullToClearIndicator(pullOffset: pullUpOffset)
+ if iState.pullUpOffset > 0 && !completedTasks.isEmpty {
+ PullToClearIndicator(pullOffset: iState.pullUpOffset)
}
}
}
diff --git a/ListlessiOS/Extensions/TaskListView+Undo.swift b/ListlessiOS/Extensions/TaskListView+Undo.swift
@@ -11,7 +11,7 @@ extension TaskListView {
guard focusedField == .scrollView else {
return .ignored
}
- guard let currentID = selectedTaskID else {
+ guard let currentID = fState.selectedTaskID else {
return .handled
}
guard let task = allTasksInDisplayOrder.first(where: { $0.id == currentID }) else {
@@ -40,7 +40,7 @@ extension TaskListView {
func showUndoToast(message: String) {
withAnimation {
- undoToast = UndoToastData(id: UUID(), message: message)
+ iState.undoToast = UndoToastData(id: UUID(), message: message)
}
}
@@ -56,7 +56,7 @@ extension TaskListView {
func dismissUndoToast() {
withAnimation {
- undoToast = nil
+ iState.undoToast = nil
}
}
}
diff --git a/ListlessiOS/Views/TaskListView.swift b/ListlessiOS/Views/TaskListView.swift
@@ -2,12 +2,6 @@ import SwiftUI
import UIKit
struct TaskListView: View, TaskListViewProtocol {
- struct FocusStateData {
- var focusedField: FocusField?
- var selectedTaskID: UUID?
- var pendingFocus: FocusField?
- }
-
struct InteractionStateData {
var dragState: DragState = .idle
var pullToCreate = PullToCreateState()
@@ -22,10 +16,6 @@ struct TaskListView: View, TaskListViewProtocol {
var draftTaskTitle: String = ""
}
- struct TaskStateData {
- var refreshID = UUID()
- }
-
@AppStorage("headingText") var headingText = "Items"
@Environment(\.undoManager) var undoManager
@Environment(\.managedObjectContext) var managedObjectContext
@@ -38,9 +28,8 @@ struct TaskListView: View, TaskListViewProtocol {
)
var tasks: FetchedResults<TaskItem>
@FocusState private var focusedFieldBinding: FocusField?
- @State private var fState = FocusStateData()
- @State private var iState = InteractionStateData()
- @State private var tState = TaskStateData()
+ @State var fState = FocusStateData()
+ @State var iState = InteractionStateData()
var focusedField: FocusField? {
get { fState.focusedField }
@@ -50,51 +39,11 @@ struct TaskListView: View, TaskListViewProtocol {
}
}
- var selectedTaskID: UUID? {
- get { fState.selectedTaskID }
- nonmutating set { fState.selectedTaskID = newValue }
- }
-
- var pendingFocus: FocusField? {
- get { fState.pendingFocus }
- nonmutating set { fState.pendingFocus = newValue }
- }
-
var dragState: DragState {
get { iState.dragState }
nonmutating set { iState.dragState = newValue }
}
- var pullToCreate: PullToCreateState {
- get { iState.pullToCreate }
- nonmutating set { iState.pullToCreate = newValue }
- }
-
- var pullUpOffset: CGFloat {
- get { iState.pullUpOffset }
- nonmutating set { iState.pullUpOffset = newValue }
- }
-
- var isDragging: Bool {
- get { iState.isDragging }
- nonmutating set { iState.isDragging = newValue }
- }
-
- var rowFrames: [UUID: CGRect] {
- get { iState.rowFrames }
- nonmutating set { iState.rowFrames = newValue }
- }
-
- var refreshID: UUID {
- get { tState.refreshID }
- nonmutating set { tState.refreshID = newValue }
- }
-
- var undoToast: UndoToastData? {
- get { iState.undoToast }
- nonmutating set { iState.undoToast = newValue }
- }
-
var draftTaskPlacement: DraftTaskPlacement? {
get { iState.draftTaskPlacement }
nonmutating set { iState.draftTaskPlacement = newValue }
@@ -149,7 +98,7 @@ struct TaskListView: View, TaskListViewProtocol {
}
private var selectedIndex: Int? {
- guard let currentID = selectedTaskID else { return nil }
+ guard let currentID = fState.selectedTaskID else { return nil }
return activeTasks.firstIndex(where: { $0.id == currentID })
}
@@ -175,7 +124,7 @@ struct TaskListView: View, TaskListViewProtocol {
private var menuCoordinatorTrigger: MenuState {
MenuState(
- selectedTaskID: selectedTaskID,
+ selectedTaskID: fState.selectedTaskID,
isScrollViewFocused: focusedField == .scrollView,
activeTaskCount: activeTasks.count,
completedTaskCount: completedTasks.count,
@@ -191,11 +140,11 @@ struct TaskListView: View, TaskListViewProtocol {
coord.moveDown = { moveSelectedTaskDown() }
coord.markCompleted = { markSelectedTaskCompleted() }
let inNavMode = focusedField == .scrollView
- coord.canDelete = selectedTaskID != nil && inNavMode
+ coord.canDelete = fState.selectedTaskID != nil && inNavMode
coord.canMoveUp = canMoveSelectionUp
coord.canMoveDown = canMoveSelectionDown
- coord.canMarkCompleted = selectedTaskID != nil && inNavMode
- coord.markCompletedTitle = completedTasks.contains(where: { $0.id == selectedTaskID })
+ coord.canMarkCompleted = fState.selectedTaskID != nil && inNavMode
+ coord.markCompletedTitle = completedTasks.contains(where: { $0.id == fState.selectedTaskID })
? "Mark as Incomplete" : "Mark as Complete"
}
@@ -215,16 +164,16 @@ struct TaskListView: View, TaskListViewProtocol {
draftTaskPlacement = nil
}
draftTaskTitle = ""
- if selectedTaskID == draftTaskID(for: placement) {
- selectedTaskID = nil
+ if fState.selectedTaskID == draftTaskID(for: placement) {
+ fState.selectedTaskID = nil
}
guard placement == .prepend else { return }
- var state = pullToCreate
+ var state = iState.pullToCreate
state.isInsertionPending = false
state.indicatorOffset = 0
- pullToCreate = state
+ iState.pullToCreate = state
}
if placement == .prepend, !hasTitle {
@@ -247,7 +196,7 @@ struct TaskListView: View, TaskListViewProtocol {
}
func didStartDrag() {
- isDragging = true
+ iState.isDragging = true
let generator = UIImpactFeedbackGenerator(style: .medium)
generator.impactOccurred()
}
@@ -262,20 +211,20 @@ struct TaskListView: View, TaskListViewProtocol {
private func dragScaleEffect(for taskID: UUID) -> CGFloat {
let liftPoints: CGFloat = 20
- guard let width = rowFrames[taskID]?.width, width > 0 else { return 1.05 }
+ guard let width = iState.rowFrames[taskID]?.width, width > 0 else { return 1.05 }
return (width + liftPoints) / width
}
private var pullToCreateRevealHeight: CGFloat {
min(
- pullToCreate.indicatorDisplayOffset(threshold: pullCreateThreshold),
+ iState.pullToCreate.indicatorDisplayOffset(threshold: pullCreateThreshold),
PullToCreateIndicator.indicatorHeight
)
}
private var pullToCreateGap: CGFloat {
- guard pullToCreate.shouldShowIndicator, !isPrependDraftVisible else { return 0 }
- let exposedPull = pullToCreate.indicatorDisplayOffset(threshold: pullCreateThreshold)
+ guard iState.pullToCreate.shouldShowIndicator, !isPrependDraftVisible else { return 0 }
+ let exposedPull = iState.pullToCreate.indicatorDisplayOffset(threshold: pullCreateThreshold)
return min(
vStackSpacing,
max(0, exposedPull - PullToCreateIndicator.indicatorHeight)
@@ -283,7 +232,7 @@ struct TaskListView: View, TaskListViewProtocol {
}
private var pullToCreateRowOverlap: CGFloat {
- guard pullToCreate.shouldShowIndicator, !isPrependDraftVisible else {
+ guard iState.pullToCreate.shouldShowIndicator, !isPrependDraftVisible else {
return 0
}
return PullToCreateIndicator.indicatorHeight - pullToCreateRevealHeight
@@ -293,12 +242,12 @@ struct TaskListView: View, TaskListViewProtocol {
/// The phantom's UITextView is created while the indicator is visible
/// (during the pull), so it's ready when the user releases.
@ViewBuilder var pullToCreateIndicatorRow: some View {
- let showIndicator = pullToCreate.shouldShowIndicator
+ let showIndicator = iState.pullToCreate.shouldShowIndicator
let showPhantom = isPrependDraftVisible
if showIndicator || showPhantom {
ZStack(alignment: .topLeading) {
PullToCreateIndicator(
- pullOffset: pullToCreate.indicatorDisplayOffset(
+ pullOffset: iState.pullToCreate.indicatorDisplayOffset(
threshold: pullCreateThreshold
),
threshold: pullCreateThreshold,
@@ -326,7 +275,7 @@ struct TaskListView: View, TaskListViewProtocol {
let accentColor = taskColor(
forIndex: 0, total: max(1, displayActiveTasks.count + 1)
)
- let isSelected = selectedTaskID == draftPrependRowID
+ let isSelected = fState.selectedTaskID == draftPrependRowID
HStack(alignment: .center, spacing: TaskRowMetrics.contentSpacing) {
Image(systemName: "circle")
.frame(width: 22, height: 22)
@@ -388,7 +337,7 @@ struct TaskListView: View, TaskListViewProtocol {
let total = max(1, displayActiveTasks.count + 1)
let index = displayActiveTasks.count
let accentColor = taskColor(forIndex: index, total: total)
- let isSelected = selectedTaskID == draftAppendRowID
+ let isSelected = fState.selectedTaskID == draftAppendRowID
HStack(alignment: .center, spacing: TaskRowMetrics.contentSpacing) {
Image(systemName: "circle")
.frame(width: 22, height: 22)
@@ -459,7 +408,7 @@ struct TaskListView: View, TaskListViewProtocol {
taskID: taskID,
index: index,
totalTasks: displayActiveTasks.count,
- isSelected: selectedTaskID == taskID,
+ isSelected: fState.selectedTaskID == taskID,
isDragging: isDraggingStateBinding,
isLastActiveTask: index == displayActiveTasks.count - 1,
focusedField: $focusedFieldBinding,
@@ -469,8 +418,8 @@ struct TaskListView: View, TaskListViewProtocol {
onSelect: { selectTask($0) },
onStartEdit: { startEditing($0) },
onEndEdit: {
- if selectedTaskID == $0 {
- selectedTaskID = nil
+ if fState.selectedTaskID == $0 {
+ fState.selectedTaskID = nil
}
endEditing($0, shouldCreateNewTask: $1)
}
@@ -491,7 +440,7 @@ struct TaskListView: View, TaskListViewProtocol {
.onGeometryChange(for: CGRect.self) { proxy in
proxy.frame(in: .global)
} action: { frame in
- rowFrames[taskID] = frame
+ iState.rowFrames[taskID] = frame
}
.id(taskID)
}
@@ -504,7 +453,7 @@ struct TaskListView: View, TaskListViewProtocol {
TaskRowView(
task: task,
taskID: taskID,
- isSelected: selectedTaskID == taskID,
+ isSelected: fState.selectedTaskID == taskID,
focusedField: $focusedFieldBinding,
onToggle: { toggleCompletion($0) },
onTitleChange: { updateTitle($0, $1) },
@@ -596,7 +545,7 @@ struct TaskListView: View, TaskListViewProtocol {
}
.padding(
.bottom,
- (pullToCreate.shouldShowIndicator && !isPrependDraftVisible)
+ (iState.pullToCreate.shouldShowIndicator && !isPrependDraftVisible)
? (pullToCreateGap - vStackSpacing) : 0
)
taskRows
@@ -605,7 +554,7 @@ struct TaskListView: View, TaskListViewProtocol {
.frame(maxWidth: .infinity, alignment: .topLeading)
.padding(.trailing, 16)
.padding(.vertical, 12)
- .offset(y: -pullToCreate.pullOffset)
+ .offset(y: -iState.pullToCreate.pullOffset)
.onChange(of: focusedFieldBinding) { oldValue, newValue in
fState.focusedField = newValue
handleFocusChange(from: oldValue, to: newValue)
@@ -614,10 +563,10 @@ struct TaskListView: View, TaskListViewProtocol {
!iState.isShowingSettings,
!iState.isShowingSyncDiagnostics
{
- if let pending = pendingFocus {
+ if let pending = fState.pendingFocus {
focusedFieldBinding = pending
fState.focusedField = pending
- pendingFocus = nil
+ fState.pendingFocus = nil
} else {
focusedFieldBinding = .scrollView
fState.focusedField = .scrollView