// // FeelsWidget.swift // FeelsWidget // // Created by Trey Tartt on 1/7/22. // import WidgetKit import SwiftUI import Intents import CoreData struct Provider: IntentTimelineProvider { /* placeholder for widget, no data gets redacted auto */ func placeholder(in context: Context) -> SimpleEntry { let date = Date() let moodEntry = PersistenceController.shared.moodEntries(forStartDate: date, count: 5) return SimpleEntry(date: date, configuration: ConfigurationIntent(), mood: moodEntry) } func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) { if context.isPreview { } let date = Date() let moodEntry = PersistenceController.shared.moodEntries(forStartDate: date, count: 5) let entry = SimpleEntry(date: date, configuration: configuration, mood: moodEntry) completion(entry) } func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline) -> ()) { var entries: [SimpleEntry] = [] var calendar = Calendar.current calendar.timeZone = NSTimeZone.local let todayStart = calendar.startOfDay(for: Date()) let userEntries = PersistenceController.shared.moodEntries(forStartDate: todayStart, count: 10) let entry = SimpleEntry(date: Date(), configuration: configuration, mood: userEntries) entries.append(entry) let timeline = Timeline(entries: entries, policy: .after(Random.widgetUpdateTime)) completion(timeline) } } struct SimpleEntry: TimelineEntry { let date: Date let configuration: ConfigurationIntent let mood: [MoodEntry] let showStats: Bool init(date: Date, configuration: ConfigurationIntent, mood: [MoodEntry], showStats: Bool = false) { self.date = date self.configuration = configuration self.mood = mood self.showStats = showStats } } struct FeelsWidgetEntryView : View { @Environment(\.sizeCategory) var sizeCategory @Environment(\.widgetFamily) var family var entry: Provider.Entry @ViewBuilder var body: some View { ZStack { Color(UIColor.systemBackground) switch family { case .systemSmall: SmallWidgetView(entry: entry) case .systemMedium: MediumWidgetView(entry: entry) case .systemLarge: LargeWidgetView(entry: entry) @unknown default: fatalError() } } } } struct SmallWidgetView: View { var entry: Provider.Entry var body: some View { VStack { EntryCardCollectionView(moodEntries: Array([entry.mood.first!])) .padding() } } } struct MediumWidgetView: View { var entry: Provider.Entry var formatter: DateFormatter { let dateFormatter = DateFormatter() dateFormatter.dateStyle = .medium return dateFormatter } var firstGroup: [MoodEntry] { Array(self.entry.mood.prefix(5)) } var body: some View { VStack { Spacer() HStack { Text(firstGroup.first?.date ?? Date(), formatter: formatter) .font(.system(.footnote)) Text(" - ") .font(.system(.footnote)) Text(firstGroup.last?.date ?? Date(), formatter: formatter) .font(.system(.footnote)) } .frame(minWidth: 0, maxWidth: .infinity) .multilineTextAlignment(.leading) EntryCardCollectionView(moodEntries: firstGroup) .frame(minHeight: 0, maxHeight: 55) .padding() Spacer() } } } struct LargeWidgetView: View { var entry: Provider.Entry var formatter: DateFormatter { let dateFormatter = DateFormatter() dateFormatter.dateStyle = .medium return dateFormatter } var firstGroup: [MoodEntry] { Array(self.entry.mood.prefix(5)) } var lastGroup: [MoodEntry] { Array(self.entry.mood.suffix(5)) } var body: some View { VStack { Spacer() HStack { Text(firstGroup.first?.date ?? Date(), formatter: formatter) .font(.system(.footnote)) Text(" - ") .font(.system(.footnote)) Text(firstGroup.last?.date ?? Date(), formatter: formatter) .font(.system(.footnote)) } .frame(minWidth: 0, maxWidth: .infinity) .multilineTextAlignment(.leading) EntryCardCollectionView(moodEntries: firstGroup) .frame(minHeight: 0, maxHeight: 55) .padding() Spacer() HStack { Text(lastGroup.first?.date ?? Date(), formatter: formatter) .font(.system(.footnote)) Text(" - ") Text(lastGroup.last?.date ?? Date(), formatter: formatter) .font(.system(.footnote)) } .frame(minWidth: 0, maxWidth: .infinity) .multilineTextAlignment(.leading) EntryCardCollectionView(moodEntries: lastGroup) .frame(minHeight: 0, maxHeight: 55) .padding() Spacer() } } } struct EntryCardCollectionView: View { var moodEntries: [MoodEntry] var body: some View { ZStack { Color(UIColor.secondarySystemBackground) HStack { ForEach(moodEntries) { mood in EntryCard(moodEntry: mood) } } .padding() } .clipShape(RoundedRectangle(cornerRadius: 25, style: .continuous)) } } struct EntryCard: View { var moodEntry: MoodEntry var body: some View { moodEntry.mood.icon.font(.system(size: 50)) } } @main struct FeelsWidget: Widget { let kind: String = "FeelsWidget" var body: some WidgetConfiguration { IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in FeelsWidgetEntryView(entry: entry) } .configurationDisplayName("Feels") .description("") .supportedFamilies([.systemSmall, .systemMedium, .systemLarge]) } } struct FeelsWidget_Previews: PreviewProvider { static var previews: some View { Group { FeelsWidgetEntryView(entry: SimpleEntry(date: Date(), configuration: ConfigurationIntent(), mood: PersistenceController.shared.randomEntries(count: 1))) .previewContext(WidgetPreviewContext(family: .systemSmall)) .environment(\.sizeCategory, .small) FeelsWidgetEntryView(entry: SimpleEntry(date: Date(), configuration: ConfigurationIntent(), mood: PersistenceController.shared.randomEntries(count: 3))) .previewContext(WidgetPreviewContext(family: .systemMedium)) .environment(\.sizeCategory, .medium) FeelsWidgetEntryView(entry: SimpleEntry(date: Date(), configuration: ConfigurationIntent(), mood: PersistenceController.shared.randomEntries(count: 10))) .previewContext(WidgetPreviewContext(family: .systemLarge)) .environment(\.sizeCategory, .large) } } }