wip
This commit is contained in:
460
FeelsWidget2/FeelsWidget.swift
Normal file
460
FeelsWidget2/FeelsWidget.swift
Normal file
@@ -0,0 +1,460 @@
|
||||
//
|
||||
// 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
|
||||
let secondaryColor: Color
|
||||
|
||||
init(image: Image, graphic: Image, date: Date, color: Color, secondaryColor: Color) {
|
||||
self.image = image
|
||||
self.date = date
|
||||
self.color = color
|
||||
self.graphic = graphic
|
||||
self.secondaryColor = secondaryColor
|
||||
}
|
||||
}
|
||||
|
||||
struct TimeLineCreator {
|
||||
static func createViews(daysBack: Int) -> [WatchTimelineView] {
|
||||
var timeLineView = [WatchTimelineView]()
|
||||
|
||||
let latestDayToShow = ShowBasedOnVoteLogics.getCurrentVotingDate(onboardingData: UserDefaultsStore.getOnboarding())
|
||||
let dates = Array(0...daysBack).map({
|
||||
Calendar.current.date(byAdding: .day, value: -$0, to: latestDayToShow)!
|
||||
})
|
||||
|
||||
for date in dates {
|
||||
let dayStart = Calendar.current.startOfDay(for: date)
|
||||
let dayEnd = Calendar.current.date(bySettingHour: 23, minute: 59, second: 59, of: dayStart)!
|
||||
let moodTint: MoodTintable.Type = UserDefaultsStore.moodTintable()
|
||||
let moodImages: MoodImagable.Type = UserDefaultsStore.moodMoodImagable()
|
||||
|
||||
if let todayEntry = PersistenceController.shared.getData(startDate: dayStart, endDate: dayEnd, includedDays: []).first {
|
||||
timeLineView.append(WatchTimelineView(image: moodImages.icon(forMood: todayEntry.mood),
|
||||
graphic: moodImages.icon(forMood: todayEntry.mood),
|
||||
date: dayStart,
|
||||
color: moodTint.color(forMood: todayEntry.mood),
|
||||
secondaryColor: moodTint.secondary(forMood: todayEntry.mood)))
|
||||
} else {
|
||||
timeLineView.append(WatchTimelineView(image: moodImages.icon(forMood: .missing),
|
||||
graphic: moodImages.icon(forMood: .missing),
|
||||
date: Date(),
|
||||
color: moodTint.color(forMood: .missing),
|
||||
secondaryColor: moodTint.secondary(forMood: .missing)))
|
||||
}
|
||||
}
|
||||
|
||||
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:
|
||||
LargeWidgetView(entry: entry)
|
||||
case .systemExtraLarge:
|
||||
LargeWidgetView(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 LargeWidgetView: View {
|
||||
var entry: Provider.Entry
|
||||
var timeLineView = [WatchTimelineView]()
|
||||
|
||||
init(entry: Provider.Entry) {
|
||||
self.entry = entry
|
||||
timeLineView = Array(TimeLineCreator.createViews(daysBack: 11).prefix(10))
|
||||
}
|
||||
|
||||
var firstGroup: ([WatchTimelineView], String) {
|
||||
return (Array(self.timeLineView.prefix(5)), UUID().uuidString)
|
||||
}
|
||||
|
||||
var secondGroup: ([WatchTimelineView], String) {
|
||||
return (Array(self.timeLineView.suffix(5)), UUID().uuidString)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Spacer()
|
||||
|
||||
ForEach([firstGroup, secondGroup], id: \.1) { group in
|
||||
VStack {
|
||||
Spacer()
|
||||
|
||||
TimeHeaderView(startDate: group.0.first!.date, endDate: group.0.last!.date)
|
||||
.frame(minWidth: 0, maxWidth: .infinity)
|
||||
.multilineTextAlignment(.leading)
|
||||
|
||||
TimeBodyView(group: group.0)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 25, style: .continuous))
|
||||
.frame(minHeight: 0, maxHeight: 55)
|
||||
.padding()
|
||||
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
if let first = timeLineView.first {
|
||||
IconView(iconViewModel: IconViewModel(backgroundImage: first.graphic,
|
||||
bgColor: first.color,
|
||||
bgOverlayColor: first.secondaryColor,
|
||||
centerImage: first.graphic,
|
||||
innerColor: first.color))
|
||||
} else {
|
||||
IconView(iconViewModel: IconViewModel.great)
|
||||
}
|
||||
}
|
||||
}
|
||||
/**********************************************************/
|
||||
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
|
||||
if let inUseWidget = UserDefaultsStore.getCustomWidgets().first(where: {
|
||||
$0.inUse == true
|
||||
}) {
|
||||
CustomWidgetView(customWidgetModel: inUseWidget)
|
||||
.frame(width: geo.size.width, height: geo.size.height)
|
||||
} else {
|
||||
CustomWidgetView(customWidgetModel: CustomWidgetModel.randomWidget)
|
||||
.frame(width: geo.size.width, height: geo.size.height)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**********************************************************/
|
||||
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 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()
|
||||
FeelsVoteWidget()
|
||||
}
|
||||
}
|
||||
|
||||
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 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 {
|
||||
FeelsGraphicWidgetEntryView(entry: SimpleEntry(date: Date(),
|
||||
configuration: ConfigurationIntent(),
|
||||
timeLineViews: [WatchTimelineView(image: HandEmojiMoodImages.icon(forMood: .great),
|
||||
graphic: HandEmojiMoodImages.icon(forMood: .great),
|
||||
date: Date(),
|
||||
color: MoodTints.Neon.color(forMood: .great),
|
||||
|
||||
secondaryColor: .white),
|
||||
WatchTimelineView(image: HandEmojiMoodImages.icon(forMood: .great),
|
||||
graphic: HandEmojiMoodImages.icon(forMood: .great),
|
||||
date: Date(),
|
||||
color: MoodTints.Neon.color(forMood: .great),
|
||||
|
||||
secondaryColor: .white)]))
|
||||
.previewContext(WidgetPreviewContext(family: .systemSmall))
|
||||
|
||||
FeelsGraphicWidgetEntryView(entry: SimpleEntry(date: Date(),
|
||||
configuration: ConfigurationIntent(),
|
||||
timeLineViews: [WatchTimelineView(image: HandEmojiMoodImages.icon(forMood: .horrible),
|
||||
graphic: HandEmojiMoodImages.icon(forMood: .horrible),
|
||||
date: Date(),
|
||||
color: MoodTints.Neon.color(forMood: .horrible),
|
||||
|
||||
secondaryColor: .white),
|
||||
WatchTimelineView(image: HandEmojiMoodImages.icon(forMood: .horrible),
|
||||
graphic: HandEmojiMoodImages.icon(forMood: .horrible),
|
||||
date: Date(),
|
||||
color: MoodTints.Neon.color(forMood: .horrible),
|
||||
|
||||
secondaryColor: .white)]))
|
||||
.previewContext(WidgetPreviewContext(family: .systemSmall))
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user