UserInterfaceSettingsRootView.swift 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. import SwiftUI
  2. import Swinject
  3. extension UserInterfaceSettings {
  4. struct RootView: BaseView {
  5. let resolver: Resolver
  6. @StateObject var state = StateModel()
  7. @State private var shouldDisplayHint: Bool = false
  8. @State var hintDetent = PresentationDetent.large
  9. @State var selectedVerboseHint: String?
  10. @State var hintLabel: String?
  11. @State private var decimalPlaceholder: Decimal = 0.0
  12. @State private var booleanPlaceholder: Bool = false
  13. @State private var displayPickerLowThreshold: Bool = false
  14. @State private var displayPickerHighThreshold: Bool = false
  15. @Environment(\.colorScheme) var colorScheme
  16. var color: LinearGradient {
  17. colorScheme == .dark ? LinearGradient(
  18. gradient: Gradient(colors: [
  19. Color.bgDarkBlue,
  20. Color.bgDarkerDarkBlue
  21. ]),
  22. startPoint: .top,
  23. endPoint: .bottom
  24. )
  25. :
  26. LinearGradient(
  27. gradient: Gradient(colors: [Color.gray.opacity(0.1)]),
  28. startPoint: .top,
  29. endPoint: .bottom
  30. )
  31. }
  32. private var glucoseFormatter: NumberFormatter {
  33. let formatter = NumberFormatter()
  34. formatter.numberStyle = .decimal
  35. formatter.maximumFractionDigits = 0
  36. if state.units == .mmolL {
  37. formatter.maximumFractionDigits = 1
  38. }
  39. formatter.roundingMode = .halfUp
  40. return formatter
  41. }
  42. private var carbsFormatter: NumberFormatter {
  43. let formatter = NumberFormatter()
  44. formatter.numberStyle = .decimal
  45. formatter.maximumFractionDigits = 0
  46. return formatter
  47. }
  48. var body: some View {
  49. Form {
  50. Section(
  51. header: Text("Home View Settings"),
  52. content: {
  53. VStack {
  54. Toggle("Show X-Axis Grid Lines", isOn: $state.xGridLines)
  55. Toggle("Show Y-Axis Grid Lines", isOn: $state.yGridLines)
  56. HStack(alignment: .top) {
  57. Text(
  58. "Lorem ipsum dolor sit amet, consetetur sadipscing elitr."
  59. )
  60. .font(.footnote)
  61. .foregroundColor(.secondary)
  62. .lineLimit(nil)
  63. Spacer()
  64. Button(
  65. action: {
  66. hintLabel = "Show Main Chart X- and Y-Axis Grid Lines"
  67. selectedVerboseHint = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr."
  68. shouldDisplayHint.toggle()
  69. },
  70. label: {
  71. HStack {
  72. Image(systemName: "questionmark.circle")
  73. }
  74. }
  75. ).buttonStyle(BorderlessButtonStyle())
  76. }.padding(.top)
  77. }.padding(.bottom)
  78. }
  79. ).listRowBackground(Color.chart)
  80. SettingInputSection(
  81. decimalValue: $decimalPlaceholder,
  82. booleanValue: $state.rulerMarks,
  83. shouldDisplayHint: $shouldDisplayHint,
  84. selectedVerboseHint: Binding(
  85. get: { selectedVerboseHint },
  86. set: {
  87. selectedVerboseHint = $0
  88. hintLabel = "Show Low and High Thresholds"
  89. }
  90. ),
  91. units: state.units,
  92. type: .boolean,
  93. label: "Show Low and High Thresholds",
  94. miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
  95. verboseHint: "Display Low and High Thresholds… bla bla bla"
  96. )
  97. Section {
  98. VStack {
  99. VStack {
  100. HStack {
  101. Text("Low Threshold")
  102. Spacer()
  103. Group {
  104. Text(state.units == .mgdL ? state.low.description : state.low.asMmolL.description)
  105. .foregroundColor(!displayPickerLowThreshold ? .primary : .accentColor)
  106. Text(state.units == .mgdL ? " mg/dL" : " mmol/L").foregroundColor(.secondary)
  107. }
  108. }
  109. .onTapGesture {
  110. displayPickerLowThreshold.toggle()
  111. }
  112. }
  113. .padding(.top)
  114. if displayPickerLowThreshold {
  115. let setting = PickerSettingsProvider.shared.settings.low
  116. Picker(selection: $state.low, label: Text("")) {
  117. ForEach(
  118. PickerSettingsProvider.shared.generatePickerValues(from: setting, units: state.units),
  119. id: \.self
  120. ) { value in
  121. let displayValue = state.units == .mgdL ? value : value.asMmolL
  122. Text("\(displayValue.description)").tag(value)
  123. }
  124. }
  125. .pickerStyle(WheelPickerStyle())
  126. .frame(maxWidth: .infinity)
  127. }
  128. VStack {
  129. HStack {
  130. Text("High Threshold")
  131. Spacer()
  132. Group {
  133. Text(state.units == .mgdL ? state.high.description : state.high.asMmolL.description)
  134. .foregroundColor(!displayPickerHighThreshold ? .primary : .accentColor)
  135. Text(state.units == .mgdL ? " mg/dL" : " mmol/L").foregroundColor(.secondary)
  136. }
  137. }
  138. .onTapGesture {
  139. displayPickerHighThreshold.toggle()
  140. }
  141. }
  142. .padding(.top)
  143. if displayPickerHighThreshold {
  144. let setting = PickerSettingsProvider.shared.settings.high
  145. Picker(selection: $state.high, label: Text("")) {
  146. ForEach(
  147. PickerSettingsProvider.shared.generatePickerValues(from: setting, units: state.units),
  148. id: \.self
  149. ) { value in
  150. let displayValue = state.units == .mgdL ? value : value.asMmolL
  151. Text("\(displayValue.description)").tag(value)
  152. }
  153. }
  154. .pickerStyle(WheelPickerStyle())
  155. .frame(maxWidth: .infinity)
  156. }
  157. HStack(alignment: .top) {
  158. Text(
  159. "Sets thresholds for low and high glucose in home view main chart and statistics view."
  160. )
  161. .lineLimit(nil)
  162. .font(.footnote)
  163. .foregroundColor(.secondary)
  164. Spacer()
  165. Button(
  166. action: {
  167. hintLabel = "Low and High Thresholds"
  168. selectedVerboseHint = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr."
  169. shouldDisplayHint.toggle()
  170. },
  171. label: {
  172. HStack {
  173. Image(systemName: "questionmark.circle")
  174. }
  175. }
  176. ).buttonStyle(BorderlessButtonStyle())
  177. }.padding(.top)
  178. }.padding(.bottom)
  179. }.listRowBackground(Color.chart)
  180. SettingInputSection(
  181. decimalValue: $state.hours,
  182. booleanValue: $booleanPlaceholder,
  183. shouldDisplayHint: $shouldDisplayHint,
  184. selectedVerboseHint: Binding(
  185. get: { selectedVerboseHint },
  186. set: {
  187. selectedVerboseHint = $0
  188. hintLabel = "X-Axis Interval Step"
  189. }
  190. ),
  191. units: state.units,
  192. type: .decimal("hours"),
  193. label: "X-Axis Interval Step",
  194. miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
  195. verboseHint: "X-Axis Interval Step… bla bla bla"
  196. )
  197. Section {
  198. VStack {
  199. Picker(
  200. selection: $state.totalInsulinDisplayType,
  201. label: Text("Total Insulin Display Type")
  202. ) {
  203. ForEach(TotalInsulinDisplayType.allCases) { selection in
  204. Text(selection.displayName).tag(selection)
  205. }
  206. }.padding(.top)
  207. HStack(alignment: .top) {
  208. Text(
  209. "Lorem ipsum dolor sit amet, consetetur sadipscing elitr."
  210. )
  211. .font(.footnote)
  212. .foregroundColor(.secondary)
  213. .lineLimit(nil)
  214. Spacer()
  215. Button(
  216. action: {
  217. hintLabel = "Total Insulin Display Type"
  218. selectedVerboseHint = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr."
  219. shouldDisplayHint.toggle()
  220. },
  221. label: {
  222. HStack {
  223. Image(systemName: "questionmark.circle")
  224. }
  225. }
  226. ).buttonStyle(BorderlessButtonStyle())
  227. }.padding(.top)
  228. }.padding(.bottom)
  229. }.listRowBackground(Color.chart)
  230. // TODO: this needs to be a picker: mmol/L or %
  231. SettingInputSection(
  232. decimalValue: $decimalPlaceholder,
  233. booleanValue: $state.overrideHbA1cUnit,
  234. shouldDisplayHint: $shouldDisplayHint,
  235. selectedVerboseHint: Binding(
  236. get: { selectedVerboseHint },
  237. set: {
  238. selectedVerboseHint = $0
  239. hintLabel = "Override HbA1c Unit"
  240. }
  241. ),
  242. units: state.units,
  243. type: .boolean,
  244. label: "Override HbA1c Unit",
  245. miniHint: "Display HbA1c in mmol/L or %. Default is percent.",
  246. verboseHint: "Override HbA1c Unit… bla bla bla",
  247. headerText: "Trio Statistics"
  248. )
  249. // TODO: this needs to be a picker: choose bar chart or progress bar
  250. SettingInputSection(
  251. decimalValue: $decimalPlaceholder,
  252. booleanValue: $state.oneDimensionalGraph,
  253. shouldDisplayHint: $shouldDisplayHint,
  254. selectedVerboseHint: Binding(
  255. get: { selectedVerboseHint },
  256. set: {
  257. selectedVerboseHint = $0
  258. hintLabel = "Standing / Laying TIR Chart"
  259. }
  260. ),
  261. units: state.units,
  262. type: .boolean,
  263. label: "Standing / Laying TIR Chart",
  264. miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
  265. verboseHint: "Standing / Laying TIR Chart… bla bla bla"
  266. )
  267. SettingInputSection(
  268. decimalValue: $state.carbsRequiredThreshold,
  269. booleanValue: $state.showCarbsRequiredBadge,
  270. shouldDisplayHint: $shouldDisplayHint,
  271. selectedVerboseHint: Binding(
  272. get: { selectedVerboseHint },
  273. set: {
  274. selectedVerboseHint = $0
  275. hintLabel = "Show Carbs Required Badge"
  276. }
  277. ),
  278. units: state.units,
  279. type: .conditionalDecimal("carbsRequiredThreshold"),
  280. label: "Show Carbs Required Badge",
  281. conditionalLabel: "Carbs Required Threshold",
  282. miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
  283. verboseHint: "Show Carbs Required Badge… bla bla bla",
  284. headerText: "Carbs Required Badge"
  285. )
  286. }
  287. .sheet(isPresented: $shouldDisplayHint) {
  288. SettingInputHintView(
  289. hintDetent: $hintDetent,
  290. shouldDisplayHint: $shouldDisplayHint,
  291. hintLabel: hintLabel ?? "",
  292. hintText: selectedVerboseHint ?? "",
  293. sheetTitle: "Help"
  294. )
  295. }
  296. .scrollContentBackground(.hidden).background(color)
  297. .onAppear(perform: configureView)
  298. .navigationBarTitle("User Interface")
  299. .navigationBarTitleDisplayMode(.automatic)
  300. }
  301. }
  302. }