GlucoseNotificationSettingsRootView.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. import ActivityKit
  2. import Combine
  3. import SwiftUI
  4. import Swinject
  5. extension GlucoseNotificationSettings {
  6. struct RootView: BaseView {
  7. let resolver: Resolver
  8. @StateObject var state = StateModel()
  9. @State private var shouldDisplayHint: Bool = false
  10. @State var hintDetent = PresentationDetent.large
  11. @State var selectedVerboseHint: String?
  12. @State var hintLabel: String?
  13. @State private var decimalPlaceholder: Decimal = 0.0
  14. @State private var booleanPlaceholder: Bool = false
  15. @State private var displayPickerLowGlucose: Bool = false
  16. @State private var displayPickerHighGlucose: Bool = false
  17. private var glucoseFormatter: NumberFormatter {
  18. let formatter = NumberFormatter()
  19. formatter.numberStyle = .decimal
  20. formatter.maximumFractionDigits = 0
  21. if state.units == .mmolL {
  22. formatter.maximumFractionDigits = 1
  23. }
  24. formatter.roundingMode = .halfUp
  25. return formatter
  26. }
  27. private var carbsFormatter: NumberFormatter {
  28. let formatter = NumberFormatter()
  29. formatter.numberStyle = .decimal
  30. formatter.maximumFractionDigits = 0
  31. return formatter
  32. }
  33. @Environment(\.colorScheme) var colorScheme
  34. var color: LinearGradient {
  35. colorScheme == .dark ? LinearGradient(
  36. gradient: Gradient(colors: [
  37. Color.bgDarkBlue,
  38. Color.bgDarkerDarkBlue
  39. ]),
  40. startPoint: .top,
  41. endPoint: .bottom
  42. )
  43. :
  44. LinearGradient(
  45. gradient: Gradient(colors: [Color.gray.opacity(0.1)]),
  46. startPoint: .top,
  47. endPoint: .bottom
  48. )
  49. }
  50. var body: some View {
  51. Form {
  52. SettingInputSection(
  53. decimalValue: $decimalPlaceholder,
  54. booleanValue: $state.glucoseBadge,
  55. shouldDisplayHint: $shouldDisplayHint,
  56. selectedVerboseHint: Binding(
  57. get: { selectedVerboseHint },
  58. set: {
  59. selectedVerboseHint = $0
  60. hintLabel = "Show Glucose App Badge"
  61. }
  62. ),
  63. units: state.units,
  64. type: .boolean,
  65. label: "Show Glucose App Badge",
  66. miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
  67. verboseHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
  68. headerText: "Various Glucose Notifications"
  69. )
  70. SettingInputSection(
  71. decimalValue: $decimalPlaceholder,
  72. booleanValue: $state.glucoseNotificationsAlways,
  73. shouldDisplayHint: $shouldDisplayHint,
  74. selectedVerboseHint: Binding(
  75. get: { selectedVerboseHint },
  76. set: {
  77. selectedVerboseHint = $0
  78. hintLabel = "Always Notify Glucose"
  79. }
  80. ),
  81. units: state.units,
  82. type: .boolean,
  83. label: "Always Notify Glucose",
  84. miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
  85. verboseHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr."
  86. )
  87. SettingInputSection(
  88. decimalValue: $decimalPlaceholder,
  89. booleanValue: $state.useAlarmSound,
  90. shouldDisplayHint: $shouldDisplayHint,
  91. selectedVerboseHint: Binding(
  92. get: { selectedVerboseHint },
  93. set: {
  94. selectedVerboseHint = $0
  95. hintLabel = "Play Alarm Sound"
  96. }
  97. ),
  98. units: state.units,
  99. type: .boolean,
  100. label: "Play Alarm Sound",
  101. miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
  102. verboseHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr."
  103. )
  104. SettingInputSection(
  105. decimalValue: $decimalPlaceholder,
  106. booleanValue: $state.addSourceInfoToGlucoseNotifications,
  107. shouldDisplayHint: $shouldDisplayHint,
  108. selectedVerboseHint: Binding(
  109. get: { selectedVerboseHint },
  110. set: {
  111. selectedVerboseHint = $0
  112. hintLabel = "Add Glucose Source to Alarm"
  113. }
  114. ),
  115. units: state.units,
  116. type: .boolean,
  117. label: "Add Glucose Source to Alarm",
  118. miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
  119. verboseHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr."
  120. )
  121. self.lowAndHighGlucoseAlertSection
  122. }
  123. .sheet(isPresented: $shouldDisplayHint) {
  124. SettingInputHintView(
  125. hintDetent: $hintDetent,
  126. shouldDisplayHint: $shouldDisplayHint,
  127. hintLabel: hintLabel ?? "",
  128. hintText: selectedVerboseHint ?? "",
  129. sheetTitle: "Help"
  130. )
  131. }
  132. .scrollContentBackground(.hidden).background(color)
  133. .onAppear(perform: configureView)
  134. .navigationBarTitle("Glucose Notifications")
  135. .navigationBarTitleDisplayMode(.automatic)
  136. }
  137. var lowAndHighGlucoseAlertSection: some View {
  138. Section {
  139. VStack {
  140. VStack {
  141. HStack {
  142. Text("Low Glucose Alarm Limit")
  143. Spacer()
  144. Group {
  145. Text(
  146. state.units == .mgdL ? state.lowGlucose.description : state.lowGlucose.formattedAsMmolL
  147. )
  148. .foregroundColor(!displayPickerLowGlucose ? .primary : .accentColor)
  149. Text(state.units == .mgdL ? " mg/dL" : " mmol/L").foregroundColor(.secondary)
  150. }
  151. }
  152. .onTapGesture {
  153. displayPickerLowGlucose.toggle()
  154. }
  155. }
  156. .padding(.top)
  157. if displayPickerLowGlucose {
  158. let setting = PickerSettingsProvider.shared.settings.lowGlucose
  159. Picker(selection: $state.lowGlucose, label: Text("")) {
  160. ForEach(
  161. PickerSettingsProvider.shared.generatePickerValues(from: setting, units: state.units),
  162. id: \.self
  163. ) { value in
  164. let displayValue = state.units == .mgdL ? value.description : value.formattedAsMmolL
  165. Text(displayValue).tag(value)
  166. }
  167. }
  168. .pickerStyle(WheelPickerStyle())
  169. .frame(maxWidth: .infinity)
  170. }
  171. VStack {
  172. HStack {
  173. Text("High Glucose Alarm Limit")
  174. Spacer()
  175. Group {
  176. Text(
  177. state.units == .mgdL ? state.highGlucose.description : state.highGlucose.formattedAsMmolL
  178. )
  179. .foregroundColor(!displayPickerHighGlucose ? .primary : .accentColor)
  180. Text(state.units == .mgdL ? " mg/dL" : " mmol/L").foregroundColor(.secondary)
  181. }
  182. }
  183. .onTapGesture {
  184. displayPickerHighGlucose.toggle()
  185. }
  186. }
  187. .padding(.top)
  188. if displayPickerHighGlucose {
  189. let setting = PickerSettingsProvider.shared.settings.highGlucose
  190. Picker(selection: $state.highGlucose, label: Text("")) {
  191. ForEach(
  192. PickerSettingsProvider.shared.generatePickerValues(from: setting, units: state.units),
  193. id: \.self
  194. ) { value in
  195. let displayValue = state.units == .mgdL ? value.description : value.formattedAsMmolL
  196. Text(displayValue).tag(value)
  197. }
  198. }
  199. .pickerStyle(WheelPickerStyle())
  200. .frame(maxWidth: .infinity)
  201. }
  202. HStack(alignment: .top) {
  203. Text(
  204. "Set the lower and upper limit for glucose alarms. See hint for more details."
  205. )
  206. .lineLimit(nil)
  207. .font(.footnote)
  208. .foregroundColor(.secondary)
  209. Spacer()
  210. Button(
  211. action: {
  212. hintLabel = "Low and High Glucose Alarm Limits"
  213. selectedVerboseHint =
  214. "These two settings limit the range outside of which you will be notified via push notifications. If your CGM readings are below 'Low' or above 'High', you will receive an alarm via push notification."
  215. shouldDisplayHint.toggle()
  216. },
  217. label: {
  218. HStack {
  219. Image(systemName: "questionmark.circle")
  220. }
  221. }
  222. ).buttonStyle(BorderlessButtonStyle())
  223. }.padding(.top)
  224. }.padding(.bottom)
  225. }.listRowBackground(Color.chart)
  226. }
  227. }
  228. }