import WidgetKit import SwiftUI import ComposeApp // MARK: - Timeline Entry struct TaskWidgetEntry: TimelineEntry { let date: Date let tasks: [CustomTask] } // MARK: - Provider struct TaskWidgetProvider: TimelineProvider { func placeholder(in context: Context) -> TaskWidgetEntry { TaskWidgetEntry(date: Date(), tasks: []) } func getSnapshot(in context: Context, completion: @escaping (TaskWidgetEntry) -> ()) { let tasks = LookupsManager.shared.allTasks let entry = TaskWidgetEntry( date: Date(), tasks: Array(tasks.prefix(5)) ) completion(entry) } func getTimeline(in context: Context, completion: @escaping (Timeline) -> ()) { let tasks = LookupsManager.shared.allTasks let entry = TaskWidgetEntry( date: Date(), tasks: Array(tasks.prefix(5)) ) // Refresh every hour let nextUpdate = Calendar.current.date(byAdding: .hour, value: 1, to: Date())! let timeline = Timeline(entries: [entry], policy: .after(nextUpdate)) completion(timeline) } } // MARK: - Widget View struct TaskWidgetEntryView: View { var entry: TaskWidgetEntry @Environment(\.widgetFamily) var family var body: some View { switch family { case .systemSmall: SmallWidgetView(tasks: entry.tasks) case .systemMedium: MediumWidgetView(tasks: entry.tasks) default: SmallWidgetView(tasks: entry.tasks) } } } // MARK: - Small Widget struct SmallWidgetView: View { let tasks: [CustomTask] var body: some View { VStack(alignment: .leading, spacing: 8) { HStack { Image(systemName: "house.fill") .foregroundColor(.blue) Text("Tasks") .font(.headline) .fontWeight(.bold) } if tasks.isEmpty { Spacer() Text("No tasks") .font(.caption) .foregroundColor(.secondary) Spacer() } else { if let firstTask = tasks.first { VStack(alignment: .leading, spacing: 4) { Text(firstTask.title) .font(.subheadline) .fontWeight(.medium) .lineLimit(2) HStack { Text(firstTask.priority?.uppercased() ?? "") .font(.caption2) .padding(.horizontal, 6) .padding(.vertical, 2) .background(priorityColor(firstTask.priority)) .foregroundColor(.white) .cornerRadius(4) Text(firstTask.dueDate ?? "") .font(.caption2) .foregroundColor(.secondary) } } } Spacer() Text("\(tasks.count) total tasks") .font(.caption2) .foregroundColor(.secondary) } } .padding() } func priorityColor(_ priority: String?) -> Color { switch priority?.lowercased() { case "urgent": return .red case "high": return .orange case "medium": return .blue default: return .gray } } } // MARK: - Medium Widget struct MediumWidgetView: View { let tasks: [CustomTask] var body: some View { VStack(alignment: .leading, spacing: 8) { HStack { Image(systemName: "house.fill") .foregroundColor(.blue) Text("My Tasks") .font(.headline) .fontWeight(.bold) Spacer() Text("\(tasks.count)") .font(.caption) .foregroundColor(.secondary) } Divider() if tasks.isEmpty { Spacer() HStack { Spacer() VStack { Image(systemName: "checkmark.circle") .font(.largeTitle) .foregroundColor(.green) Text("All done!") .font(.caption) .foregroundColor(.secondary) } Spacer() } Spacer() } else { ForEach(tasks.prefix(3), id: \.id) { task in HStack(spacing: 8) { Circle() .fill(priorityColor(task.priority)) .frame(width: 8, height: 8) VStack(alignment: .leading, spacing: 2) { Text(task.title) .font(.caption) .fontWeight(.medium) .lineLimit(1) Text(task.dueDate ?? "No due date") .font(.caption2) .foregroundColor(.secondary) } Spacer() Text(task.priority?.uppercased() ?? "") .font(.caption2) .foregroundColor(.secondary) } } } } .padding() } func priorityColor(_ priority: String?) -> Color { switch priority?.lowercased() { case "urgent": return .red case "high": return .orange case "medium": return .blue default: return .gray } } } // MARK: - Widget Configuration @main struct TaskWidget: Widget { let kind: String = "TaskWidget" var body: some WidgetConfiguration { StaticConfiguration(kind: kind, provider: TaskWidgetProvider()) { entry in if #available(iOS 17.0, *) { TaskWidgetEntryView(entry: entry) .containerBackground(.fill.tertiary, for: .widget) } else { TaskWidgetEntryView(entry: entry) .padding() .background() } } .configurationDisplayName("My Tasks") .description("View your upcoming tasks at a glance") .supportedFamilies([.systemSmall, .systemMedium]) } } // MARK: - Preview struct TaskWidget_Previews: PreviewProvider { static var previews: some View { let sampleTasks = [ CustomTask( id: 1, residence: 1, createdBy: 1, createdByUsername: "user", title: "Fix leaky faucet", description: "Kitchen sink", category: "plumbing", priority: "high", status: "pending", dueDate: "2024-12-15", estimatedCost: nil, actualCost: nil, notes: nil, createdAt: "2024-01-01T00:00:00Z", updatedAt: "2024-01-01T00:00:00Z", showCompletedButton: false, daysUntilDue: nil, isOverdue: nil, lastCompletion: nil ) ] TaskWidgetEntryView(entry: TaskWidgetEntry(date: Date(), tasks: sampleTasks)) .previewContext(WidgetPreviewContext(family: .systemSmall)) TaskWidgetEntryView(entry: TaskWidgetEntry(date: Date(), tasks: sampleTasks)) .previewContext(WidgetPreviewContext(family: .systemMedium)) } }