|
|
|
|
@ -9,27 +9,69 @@ import SwiftUI |
|
|
|
|
import CoreData |
|
|
|
|
|
|
|
|
|
class RecordsModel: ObservableObject { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Published var filters: [Filter] = [] |
|
|
|
|
@Published var recordsByFilter: [Filter : [Record]] = [:] |
|
|
|
|
|
|
|
|
|
func loadFilters(activity: Activity, timeFrame: TimeFrame, context: NSManagedObjectContext) { |
|
|
|
|
self.filters = timeFrame.filters(context: context) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
struct RecordsView: View { |
|
|
|
|
|
|
|
|
|
@Environment(\.managedObjectContext) private var viewContext |
|
|
|
|
|
|
|
|
|
var activity: Activity |
|
|
|
|
|
|
|
|
|
var timeFrame: TimeFrame { |
|
|
|
|
didSet { |
|
|
|
|
self._loadFilters() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@StateObject var model: RecordsModel = RecordsModel() |
|
|
|
|
|
|
|
|
|
// var request: FetchRequest<Record> |
|
|
|
|
|
|
|
|
|
// init(activity: Activity, timeFrame: TimeFrame) { |
|
|
|
|
// self.activity = activity |
|
|
|
|
// self.timeFrame = timeFrame |
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
var body: some View { |
|
|
|
|
|
|
|
|
|
if self.recordsByFilter.isEmpty { |
|
|
|
|
self.filters = timeFrame.filters(context: context) |
|
|
|
|
for filter in filters { |
|
|
|
|
let records = self._retrieveRecords(activity: activity, filter: filter, context: context) |
|
|
|
|
self.recordsByFilter[filter] = records |
|
|
|
|
List { |
|
|
|
|
ForEach(self.model.filters) { filter in |
|
|
|
|
RecordsSectionView(activity: self.activity, filter: filter) |
|
|
|
|
.environment(\.managedObjectContext, viewContext) |
|
|
|
|
} |
|
|
|
|
}.onChange(of: self.timeFrame) { newValue in |
|
|
|
|
self._loadFilters() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fileprivate func _loadFilters() { |
|
|
|
|
self.model.loadFilters(activity: self.activity, timeFrame: self.timeFrame, context: self.viewContext) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
class RecordsSectionModel: ObservableObject { |
|
|
|
|
|
|
|
|
|
@Published var records: [Record] = [] |
|
|
|
|
|
|
|
|
|
func loadRecords(activity: Activity, filter: Filter, context: NSManagedObjectContext) { |
|
|
|
|
let records = self._retrieveRecords(activity: activity, filter: filter, context: context) |
|
|
|
|
self.records = records |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fileprivate func _retrieveRecords(activity: Activity, filter: Filter, context: NSManagedObjectContext) -> [Record] { |
|
|
|
|
do { |
|
|
|
|
let request = Record.fetchRequest() |
|
|
|
|
let activityPredicate = NSPredicate(format: "activity = %@", activity) |
|
|
|
|
request.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [activityPredicate, filter.predicate]) |
|
|
|
|
request.sortDescriptors = [NSSortDescriptor(key: "start", ascending: true)] |
|
|
|
|
request.sortDescriptors = [NSSortDescriptor(key: "start", ascending: false)] |
|
|
|
|
return try context.fetch(request) |
|
|
|
|
} catch { |
|
|
|
|
Logger.log(error) |
|
|
|
|
@ -37,65 +79,54 @@ class RecordsModel: ObservableObject { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func records(by filter: Filter) -> [Record] { |
|
|
|
|
return self.recordsByFilter[filter] ?? [] |
|
|
|
|
func deleteItem(_ indexSet: IndexSet, context: NSManagedObjectContext) { |
|
|
|
|
|
|
|
|
|
for index in indexSet { |
|
|
|
|
let item = self.records[index] |
|
|
|
|
context.delete(item) |
|
|
|
|
|
|
|
|
|
do { |
|
|
|
|
try context.save() |
|
|
|
|
} catch { |
|
|
|
|
Logger.error(error) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
struct RecordsView: View { |
|
|
|
|
struct RecordsSectionView: View { |
|
|
|
|
|
|
|
|
|
@Environment(\.managedObjectContext) private var viewContext |
|
|
|
|
|
|
|
|
|
var activity: Activity |
|
|
|
|
var timeFrame: TimeFrame |
|
|
|
|
var request: FetchRequest<Record> |
|
|
|
|
|
|
|
|
|
@StateObject private var model: RecordsModel = RecordsModel() |
|
|
|
|
@State var filter: Filter |
|
|
|
|
|
|
|
|
|
init(activity: Activity, timeFrame: TimeFrame) { |
|
|
|
|
self.activity = activity |
|
|
|
|
self.timeFrame = timeFrame |
|
|
|
|
let predicate = NSPredicate(format: "activity = %@", activity) |
|
|
|
|
self.request = FetchRequest(sortDescriptors: [NSSortDescriptor(key: "start", ascending: false)], predicate: predicate) |
|
|
|
|
} |
|
|
|
|
@StateObject var model: RecordsSectionModel = RecordsSectionModel() |
|
|
|
|
|
|
|
|
|
var body: some View { |
|
|
|
|
|
|
|
|
|
ForEach(self.model.filters) { filter in |
|
|
|
|
|
|
|
|
|
Section(filter.localizedString) { |
|
|
|
|
ForEach(self.model.records(by: filter)) { record in |
|
|
|
|
HStack { |
|
|
|
|
Text(record.formattedDay) |
|
|
|
|
Spacer() |
|
|
|
|
if let duration = record.duration { |
|
|
|
|
Text(duration.minuteSecond) |
|
|
|
|
} |
|
|
|
|
Section(filter.localizedString) { |
|
|
|
|
ForEach(self.model.records) { record in |
|
|
|
|
HStack { |
|
|
|
|
Text(record.formattedDay) |
|
|
|
|
Spacer() |
|
|
|
|
if let duration = record.duration { |
|
|
|
|
Text(duration.minuteSecond) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
.onDelete(perform: _deleteItem) |
|
|
|
|
.onAppear { |
|
|
|
|
self.model.loadFilters(activity: self.activity, timeFrame: self.timeFrame, context: self.viewContext) |
|
|
|
|
}.onDelete(perform: _deleteItem) |
|
|
|
|
}.onAppear { |
|
|
|
|
self.model.loadRecords(activity: self.activity, |
|
|
|
|
filter: self.filter, |
|
|
|
|
context: viewContext) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fileprivate func _deleteItem(_ indexSet: IndexSet) { |
|
|
|
|
|
|
|
|
|
for index in indexSet { |
|
|
|
|
let item = self.request.wrappedValue[index] |
|
|
|
|
viewContext.delete(item) |
|
|
|
|
|
|
|
|
|
do { |
|
|
|
|
try viewContext.save() |
|
|
|
|
} catch { |
|
|
|
|
Logger.error(error) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
self.model.deleteItem(indexSet, context: self.viewContext) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
@ -103,6 +134,7 @@ struct RecordsView: View { |
|
|
|
|
struct RecordsView_Previews: PreviewProvider { |
|
|
|
|
static var previews: some View { |
|
|
|
|
RecordsView(activity: |
|
|
|
|
Activity.fake(context: PersistenceController.preview.container.viewContext), timeFrame: .all) |
|
|
|
|
Activity.fake(context: PersistenceController.preview.container.viewContext), |
|
|
|
|
timeFrame: .all) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|