GlucoseNotificationSettingsRootView.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  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: AnyView?
  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.map { AnyView($0) }
  60. hintLabel = "Show Glucose App Badge"
  61. }
  62. ),
  63. units: state.units,
  64. type: .boolean,
  65. label: "Show Glucose App Badge",
  66. miniHint: "Show your current glucose on Trio app icon",
  67. verboseHint: VStack(alignment: .leading, spacing: 10) {
  68. Text("Default: OFF").bold()
  69. Text("This will add your current glucose on the top right of your Trio icon as a red notification badge.")
  70. },
  71. headerText: "Various Glucose Notifications"
  72. )
  73. SettingInputSection(
  74. decimalValue: $decimalPlaceholder,
  75. booleanValue: $state.glucoseNotificationsAlways,
  76. shouldDisplayHint: $shouldDisplayHint,
  77. selectedVerboseHint: Binding(
  78. get: { selectedVerboseHint },
  79. set: {
  80. selectedVerboseHint = $0.map { AnyView($0) }
  81. hintLabel = "Always Notify Glucose"
  82. }
  83. ),
  84. units: state.units,
  85. type: .boolean,
  86. label: "Always Notify Glucose",
  87. miniHint: "Trigger a notification every time your glucose is updated",
  88. verboseHint: VStack(alignment: .leading, spacing: 10) {
  89. Text("Default: OFF").bold()
  90. Text("A notification will be triggered every time your glucose is updated in Trio.")
  91. }
  92. )
  93. SettingInputSection(
  94. decimalValue: $decimalPlaceholder,
  95. booleanValue: $state.useAlarmSound,
  96. shouldDisplayHint: $shouldDisplayHint,
  97. selectedVerboseHint: Binding(
  98. get: { selectedVerboseHint },
  99. set: {
  100. selectedVerboseHint = $0.map { AnyView($0) }
  101. hintLabel = "Play Alarm Sound"
  102. }
  103. ),
  104. units: state.units,
  105. type: .boolean,
  106. label: "Play Alarm Sound",
  107. miniHint: "Alarm with every Trio notification",
  108. verboseHint: VStack(alignment: .leading, spacing: 10) {
  109. Text("Default: OFF").bold()
  110. Text("This will cause a sound to be triggered by every Trio notification.")
  111. }
  112. )
  113. SettingInputSection(
  114. decimalValue: $decimalPlaceholder,
  115. booleanValue: $state.addSourceInfoToGlucoseNotifications,
  116. shouldDisplayHint: $shouldDisplayHint,
  117. selectedVerboseHint: Binding(
  118. get: { selectedVerboseHint },
  119. set: {
  120. selectedVerboseHint = $0.map { AnyView($0) }
  121. hintLabel = "Add Glucose Source to Alarm"
  122. }
  123. ),
  124. units: state.units,
  125. type: .boolean,
  126. label: "Add Glucose Source to Alarm",
  127. miniHint: "Source of the glucose reading will be added to the notification",
  128. verboseHint: VStack(alignment: .leading, spacing: 10) {
  129. Text("Default: OFF").bold()
  130. Text("The source of the glucose reading will be added to the notification.")
  131. }
  132. )
  133. self.lowAndHighGlucoseAlertSection
  134. }
  135. .sheet(isPresented: $shouldDisplayHint) {
  136. SettingInputHintView(
  137. hintDetent: $hintDetent,
  138. shouldDisplayHint: $shouldDisplayHint,
  139. hintLabel: hintLabel ?? "",
  140. hintText: selectedVerboseHint ?? AnyView(EmptyView()),
  141. sheetTitle: "Help"
  142. )
  143. }
  144. .scrollContentBackground(.hidden).background(color)
  145. .onAppear(perform: configureView)
  146. .navigationBarTitle("Glucose Notifications")
  147. .navigationBarTitleDisplayMode(.automatic)
  148. }
  149. var lowAndHighGlucoseAlertSection: some View {
  150. Section {
  151. VStack {
  152. VStack {
  153. HStack {
  154. Text("Low Glucose Alarm Limit")
  155. Spacer()
  156. Group {
  157. Text(
  158. state.units == .mgdL ? state.lowGlucose.description : state.lowGlucose.formattedAsMmolL
  159. )
  160. .foregroundColor(!displayPickerLowGlucose ? .primary : .accentColor)
  161. Text(state.units == .mgdL ? " mg/dL" : " mmol/L").foregroundColor(.secondary)
  162. }
  163. }
  164. .onTapGesture {
  165. displayPickerLowGlucose.toggle()
  166. }
  167. }
  168. .padding(.top)
  169. if displayPickerLowGlucose {
  170. let setting = PickerSettingsProvider.shared.settings.lowGlucose
  171. Picker(selection: $state.lowGlucose, label: Text("")) {
  172. ForEach(
  173. PickerSettingsProvider.shared.generatePickerValues(from: setting, units: state.units),
  174. id: \.self
  175. ) { value in
  176. let displayValue = state.units == .mgdL ? value.description : value.formattedAsMmolL
  177. Text(displayValue).tag(value)
  178. }
  179. }
  180. .pickerStyle(WheelPickerStyle())
  181. .frame(maxWidth: .infinity)
  182. }
  183. VStack {
  184. HStack {
  185. Text("High Glucose Alarm Limit")
  186. Spacer()
  187. Group {
  188. Text(
  189. state.units == .mgdL ? state.highGlucose.description : state.highGlucose.formattedAsMmolL
  190. )
  191. .foregroundColor(!displayPickerHighGlucose ? .primary : .accentColor)
  192. Text(state.units == .mgdL ? " mg/dL" : " mmol/L").foregroundColor(.secondary)
  193. }
  194. }
  195. .onTapGesture {
  196. displayPickerHighGlucose.toggle()
  197. }
  198. }
  199. .padding(.top)
  200. if displayPickerHighGlucose {
  201. let setting = PickerSettingsProvider.shared.settings.highGlucose
  202. Picker(selection: $state.highGlucose, label: Text("")) {
  203. ForEach(
  204. PickerSettingsProvider.shared.generatePickerValues(from: setting, units: state.units),
  205. id: \.self
  206. ) { value in
  207. let displayValue = state.units == .mgdL ? value.description : value.formattedAsMmolL
  208. Text(displayValue).tag(value)
  209. }
  210. }
  211. .pickerStyle(WheelPickerStyle())
  212. .frame(maxWidth: .infinity)
  213. }
  214. HStack(alignment: .top) {
  215. Text(
  216. "Sets the lower and upper limit for glucose alarms"
  217. )
  218. .lineLimit(nil)
  219. .font(.footnote)
  220. .foregroundColor(.secondary)
  221. Spacer()
  222. Button(
  223. action: {
  224. hintLabel = "Low and High Glucose Alarm Limits"
  225. selectedVerboseHint =
  226. AnyView(VStack(spacing: 10) {
  227. Text("Low Default: 70 mg/dL").bold()
  228. Text("High Default: 180 mg/dL").bold()
  229. VStack(alignment: .leading, spacing: 10) {
  230. Text(
  231. "These two settings determine the range outside of which you will be notified via push notifications."
  232. )
  233. Text(
  234. "If your CGM readings are below the Low value or above the High value, you will receive a glucose alarm."
  235. )
  236. }
  237. })
  238. shouldDisplayHint.toggle()
  239. },
  240. label: {
  241. HStack {
  242. Image(systemName: "questionmark.circle")
  243. }
  244. }
  245. ).buttonStyle(BorderlessButtonStyle())
  246. }.padding(.top)
  247. }.padding(.bottom)
  248. }.listRowBackground(Color.chart)
  249. }
  250. }
  251. }