// // ReflectComplication.swift // Reflect Watch App // // WidgetKit complications for Apple Watch. // import WidgetKit import SwiftUI // MARK: - Timeline Provider struct ReflectTimelineProvider: TimelineProvider { func placeholder(in context: Context) -> ReflectEntry { ReflectEntry(date: Date(), mood: nil, streak: 0) } func getSnapshot(in context: Context, completion: @escaping (ReflectEntry) -> Void) { Task { @MainActor in let entry = createEntry() completion(entry) } } func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) { Task { @MainActor in let entry = createEntry() // Refresh at midnight for the next day let tomorrow = Calendar.current.startOfDay( for: Calendar.current.date(byAdding: .day, value: 1, to: Date())! ) let timeline = Timeline(entries: [entry], policy: .after(tomorrow)) completion(timeline) } } @MainActor private func createEntry() -> ReflectEntry { let todayEntry = ExtensionDataProvider.shared.getTodayEntry() let streak = ExtensionDataProvider.shared.getCurrentStreak() return ReflectEntry( date: Date(), mood: todayEntry?.mood, streak: streak ) } } // MARK: - Timeline Entry struct ReflectEntry: TimelineEntry { let date: Date let mood: Mood? let streak: Int } // MARK: - Complication Views struct ReflectComplicationEntryView: View { var entry: ReflectEntry @Environment(\.widgetFamily) var family var body: some View { switch family { case .accessoryCircular: CircularView(entry: entry) case .accessoryCorner: CornerView(entry: entry) case .accessoryInline: InlineView(entry: entry) case .accessoryRectangular: RectangularView(entry: entry) default: CircularView(entry: entry) } } } // MARK: - Circular Complication struct CircularView: View { let entry: ReflectEntry var body: some View { ZStack { AccessoryWidgetBackground() if let mood = entry.mood { Text(mood.watchEmoji) .font(.system(size: 24)) } else { VStack(spacing: 0) { Image(systemName: "face.smiling") .font(.system(size: 18)) Text("Log") .font(.system(size: 10)) } } } } } // MARK: - Corner Complication struct CornerView: View { let entry: ReflectEntry var body: some View { if let mood = entry.mood { Text(mood.watchEmoji) .font(.system(size: 20)) .widgetLabel { Text(mood.widgetDisplayName) } } else { Image(systemName: "face.smiling") .font(.system(size: 20)) .widgetLabel { Text("Log mood") } } } } // MARK: - Inline Complication struct InlineView: View { let entry: ReflectEntry var body: some View { if entry.streak > 0 { Label("\(entry.streak) day streak", systemImage: "flame.fill") } else if let mood = entry.mood { Text("\(mood.watchEmoji) \(mood.widgetDisplayName)") } else { Label("Log your mood", systemImage: "face.smiling") } } } // MARK: - Rectangular Complication struct RectangularView: View { let entry: ReflectEntry var body: some View { HStack { if let mood = entry.mood { Text(mood.watchEmoji) .font(.system(size: 28)) VStack(alignment: .leading, spacing: 2) { Text("Today") .font(.system(size: 12)) .foregroundColor(.secondary) Text(mood.widgetDisplayName) .font(.system(size: 14, weight: .semibold)) if entry.streak > 1 { Label("\(entry.streak) days", systemImage: "flame.fill") .font(.system(size: 10)) .foregroundColor(.orange) } } } else { Image(systemName: "face.smiling") .font(.system(size: 24)) VStack(alignment: .leading, spacing: 2) { Text("Reflect") .font(.system(size: 14, weight: .semibold)) Text("Tap to log mood") .font(.system(size: 12)) .foregroundColor(.secondary) } } Spacer() } } } // MARK: - Widget Configuration struct ReflectComplication: Widget { let kind: String = "ReflectComplication" var body: some WidgetConfiguration { StaticConfiguration(kind: kind, provider: ReflectTimelineProvider()) { entry in ReflectComplicationEntryView(entry: entry) .containerBackground(.fill.tertiary, for: .widget) } .configurationDisplayName("Reflect") .description("See today's mood and streak.") .supportedFamilies([ .accessoryCircular, .accessoryCorner, .accessoryInline, .accessoryRectangular ]) } } // MARK: - Preview #Preview("Circular - Mood") { CircularView(entry: ReflectEntry(date: Date(), mood: .great, streak: 5)) .previewContext(WidgetPreviewContext(family: .accessoryCircular)) } #Preview("Circular - Empty") { CircularView(entry: ReflectEntry(date: Date(), mood: nil, streak: 0)) .previewContext(WidgetPreviewContext(family: .accessoryCircular)) } #Preview("Rectangular - Mood") { RectangularView(entry: ReflectEntry(date: Date(), mood: .good, streak: 7)) .previewContext(WidgetPreviewContext(family: .accessoryRectangular)) } #Preview("Inline - Streak") { InlineView(entry: ReflectEntry(date: Date(), mood: .great, streak: 5)) .previewContext(WidgetPreviewContext(family: .accessoryInline)) } #Preview("Corner - Mood") { CornerView(entry: ReflectEntry(date: Date(), mood: .average, streak: 3)) .previewContext(WidgetPreviewContext(family: .accessoryCorner)) }