Merge branch 'develop' into sharing
This commit is contained in:
@@ -160,6 +160,7 @@
|
|||||||
1CAD603227A5C1C800C520BD /* ContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
1CAD603227A5C1C800C520BD /* ContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||||
1CAD603327A5C1C800C520BD /* HeaderStatsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderStatsView.swift; sourceTree = "<group>"; };
|
1CAD603327A5C1C800C520BD /* HeaderStatsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderStatsView.swift; sourceTree = "<group>"; };
|
||||||
1CAD603D27A6ECCD00C520BD /* SwitchableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchableView.swift; sourceTree = "<group>"; };
|
1CAD603D27A6ECCD00C520BD /* SwitchableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchableView.swift; sourceTree = "<group>"; };
|
||||||
|
1CC03FA627B5865600B530AF /* Shared 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Shared 2.xcdatamodel"; sourceTree = "<group>"; };
|
||||||
1CC469A9278F30A0003E0C6E /* BGTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGTask.swift; sourceTree = "<group>"; };
|
1CC469A9278F30A0003E0C6E /* BGTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGTask.swift; sourceTree = "<group>"; };
|
||||||
1CC469AB27907D48003E0C6E /* DayChartView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayChartView.swift; sourceTree = "<group>"; };
|
1CC469AB27907D48003E0C6E /* DayChartView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayChartView.swift; sourceTree = "<group>"; };
|
||||||
1CD90AEC278C7DDF001C4FEA /* Shared.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Shared.xcdatamodel; sourceTree = "<group>"; };
|
1CD90AEC278C7DDF001C4FEA /* Shared.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Shared.xcdatamodel; sourceTree = "<group>"; };
|
||||||
@@ -1227,9 +1228,10 @@
|
|||||||
1CD90AEB278C7DDF001C4FEA /* Feels.xcdatamodeld */ = {
|
1CD90AEB278C7DDF001C4FEA /* Feels.xcdatamodeld */ = {
|
||||||
isa = XCVersionGroup;
|
isa = XCVersionGroup;
|
||||||
children = (
|
children = (
|
||||||
|
1CC03FA627B5865600B530AF /* Shared 2.xcdatamodel */,
|
||||||
1CD90AEC278C7DDF001C4FEA /* Shared.xcdatamodel */,
|
1CD90AEC278C7DDF001C4FEA /* Shared.xcdatamodel */,
|
||||||
);
|
);
|
||||||
currentVersion = 1CD90AEC278C7DDF001C4FEA /* Shared.xcdatamodel */;
|
currentVersion = 1CC03FA627B5865600B530AF /* Shared 2.xcdatamodel */;
|
||||||
path = Feels.xcdatamodeld;
|
path = Feels.xcdatamodeld;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
versionGroupType = wrapper.xcdatamodel;
|
versionGroupType = wrapper.xcdatamodel;
|
||||||
|
|||||||
@@ -16,9 +16,14 @@ class AppDelegate: NSObject, UIApplicationDelegate {
|
|||||||
|
|
||||||
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
|
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
|
||||||
// PersistenceController.shared.clearDB()
|
// PersistenceController.shared.clearDB()
|
||||||
|
PersistenceController.shared.fillInMissingDates()
|
||||||
UNUserNotificationCenter.current().delegate = self
|
UNUserNotificationCenter.current().delegate = self
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func applicationWillEnterForeground(_ application: UIApplication) {
|
||||||
|
PersistenceController.shared.fillInMissingDates()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AppDelegate: UNUserNotificationCenterDelegate {
|
extension AppDelegate: UNUserNotificationCenterDelegate {
|
||||||
@@ -42,15 +47,15 @@ extension AppDelegate: UNUserNotificationCenterDelegate {
|
|||||||
|
|
||||||
switch action {
|
switch action {
|
||||||
case .horrible:
|
case .horrible:
|
||||||
PersistenceController.shared.add(mood: .horrible, forDate: date)
|
PersistenceController.shared.add(mood: .horrible, forDate: date, entryType: .notification)
|
||||||
case .bad:
|
case .bad:
|
||||||
PersistenceController.shared.add(mood: .bad, forDate: date)
|
PersistenceController.shared.add(mood: .bad, forDate: date, entryType: .notification)
|
||||||
case .average:
|
case .average:
|
||||||
PersistenceController.shared.add(mood: .average, forDate: date)
|
PersistenceController.shared.add(mood: .average, forDate: date, entryType: .notification)
|
||||||
case .good:
|
case .good:
|
||||||
PersistenceController.shared.add(mood: .good, forDate: date)
|
PersistenceController.shared.add(mood: .good, forDate: date, entryType: .notification)
|
||||||
case .great:
|
case .great:
|
||||||
PersistenceController.shared.add(mood: .great, forDate: date)
|
PersistenceController.shared.add(mood: .great, forDate: date, entryType: .notification)
|
||||||
}
|
}
|
||||||
UIApplication.shared.applicationIconBadgeNumber = 0
|
UIApplication.shared.applicationIconBadgeNumber = 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>_XCCurrentVersionName</key>
|
<key>_XCCurrentVersionName</key>
|
||||||
<string>Shared.xcdatamodel</string>
|
<string>Shared 2.xcdatamodel</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
15
Shared/Feels.xcdatamodeld/Shared 2.xcdatamodel/contents
Normal file
15
Shared/Feels.xcdatamodeld/Shared 2.xcdatamodel/contents
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19574" systemVersion="21D49" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="YES" userDefinedModelVersionIdentifier="">
|
||||||
|
<entity name="MoodEntry" representedClassName="MoodEntry" syncable="YES" codeGenerationType="class">
|
||||||
|
<attribute name="canDelete" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||||
|
<attribute name="canEdit" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||||
|
<attribute name="entryType" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
|
<attribute name="forDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
|
<attribute name="moodValue" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
|
<attribute name="timestamp" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
|
<attribute name="weekDay" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
|
</entity>
|
||||||
|
<elements>
|
||||||
|
<element name="MoodEntry" positionX="-63" positionY="-18" width="128" height="134"/>
|
||||||
|
</elements>
|
||||||
|
</model>
|
||||||
@@ -36,7 +36,6 @@ struct FeelsApp: App {
|
|||||||
|
|
||||||
if phase == .active {
|
if phase == .active {
|
||||||
UIApplication.shared.applicationIconBadgeNumber = 0
|
UIApplication.shared.applicationIconBadgeNumber = 0
|
||||||
persistenceController.fillInMissingDates()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,8 +86,8 @@ class ContentModeViewModel: ObservableObject {
|
|||||||
getGroupedData()
|
getGroupedData()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func add(mood: Mood, forDate date: Date) {
|
public func add(mood: Mood, forDate date: Date, entryType: EntryType) {
|
||||||
PersistenceController.shared.add(mood: mood, forDate: date)
|
PersistenceController.shared.add(mood: mood, forDate: date, entryType: entryType)
|
||||||
getGroupedData()
|
getGroupedData()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@ class ContentModeViewModel: ObservableObject {
|
|||||||
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
|
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
|
||||||
}
|
}
|
||||||
|
|
||||||
PersistenceController.shared.add(mood: mood, forDate: forDate)
|
PersistenceController.shared.add(mood: mood, forDate: forDate, entryType: .listView)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try PersistenceController.shared.viewContext.save()
|
try PersistenceController.shared.viewContext.save()
|
||||||
@@ -135,7 +135,7 @@ class ContentModeViewModel: ObservableObject {
|
|||||||
entriesToDelete.forEach({ entry in
|
entriesToDelete.forEach({ entry in
|
||||||
let entryDate = entry.forDate!
|
let entryDate = entry.forDate!
|
||||||
PersistenceController.shared.viewContext.delete(entry)
|
PersistenceController.shared.viewContext.delete(entry)
|
||||||
self.add(mood: .missing, forDate: entryDate)
|
self.add(mood: .missing, forDate: entryDate, entryType: .listView)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,11 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
enum EntryType: Int {
|
||||||
|
case notification
|
||||||
|
case header
|
||||||
|
case listView
|
||||||
|
}
|
||||||
|
|
||||||
extension MoodEntry {
|
extension MoodEntry {
|
||||||
var moodString: String {
|
var moodString: String {
|
||||||
|
|||||||
@@ -34,7 +34,12 @@ class PersistenceController {
|
|||||||
return data.first
|
return data.first
|
||||||
}
|
}
|
||||||
|
|
||||||
public func add(mood: Mood, forDate date: Date) {
|
public func add(mood: Mood, forDate date: Date, entryType: EntryType) {
|
||||||
|
if let existingEntry = getEntry(byDate: date) {
|
||||||
|
viewContext.delete(existingEntry)
|
||||||
|
try? viewContext.save()
|
||||||
|
}
|
||||||
|
|
||||||
let newItem = MoodEntry(context: viewContext)
|
let newItem = MoodEntry(context: viewContext)
|
||||||
newItem.timestamp = Date()
|
newItem.timestamp = Date()
|
||||||
newItem.moodValue = Int16(mood.rawValue)
|
newItem.moodValue = Int16(mood.rawValue)
|
||||||
@@ -42,6 +47,7 @@ class PersistenceController {
|
|||||||
newItem.weekDay = Int16(Calendar.current.component(.weekday, from: date))
|
newItem.weekDay = Int16(Calendar.current.component(.weekday, from: date))
|
||||||
newItem.canEdit = true
|
newItem.canEdit = true
|
||||||
newItem.canDelete = true
|
newItem.canDelete = true
|
||||||
|
newItem.entryType = Int16(entryType.rawValue)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try viewContext.save()
|
try viewContext.save()
|
||||||
@@ -139,14 +145,26 @@ class PersistenceController {
|
|||||||
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "forDate", ascending: false)]
|
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "forDate", ascending: false)]
|
||||||
let entries = try! viewContext.fetch(fetchRequest)
|
let entries = try! viewContext.fetch(fetchRequest)
|
||||||
|
|
||||||
if let earliestDate = entries.last?.forDate,
|
if let firstEntry = entries.last?.forDate {
|
||||||
let diffInDays = Calendar.current.dateComponents([.day], from: earliestDate, to: Date()).day,
|
let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: Date())!
|
||||||
diffInDays > 1 {
|
let allDates: [Date] = Date.dates(from: firstEntry, to: yesterday).map({
|
||||||
for idx in 1..<diffInDays {
|
let zeroDate = Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: $0)!
|
||||||
if let searchDay = Calendar.current.date(byAdding: .day, value: -idx, to: Date()),
|
return zeroDate
|
||||||
entries.filter({ Calendar.current.isDate($0.forDate!, inSameDayAs:searchDay) }).isEmpty {
|
})
|
||||||
self.add(mood: .missing, forDate: searchDay)
|
|
||||||
|
let existingEntries: [Date] = entries.compactMap({
|
||||||
|
if let date = $0.forDate {
|
||||||
|
let zeroDate = Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: date)!
|
||||||
|
return zeroDate
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
let allDatesSet = Set(allDates)
|
||||||
|
let existingEntriesSet = Set(existingEntries)
|
||||||
|
let missing = Array(allDatesSet.subtracting(existingEntriesSet)).sorted(by: >)
|
||||||
|
for date in missing {
|
||||||
|
add(mood: .missing, forDate: date, entryType: .listView)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -202,6 +220,8 @@ class PersistenceController {
|
|||||||
for description in container.persistentStoreDescriptions {
|
for description in container.persistentStoreDescriptions {
|
||||||
description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
|
description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
|
||||||
description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
|
description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
|
||||||
|
description.setOption(true as NSNumber, forKey: NSMigratePersistentStoresAutomaticallyOption)
|
||||||
|
description.setOption(true as NSNumber, forKey: NSInferMappingModelAutomaticallyOption)
|
||||||
}
|
}
|
||||||
|
|
||||||
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
|
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
|
||||||
|
|||||||
@@ -69,3 +69,17 @@ extension View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Date {
|
||||||
|
static func dates(from fromDate: Date, to toDate: Date) -> [Date] {
|
||||||
|
var dates: [Date] = []
|
||||||
|
var date = fromDate
|
||||||
|
|
||||||
|
while date <= toDate {
|
||||||
|
dates.append(date)
|
||||||
|
guard let newDate = Calendar.current.date(byAdding: .day, value: 1, to: date) else { break }
|
||||||
|
date = newDate
|
||||||
|
}
|
||||||
|
return dates
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ struct ContentView: View {
|
|||||||
viewModel.update(entry: selectedEntry, toMood: mood)
|
viewModel.update(entry: selectedEntry, toMood: mood)
|
||||||
}
|
}
|
||||||
showUpdateEntryAlert = false
|
showUpdateEntryAlert = false
|
||||||
|
selectedEntry = nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,7 +280,7 @@ struct ContentView: View {
|
|||||||
if viewModel.shouldShowVotingHeader() {
|
if viewModel.shouldShowVotingHeader() {
|
||||||
AddMoodHeaderView(addItemHeaderClosure: { (mood, date) in
|
AddMoodHeaderView(addItemHeaderClosure: { (mood, date) in
|
||||||
withAnimation {
|
withAnimation {
|
||||||
viewModel.add(mood: mood, forDate: date)
|
viewModel.add(mood: mood, forDate: date, entryType: .header)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.frame(height: headerHeight)
|
.frame(height: headerHeight)
|
||||||
@@ -319,7 +320,7 @@ struct ContentView: View {
|
|||||||
.padding()
|
.padding()
|
||||||
AddMoodHeaderView(addItemHeaderClosure: { (mood, date) in
|
AddMoodHeaderView(addItemHeaderClosure: { (mood, date) in
|
||||||
withAnimation {
|
withAnimation {
|
||||||
viewModel.add(mood: mood, forDate: date)
|
viewModel.add(mood: mood, forDate: date, entryType: .header)
|
||||||
}
|
}
|
||||||
}, overrideDay: viewModel.shouldShowVotingHeader() ? .Today : .Previous)
|
}, overrideDay: viewModel.shouldShowVotingHeader() ? .Today : .Previous)
|
||||||
}
|
}
|
||||||
@@ -368,6 +369,10 @@ struct ContentView: View {
|
|||||||
theme.currentTheme.bg
|
theme.currentTheme.bg
|
||||||
.edgesIgnoringSafeArea(.all)
|
.edgesIgnoringSafeArea(.all)
|
||||||
)
|
)
|
||||||
|
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
|
||||||
|
PersistenceController.shared.fillInMissingDates()
|
||||||
|
viewModel.updateData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,13 +58,20 @@ struct SmallRollUpHeaderView: View {
|
|||||||
private var circularViews: some View {
|
private var circularViews: some View {
|
||||||
HStack {
|
HStack {
|
||||||
ForEach(entries, id: \.0) { (mood, value) in
|
ForEach(entries, id: \.0) { (mood, value) in
|
||||||
Text(String(value))
|
ZStack {
|
||||||
.font(.title)
|
Circle().fill(mood.color)
|
||||||
.fontWeight(.bold)
|
.frame(width: 50, height: 50)
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
.padding()
|
Text(String(value))
|
||||||
.background(Circle().fill(mood.color))
|
.font(.title)
|
||||||
.foregroundColor(Color(UIColor.white))
|
.fontWeight(.bold)
|
||||||
|
.frame(maxWidth: 50)
|
||||||
|
.foregroundColor(Color(UIColor.white))
|
||||||
|
.scaledToFill()
|
||||||
|
.minimumScaleFactor(0.5)
|
||||||
|
.lineLimit(1)
|
||||||
|
}
|
||||||
|
.padding([.leading, .trailing], 5)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user