import WidgetKit import SwiftUI import AppIntents // MARK: - Interactive intents /// Tapping a checkbox toggles the task's completion directly in the App Group. struct ToggleTaskIntent: AppIntent { static var title: LocalizedStringResource = "Toggle Task Complete" @Parameter(title: "Task ID") var taskId: String init() {} init(taskId: String) { self.taskId = taskId } func perform() async throws -> some IntentResult { toggleWidgetTaskComplete(id: taskId) return .result() } } /// The "+" button opens the app and asks it to present the add-task sheet. struct AddTaskFromWidgetIntent: AppIntent { static var title: LocalizedStringResource = "Add Task" static var openAppWhenRun = true func perform() async throws -> some IntentResult { UserDefaults.kisani.set(true, forKey: "kisani.widget.openAddTask") return .result() } } // MARK: - Entry + Provider struct TasksEntry: TimelineEntry { let date: Date let tasks: [WidgetTask] let openCount: Int } struct TasksProvider: TimelineProvider { func placeholder(in context: Context) -> TasksEntry { TasksEntry(date: .now, tasks: [ WidgetTask(id: UUID(), title: "Pray for Uganda", dueDate: .now, isComplete: false, quadrant: "Urgent + Important", category: "reminder"), WidgetTask(id: UUID(), title: "Masters of the Universe", dueDate: .now, isComplete: false, quadrant: "Schedule It", category: "reminder") ], openCount: 2) } func getSnapshot(in context: Context, completion: @escaping (TasksEntry) -> Void) { completion(loadEntry()) } func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) { let next = Calendar.current.startOfDay(for: Date().addingTimeInterval(86400)) completion(Timeline(entries: [loadEntry()], policy: .after(next))) } private func loadEntry() -> TasksEntry { let tasks = todayWidgetTasks() return TasksEntry(date: .now, tasks: tasks, openCount: tasks.filter { !$0.isComplete }.count) } } // MARK: - Widget struct MyTasksWidget: Widget { let kind = "KisaniMyTasks" var body: some WidgetConfiguration { StaticConfiguration(kind: kind, provider: TasksProvider()) { entry in MyTasksView(entry: entry) } .configurationDisplayName("My Tasks") .description("Today's tasks — check them off right from the widget.") .supportedFamilies([.systemSmall, .systemMedium, .systemLarge]) } } struct MyTasksView: View { let entry: TasksEntry @Environment(\.widgetFamily) var family private var maxRows: Int { switch family { case .systemLarge: return 9 case .systemMedium: return 4 default: return 3 } } var body: some View { VStack(alignment: .leading, spacing: 8) { HStack(spacing: 6) { Text("Today") .font(.system(size: 18, weight: .bold)) .foregroundColor(Color(red: 0.42, green: 0.5, blue: 0.96)) Text("\(entry.openCount)") .font(.system(size: 15, weight: .semibold)) .foregroundColor(Color(white: 0.5)) Spacer() Button(intent: AddTaskFromWidgetIntent()) { Image(systemName: "plus") .font(.system(size: 16, weight: .semibold)) .foregroundColor(Color(red: 0.42, green: 0.5, blue: 0.96)) } .buttonStyle(.plain) } if entry.tasks.isEmpty { Spacer() Text("All clear for today 🎉") .font(.system(size: 13)) .foregroundColor(Color(white: 0.5)) .frame(maxWidth: .infinity, alignment: .center) Spacer() } else { ForEach(entry.tasks.prefix(maxRows)) { task in HStack(spacing: 10) { Button(intent: ToggleTaskIntent(taskId: task.id.uuidString)) { Image(systemName: task.isComplete ? "checkmark.circle.fill" : "circle") .font(.system(size: 18)) .foregroundColor(task.isComplete ? task.accentColor : Color(white: 0.45)) } .buttonStyle(.plain) Text(task.title) .font(.system(size: 14)) .foregroundColor(task.isComplete ? Color(white: 0.45) : .white) .strikethrough(task.isComplete, color: Color(white: 0.45)) .lineLimit(1) Spacer(minLength: 0) } } if entry.tasks.count > maxRows { Text("+\(entry.tasks.count - maxRows) more") .font(.system(size: 11, weight: .medium)) .foregroundColor(Color(white: 0.4)) .padding(.leading, 28) } Spacer(minLength: 0) } } .containerBackground(.black, for: .widget) } }