|
|
|
|
@ -42,11 +42,11 @@ class TimerSpot : Identifiable, Equatable { |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
class TimersModel : ObservableObject { |
|
|
|
|
|
|
|
|
|
@Published var spots: [TimerSpot] = [] |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
//class TimersModel : ObservableObject { |
|
|
|
|
// |
|
|
|
|
// @Published var spots: [TimerSpot] = [] |
|
|
|
|
// |
|
|
|
|
//} |
|
|
|
|
|
|
|
|
|
struct ContentView<T : AbstractTimer>: View { |
|
|
|
|
|
|
|
|
|
@ -55,30 +55,17 @@ struct ContentView<T : AbstractTimer>: View { |
|
|
|
|
|
|
|
|
|
@Environment(\.managedObjectContext) private var viewContext |
|
|
|
|
|
|
|
|
|
@StateObject fileprivate var model: TimersModel = TimersModel() |
|
|
|
|
// @StateObject fileprivate var model: TimersModel = TimersModel() |
|
|
|
|
|
|
|
|
|
@FetchRequest( |
|
|
|
|
sortDescriptors: [NSSortDescriptor(keyPath: \T.order, ascending: true)], |
|
|
|
|
animation: .default) |
|
|
|
|
private var timers: FetchedResults<T> { |
|
|
|
|
didSet { |
|
|
|
|
self._buildItemsList() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
private var timers: FetchedResults<T> |
|
|
|
|
|
|
|
|
|
var coreDataPublisher: NotificationCenter.Publisher { NotificationCenter.default |
|
|
|
|
.publisher(for: .NSManagedObjectContextDidSave, object: viewContext) } |
|
|
|
|
var cloudkitPublisher: NotificationCenter.Publisher { NotificationCenter.default |
|
|
|
|
.publisher(for: Notification.Name(rawValue: "NSPersistentStoreRemoteChangeNotificationOptionKey"), object: viewContext) } |
|
|
|
|
// var coreDataPublisher: NotificationCenter.Publisher { NotificationCenter.default |
|
|
|
|
// .publisher(for: .NSManagedObjectContextDidSave, object: viewContext) } |
|
|
|
|
|
|
|
|
|
@State private var isEditing: Bool = false { |
|
|
|
|
didSet { |
|
|
|
|
if self.isEditing == false { |
|
|
|
|
self._saveOrder() |
|
|
|
|
} |
|
|
|
|
self._buildItemsList() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@State private var isEditing: Bool = false |
|
|
|
|
|
|
|
|
|
fileprivate let itemSpacing: CGFloat = 10.0 |
|
|
|
|
|
|
|
|
|
@ -101,43 +88,41 @@ struct ContentView<T : AbstractTimer>: View { |
|
|
|
|
spacing: itemSpacing |
|
|
|
|
) { |
|
|
|
|
|
|
|
|
|
if !self.isEditing { |
|
|
|
|
|
|
|
|
|
ForEach(self.model.spots) { spot in |
|
|
|
|
if let timer = spot.timer { |
|
|
|
|
DialView(timer: timer, isEditingBinding: self.$isEditing, frameSize: width) |
|
|
|
|
.environment(\.managedObjectContext, viewContext) |
|
|
|
|
.environmentObject(Conductor.maestro) |
|
|
|
|
.environmentObject(boringContext) |
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
Color.clear |
|
|
|
|
.frame(width: width, height: 80.0) |
|
|
|
|
.cornerRadius(20.0) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
ReorderableForEach(items: Array(timers)) { timer in |
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
|
|
ReorderableForEach(items: self.model.spots) { spot in |
|
|
|
|
|
|
|
|
|
if let timer = spot.timer { |
|
|
|
|
DialView(timer: timer, isEditingBinding: self.$isEditing, frameSize: width) |
|
|
|
|
.environment(\.managedObjectContext, viewContext) |
|
|
|
|
.environmentObject(Conductor.maestro) |
|
|
|
|
.environmentObject(boringContext) |
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
|
|
Color(white: 0.9) |
|
|
|
|
.frame(width: width, height: 80.0) |
|
|
|
|
.cornerRadius(20.0) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} moveAction: { from, to in |
|
|
|
|
self._reorderSpots(from: from, to: to) |
|
|
|
|
} |
|
|
|
|
DialView(timer: timer, isEditingBinding: self.$isEditing, frameSize: width) |
|
|
|
|
.environment(\.managedObjectContext, viewContext) |
|
|
|
|
.environmentObject(Conductor.maestro) |
|
|
|
|
.environmentObject(boringContext) |
|
|
|
|
} moveAction: { from, to in |
|
|
|
|
self._reorder(from: from, to: to) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// if !self.isEditing { |
|
|
|
|
// |
|
|
|
|
// |
|
|
|
|
// } |
|
|
|
|
// else { |
|
|
|
|
// |
|
|
|
|
// ReorderableForEach(items: self.model.spots) { spot in |
|
|
|
|
// |
|
|
|
|
// if let timer = spot.timer { |
|
|
|
|
// DialView(timer: timer, isEditingBinding: self.$isEditing, frameSize: width) |
|
|
|
|
// .environment(\.managedObjectContext, viewContext) |
|
|
|
|
// .environmentObject(Conductor.maestro) |
|
|
|
|
// .environmentObject(boringContext) |
|
|
|
|
// |
|
|
|
|
// } else { |
|
|
|
|
// |
|
|
|
|
// Color(white: 0.9) |
|
|
|
|
// .frame(width: width, height: 80.0) |
|
|
|
|
// .cornerRadius(20.0) |
|
|
|
|
// } |
|
|
|
|
// |
|
|
|
|
// } moveAction: { from, to in |
|
|
|
|
// self._reorderSpots(from: from, to: to) |
|
|
|
|
// } |
|
|
|
|
// } |
|
|
|
|
} |
|
|
|
|
}.padding(.horizontal, itemSpacing) |
|
|
|
|
|
|
|
|
|
@ -188,13 +173,13 @@ struct ContentView<T : AbstractTimer>: View { |
|
|
|
|
// self._buildItemsList() |
|
|
|
|
// } |
|
|
|
|
// }) |
|
|
|
|
.onReceive(coreDataPublisher, perform: { _ in |
|
|
|
|
withAnimation { |
|
|
|
|
self._buildItemsList() |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
// .onReceive(coreDataPublisher, perform: { _ in |
|
|
|
|
// withAnimation { |
|
|
|
|
// self._buildItemsList() |
|
|
|
|
// } |
|
|
|
|
// }) |
|
|
|
|
.onAppear { |
|
|
|
|
self._buildItemsList() |
|
|
|
|
// self._buildItemsList() |
|
|
|
|
self._askPermissions() |
|
|
|
|
} |
|
|
|
|
.onOpenURL { url in |
|
|
|
|
@ -203,32 +188,32 @@ struct ContentView<T : AbstractTimer>: View { |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fileprivate func _buildItemsList() { |
|
|
|
|
|
|
|
|
|
var spots: [TimerSpot] = [] |
|
|
|
|
|
|
|
|
|
let more: Int = self.isEditing ? 20 : 0 // add 20 empty spots when editing |
|
|
|
|
|
|
|
|
|
let count = max(self.timers.count, Int(self.timers.last?.order ?? 0) + 1) + more |
|
|
|
|
|
|
|
|
|
for i in 0..<count { |
|
|
|
|
let timer = self.timers.first(where: { $0.order == i }) |
|
|
|
|
let spot = TimerSpot(order: Int16(i), timer: timer) |
|
|
|
|
spots.append(spot) |
|
|
|
|
} |
|
|
|
|
self.model.spots = spots |
|
|
|
|
} |
|
|
|
|
// fileprivate func _buildItemsList() { |
|
|
|
|
// |
|
|
|
|
// var spots: [TimerSpot] = [] |
|
|
|
|
// |
|
|
|
|
// let more: Int = self.isEditing ? 20 : 0 // add 20 empty spots when editing |
|
|
|
|
// |
|
|
|
|
// let count = max(self.timers.count, Int(self.timers.last?.order ?? 0) + 1) + more |
|
|
|
|
// |
|
|
|
|
// for i in 0..<count { |
|
|
|
|
// let timer = self.timers.first(where: { $0.order == i }) |
|
|
|
|
// let spot = TimerSpot(order: Int16(i), timer: timer) |
|
|
|
|
// spots.append(spot) |
|
|
|
|
// } |
|
|
|
|
// self.model.spots = spots |
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
fileprivate func _saveOrder() { |
|
|
|
|
for (i, spot) in self.model.spots.enumerated() { |
|
|
|
|
spot.setOrder(order: Int16(i)) |
|
|
|
|
} |
|
|
|
|
do { |
|
|
|
|
try viewContext.save() |
|
|
|
|
} catch { |
|
|
|
|
self.boringContext.error = error |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// fileprivate func _saveOrder() { |
|
|
|
|
// for (i, spot) in self.model.spots.enumerated() { |
|
|
|
|
// spot.setOrder(order: Int16(i)) |
|
|
|
|
// } |
|
|
|
|
// do { |
|
|
|
|
// try viewContext.save() |
|
|
|
|
// } catch { |
|
|
|
|
// self.boringContext.error = error |
|
|
|
|
// } |
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
// MARK: - Subviews |
|
|
|
|
|
|
|
|
|
@ -249,25 +234,25 @@ struct ContentView<T : AbstractTimer>: View { |
|
|
|
|
|
|
|
|
|
// MARK: - Business |
|
|
|
|
|
|
|
|
|
fileprivate func _reorderSpots(from: IndexSet, to: Int) { |
|
|
|
|
var spots: [TimerSpot] = self.model.spots |
|
|
|
|
spots.move(fromOffsets: from, toOffset: to) |
|
|
|
|
self.model.spots = spots |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// fileprivate func _reorder(from: IndexSet, to: Int) { |
|
|
|
|
// var timers: [AbstractTimer] = Array(self.timers) |
|
|
|
|
// timers.move(fromOffsets: from, toOffset: to) |
|
|
|
|
// for (i, countdown) in timers.enumerated() { |
|
|
|
|
// countdown.order = Int16(i) |
|
|
|
|
// } |
|
|
|
|
// do { |
|
|
|
|
// try viewContext.save() |
|
|
|
|
// } catch { |
|
|
|
|
// self.boringContext.error = error |
|
|
|
|
// } |
|
|
|
|
// fileprivate func _reorderSpots(from: IndexSet, to: Int) { |
|
|
|
|
// var spots: [TimerSpot] = self.model.spots |
|
|
|
|
// spots.move(fromOffsets: from, toOffset: to) |
|
|
|
|
// self.model.spots = spots |
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
fileprivate func _reorder(from: IndexSet, to: Int) { |
|
|
|
|
var timers: [AbstractTimer] = Array(self.timers) |
|
|
|
|
timers.move(fromOffsets: from, toOffset: to) |
|
|
|
|
for (i, countdown) in timers.enumerated() { |
|
|
|
|
countdown.order = Int16(i) |
|
|
|
|
} |
|
|
|
|
do { |
|
|
|
|
try viewContext.save() |
|
|
|
|
} catch { |
|
|
|
|
self.boringContext.error = error |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fileprivate func _askPermissions() { |
|
|
|
|
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .criticalAlert]) { success, error in |
|
|
|
|
print("requestAuthorization > success = \(success), error = \(String(describing: error))") |
|
|
|
|
|