diff --git a/Feels.xcodeproj/project.pbxproj b/Feels.xcodeproj/project.pbxproj
index 33d2ea0..ac2183e 100644
--- a/Feels.xcodeproj/project.pbxproj
+++ b/Feels.xcodeproj/project.pbxproj
@@ -7,13 +7,14 @@
objects = {
/* Begin PBXBuildFile section */
- 1C412080278E23CC00D9153A /* Charts in Frameworks */ = {isa = PBXBuildFile; productRef = 1C41207F278E23CC00D9153A /* Charts */; };
1C412082278F2B8800D9153A /* FilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C412081278F2B8800D9153A /* FilterView.swift */; };
1C412083278F2B8800D9153A /* FilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C412081278F2B8800D9153A /* FilterView.swift */; };
1C683FCA2792281400745862 /* Stats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C683FC92792281400745862 /* Stats.swift */; };
1C683FCB2792281400745862 /* Stats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C683FC92792281400745862 /* Stats.swift */; };
1C683FCC2792281400745862 /* Stats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C683FC92792281400745862 /* Stats.swift */; };
1C744F2C278CE15600953A57 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C744F2B278CE15600953A57 /* AppDelegate.swift */; };
+ 1CA2662A2793843200C0E12C /* Charts in Frameworks */ = {isa = PBXBuildFile; productRef = 1CA266292793843200C0E12C /* Charts */; };
+ 1CA2662D2793908700C0E12C /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90AEF278C7DDF001C4FEA /* Persistence.swift */; };
1CC469AA278F30A0003E0C6E /* BGTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CC469A9278F30A0003E0C6E /* BGTask.swift */; };
1CC469AC27907D48003E0C6E /* CircleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CC469AB27907D48003E0C6E /* CircleView.swift */; };
1CD90B07278C7DE0001C4FEA /* Tests_iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B06278C7DE0001C4FEA /* Tests_iOS.swift */; };
@@ -25,7 +26,6 @@
1CD90B18278C7DE0001C4FEA /* FeelsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90AED278C7DDF001C4FEA /* FeelsApp.swift */; };
1CD90B19278C7DE0001C4FEA /* FeelsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90AED278C7DDF001C4FEA /* FeelsApp.swift */; };
1CD90B1C278C7DE0001C4FEA /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90AEF278C7DDF001C4FEA /* Persistence.swift */; };
- 1CD90B1D278C7DE0001C4FEA /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90AEF278C7DDF001C4FEA /* Persistence.swift */; };
1CD90B1E278C7DE0001C4FEA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1CD90AF0278C7DE0001C4FEA /* Assets.xcassets */; };
1CD90B1F278C7DE0001C4FEA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1CD90AF0278C7DE0001C4FEA /* Assets.xcassets */; };
1CD90B37278C7E38001C4FEA /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B32278C7E38001C4FEA /* SettingsView.swift */; };
@@ -45,7 +45,6 @@
1CD90B52278C7E7A001C4FEA /* FeelsWidget.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B4E278C7E7A001C4FEA /* FeelsWidget.intentdefinition */; };
1CD90B53278C7E7A001C4FEA /* FeelsWidget.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B4E278C7E7A001C4FEA /* FeelsWidget.intentdefinition */; };
1CD90B56278C7E7A001C4FEA /* FeelsWidgetExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 1CD90B45278C7E7A001C4FEA /* FeelsWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
- 1CD90B5B278C7E91001C4FEA /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90AEF278C7DDF001C4FEA /* Persistence.swift */; };
1CD90B5D278C7EAD001C4FEA /* Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B5C278C7EAD001C4FEA /* Random.swift */; };
1CD90B5E278C7EAD001C4FEA /* Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B5C278C7EAD001C4FEA /* Random.swift */; };
1CD90B5F278C7EAD001C4FEA /* Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B5C278C7EAD001C4FEA /* Random.swift */; };
@@ -149,7 +148,7 @@
buildActionMask = 2147483647;
files = (
1CD90B6C278C7F78001C4FEA /* CloudKit.framework in Frameworks */,
- 1C412080278E23CC00D9153A /* Charts in Frameworks */,
+ 1CA2662A2793843200C0E12C /* Charts in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -324,7 +323,7 @@
);
name = "Feels (iOS)";
packageProductDependencies = (
- 1C41207F278E23CC00D9153A /* Charts */,
+ 1CA266292793843200C0E12C /* Charts */,
);
productName = "Feels (iOS)";
productReference = 1CD90AF5278C7DE0001C4FEA /* Feels.app */;
@@ -439,7 +438,7 @@
);
mainGroup = 1CD90AE5278C7DDF001C4FEA;
packageReferences = (
- 1C41207E278E23CB00D9153A /* XCRemoteSwiftPackageReference "Charts" */,
+ 1CA266282793843200C0E12C /* XCRemoteSwiftPackageReference "Charts" */,
);
productRefGroup = 1CD90AF6278C7DE0001C4FEA /* Products */;
projectDirPath = "";
@@ -535,7 +534,6 @@
1CD90B38278C7E38001C4FEA /* SettingsView.swift in Sources */,
1CD90B67278C7EBA001C4FEA /* MoodEntryExtension.swift in Sources */,
1CD90B77278C8119001C4FEA /* LocalNotification.swift in Sources */,
- 1CD90B1D278C7DE0001C4FEA /* Persistence.swift in Sources */,
1CD90B19278C7DE0001C4FEA /* FeelsApp.swift in Sources */,
1CD90B5E278C7EAD001C4FEA /* Random.swift in Sources */,
);
@@ -565,8 +563,8 @@
buildActionMask = 2147483647;
files = (
1CD90B65278C7EBA001C4FEA /* Mood.swift in Sources */,
+ 1CA2662D2793908700C0E12C /* Persistence.swift in Sources */,
1CD90B5F278C7EAD001C4FEA /* Random.swift in Sources */,
- 1CD90B5B278C7E91001C4FEA /* Persistence.swift in Sources */,
1CD90B68278C7EBA001C4FEA /* MoodEntryExtension.swift in Sources */,
1CD90B71278C80CA001C4FEA /* Feels.xcdatamodeld in Sources */,
1C683FCB2792281400745862 /* Stats.swift in Sources */,
@@ -1040,7 +1038,7 @@
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
- 1C41207E278E23CB00D9153A /* XCRemoteSwiftPackageReference "Charts" */ = {
+ 1CA266282793843200C0E12C /* XCRemoteSwiftPackageReference "Charts" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/danielgindi/Charts";
requirement = {
@@ -1051,9 +1049,9 @@
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
- 1C41207F278E23CC00D9153A /* Charts */ = {
+ 1CA266292793843200C0E12C /* Charts */ = {
isa = XCSwiftPackageProductDependency;
- package = 1C41207E278E23CB00D9153A /* XCRemoteSwiftPackageReference "Charts" */;
+ package = 1CA266282793843200C0E12C /* XCRemoteSwiftPackageReference "Charts" */;
productName = Charts;
};
/* End XCSwiftPackageProductDependency section */
diff --git a/Feels.xcodeproj/xcuserdata/treyt.xcuserdatad/xcschemes/xcschememanagement.plist b/Feels.xcodeproj/xcuserdata/treyt.xcuserdatad/xcschemes/xcschememanagement.plist
index ff9be8c..ed7ab37 100644
--- a/Feels.xcodeproj/xcuserdata/treyt.xcuserdatad/xcschemes/xcschememanagement.plist
+++ b/Feels.xcodeproj/xcuserdata/treyt.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -7,12 +7,12 @@
Feels (iOS).xcscheme_^#shared#^_
orderHint
- 0
+ 1
Feels (macOS).xcscheme_^#shared#^_
orderHint
- 1
+ 0
FeelsWidgetExtension.xcscheme_^#shared#^_
diff --git a/FeelsWidget/Assets.xcassets/average.imageset/Contents.json b/FeelsWidget/Assets.xcassets/average.imageset/Contents.json
new file mode 100644
index 0000000..00fb6a2
--- /dev/null
+++ b/FeelsWidget/Assets.xcassets/average.imageset/Contents.json
@@ -0,0 +1,24 @@
+{
+ "images" : [
+ {
+ "filename" : "meh-regular.svg",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "template-rendering-intent" : "template"
+ }
+}
diff --git a/FeelsWidget/Assets.xcassets/average.imageset/meh-regular.svg b/FeelsWidget/Assets.xcassets/average.imageset/meh-regular.svg
new file mode 100644
index 0000000..9ad9cf9
--- /dev/null
+++ b/FeelsWidget/Assets.xcassets/average.imageset/meh-regular.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/FeelsWidget/Assets.xcassets/bad.imageset/Contents.json b/FeelsWidget/Assets.xcassets/bad.imageset/Contents.json
new file mode 100644
index 0000000..a8ac09d
--- /dev/null
+++ b/FeelsWidget/Assets.xcassets/bad.imageset/Contents.json
@@ -0,0 +1,24 @@
+{
+ "images" : [
+ {
+ "filename" : "frown-regular.svg",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "template-rendering-intent" : "template"
+ }
+}
diff --git a/FeelsWidget/Assets.xcassets/bad.imageset/frown-regular.svg b/FeelsWidget/Assets.xcassets/bad.imageset/frown-regular.svg
new file mode 100644
index 0000000..e32249b
--- /dev/null
+++ b/FeelsWidget/Assets.xcassets/bad.imageset/frown-regular.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/FeelsWidget/Assets.xcassets/good.imageset/Contents.json b/FeelsWidget/Assets.xcassets/good.imageset/Contents.json
new file mode 100644
index 0000000..a685e3c
--- /dev/null
+++ b/FeelsWidget/Assets.xcassets/good.imageset/Contents.json
@@ -0,0 +1,24 @@
+{
+ "images" : [
+ {
+ "filename" : "grin-regular.svg",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "template-rendering-intent" : "template"
+ }
+}
diff --git a/FeelsWidget/Assets.xcassets/good.imageset/grin-regular.svg b/FeelsWidget/Assets.xcassets/good.imageset/grin-regular.svg
new file mode 100644
index 0000000..380aed4
--- /dev/null
+++ b/FeelsWidget/Assets.xcassets/good.imageset/grin-regular.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/FeelsWidget/Assets.xcassets/great.imageset/Contents.json b/FeelsWidget/Assets.xcassets/great.imageset/Contents.json
new file mode 100644
index 0000000..f3c745f
--- /dev/null
+++ b/FeelsWidget/Assets.xcassets/great.imageset/Contents.json
@@ -0,0 +1,24 @@
+{
+ "images" : [
+ {
+ "filename" : "smile-beam-regular.svg",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "template-rendering-intent" : "template"
+ }
+}
diff --git a/FeelsWidget/Assets.xcassets/great.imageset/smile-beam-regular.svg b/FeelsWidget/Assets.xcassets/great.imageset/smile-beam-regular.svg
new file mode 100644
index 0000000..3c09689
--- /dev/null
+++ b/FeelsWidget/Assets.xcassets/great.imageset/smile-beam-regular.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/FeelsWidget/Assets.xcassets/horrible.imageset/Contents.json b/FeelsWidget/Assets.xcassets/horrible.imageset/Contents.json
new file mode 100644
index 0000000..d7421a5
--- /dev/null
+++ b/FeelsWidget/Assets.xcassets/horrible.imageset/Contents.json
@@ -0,0 +1,24 @@
+{
+ "images" : [
+ {
+ "filename" : "sad-tear-regular.svg",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "template-rendering-intent" : "template"
+ }
+}
diff --git a/FeelsWidget/Assets.xcassets/horrible.imageset/sad-tear-regular.svg b/FeelsWidget/Assets.xcassets/horrible.imageset/sad-tear-regular.svg
new file mode 100644
index 0000000..edae2d2
--- /dev/null
+++ b/FeelsWidget/Assets.xcassets/horrible.imageset/sad-tear-regular.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/FeelsWidget/Assets.xcassets/missing.imageset/Contents.json b/FeelsWidget/Assets.xcassets/missing.imageset/Contents.json
new file mode 100644
index 0000000..c82dff9
--- /dev/null
+++ b/FeelsWidget/Assets.xcassets/missing.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "exclamation-solid.svg",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/FeelsWidget/Assets.xcassets/missing.imageset/exclamation-solid.svg b/FeelsWidget/Assets.xcassets/missing.imageset/exclamation-solid.svg
new file mode 100644
index 0000000..32de166
--- /dev/null
+++ b/FeelsWidget/Assets.xcassets/missing.imageset/exclamation-solid.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/FeelsWidget/FeelsWidget.swift b/FeelsWidget/FeelsWidget.swift
index 4b4ea31..ab0b98a 100644
--- a/FeelsWidget/FeelsWidget.swift
+++ b/FeelsWidget/FeelsWidget.swift
@@ -10,39 +10,97 @@ import SwiftUI
import Intents
import CoreData
+class WatchTimelineView: Identifiable {
+ let id = UUID()
+ let image: Image
+ let date: Date
+ let color: Color
+
+ init(image: Image, date: Date, color: Color) {
+ self.image = image
+ self.date = date
+ self.color = color
+ }
+}
+
+struct TimeLineCreator {
+ static func getData() -> [MoodEntry] {
+ let dateAtEnd = Calendar.current.date(bySettingHour: 23, minute: 59, second: 59, of: Date())!
+ var tenDaysAgo = Calendar.current.date(byAdding: .day, value: -10, to: dateAtEnd)!
+ tenDaysAgo = Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: tenDaysAgo)!
+ let moodEntry = PersistenceController.shared.getData(startDate: tenDaysAgo, endDate: dateAtEnd, includedDays: [1,2,3,4,5,6,7])
+ return moodEntry
+ }
+
+ static func createTimeLineViews(fromEntries: [MoodEntry]) -> [WatchTimelineView] {
+ var returnViews = [WatchTimelineView]()
+
+ for pastDays in 0...10 {
+ let pastDate = Calendar.current.date(byAdding: .day, value: -pastDays, to: Date())!
+
+ if let item = fromEntries.filter({ entry in
+ let components = Calendar.current.dateComponents([.day, .month, .year], from: pastDate)
+ let day = components.day
+ let month = components.month
+ let year = components.year
+
+ let entryComponents = Calendar.current.dateComponents([.day, .month, .year], from: entry.forDate!)
+ let entryDay = entryComponents.day
+ let entryMonth = entryComponents.month
+ let entryYear = entryComponents.year
+
+ return day == entryDay && month == entryMonth && year == entryYear
+ }).first {
+ let timeLineView = WatchTimelineView(image: item.mood.icon, date: pastDate, color: item.mood.color)
+ returnViews.append(timeLineView)
+ } else {
+ let timeLineView = WatchTimelineView(image: Mood.missing.icon, date: pastDate, color: Mood.missing.color)
+ returnViews.append(timeLineView)
+ }
+ }
+ return returnViews
+ }
+}
+
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: 10)
- return SimpleEntry(date: date, configuration: ConfigurationIntent(), mood: moodEntry)
+ var sampleViews = [WatchTimelineView]()
+ for pastDay in 0...10 {
+ let pastDate = Calendar.current.date(byAdding: .day, value: -pastDay, to: Date())!
+ let mood = Mood.allValues.randomElement()!
+ sampleViews.append( WatchTimelineView(image: mood.icon, date: pastDate, color: mood.color) )
+ }
+ return SimpleEntry(date: Date(), configuration: ConfigurationIntent(), timeLineViews: sampleViews)
}
func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
+ var timeLineViews = [WatchTimelineView]()
+
if context.isPreview {
-
+ for pastDay in 0...10 {
+ let pastDate = Calendar.current.date(byAdding: .day, value: -pastDay, to: Date())!
+ let mood = Mood.allValues.randomElement()!
+ timeLineViews.append( WatchTimelineView(image: mood.icon, date: pastDate, color: mood.color) )
+ }
+ } else {
+ let data = TimeLineCreator.getData()
+ timeLineViews = TimeLineCreator.createTimeLineViews(fromEntries: data)
}
- 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)
+ let entry = SimpleEntry(date: Date(), configuration: configuration, timeLineViews: timeLineViews)
completion(entry)
}
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline) -> ()) {
- var calendar = Calendar.current
- calendar.timeZone = NSTimeZone.local
- let todayStart = calendar.startOfDay(for: Date())
- let userEntries = PersistenceController.shared.moodEntries(forStartDate: todayStart, count: 10)
+ let data = TimeLineCreator.getData()
+ let views = TimeLineCreator.createTimeLineViews(fromEntries: data)
- let entry = SimpleEntry(date: Date(), configuration: configuration, mood: userEntries)
- let timeline = Timeline(entries: [entry], policy: .after(Random.widgetUpdateTime))
+ let entry = SimpleEntry(date: Date(), configuration: configuration, timeLineViews: views)
+ let timeline = Timeline(entries: [entry], policy: .after(Random.tomorrowMidnightThirty))
completion(timeline)
}
}
@@ -50,13 +108,13 @@ struct Provider: IntentTimelineProvider {
struct SimpleEntry: TimelineEntry {
let date: Date
let configuration: ConfigurationIntent
- let mood: [MoodEntry]
+ let timeLineViews: [WatchTimelineView]
let showStats: Bool
- init(date: Date, configuration: ConfigurationIntent, mood: [MoodEntry], showStats: Bool = false) {
+ init(date: Date, configuration: ConfigurationIntent, timeLineViews: [WatchTimelineView], showStats: Bool = false) {
self.date = date
self.configuration = configuration
- self.mood = mood
+ self.timeLineViews = timeLineViews
self.showStats = showStats
}
}
@@ -71,19 +129,21 @@ struct FeelsWidgetEntryView : View {
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)
+ MediumWidgetView(entry: entry)
case .systemExtraLarge:
- LargeWidgetView(entry: entry)
+ 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()
}
}
}
@@ -92,14 +152,55 @@ struct SmallWidgetView: View {
var entry: Provider.Entry
var body: some View {
- VStack {
- if let first = entry.mood.first {
- EntryCardCollectionView(moodEntries: Array([first]))
- .padding()
- } else {
- Text("🤷♂️")
- .font(.system(size: 50))
+ ZStack {
+ Color(UIColor.secondarySystemBackground)
+ HStack {
+ ForEach([entry.timeLineViews.first!]) { watchView in
+ EntryCard(timeLineView: watchView)
+ }
}
+ .padding()
+ }
+ .clipShape(RoundedRectangle(cornerRadius: 25, style: .continuous))
+ .frame(minHeight: 0, maxHeight: 55)
+ .padding()
+ }
+}
+
+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()
}
}
}
@@ -107,32 +208,20 @@ struct SmallWidgetView: View {
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 firstGroup: [WatchTimelineView] {
+ Array(self.entry.timeLineViews.prefix(5))
}
var body: some View {
VStack {
Spacer()
- HStack {
- Text(firstGroup.first?.forDate ?? Date(), formatter: formatter)
- .font(.system(.footnote))
- Text(" - ")
- .font(.system(.footnote))
- Text(firstGroup.last?.forDate ?? Date(), formatter: formatter)
- .font(.system(.footnote))
- }
- .frame(minWidth: 0, maxWidth: .infinity)
- .multilineTextAlignment(.leading)
+ TimeHeaderView(startDate: firstGroup.first!.date, endDate: firstGroup.last!.date)
+ .frame(minWidth: 0, maxWidth: .infinity)
+ .multilineTextAlignment(.leading)
- EntryCardCollectionView(moodEntries: firstGroup)
+ TimeBodyView(group: firstGroup)
+ .clipShape(RoundedRectangle(cornerRadius: 25, style: .continuous))
.frame(minHeight: 0, maxHeight: 55)
.padding()
@@ -141,86 +230,47 @@ struct MediumWidgetView: View {
}
}
-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?.forDate ?? Date(), formatter: formatter)
- .font(.system(.footnote))
- Text(" - ")
- .font(.system(.footnote))
- Text(firstGroup.last?.forDate ?? 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?.forDate ?? Date(), formatter: formatter)
- .font(.system(.footnote))
- Text(" - ")
- Text(lastGroup.last?.forDate ?? Date(), formatter: formatter)
- .font(.system(.footnote))
- }
- .frame(minWidth: 0, maxWidth: .infinity)
- .multilineTextAlignment(.leading)
-
- EntryCardCollectionView(moodEntries: lastGroup)
- .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 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 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 timeLineView: WatchTimelineView
var body: some View {
- moodEntry.mood.icon.font(.system(size: 50))
+ timeLineView.image
+ .resizable()
+ .aspectRatio(contentMode: .fit)
+ .frame(width: 50, height: 50, alignment: .center)
+ .foregroundColor(timeLineView.color)
}
}
@@ -236,30 +286,37 @@ struct FeelsWidget: Widget {
}
.configurationDisplayName("Feels")
.description("")
- .supportedFamilies([.systemSmall, .systemMedium, .systemLarge])
+ .supportedFamilies([.systemSmall, .systemMedium])
}
}
struct FeelsWidget_Previews: PreviewProvider {
+ static var data: [WatchTimelineView] {
+ var data = PersistenceController.shared.randomEntries(count: 10)
+ data.remove(at: 2)
+ let views = TimeLineCreator.createTimeLineViews(fromEntries: data)
+ return views
+ }
+
static var previews: some View {
Group {
FeelsWidgetEntryView(entry: SimpleEntry(date: Date(),
configuration: ConfigurationIntent(),
- mood: PersistenceController.shared.randomEntries(count: 1)))
+ timeLineViews: FeelsWidget_Previews.data))
.previewContext(WidgetPreviewContext(family: .systemSmall))
.environment(\.sizeCategory, .small)
FeelsWidgetEntryView(entry: SimpleEntry(date: Date(),
configuration: ConfigurationIntent(),
- mood: PersistenceController.shared.randomEntries(count: 3)))
+ timeLineViews: FeelsWidget_Previews.data))
.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)
+//
+// FeelsWidgetEntryView(entry: SimpleEntry(date: Date(),
+// configuration: ConfigurationIntent(),
+// timeLineViews: FeelsWidget_Previews.data))
+// .previewContext(WidgetPreviewContext(family: .systemLarge))
+// .environment(\.sizeCategory, .large)
}
}
}
diff --git a/Shared/AppDelegate.swift b/Shared/AppDelegate.swift
index 97eff28..84649a7 100644
--- a/Shared/AppDelegate.swift
+++ b/Shared/AppDelegate.swift
@@ -12,21 +12,15 @@ import WidgetKit
// AppDelegate.swift
class AppDelegate: NSObject, UIApplicationDelegate {
+
+
+
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
// PersistenceController.shared.clearDB()
- NotificationCenter.default.addObserver(self,
- selector: #selector(fetchChanges),
- name: .NSPersistentStoreRemoteChange,
- object: PersistenceController.shared.container.persistentStoreCoordinator)
-
application.registerForRemoteNotifications()
UNUserNotificationCenter.current().delegate = self
return true
}
-
- @objc func fetchChanges(note: Notification) {
- WidgetCenter.shared.reloadAllTimelines()
- }
}
extension AppDelegate: UNUserNotificationCenterDelegate {
diff --git a/Shared/Assets.xcassets/missing.imageset/Contents.json b/Shared/Assets.xcassets/missing.imageset/Contents.json
index dabdec4..c82dff9 100644
--- a/Shared/Assets.xcassets/missing.imageset/Contents.json
+++ b/Shared/Assets.xcassets/missing.imageset/Contents.json
@@ -1,7 +1,7 @@
{
"images" : [
{
- "filename" : "Screen Shot 2022-01-13 at 5.02.34 PM.png",
+ "filename" : "exclamation-solid.svg",
"idiom" : "universal",
"scale" : "1x"
},
diff --git a/Shared/Assets.xcassets/missing.imageset/Screen Shot 2022-01-13 at 5.02.34 PM.png b/Shared/Assets.xcassets/missing.imageset/Screen Shot 2022-01-13 at 5.02.34 PM.png
deleted file mode 100644
index f2e0326..0000000
Binary files a/Shared/Assets.xcassets/missing.imageset/Screen Shot 2022-01-13 at 5.02.34 PM.png and /dev/null differ
diff --git a/Shared/Assets.xcassets/missing.imageset/exclamation-solid.svg b/Shared/Assets.xcassets/missing.imageset/exclamation-solid.svg
new file mode 100644
index 0000000..32de166
--- /dev/null
+++ b/Shared/Assets.xcassets/missing.imageset/exclamation-solid.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Shared/Persistence.swift b/Shared/Persistence.swift
index 275866d..e6e5909 100644
--- a/Shared/Persistence.swift
+++ b/Shared/Persistence.swift
@@ -6,8 +6,14 @@
//
import CoreData
+import WidgetKit
+import Foundation
+import UIKit
-struct PersistenceController {
+class PersistenceController {
+ private var dataUpdateCall: (() -> Void)?
+ private let callDelay = 10
+
static let shared = PersistenceController.persistenceController
private static var persistenceController: PersistenceController {
@@ -38,28 +44,6 @@ struct PersistenceController {
}
}
- public func moodEntries(forStartDate date: Date, count: Int) -> [MoodEntry] {
- let fetchRequest = NSFetchRequest(entityName: "MoodEntry")
-
- fetchRequest.fetchLimit = count
- fetchRequest.sortDescriptors = [NSSortDescriptor(key: "forDate", ascending: false)]
-
-// var calendar = Calendar.current
-// calendar.timeZone = NSTimeZone.local
-// let dateFrom = calendar.startOfDay(for: Date())
- // Set predicate as date being today's date
-// let fromPredicate = NSPredicate(format: "date <= %@", dateFrom as NSDate)
-// let datePredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [fromPredicate])
-// fetchRequest.predicate = datePredicate
- let entries = try! viewContext.fetch(fetchRequest)
-
- if entries.count >= count {
- return Array(entries)
- } else {
- return entries
- }
- }
-
public var earliestEntry: MoodEntry? {
let fetchRequest = NSFetchRequest(entityName: "MoodEntry")
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "forDate", ascending: true)]
@@ -75,6 +59,14 @@ struct PersistenceController {
}
public func getData(startDate: Date, endDate: Date, includedDays: [Int]) -> [MoodEntry] {
+ try! viewContext.setQueryGenerationFrom(.current)
+ viewContext.refreshAllObjects()
+
+ let fakeRequest = NSFetchRequest(entityName: "MoodEntry")
+ let fakeData = try! viewContext.fetch(fakeRequest)
+ for item in fakeData {
+ print(item.forDate!)
+ }
var includedDays16 = [Int16]()
if includedDays.isEmpty {
@@ -84,17 +76,16 @@ struct PersistenceController {
Int16($0)
})
}
- let predicate = NSPredicate(format: "%K >= %@ && %K <= %@ && weekDay IN %@",
- "forDate",
+ let predicate = NSPredicate(format: "forDate >= %@ && forDate <= %@ && weekDay IN %@",
startDate as NSDate,
- "forDate",
endDate as NSDate,
includedDays16)
let fetchRequest = NSFetchRequest(entityName: "MoodEntry")
fetchRequest.predicate = predicate
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "forDate", ascending: true)]
- return try! viewContext.fetch(fetchRequest)
+ let data = try! viewContext.fetch(fetchRequest)
+ return data
}
func populateTestData() {
diff --git a/Shared/Random.swift b/Shared/Random.swift
index ff689b3..45f39fe 100644
--- a/Shared/Random.swift
+++ b/Shared/Random.swift
@@ -8,7 +8,7 @@
import Foundation
class Random {
- static var widgetUpdateTime: Date {
+ static var tomorrowMidnightThirty: Date {
let components = DateComponents(hour: 0, minute: 30, second: 0)
var updateTime = Date()
if let tomorrow = Calendar.current.date(byAdding: .day, value: 1, to: Date()),
diff --git a/Shared/views/AddMoodHeaderView.swift b/Shared/views/AddMoodHeaderView.swift
index 89dba7f..b0aff8e 100644
--- a/Shared/views/AddMoodHeaderView.swift
+++ b/Shared/views/AddMoodHeaderView.swift
@@ -26,7 +26,7 @@ struct AddMoodHeaderView: View {
ForEach(Mood.allValues.reversed()) { mood in
VStack {
Button(action: {
- addItem(withMoodValue: mood.rawValue)
+ addItem(withMood: mood)
}, label: {
mood.icon
.resizable()
@@ -47,21 +47,9 @@ struct AddMoodHeaderView: View {
.padding()
}
- private func addItem(withMoodValue moodValue: Int) {
+ private func addItem(withMood mood: Mood) {
withAnimation {
- let newItem = MoodEntry(context: viewContext)
- newItem.timestamp = Date()
- newItem.moodValue = Int16(moodValue)
- newItem.forDate = Date()
-
- do {
- try viewContext.save()
- } catch {
- // Replace this implementation with code to handle the error appropriately.
- // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
- let nsError = error as NSError
- fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
- }
+ PersistenceController.shared.add(mood: mood, forDate: Date())
}
}
}
diff --git a/Shared/views/FilterView.swift b/Shared/views/FilterView.swift
index 4234988..8eaa47e 100644
--- a/Shared/views/FilterView.swift
+++ b/Shared/views/FilterView.swift
@@ -122,7 +122,7 @@ struct FilterView: View {
for day in 1...numDays {
if let item = monthEntries.filter({ entry in
- let components = calendar.dateComponents([.day, .weekday], from: entry.forDate!)
+ let components = calendar.dateComponents([.day], from: entry.forDate!)
let date = components.day
return day == date
}).first {