DataTableRootView.swift 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. import SwiftUI
  2. import Swinject
  3. extension DataTable {
  4. struct RootView: BaseView {
  5. let resolver: Resolver
  6. @StateObject var state = StateModel()
  7. @State private var isRemoveCarbsAlertPresented = false
  8. @State private var removeCarbsAlert: Alert?
  9. @State private var isRemoveInsulinAlertPresented = false
  10. @State private var removeInsulinAlert: Alert?
  11. private var glucoseFormatter: NumberFormatter {
  12. let formatter = NumberFormatter()
  13. formatter.numberStyle = .decimal
  14. formatter.maximumFractionDigits = 0
  15. if state.units == .mmolL {
  16. formatter.minimumFractionDigits = 1
  17. formatter.maximumFractionDigits = 1
  18. }
  19. formatter.roundingMode = .halfUp
  20. return formatter
  21. }
  22. private var dateFormatter: DateFormatter {
  23. let formatter = DateFormatter()
  24. formatter.timeStyle = .short
  25. return formatter
  26. }
  27. var body: some View {
  28. VStack {
  29. Picker("Mode", selection: $state.mode) {
  30. ForEach(Mode.allCases.indexed(), id: \.1) { index, item in
  31. Text(item.name).tag(index)
  32. }
  33. }
  34. .pickerStyle(SegmentedPickerStyle())
  35. .padding(.horizontal)
  36. Form {
  37. switch state.mode {
  38. case .treatments: treatmentsList
  39. case .glucose: glucoseList
  40. }
  41. }
  42. }
  43. .onAppear(perform: configureView)
  44. .navigationTitle("History")
  45. .navigationBarTitleDisplayMode(.automatic)
  46. .navigationBarItems(
  47. leading: Button("Close", action: state.hideModal),
  48. trailing: state.mode == .glucose ? EditButton().asAny() : EmptyView().asAny()
  49. )
  50. }
  51. private var treatmentsList: some View {
  52. List {
  53. ForEach(state.treatments) { item in
  54. treatmentView(item)
  55. }
  56. }
  57. }
  58. private var glucoseList: some View {
  59. List {
  60. ForEach(state.glucose) { item in
  61. gluciseView(item)
  62. }.onDelete(perform: deleteGlucose)
  63. }
  64. }
  65. @ViewBuilder private func treatmentView(_ item: Treatment) -> some View {
  66. HStack {
  67. Image(systemName: "circle.fill").foregroundColor(item.color)
  68. Text(dateFormatter.string(from: item.date))
  69. .moveDisabled(true)
  70. Text(item.type.name)
  71. Text(item.amountText).foregroundColor(.secondary)
  72. if let duration = item.durationText {
  73. Text(duration).foregroundColor(.secondary)
  74. }
  75. if item.type == .carbs {
  76. Spacer()
  77. Image(systemName: "xmark.circle").foregroundColor(.secondary)
  78. .contentShape(Rectangle())
  79. .padding(.vertical)
  80. .onTapGesture {
  81. removeCarbsAlert = Alert(
  82. title: Text("Delete carbs?"),
  83. message: Text(item.amountText),
  84. primaryButton: .destructive(
  85. Text("Delete"),
  86. action: { state.deleteCarbs(item) }
  87. ),
  88. secondaryButton: .cancel()
  89. )
  90. isRemoveCarbsAlertPresented = true
  91. }
  92. .alert(isPresented: $isRemoveCarbsAlertPresented) {
  93. removeCarbsAlert!
  94. }
  95. }
  96. if item.type == .fpus {
  97. Spacer()
  98. Image(systemName: "xmark.circle").foregroundColor(.secondary)
  99. .contentShape(Rectangle())
  100. .padding(.vertical)
  101. .onTapGesture {
  102. removeCarbsAlert = Alert(
  103. title: Text("Delete carb equivalents?"),
  104. message: Text(""), // Temporary fix. New to fix real amount of carb equivalents later
  105. primaryButton: .destructive(
  106. Text("Delete"),
  107. action: { state.deleteCarbs(item) }
  108. ),
  109. secondaryButton: .cancel()
  110. )
  111. isRemoveCarbsAlertPresented = true
  112. }
  113. .alert(isPresented: $isRemoveCarbsAlertPresented) {
  114. removeCarbsAlert!
  115. }
  116. }
  117. if item.type == .bolus {
  118. Spacer()
  119. Image(systemName: "xmark.circle").foregroundColor(.secondary)
  120. .contentShape(Rectangle())
  121. .padding(.vertical)
  122. .onTapGesture {
  123. removeInsulinAlert = Alert(
  124. title: Text("Delete insulin?"),
  125. message: Text(item.amountText),
  126. primaryButton: .destructive(
  127. Text("Delete"),
  128. action: { state.deleteInsulin(item) }
  129. ),
  130. secondaryButton: .cancel()
  131. )
  132. isRemoveInsulinAlertPresented = true
  133. }
  134. .alert(isPresented: $isRemoveInsulinAlertPresented) {
  135. removeInsulinAlert!
  136. }
  137. }
  138. }
  139. }
  140. @ViewBuilder private func gluciseView(_ item: Glucose) -> some View {
  141. VStack(alignment: .leading, spacing: 4) {
  142. HStack {
  143. Text(dateFormatter.string(from: item.glucose.dateString))
  144. Spacer()
  145. Text(item.glucose.glucose.map {
  146. glucoseFormatter.string(from: Double(
  147. state.units == .mmolL ? $0.asMmolL : Decimal($0)
  148. ) as NSNumber)!
  149. } ?? "--")
  150. Text(state.units.rawValue)
  151. Text(item.glucose.direction?.symbol ?? "--")
  152. }
  153. Text("ID: " + item.glucose.id).font(.caption2).foregroundColor(.secondary)
  154. }
  155. }
  156. private func deleteGlucose(at offsets: IndexSet) {
  157. state.deleteGlucose(at: offsets[offsets.startIndex])
  158. }
  159. }
  160. }