Files
Reflect/FeelsWidget/FeelsWidget.swift
Trey t 0109aee8f8 switch db between debug and release
on widgets if its before the voting time show yesterdays vote, if after either show no vote or current vote

user shared user defaults
2022-01-28 10:27:33 -06:00

410 lines
13 KiB
Swift

//
// FeelsWidget.swift
// FeelsWidget
//
// Created by Trey Tartt on 1/7/22.
//
import WidgetKit
import SwiftUI
import Intents
import CoreData
class WatchTimelineView: Identifiable {
let id = UUID()
let image: Image
let graphic: Image
let date: Date
let color: Color
init(image: Image, date: Date, color: Color, graphic: Image) {
self.image = image
self.date = date
self.color = color
self.graphic = graphic
}
}
struct TimeLineCreator {
static func createViews(daysBack: Int) -> [WatchTimelineView] {
var timeLineView = [WatchTimelineView]()
var startDayOffset = 0
if !UserDefaultsStore.getOnboarding().ableToVoteBasedOnCurentTime() {
startDayOffset = 1
}
for day in startDayOffset..<daysBack{
let day = Calendar.current.date(byAdding: .day, value: -day, to: Date())!
let dayStart = Calendar.current.startOfDay(for: day)
let dayEnd = Calendar.current.date(bySettingHour: 23, minute: 59, second: 59, of: dayStart)!
if let todayEntry = PersistenceController.shared.getData(startDate: dayStart, endDate: dayEnd, includedDays: []).first {
timeLineView.append(WatchTimelineView(image: todayEntry.mood.icon,
date: dayStart,
color: todayEntry.mood.color,
graphic: todayEntry.mood.graphic))
} else {
timeLineView.append(WatchTimelineView(image: Mood.missing.icon,
date: dayStart,
color: Mood.missing.color,
graphic: Mood.missing.graphic))
}
}
timeLineView = timeLineView.sorted(by: { $0.date > $1.date })
return timeLineView
}
}
struct Provider: IntentTimelineProvider {
let timeLineCreator = TimeLineCreator()
/*
placeholder for widget, no data
gets redacted auto
*/
func placeholder(in context: Context) -> SimpleEntry {
return SimpleEntry(date: Date(),
configuration: ConfigurationIntent(),
timeLineViews: Array(TimeLineCreator.createViews(daysBack: 11).prefix(10)))
}
func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date(),
configuration: ConfigurationIntent(),
timeLineViews: Array(TimeLineCreator.createViews(daysBack: 11).prefix(10)))
completion(entry)
}
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
let entry = SimpleEntry(date: Calendar.current.date(byAdding: .second, value: 15, to: Date())!,
configuration: ConfigurationIntent(),
timeLineViews: nil)
let midNightEntry = SimpleEntry(date: Calendar.current.date(bySettingHour: 23, minute: 59, second: 59, of: Date())!,
configuration: ConfigurationIntent(),
timeLineViews: nil)
let date = Calendar.current.date(byAdding: .second, value: 10, to: Date())!
let timeline = Timeline(entries: [entry, midNightEntry], policy: .after(date))
completion(timeline)
}
}
struct SimpleEntry: TimelineEntry {
let date: Date
let configuration: ConfigurationIntent
let timeLineViews: [WatchTimelineView]?
let showStats: Bool
init(date: Date, configuration: ConfigurationIntent, timeLineViews: [WatchTimelineView]?, showStats: Bool = false) {
self.date = date
self.configuration = configuration
self.timeLineViews = timeLineViews
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:
MediumWidgetView(entry: entry)
case .systemExtraLarge:
MediumWidgetView(entry: entry)
@unknown default:
fatalError()
}
}.onReceive(NotificationCenter.default.publisher(for: .NSPersistentStoreRemoteChange)) { _ in
// make sure you don't call this too often
WidgetCenter.shared.reloadAllTimelines()
}
}
}
struct SmallWidgetView: View {
var entry: Provider.Entry
var timeLineView = [WatchTimelineView]()
init(entry: Provider.Entry) {
self.entry = entry
timeLineView = [TimeLineCreator.createViews(daysBack: 2).first!]
}
var body: some View {
ZStack {
Color(UIColor.secondarySystemBackground)
HStack {
ForEach(self.timeLineView) { watchView in
EntryCard(timeLineView: watchView)
}
}
.padding()
}
.clipShape(RoundedRectangle(cornerRadius: 25, style: .continuous))
.frame(minHeight: 0, maxHeight: 55)
.padding()
}
}
struct MediumWidgetView: View {
var entry: Provider.Entry
var timeLineView = [WatchTimelineView]()
init(entry: Provider.Entry) {
self.entry = entry
timeLineView = Array(TimeLineCreator.createViews(daysBack: 6).prefix(5))
}
var body: some View {
VStack {
Spacer()
TimeHeaderView(startDate: timeLineView.first!.date, endDate: timeLineView.last!.date)
.frame(minWidth: 0, maxWidth: .infinity)
.multilineTextAlignment(.leading)
TimeBodyView(group: timeLineView)
.clipShape(RoundedRectangle(cornerRadius: 25, style: .continuous))
.frame(minHeight: 0, maxHeight: 55)
.padding()
Spacer()
}
}
}
/**********************************************************/
struct FeelsGraphicWidgetEntryView : View {
@Environment(\.sizeCategory) var sizeCategory
@Environment(\.widgetFamily) var family
var entry: Provider.Entry
@ViewBuilder
var body: some View {
SmallGraphicWidgetView(entry: entry)
.onReceive(NotificationCenter.default.publisher(for: .NSPersistentStoreRemoteChange)) { _ in
// make sure you don't call this too often
WidgetCenter.shared.reloadAllTimelines()
}
}
}
struct SmallGraphicWidgetView: View {
var entry: Provider.Entry
var timeLineView = [WatchTimelineView]()
init(entry: Provider.Entry) {
self.entry = entry
timeLineView = [TimeLineCreator.createViews(daysBack: 2).first!]
}
var body: some View {
GeometryReader { geo in
timeLineView.first!.graphic
.resizable()
.scaledToFit()
}
}
}
/**********************************************************/
struct FeelsIconWidgetEntryView : View {
@Environment(\.sizeCategory) var sizeCategory
@Environment(\.widgetFamily) var family
var entry: Provider.Entry
@ViewBuilder
var body: some View {
SmallIconView(entry: entry)
}
}
struct SmallIconView: View {
var entry: Provider.Entry
var body: some View {
GeometryReader { geo in
Mood.missing.graphic
.resizable()
.scaledToFit()
}
}
}
/**********************************************************/
struct TimeHeaderView: View {
let startDate: Date
let endDate: Date
var formatter: DateFormatter {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
return dateFormatter
}
var body: some View {
HStack {
Text(startDate, formatter: formatter)
.font(.system(.footnote))
Text(" - ")
.font(.system(.footnote))
Text(endDate, formatter: formatter)
.font(.system(.footnote))
}
}
}
struct TimeBodyView: View {
let group: [WatchTimelineView]
var body: some View {
ZStack {
Color(UIColor.secondarySystemBackground)
HStack {
ForEach(group) { watchView in
EntryCard(timeLineView: watchView)
}
}
.padding()
}
}
}
//struct LargeWidgetView: View {
// var entry: Provider.Entry
//
// var formatter: DateFormatter {
// let dateFormatter = DateFormatter()
// dateFormatter.dateStyle = .medium
// return dateFormatter
// }
//
//
// var body: some View {
// VStack {
// Spacer()
//
// ForEach([Array(self.entry.timeLineViews.prefix(5)), Array(self.entry.timeLineViews.suffix(5))]) { group in
//
// TimeHeaderView(startDate: group.first!, endDate: group.last!)
// .frame(minWidth: 0, maxWidth: .infinity)
// .multilineTextAlignment(.leading)
//
// TimeBodyView(group: group)
// .clipShape(RoundedRectangle(cornerRadius: 25, style: .continuous))
// .frame(minHeight: 0, maxHeight: 55)
// .padding()
//
// Spacer()
// }
// }
// }
//}
struct EntryCard: View {
var timeLineView: WatchTimelineView
var body: some View {
timeLineView.image
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 50, height: 50, alignment: .center)
.foregroundColor(timeLineView.color)
}
}
@main
struct FeelsBundle: WidgetBundle {
var body: some Widget {
FeelsWidget()
FeelsGraphicWidget()
FeelsIconWidget()
}
}
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])
}
}
struct FeelsIconWidget: Widget {
let kind: String = "FeelsIconWidget"
var body: some WidgetConfiguration {
IntentConfiguration(kind: kind,
intent: ConfigurationIntent.self,
provider: Provider()) { entry in
FeelsIconWidgetEntryView(entry: entry)
}
.configurationDisplayName("Feels Icon")
.description("")
.supportedFamilies([.systemSmall])
}
}
struct FeelsGraphicWidget: Widget {
let kind: String = "FeelsGraphicWidget"
var body: some WidgetConfiguration {
IntentConfiguration(kind: kind,
intent: ConfigurationIntent.self,
provider: Provider()) { entry in
FeelsGraphicWidgetEntryView(entry: entry)
}
.configurationDisplayName("Mood Graphic")
.description("")
.supportedFamilies([.systemSmall])
}
}
struct FeelsWidget_Previews: PreviewProvider {
static var previews: some View {
Group {
// FeelsWidgetEntryView(entry: SimpleEntry(date: Date(),
// configuration: ConfigurationIntent(),
// timeLineViews: FeelsWidget_Previews.data))
// .previewContext(WidgetPreviewContext(family: .systemSmall))
// .environment(\.sizeCategory, .small)
//
// FeelsWidgetEntryView(entry: SimpleEntry(date: Date(),
// configuration: ConfigurationIntent(),
// timeLineViews: FeelsWidget_Previews.data))
// .previewContext(WidgetPreviewContext(family: .systemMedium))
// .environment(\.sizeCategory, .medium)
//
// FeelsWidgetEntryView(entry: SimpleEntry(date: Date(),
// configuration: ConfigurationIntent(),
// timeLineViews: FeelsWidget_Previews.data))
// .previewContext(WidgetPreviewContext(family: .systemLarge))
// .environment(\.sizeCategory, .large)
}
}
}