diff --git a/Shared/FeelsApp.swift b/Shared/FeelsApp.swift index 42e36f7..0bc183a 100644 --- a/Shared/FeelsApp.swift +++ b/Shared/FeelsApp.swift @@ -15,9 +15,9 @@ struct FeelsApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate let persistenceController = PersistenceController.shared - + init() { -// persistenceController.fillInMissingDates() + persistenceController.fillInMissingDates() BGTaskScheduler.shared.cancelAllTaskRequests() BGTaskScheduler.shared.register(forTaskWithIdentifier: BGTask.updateDBMissingID, using: nil) { (task) in BGTask.runFillInMissingDatesTask(task: task as! BGProcessingTask) diff --git a/Shared/Models/ContentModeViewModel.swift b/Shared/Models/ContentModeViewModel.swift index 5856d73..6488bb9 100644 --- a/Shared/Models/ContentModeViewModel.swift +++ b/Shared/Models/ContentModeViewModel.swift @@ -63,6 +63,36 @@ class ContentModeViewModel: ObservableObject { getGroupedData() } + public func update(entry: MoodEntry, toMood mood: Mood) { + let forDate = entry.forDate! + + if let entry = PersistenceController.shared.getEntry(byDate: entry.forDate!) { + PersistenceController.shared.viewContext.delete(entry) + } + + do { + try PersistenceController.shared.viewContext.save() + getGroupedData() + } 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: forDate) + + do { + try PersistenceController.shared.viewContext.save() + getGroupedData() + } 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)") + } + } + public func delete(offsets: IndexSet, inMonth month: Int, inYear year: Int) { if let monthEntries = grouped[year], let entries = monthEntries[month] { diff --git a/Shared/Persistence.swift b/Shared/Persistence.swift index 29cf56a..21adb8e 100644 --- a/Shared/Persistence.swift +++ b/Shared/Persistence.swift @@ -29,6 +29,17 @@ class PersistenceController { return PersistenceController.shared.container.viewContext } + public func getEntry(byDate date: Date) -> MoodEntry? { + let predicate = NSPredicate(format: "forDate == %@", + date as NSDate) + + let fetchRequest = NSFetchRequest(entityName: "MoodEntry") + fetchRequest.predicate = predicate + fetchRequest.sortDescriptors = [NSSortDescriptor(key: "forDate", ascending: true)] + let data = try! viewContext.fetch(fetchRequest) + return data.first + } + public func add(mood: Mood, forDate date: Date) { let newItem = MoodEntry(context: viewContext) newItem.timestamp = Date() diff --git a/Shared/views/BGView.swift b/Shared/views/BGView.swift index cc66668..32dc40c 100644 --- a/Shared/views/BGView.swift +++ b/Shared/views/BGView.swift @@ -32,7 +32,7 @@ struct BGViewItem: View { } } -struct BGView: View { +struct BGView: View, Equatable { var numAcross: Int var numDown: Int let iconSize = 35 @@ -61,6 +61,10 @@ struct BGView: View { } .padding(.top, -50) } + + static func == (lhs: BGView, rhs: BGView) -> Bool { + return true + } } struct BGView_Previews: PreviewProvider { diff --git a/Shared/views/ContentView.swift b/Shared/views/ContentView.swift index f18cdab..fb1ade7 100644 --- a/Shared/views/ContentView.swift +++ b/Shared/views/ContentView.swift @@ -15,7 +15,9 @@ struct ContentView: View { @State private var showingSheet = false @State private var showTodayInput = true - + @State private var selectedMissingEntry: MoodEntry? + @State private var showMissingAlert = false + @ObservedObject var viewModel = ContentModeViewModel() init(){ @@ -46,7 +48,20 @@ struct ContentView: View { needsOnboarding = false viewModel.updateOnboardingData(onboardingData: onboardingData) }) - }) + }).alert(String(localized: "content_view_fill_in_missing_entry"), isPresented: $showMissingAlert) { + ForEach(Mood.allValues) { mood in + Button(mood.strValue, action: { + if let selectedMissingEntry = selectedMissingEntry { + viewModel.update(entry: selectedMissingEntry, toMood: mood) + } + showMissingAlert = false + }) + } + Button(String(localized: "content_view_fill_in_missing_entry_cancel"), role: .cancel, action: { + selectedMissingEntry = nil + showMissingAlert = false + }) + } } private var settingsButtonView: some View { @@ -114,9 +129,15 @@ struct ContentView: View { }) { // for reach all entries ForEach(entries.sorted(by: { - $0.forDate! > $1.forDate! + return $0.forDate! > $1.forDate! }), id: \.self) { entry in entryListView(entry: entry) + .onTapGesture(perform: { + if entry.moodValue == Mood.missing.rawValue { + selectedMissingEntry = entry + showMissingAlert = true + } + }) }.onDelete(perform: { offsets in withAnimation { viewModel.delete(offsets: offsets, inMonth: month, inYear: year) @@ -167,7 +188,9 @@ struct ContentView: View { private var mainView: some View { ZStack { - BGView() + + BGView().equatable() + VStack{ settingsButtonView if viewModel.shouldShowVotingHeader() { @@ -182,7 +205,7 @@ struct ContentView: View { HeaderStatsView(fakeData: false, backDays: 30) .frame(height: 180) // should match backDays above - Text("Past \(30) days") + Text(String(format: String(localized: "content_view_header_title"), 30)) .font(.body) .foregroundColor(Color(UIColor.systemGray)) .frame(maxWidth: .infinity, alignment: .center) diff --git a/en.lproj/Localizable.strings b/en.lproj/Localizable.strings index 1c5c3c6..e8a768f 100644 --- a/en.lproj/Localizable.strings +++ b/en.lproj/Localizable.strings @@ -33,6 +33,9 @@ "content_view_tab_main" = "Main"; "content_view_tab_filter" = "Filter"; "content_view_tab_stats" = "Stats"; +"content_view_fill_in_missing_entry" = "Fill in missing entry"; +"content_view_fill_in_missing_entry_cancel" = "Cancel"; +"content_view_header_title" = "Past %d days"; "filter_view_total" = "Total"; "filter_view_show_filters" = "Show Filters";