SettingItems.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. import Foundation
  2. import LoopKitUI
  3. import SwiftUI
  4. struct SettingItem: Identifiable {
  5. let id = UUID()
  6. let title: LocalizedStringKey
  7. let view: Screen
  8. let searchContents: [LocalizedStringKey]?
  9. let path: [LocalizedStringKey]?
  10. init(
  11. title: LocalizedStringKey,
  12. view: Screen,
  13. searchContents: [LocalizedStringKey]? = nil,
  14. path: [LocalizedStringKey]? = nil
  15. ) {
  16. self.title = title
  17. self.view = view
  18. self.searchContents = searchContents
  19. self.path = path
  20. }
  21. }
  22. struct FilteredSettingItem: Identifiable {
  23. let id = UUID()
  24. let settingItem: SettingItem
  25. let matchedContent: LocalizedStringKey
  26. }
  27. enum SettingItems {
  28. static let trioConfig = [
  29. SettingItem(title: "Devices", view: .devices),
  30. SettingItem(title: "Therapy", view: .therapySettings),
  31. SettingItem(title: "Algorithm", view: .algorithmSettings),
  32. SettingItem(title: "Features", view: .featureSettings),
  33. SettingItem(title: "Notifications", view: .notificationSettings),
  34. SettingItem(title: "Services", view: .serviceSettings)
  35. ]
  36. static let devicesItems = [
  37. SettingItem(title: "Insulin Pump", view: .pumpConfig, path: ["Devices"]),
  38. SettingItem(
  39. title: "CGM",
  40. view: .cgm,
  41. searchContents: ["Smooth Glucose Value"],
  42. path: ["Devices", "Continuous Glucose Monitor"]
  43. ),
  44. SettingItem(title: "Smart Watch", view: .watch, path: ["Devices"]),
  45. SettingItem(
  46. title: "Apple Watch",
  47. view: .watch,
  48. searchContents: ["Display on Watch", "Show Protein and Fat", "Confirm Bolus Faster"],
  49. path: ["Devices", "Smart Watch", "Apple Watch"]
  50. )
  51. ]
  52. static let therapyItems = [
  53. SettingItem(
  54. title: "Units and Limits",
  55. view: .unitsAndLimits,
  56. searchContents: ["Glucose Units", "Max Basal", "Max Bolus", "Max IOB", "Max COB"],
  57. path: ["Therapy Settings", "Units and Limits"]
  58. ),
  59. SettingItem(title: "Basal Rates", view: .basalProfileEditor, path: ["Therapy Settings"]),
  60. SettingItem(title: "Insulin Sensitivities", view: .isfEditor, path: ["Therapy Settings"]),
  61. SettingItem(title: "ISF", view: .isfEditor, path: ["Therapy Settings"]),
  62. SettingItem(title: "Carb Ratios", view: .crEditor, path: ["Therapy Settings"]),
  63. SettingItem(title: "CR", view: .crEditor, path: ["Therapy Settings"]),
  64. SettingItem(title: "Target Glucose", view: .targetsEditor, path: ["Therapy Settings"])
  65. ]
  66. static let algorithmItems = [
  67. SettingItem(
  68. title: "Autosens",
  69. view: .autosensSettings,
  70. searchContents: ["Autosens Max", "Autosens Min", "Rewind Resets Autosens"],
  71. path: ["Algorithm", "Autosens"]
  72. ),
  73. SettingItem(
  74. title: "Super Micro Bolus (SMB)",
  75. view: .smbSettings,
  76. searchContents: [
  77. "Enable SMB Always",
  78. "Max SMB Basal Minutes",
  79. "Max UAM SMB Basal Minutes",
  80. "Max Delta-BG Threshold SMB",
  81. "SMB Delivery Ratio",
  82. "SMB Interval"
  83. ],
  84. path: ["Algorithm", "Super Micro Bolus (SMB)"]
  85. ),
  86. SettingItem(
  87. title: "Dynamic Sensitivity",
  88. view: .dynamicISF,
  89. searchContents: [
  90. "Activate Dynamic Sensitivity (ISF)",
  91. "Activate Dynamic Carb Ratio (CR)",
  92. "Use Sigmoid Formula",
  93. "Adjustment Factor",
  94. "Sigmoid Adjustment Factor",
  95. "Weighted Average of TDD",
  96. "Adjust Basal",
  97. "Minimum Safety Threshold"
  98. ],
  99. path: ["Algorithm", "Dynamic Sensitivity"]
  100. ),
  101. SettingItem(
  102. title: "Target Behavior",
  103. view: .targetBehavior,
  104. searchContents: [
  105. "High Temptarget Raises Sensitivity",
  106. "Low Temptarget Lowers Sensitivity",
  107. "Sensitivity Raises Target",
  108. "Resistance Lowers Target",
  109. "Half Basal Exercise Target"
  110. ],
  111. path: ["Algorithm", "Target Behavior"]
  112. ),
  113. SettingItem(
  114. title: "Additionals",
  115. view: .algorithmAdvancedSettings,
  116. searchContents: [
  117. "Max Daily Safety Multiplier",
  118. "Current Basal Safety Multiplier",
  119. "Use Custom Peak Time",
  120. "Duration of Insulin Action", "DIA",
  121. "Insulin Peak Time",
  122. "Skip Neutral Temps",
  123. "Unsuspend If No Temp",
  124. "Suspend Zeros IOB",
  125. "Min 5m Carbimpact",
  126. "Autotune ISF Adjustment Fractio",
  127. "Remaining Carbs Fraction",
  128. "Remaining Carbs Cap",
  129. "Noisy CGM Target Multiplier"
  130. ],
  131. path: ["Algorithm", "Additionals"]
  132. )
  133. ]
  134. static let trioFeaturesItems = [
  135. SettingItem(
  136. title: "Bolus Calculator",
  137. view: .bolusCalculatorConfig,
  138. searchContents: [
  139. "Display Meal Presets",
  140. "Recommended Bolus Percentage",
  141. "Enable Fatty Meal Factor",
  142. "Fatty Meal Factor",
  143. "Enable Super Bolus",
  144. "Super Bolus Factor"
  145. ],
  146. path: ["Features", "Bolus Calculator"]
  147. ),
  148. SettingItem(
  149. title: "Meal Settings",
  150. view: .mealSettings,
  151. searchContents: [
  152. "Max Carbs",
  153. "Max Fat",
  154. "Max Protein",
  155. "Display and Allow Fat and Protein Entries",
  156. "Fat and Protein Delay",
  157. "Maximum Duration (hours)",
  158. "Spread Interval (minutes)",
  159. "Fat and Protein Factor"
  160. ],
  161. path: ["Features", "Meal Settings"]
  162. ),
  163. SettingItem(
  164. title: "Shortcuts",
  165. view: .shortcutsConfig,
  166. searchContents: ["Allow Bolusing with Shortcuts"],
  167. path: ["Features", "Shortcuts"]
  168. ),
  169. SettingItem(
  170. title: "Remote Control",
  171. view: .remoteControlConfig,
  172. searchContents: ["Remote Control"],
  173. path: ["Features", "Remote Control"]
  174. ),
  175. SettingItem(
  176. title: "User Interface",
  177. view: .userInterfaceSettings,
  178. searchContents: [
  179. "Show X-Axis Grid Lines",
  180. "Show Y-Axis Grid Line",
  181. "Show Low and High Thresholds",
  182. "Low Threshold",
  183. "High Threshold",
  184. "X-Axis Interval Step",
  185. "Total Insulin Display Type",
  186. "Total Daily Dose",
  187. "Total Insulin in Scope",
  188. "Override HbA1c Unit",
  189. "Standing / Laying TIR Chart",
  190. "Show Carbs Required Badge",
  191. "Carbs Required Threshold",
  192. "Forecast Display Type",
  193. "Trio Color Scheme",
  194. "Glucose Color Scheme"
  195. ],
  196. path: ["Features", "User Interface"]
  197. ),
  198. SettingItem(title: "App Icons", view: .iconConfig),
  199. SettingItem(title: "Autotune", view: .autotuneConfig)
  200. ]
  201. static let notificationItems = [
  202. SettingItem(title: "Manage iOS Preferences", view: .notificationSettings),
  203. SettingItem(
  204. title: "Trio Notifications",
  205. view: .glucoseNotificationSettings,
  206. searchContents: [
  207. "Always Notify Pump",
  208. "Always Notify CGM",
  209. "Always Notify Carb",
  210. "Always Notify Algorithm",
  211. "Show Glucose App Badge",
  212. "Always Notify Glucose",
  213. "Play Alarm Sound",
  214. "Add Glucose Source to Alarm",
  215. "Low Glucose Alarm Limit",
  216. "High Glucose Alarm Limit"
  217. ],
  218. path: ["Notifications", "Trio Notifications"] // Glucose
  219. ),
  220. SettingItem(
  221. title: "Live Activity",
  222. view: .liveActivitySettings,
  223. searchContents: [
  224. "Enable Live Activity",
  225. "Lock Screen Widget Style"
  226. ],
  227. path: ["Notifications", "Live Activity"]
  228. ),
  229. SettingItem(
  230. title: "Calendar Events",
  231. view: .calendarEventSettings,
  232. searchContents: [
  233. "Create Calendar Events",
  234. "Choose Calendar",
  235. "Display Emojis as Labels",
  236. "Display IOB and COB"
  237. ],
  238. path: ["Notifications", "Calendar Events"]
  239. )
  240. ]
  241. static let serviceItems = [
  242. SettingItem(
  243. title: "Nightscout",
  244. view: .nighscoutConfig,
  245. searchContents: [
  246. "Import Settings",
  247. "Backfill Glucose"
  248. ],
  249. path: ["Services", "Nightscout"]
  250. ),
  251. SettingItem(
  252. title: "Nightscout Upload",
  253. view: .nighscoutConfig,
  254. searchContents: [
  255. "Allow Uploading to Nightscout",
  256. "Upload Glucose"
  257. ],
  258. path: ["Services", "Nightscout", "Upload"]
  259. ),
  260. SettingItem(
  261. title: "Nightscout Fetch & Remote Control",
  262. view: .nighscoutConfig,
  263. searchContents: [
  264. "Allow Fetching From Nightscout"
  265. ],
  266. path: ["Services", "Nightscout", "Fetch and Remote Control"]
  267. ),
  268. SettingItem(title: "Tidepool", view: .serviceSettings, path: ["Services"]),
  269. SettingItem(title: "Apple Health", view: .healthkit, path: ["Services"])
  270. ]
  271. static var allItems: [SettingItem] {
  272. trioConfig + devicesItems + therapyItems + algorithmItems + trioFeaturesItems + notificationItems + serviceItems
  273. }
  274. static func filteredItems(searchText: String) -> [FilteredSettingItem] {
  275. allItems.flatMap { item in
  276. var results = [FilteredSettingItem]()
  277. let searchTextToLower = searchText.lowercased()
  278. if item.title.stringValue.localizedCaseInsensitiveContains(searchTextToLower) ||
  279. item.title.englishValue.localizedCaseInsensitiveContains(searchTextToLower)
  280. {
  281. results.append(FilteredSettingItem(settingItem: item, matchedContent: item.title))
  282. }
  283. if let matchedContents = item.searchContents?.filter({
  284. $0.stringValue.localizedCaseInsensitiveContains(searchTextToLower) ||
  285. $0.englishValue.localizedCaseInsensitiveContains(searchTextToLower)
  286. }) {
  287. results.append(contentsOf: matchedContents.map { FilteredSettingItem(settingItem: item, matchedContent: $0) })
  288. }
  289. return results
  290. }
  291. }
  292. }
  293. extension LocalizedStringKey {
  294. var stringValue: String {
  295. let mirror = Mirror(reflecting: self)
  296. let children = mirror.children
  297. if let label = children.first(where: { $0.label == "key" })?.value as? String {
  298. return NSLocalizedString(label, comment: "")
  299. } else {
  300. return ""
  301. }
  302. }
  303. var englishValue: String {
  304. let mirror = Mirror(reflecting: self)
  305. let children = mirror.children
  306. if let key = children.first(where: { $0.label == "key" })?.value as? String {
  307. if let path = Bundle.main.path(forResource: "en", ofType: "lproj"),
  308. let bundle = Bundle(path: path)
  309. {
  310. return bundle.localizedString(forKey: key, value: nil, table: nil)
  311. }
  312. }
  313. return ""
  314. }
  315. }