|
|
@@ -1,3 +1,4 @@
|
|
|
+import Charts
|
|
|
import SwiftUI
|
|
|
import Swinject
|
|
|
|
|
|
@@ -7,6 +8,9 @@ extension BasalProfileEditor {
|
|
|
@State var state = StateModel()
|
|
|
@State private var editMode = EditMode.inactive
|
|
|
|
|
|
+ let chartScale = Calendar.current
|
|
|
+ .date(from: DateComponents(year: 2001, month: 01, day: 01, hour: 0, minute: 0, second: 0))
|
|
|
+
|
|
|
@Environment(\.colorScheme) var colorScheme
|
|
|
var color: LinearGradient {
|
|
|
colorScheme == .dark ? LinearGradient(
|
|
|
@@ -38,11 +42,92 @@ extension BasalProfileEditor {
|
|
|
return formatter
|
|
|
}
|
|
|
|
|
|
- var body: some View {
|
|
|
- Form {
|
|
|
+ var basalScheduleChart: some View {
|
|
|
+ Chart {
|
|
|
+ ForEach(state.chartData!, id: \.self) { profile in
|
|
|
+ RectangleMark(
|
|
|
+ xStart: .value("start", profile.startDate),
|
|
|
+ xEnd: .value("end", profile.endDate!),
|
|
|
+ yStart: .value("rate-start", profile.amount),
|
|
|
+ yEnd: .value("rate-end", 0)
|
|
|
+ ).foregroundStyle(
|
|
|
+ .linearGradient(
|
|
|
+ colors: [
|
|
|
+ Color.insulin.opacity(0.6),
|
|
|
+ Color.insulin.opacity(0.1)
|
|
|
+ ],
|
|
|
+ startPoint: .bottom,
|
|
|
+ endPoint: .top
|
|
|
+ )
|
|
|
+ ).alignsMarkStylesWithPlotArea()
|
|
|
+
|
|
|
+ LineMark(x: .value("End Date", profile.endDate!), y: .value("Amount", profile.amount))
|
|
|
+ .lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.insulin)
|
|
|
+
|
|
|
+ LineMark(x: .value("Start Date", profile.startDate), y: .value("Amount", profile.amount))
|
|
|
+ .lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.insulin)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .chartXAxis {
|
|
|
+ AxisMarks(values: .automatic(desiredCount: 6)) { _ in
|
|
|
+ AxisValueLabel(format: .dateTime.hour())
|
|
|
+ AxisGridLine(centered: true, stroke: StrokeStyle(lineWidth: 1, dash: [2, 4]))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .chartYAxis {
|
|
|
+ AxisMarks(values: .automatic(desiredCount: 2)) { _ in
|
|
|
+ AxisValueLabel()
|
|
|
+ AxisGridLine(centered: true, stroke: StrokeStyle(lineWidth: 1, dash: [2, 4]))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .chartXScale(
|
|
|
+ domain: Calendar.current.startOfDay(for: chartScale!) ... Calendar.current.startOfDay(for: chartScale!)
|
|
|
+ .addingTimeInterval(60 * 60 * 24)
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ var saveButton: some View {
|
|
|
+ ZStack {
|
|
|
let shouldDisableButton = state.syncInProgress || state.items.isEmpty || !state.hasChanges
|
|
|
|
|
|
+ Rectangle()
|
|
|
+ .frame(width: UIScreen.main.bounds.width, height: 65)
|
|
|
+ .foregroundStyle(colorScheme == .dark ? Color.bgDarkerDarkBlue : Color.white)
|
|
|
+ .background(.thinMaterial)
|
|
|
+ .opacity(0.8)
|
|
|
+ .clipShape(Rectangle())
|
|
|
+
|
|
|
+ Group {
|
|
|
+ HStack {
|
|
|
+ Button {
|
|
|
+ let impactHeavy = UIImpactFeedbackGenerator(style: .heavy)
|
|
|
+ impactHeavy.impactOccurred()
|
|
|
+ state.save()
|
|
|
+ } label: {
|
|
|
+ HStack {
|
|
|
+ if state.syncInProgress {
|
|
|
+ ProgressView().padding(.trailing, 10)
|
|
|
+ }
|
|
|
+ Text(state.syncInProgress ? "Saving..." : "Save")
|
|
|
+ }.padding(10)
|
|
|
+ }
|
|
|
+ .frame(width: UIScreen.main.bounds.width * 0.9, alignment: .center)
|
|
|
+ .disabled(shouldDisableButton)
|
|
|
+ .background(shouldDisableButton ? Color(.systemGray4) : Color(.systemBlue))
|
|
|
+ .tint(.white)
|
|
|
+ .clipShape(RoundedRectangle(cornerRadius: 8))
|
|
|
+ }
|
|
|
+ }.padding(5)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ var body: some View {
|
|
|
+ Form {
|
|
|
Section(header: Text("Schedule")) {
|
|
|
+ if !state.items.isEmpty {
|
|
|
+ basalScheduleChart.padding(.vertical)
|
|
|
+ }
|
|
|
+
|
|
|
list
|
|
|
}.listRowBackground(Color.chart)
|
|
|
|
|
|
@@ -58,25 +143,8 @@ extension BasalProfileEditor {
|
|
|
.foregroundColor(.secondary)
|
|
|
}
|
|
|
}.listRowBackground(Color.chart)
|
|
|
-
|
|
|
- Section {
|
|
|
- HStack {
|
|
|
- if state.syncInProgress {
|
|
|
- ProgressView().padding(.trailing, 10)
|
|
|
- }
|
|
|
- Button {
|
|
|
- let impactHeavy = UIImpactFeedbackGenerator(style: .heavy)
|
|
|
- impactHeavy.impactOccurred()
|
|
|
- state.save()
|
|
|
- } label: {
|
|
|
- Text(state.syncInProgress ? "Saving..." : "Save")
|
|
|
- }
|
|
|
- .disabled(shouldDisableButton)
|
|
|
- .frame(maxWidth: .infinity, alignment: .center)
|
|
|
- .tint(.white)
|
|
|
- }
|
|
|
- }.listRowBackground(shouldDisableButton ? Color(.systemGray4) : Color(.systemBlue))
|
|
|
}
|
|
|
+ .safeAreaInset(edge: .bottom, spacing: 30) { saveButton }
|
|
|
.alert(isPresented: $state.showAlert) {
|
|
|
Alert(
|
|
|
title: Text("Unable to Save"),
|
|
|
@@ -84,9 +152,11 @@ extension BasalProfileEditor {
|
|
|
dismissButton: .default(Text("Close"))
|
|
|
)
|
|
|
}
|
|
|
- .onChange(of: state.items) { state.calcTotal() }
|
|
|
+ .onChange(of: state.items) {
|
|
|
+ state.calcTotal()
|
|
|
+ state.caluclateChartData()
|
|
|
+ }
|
|
|
.scrollContentBackground(.hidden).background(color)
|
|
|
- .onAppear(perform: configureView)
|
|
|
.navigationTitle("Basal Profile")
|
|
|
.navigationBarTitleDisplayMode(.automatic)
|
|
|
.toolbar(content: {
|
|
|
@@ -99,7 +169,9 @@ extension BasalProfileEditor {
|
|
|
})
|
|
|
.environment(\.editMode, $editMode)
|
|
|
.onAppear {
|
|
|
+ configureView()
|
|
|
state.validate()
|
|
|
+ state.caluclateChartData()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -183,6 +255,7 @@ extension BasalProfileEditor {
|
|
|
state.items.remove(atOffsets: offsets)
|
|
|
state.validate()
|
|
|
state.calcTotal()
|
|
|
+ state.caluclateChartData()
|
|
|
}
|
|
|
}
|
|
|
}
|