Show completed task feature #5

Merged
javier merged 4 commits from app/show-completed into main 2026-02-11 13:00:46 +00:00
2 changed files with 83 additions and 19 deletions
Showing only changes of commit 3c34d87db1 - Show all commits
@@ -19,36 +19,50 @@ extension FetchDescriptor where T == Todo {
/// ///
/// - Parameters: /// - Parameters:
/// - group: The todo group category to filter by. /// - group: The todo group category to filter by.
/// - showCompleted: Whether to include completed tasks. Only applies to groups other than `.completed`.
/// - calendar: The calendar to use for date calculations. Defaults to `.current`. /// - calendar: The calendar to use for date calculations. Defaults to `.current`.
init( init(
group: TodoGroup, group: TodoGroup,
showCompleted: Bool,
calendar: Calendar = .current calendar: Calendar = .current
) { ) {
let predicate: Predicate<Todo>
let today = calendar.startOfDay(for: .now) let today = calendar.startOfDay(for: .now)
let tomorrow = calendar.date(byAdding: .day, value: 1, to: today) ?? today let tomorrow = calendar.date(byAdding: .day, value: 1, to: today) ?? today
switch group { switch group {
case .completed: case .completed:
self.init( predicate = #Predicate { $0.completed != nil }
predicate: #Predicate { $0.completed != nil },
sortBy: [.init(\.completed, order: .reverse)]
)
case .overdue: case .overdue:
self.init( predicate = showCompleted
predicate: #Predicate { $0.completed == nil && $0.due < today }, ? #Predicate { $0.due < today }
sortBy: [.init(\.due, order: .reverse)] : #Predicate { $0.completed == nil && $0.due < today }
)
case .today: case .today:
self.init( predicate = showCompleted
predicate: #Predicate { $0.completed == nil && $0.due >= today && $0.due < tomorrow }, ? #Predicate { $0.due >= today && $0.due < tomorrow }
sortBy: [.init(\.due, order: .forward)] : #Predicate { $0.completed == nil && $0.due >= today && $0.due < tomorrow }
)
case .upcoming: case .upcoming:
self.init( predicate = showCompleted
predicate: #Predicate { $0.completed == nil && $0.due >= tomorrow }, ? #Predicate { $0.due >= tomorrow }
sortBy: [.init(\.due, order: .forward)] : #Predicate { $0.completed == nil && $0.due >= tomorrow }
)
} }
let sortDescriptors: [SortDescriptor] =
switch group {
case .completed:
[.init(\Todo.completed, order: .reverse)]
case .overdue:
[.init(\Todo.due, order: .reverse)]
case .today:
[.init(\Todo.due, order: .forward)]
case .upcoming:
[.init(\Todo.due, order: .forward)]
}
self.init(
predicate: predicate,
sortBy: sortDescriptors
)
} }
} }
@@ -20,7 +20,10 @@ struct TodoFetchDescriptorTests {
let context = container.mainContext let context = container.mainContext
// WHEN // WHEN
let descriptor = FetchDescriptor<Todo>(group: .today) let descriptor = FetchDescriptor<Todo>(
group: .today,
showCompleted: .random()
)
let results = try context.fetch(descriptor) let results = try context.fetch(descriptor)
// THEN // THEN
@@ -28,7 +31,7 @@ struct TodoFetchDescriptorTests {
} }
@Test( @Test(
"initializes with group when filled tasks", "initializes with group and show completed disabled when filled tasks",
arguments: TodoGroup.allCases arguments: TodoGroup.allCases
) )
@MainActor @MainActor
@@ -40,7 +43,10 @@ struct TodoFetchDescriptorTests {
insert(into: context) insert(into: context)
// WHEN // WHEN
let descriptor = FetchDescriptor<Todo>(group: group) let descriptor = FetchDescriptor<Todo>(
group: group,
showCompleted: false
)
let results = try context.fetch(descriptor) let results = try context.fetch(descriptor)
// THEN // THEN
@@ -62,6 +68,50 @@ struct TodoFetchDescriptorTests {
} }
} }
@Test(
"initializes with group and show completed enabled when filled tasks",
arguments: TodoGroup.allCases
)
@MainActor
func initializationWithShowCompleted(for group: TodoGroup) throws {
// GIVEN
let container = try modelContainer
let context = container.mainContext
insert(into: context)
// WHEN
let descriptor = FetchDescriptor<Todo>(
group: group,
showCompleted: true
)
let results = try context.fetch(descriptor)
// THEN
if group == .overdue || group == .upcoming {
#expect(results.count == 3)
} else {
#expect(results.count == 2)
}
switch group {
case .completed:
#expect(results[0].title == "Completed task #2")
#expect(results[1].title == "Completed task #1")
case .overdue:
#expect(results[0].title == "Completed task #1")
#expect(results[1].title == "Overdue task #2")
#expect(results[2].title == "Overdue task #1")
case .today:
#expect(results[0].title == "Today task #1")
#expect(results[1].title == "Today task #2")
case .upcoming:
#expect(results[0].title == "Upcoming task #1")
#expect(results[1].title == "Upcoming task #2")
#expect(results[2].title == "Completed task #2")
}
}
} }
// MARK: - Helpers // MARK: - Helpers