ISFEditorRootView.swift 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. import SwiftUI
  2. extension ISFEditor {
  3. struct RootView: BaseView {
  4. @EnvironmentObject var viewModel: ViewModel<Provider>
  5. @State private var editMode = EditMode.inactive
  6. private var dateFormatter: DateFormatter {
  7. let formatter = DateFormatter()
  8. formatter.timeZone = TimeZone(secondsFromGMT: 0)
  9. formatter.timeStyle = .short
  10. return formatter
  11. }
  12. private var rateFormatter: NumberFormatter {
  13. let formatter = NumberFormatter()
  14. formatter.numberStyle = .decimal
  15. formatter.maximumFractionDigits = 1
  16. return formatter
  17. }
  18. var body: some View {
  19. Form {
  20. if let newISF = viewModel.autosensISF {
  21. Section(header: Text("Autosens")) {
  22. HStack {
  23. Text("New ISF")
  24. Spacer()
  25. Text(rateFormatter.string(from: newISF as NSNumber) ?? "0" + " \(viewModel.units)/U")
  26. }
  27. }
  28. }
  29. Section(header: Text("Schedule")) {
  30. list
  31. addButton
  32. }
  33. Section {
  34. Button { viewModel.save() }
  35. label: {
  36. Text("Save")
  37. }
  38. .disabled(viewModel.items.isEmpty)
  39. }
  40. }
  41. .navigationTitle("Insulin Sensitivities")
  42. .navigationBarTitleDisplayMode(.automatic)
  43. .navigationBarItems(
  44. trailing: EditButton()
  45. )
  46. .environment(\.editMode, $editMode)
  47. .onAppear {
  48. viewModel.validate()
  49. }
  50. }
  51. private func pickers(for index: Int) -> some View {
  52. GeometryReader { geometry in
  53. VStack {
  54. HStack {
  55. Text("Rate").frame(width: geometry.size.width / 2)
  56. Text("Time").frame(width: geometry.size.width / 2)
  57. }
  58. HStack(spacing: 0) {
  59. Picker(selection: $viewModel.items[index].rateIndex, label: EmptyView()) {
  60. ForEach(0 ..< viewModel.rateValues.count, id: \.self) { i in
  61. Text(
  62. (
  63. self.rateFormatter
  64. .string(from: viewModel.rateValues[i] as NSNumber) ?? ""
  65. ) + " \(viewModel.units.rawValue)/U"
  66. ).tag(i)
  67. }
  68. }
  69. .frame(maxWidth: geometry.size.width / 2)
  70. .clipped()
  71. Picker(selection: $viewModel.items[index].timeIndex, label: EmptyView()) {
  72. ForEach(0 ..< viewModel.timeValues.count, id: \.self) { i in
  73. Text(
  74. self.dateFormatter
  75. .string(from: Date(
  76. timeIntervalSince1970: viewModel
  77. .timeValues[i]
  78. ))
  79. ).tag(i)
  80. }
  81. }
  82. .frame(maxWidth: geometry.size.width / 2)
  83. .clipped()
  84. }
  85. }
  86. }
  87. }
  88. private var list: some View {
  89. List {
  90. ForEach(viewModel.items.indexed(), id: \.1.id) { index, item in
  91. NavigationLink(destination: pickers(for: index)) {
  92. HStack {
  93. Text("Rate").foregroundColor(.secondary)
  94. Text(
  95. "\(rateFormatter.string(from: viewModel.rateValues[item.rateIndex] as NSNumber) ?? "0") \(viewModel.units.rawValue)/U"
  96. )
  97. Spacer()
  98. Text("starts at").foregroundColor(.secondary)
  99. Text(
  100. "\(dateFormatter.string(from: Date(timeIntervalSince1970: viewModel.timeValues[item.timeIndex])))"
  101. )
  102. }
  103. }
  104. .moveDisabled(true)
  105. }
  106. .onDelete(perform: onDelete)
  107. }
  108. }
  109. private var addButton: some View {
  110. guard viewModel.canAdd else {
  111. return AnyView(EmptyView())
  112. }
  113. switch editMode {
  114. case .inactive:
  115. return AnyView(Button(action: onAdd) { Text("Add") })
  116. default:
  117. return AnyView(EmptyView())
  118. }
  119. }
  120. func onAdd() {
  121. viewModel.add()
  122. }
  123. private func onDelete(offsets: IndexSet) {
  124. viewModel.items.remove(atOffsets: offsets)
  125. viewModel.validate()
  126. }
  127. }
  128. }