diff --git a/App/Sources/Views/TaskList/TaskListContent.swift b/App/Sources/Views/TaskList/TaskListContent.swift new file mode 100644 index 0000000..5857a5d --- /dev/null +++ b/App/Sources/Views/TaskList/TaskListContent.swift @@ -0,0 +1,104 @@ +import SwiftData +import SwiftUI + +extension TaskListView { + + /// Internal content view that manages the dynamic SwiftData query for tasks. + /// + /// This view is responsible for querying and displaying the list of tasks based on + /// the specified group and completion status filter. It exists as a separate view + /// to enable dynamic query updates when filter parameters change. + struct Content: View { + + // MARK: Properties + + /// Query to fetch todos filtered by group and completion status. + /// + /// This query is initialized with a `FetchDescriptor` that applies predicates + /// based on the group type and completion filter. SwiftData automatically + /// monitors changes and updates the view when the underlying data changes. + @Query + private var tasks: [Todo] + + // MARK: Initializers + + /// Creates a content view with a configured task query. + /// + /// This initializer sets up the `@Query` with a fetch descriptor that filters + /// tasks based on the specified group and completion status. When these parameters + /// change, SwiftUI recreates the view with new values, causing the query to update. + /// + /// - Parameters: + /// - group: The todo group category to filter by (today, upcoming, overdue, or completed). + /// - showCompleted: Whether to include completed tasks in the results. Only applies to groups other than `.completed`. + init( + group: TodoGroup, + showCompleted: Bool + ) { + _tasks = .init( + FetchDescriptor( + group: group, + showCompleted: showCompleted + ) + ) + } + + // MARK: Body + + /// The content and behavior of the view. + /// + /// Displays either a content unavailable state or a list of tasks + /// based on whether any tasks match the current filters. + var body: some View { + Group { + if tasks.isEmpty { + ContentUnavailableView( + "view.task-list-content.content-unavailable.title", + systemImage: "checkmark.circle" + ) + } + else { + List { + ForEach(tasks) { todo in + TaskListView.Item(todo) + } + } + .listStyle(.grouped) + } + } + } + } + +} + +// MARK: - Previews + +#Preview( + "Task list content when there are no tasks", + traits: .emptyContainer +) { + TaskListView.Content( + group: .today, + showCompleted: .random() + ) +} + +#Preview( + "Task list content when completed tasks not shown", + traits: .sampleTasks +) { + TaskListView.Content( + group: .today, + showCompleted: false + ) +} + +#Preview( + "Task list content when completed tasks shown", + traits: .sampleTasks +) { + TaskListView.Content( + group: .today, + showCompleted: true + ) +}