GlucoseNotificationSettingsRootView.swift 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  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. List {
  52. SettingInputSection(
  53. decimalValue: $decimalPlaceholder,
  54. booleanValue: $state.notificationsPump,
  55. shouldDisplayHint: $shouldDisplayHint,
  56. selectedVerboseHint: Binding(
  57. get: { selectedVerboseHint },
  58. set: {
  59. selectedVerboseHint = $0
  60. hintLabel = "Always Notify Pump"
  61. }
  62. ),
  63. units: state.units,
  64. type: .boolean,
  65. label: "Always Notify Pump",
  66. miniHint: "Always Notify Pump Warnings",
  67. verboseHint: "With iOS Trio Notifications enabled, you can let Trio display most Pump Notifications in iOS Notification Center as a Banner, List and on the Lock Screen. It allows you to refer to Trio Information at a glance and troubleshoot any informational issue. Set iOS Notifications Banner Style to Persistent to display banners in the app until dismissed.\n\nIf iOS Trio Notifications is disabled, Trio will display these messages in-app as a banner only.\n\nAn example of a Pump Warning is 'Pod Expiration Reminder'",
  68. headerText: "Trio Information Notifications"
  69. )
  70. SettingInputSection(
  71. decimalValue: $decimalPlaceholder,
  72. booleanValue: $state.notificationsCgm,
  73. shouldDisplayHint: $shouldDisplayHint,
  74. selectedVerboseHint: Binding(
  75. get: { selectedVerboseHint },
  76. set: {
  77. selectedVerboseHint = $0
  78. hintLabel = "Always Notify CGM"
  79. }
  80. ),
  81. units: state.units,
  82. type: .boolean,
  83. label: "Always Notify CGM",
  84. miniHint: "Always Notify CGM Warnings",
  85. verboseHint: "With iOS Trio Notifications enabled, you can let Trio display most CGM Notifications in iOS Notification Center as a Banner, List and on the Lock Screen. It allows you to refer to Trio Information at a glance and troubleshoot any informational issue. Set iOS Notifications Banner Style to Persistent to display banners in the app until dismissed.\n\nIf iOS Trio Notifications is disabled, Trio will display these messages in-app as a banner only.\n\nAn example of a CGM Warning is 'Unable to open the app'"
  86. )
  87. SettingInputSection(
  88. decimalValue: $decimalPlaceholder,
  89. booleanValue: $state.notificationsCarb,
  90. shouldDisplayHint: $shouldDisplayHint,
  91. selectedVerboseHint: Binding(
  92. get: { selectedVerboseHint },
  93. set: {
  94. selectedVerboseHint = $0
  95. hintLabel = "Always Notify Carb"
  96. }
  97. ),
  98. units: state.units,
  99. type: .boolean,
  100. label: "Always Notify Carb",
  101. miniHint: "Always Notify Carb Warnings",
  102. verboseHint: "With iOS Trio Notifications enabled, you can let Trio display most Carb Notifications in iOS Notification Center as a Banner, List and on the Lock Screen. It allows you to refer to Trio Information at a glance and troubleshoot any informational issue. Set iOS Notifications Banner Style to Persistent to display banners in the app until dismissed.\n\nIf iOS Trio Notifications is disabled, Trio will display these messages in-app as a banner only.\n\nAn example of a Carb Warning is 'Carbs required: 30 g'"
  103. )
  104. SettingInputSection(
  105. decimalValue: $decimalPlaceholder,
  106. booleanValue: $state.notificationsAlgorithm,
  107. shouldDisplayHint: $shouldDisplayHint,
  108. selectedVerboseHint: Binding(
  109. get: { selectedVerboseHint },
  110. set: {
  111. selectedVerboseHint = $0
  112. hintLabel = "Always Notify Algorithm"
  113. }
  114. ),
  115. units: state.units,
  116. type: .boolean,
  117. label: "Always Notify Algorithm",
  118. miniHint: "Always Notify Algorithm Warnings",
  119. verboseHint: "With iOS Trio Notifications enabled, you can let Trio display most Algorithm Notifications in iOS Notification Center as a Banner, List and on the Lock Screen. It allows you to refer to Trio Information at a glance and troubleshoot any informational issue. Set iOS Notifications Banner Style to Persistent to display banners in the app until dismissed.\n\nIf iOS Trio Notifications is disabled, Trio will display these messages in-app as a banner only.\n\nAn example of an Algorithm Warning is 'Error: Invalid glucose: Not enough glucose data'"
  120. )
  121. SettingInputSection(
  122. decimalValue: $decimalPlaceholder,
  123. booleanValue: $state.glucoseBadge,
  124. shouldDisplayHint: $shouldDisplayHint,
  125. selectedVerboseHint: Binding(
  126. get: { selectedVerboseHint },
  127. set: {
  128. selectedVerboseHint = $0.map { AnyView($0) }
  129. hintLabel = "Show Glucose App Badge"
  130. }
  131. ),
  132. units: state.units,
  133. type: .boolean,
  134. label: "Show Glucose App Badge",
  135. miniHint: "Show your current glucose on Trio app icon.",
  136. verboseHint: VStack(alignment: .leading, spacing: 10) {
  137. Text("Default: OFF").bold()
  138. Text("This will add your current glucose on the top right of your Trio icon as a red notification badge.")
  139. },
  140. headerText: "Various Glucose Notifications"
  141. )
  142. SettingInputSection(
  143. decimalValue: $decimalPlaceholder,
  144. booleanValue: $state.glucoseNotificationsAlways,
  145. shouldDisplayHint: $shouldDisplayHint,
  146. selectedVerboseHint: Binding(
  147. get: { selectedVerboseHint },
  148. set: {
  149. selectedVerboseHint = $0.map { AnyView($0) }
  150. hintLabel = "Always Notify Glucose"
  151. }
  152. ),
  153. units: state.units,
  154. type: .boolean,
  155. label: "Always Notify Glucose",
  156. miniHint: "Trigger a notification every time your glucose is updated.",
  157. verboseHint: VStack(alignment: .leading, spacing: 10) {
  158. Text("Default: OFF").bold()
  159. Text("A notification will be triggered every time your glucose is updated in Trio.")
  160. }
  161. )
  162. SettingInputSection(
  163. decimalValue: $decimalPlaceholder,
  164. booleanValue: $state.useAlarmSound,
  165. shouldDisplayHint: $shouldDisplayHint,
  166. selectedVerboseHint: Binding(
  167. get: { selectedVerboseHint },
  168. set: {
  169. selectedVerboseHint = $0.map { AnyView($0) }
  170. hintLabel = "Play Alarm Sound"
  171. }
  172. ),
  173. units: state.units,
  174. type: .boolean,
  175. label: "Play Alarm Sound",
  176. miniHint: "Alarm with every Trio notification.",
  177. verboseHint: VStack(alignment: .leading, spacing: 10) {
  178. Text("Default: OFF").bold()
  179. Text("This will cause a sound to be triggered by every Trio notification.")
  180. }
  181. )
  182. SettingInputSection(
  183. decimalValue: $decimalPlaceholder,
  184. booleanValue: $state.addSourceInfoToGlucoseNotifications,
  185. shouldDisplayHint: $shouldDisplayHint,
  186. selectedVerboseHint: Binding(
  187. get: { selectedVerboseHint },
  188. set: {
  189. selectedVerboseHint = $0.map { AnyView($0) }
  190. hintLabel = "Add Glucose Source to Alarm"
  191. }
  192. ),
  193. units: state.units,
  194. type: .boolean,
  195. label: "Add Glucose Source to Alarm",
  196. miniHint: "Source of the glucose reading will be added to the notification.",
  197. verboseHint: VStack(alignment: .leading, spacing: 10) {
  198. Text("Default: OFF").bold()
  199. Text("The source of the glucose reading will be added to the notification.")
  200. }
  201. )
  202. self.lowAndHighGlucoseAlertSection
  203. }
  204. .listSectionSpacing(sectionSpacing)
  205. .sheet(isPresented: $shouldDisplayHint) {
  206. SettingInputHintView(
  207. hintDetent: $hintDetent,
  208. shouldDisplayHint: $shouldDisplayHint,
  209. hintLabel: hintLabel ?? "",
  210. hintText: selectedVerboseHint ?? AnyView(EmptyView()),
  211. sheetTitle: "Help"
  212. )
  213. }
  214. .scrollContentBackground(.hidden).background(color)
  215. .onAppear(perform: configureView)
  216. .navigationBarTitle("Trio Notifications")
  217. .navigationBarTitleDisplayMode(.automatic)
  218. }
  219. var lowAndHighGlucoseAlertSection: some View {
  220. Section {
  221. VStack {
  222. VStack {
  223. HStack {
  224. Text("Low Glucose Alarm Limit")
  225. Spacer()
  226. Group {
  227. Text(
  228. state.units == .mgdL ? state.lowGlucose.description : state.lowGlucose.formattedAsMmolL
  229. )
  230. .foregroundColor(!displayPickerLowGlucose ? .primary : .accentColor)
  231. Text(state.units == .mgdL ? " mg/dL" : " mmol/L").foregroundColor(.secondary)
  232. }
  233. }
  234. .onTapGesture {
  235. displayPickerLowGlucose.toggle()
  236. }
  237. }
  238. .padding(.top)
  239. if displayPickerLowGlucose {
  240. let setting = PickerSettingsProvider.shared.settings.lowGlucose
  241. Picker(selection: $state.lowGlucose, label: Text("")) {
  242. ForEach(
  243. PickerSettingsProvider.shared.generatePickerValues(from: setting, units: state.units),
  244. id: \.self
  245. ) { value in
  246. let displayValue = state.units == .mgdL ? value.description : value.formattedAsMmolL
  247. Text(displayValue).tag(value)
  248. }
  249. }
  250. .pickerStyle(WheelPickerStyle())
  251. .frame(maxWidth: .infinity)
  252. }
  253. VStack {
  254. HStack {
  255. Text("High Glucose Alarm Limit")
  256. Spacer()
  257. Group {
  258. Text(
  259. state.units == .mgdL ? state.highGlucose.description : state.highGlucose.formattedAsMmolL
  260. )
  261. .foregroundColor(!displayPickerHighGlucose ? .primary : .accentColor)
  262. Text(state.units == .mgdL ? " mg/dL" : " mmol/L").foregroundColor(.secondary)
  263. }
  264. }
  265. .onTapGesture {
  266. displayPickerHighGlucose.toggle()
  267. }
  268. }
  269. .padding(.top)
  270. if displayPickerHighGlucose {
  271. let setting = PickerSettingsProvider.shared.settings.highGlucose
  272. Picker(selection: $state.highGlucose, label: Text("")) {
  273. ForEach(
  274. PickerSettingsProvider.shared.generatePickerValues(from: setting, units: state.units),
  275. id: \.self
  276. ) { value in
  277. let displayValue = state.units == .mgdL ? value.description : value.formattedAsMmolL
  278. Text(displayValue).tag(value)
  279. }
  280. }
  281. .pickerStyle(WheelPickerStyle())
  282. .frame(maxWidth: .infinity)
  283. }
  284. HStack(alignment: .center) {
  285. Text(
  286. "Sets the lower and upper limit for glucose alarms."
  287. )
  288. .lineLimit(nil)
  289. .font(.footnote)
  290. .foregroundColor(.secondary)
  291. Spacer()
  292. Button(
  293. action: {
  294. hintLabel = "Low and High Glucose Alarm Limits"
  295. selectedVerboseHint =
  296. AnyView(VStack(alignment: .leading, spacing: 10) {
  297. Text("Low Default: 70 mg/dL").bold()
  298. Text("High Default: 180 mg/dL").bold()
  299. VStack(alignment: .leading, spacing: 10) {
  300. Text(
  301. "These two settings determine the range outside of which you will be notified via push notifications."
  302. )
  303. Text(
  304. "If your CGM readings are below the Low value or above the High value, you will receive a glucose alarm."
  305. )
  306. }
  307. })
  308. shouldDisplayHint.toggle()
  309. },
  310. label: {
  311. HStack {
  312. Image(systemName: "questionmark.circle")
  313. }
  314. }
  315. ).buttonStyle(BorderlessButtonStyle())
  316. }.padding(.top)
  317. }.padding(.bottom)
  318. }.listRowBackground(Color.chart)
  319. }
  320. }
  321. }