GeneralSettingsView.swift 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. // LoopFollow
  2. // GeneralSettingsView.swift
  3. import SwiftUI
  4. struct GeneralSettingsView: View {
  5. @ObservedObject var colorBGText = Storage.shared.colorBGText
  6. @ObservedObject var appBadge = Storage.shared.appBadge
  7. @ObservedObject var appearanceMode = Storage.shared.appearanceMode
  8. @ObservedObject var showStats = Storage.shared.showStats
  9. @ObservedObject var useIFCC = Storage.shared.useIFCC
  10. @ObservedObject var showSmallGraph = Storage.shared.showSmallGraph
  11. @ObservedObject var screenlockSwitchState = Storage.shared.screenlockSwitchState
  12. @ObservedObject var showDisplayName = Storage.shared.showDisplayName
  13. @ObservedObject var snoozerEmoji = Storage.shared.snoozerEmoji
  14. @ObservedObject var forcePortraitMode = Storage.shared.forcePortraitMode
  15. @ObservedObject var persistentNotification = Storage.shared.persistentNotification
  16. @ObservedObject var graphTimeZoneEnabled = Storage.shared.graphTimeZoneEnabled
  17. @ObservedObject var graphTimeZoneIdentifier = Storage.shared.graphTimeZoneIdentifier
  18. // Speak-BG settings
  19. @ObservedObject var speakBG = Storage.shared.speakBG
  20. @ObservedObject var speakBGAlways = Storage.shared.speakBGAlways
  21. @ObservedObject var speakLanguage = Storage.shared.speakLanguage
  22. @ObservedObject var speakLowBG = Storage.shared.speakLowBG
  23. @ObservedObject var speakProactiveLowBG = Storage.shared.speakProactiveLowBG
  24. @ObservedObject var speakLowBGLimit = Storage.shared.speakLowBGLimit
  25. @ObservedObject var speakFastDropDelta = Storage.shared.speakFastDropDelta
  26. @ObservedObject var speakHighBG = Storage.shared.speakHighBG
  27. @ObservedObject var speakHighBGLimit = Storage.shared.speakHighBGLimit
  28. var body: some View {
  29. NavigationView {
  30. Form {
  31. Section("App Settings") {
  32. Toggle("Display App Badge", isOn: $appBadge.value)
  33. Toggle("Persistent Notification", isOn: $persistentNotification.value)
  34. }
  35. Section("Display") {
  36. Picker("Appearance", selection: $appearanceMode.value) {
  37. ForEach(AppearanceMode.allCases, id: \.self) { mode in
  38. Text(mode.displayName).tag(mode)
  39. }
  40. }
  41. Toggle("Display Stats", isOn: $showStats.value)
  42. Toggle("Use IFCC A1C", isOn: $useIFCC.value)
  43. Toggle("Display Small Graph", isOn: $showSmallGraph.value)
  44. Toggle("Color BG Text", isOn: $colorBGText.value)
  45. Toggle("Keep Screen Active", isOn: $screenlockSwitchState.value)
  46. Toggle("Show Display Name", isOn: $showDisplayName.value)
  47. Toggle("Snoozer emoji", isOn: $snoozerEmoji.value)
  48. Toggle("Force portrait mode", isOn: $forcePortraitMode.value)
  49. .onChange(of: forcePortraitMode.value) { _ in
  50. if #available(iOS 16.0, *) {
  51. let window = UIApplication.shared.connectedScenes
  52. .compactMap { $0 as? UIWindowScene }
  53. .flatMap { $0.windows }
  54. .first
  55. window?.rootViewController?.setNeedsUpdateOfSupportedInterfaceOrientations()
  56. }
  57. }
  58. }
  59. Section("Time Zone") {
  60. Toggle("Time Zone Override", isOn: $graphTimeZoneEnabled.value)
  61. .onChange(of: graphTimeZoneEnabled.value) { _ in markChartSettingsDirty() }
  62. if graphTimeZoneEnabled.value {
  63. Picker("Time Zone", selection: $graphTimeZoneIdentifier.value) {
  64. ForEach(Self.sortedTimeZones, id: \.identifier) { tz in
  65. Text(Self.timeZoneLabel(tz)).tag(tz.identifier)
  66. }
  67. }
  68. .onChange(of: graphTimeZoneIdentifier.value) { _ in markChartSettingsDirty() }
  69. }
  70. }
  71. Section("Speak BG") {
  72. Toggle("Speak BG", isOn: $speakBG.value.animation())
  73. if speakBG.value {
  74. Picker("Language", selection: $speakLanguage.value) {
  75. Text("English").tag("en")
  76. Text("Italian").tag("it")
  77. Text("Slovak").tag("sk")
  78. Text("Swedish").tag("sv")
  79. }
  80. Toggle("Always", isOn: $speakBGAlways.value.animation())
  81. if !speakBGAlways.value {
  82. Toggle("Low", isOn: $speakLowBG.value.animation())
  83. .onChange(of: speakLowBG.value) { newValue in
  84. if newValue {
  85. speakProactiveLowBG.value = false
  86. }
  87. }
  88. Toggle("Proactive Low", isOn: $speakProactiveLowBG.value.animation())
  89. .onChange(of: speakProactiveLowBG.value) { newValue in
  90. if newValue {
  91. speakLowBG.value = false
  92. }
  93. }
  94. if speakLowBG.value || speakProactiveLowBG.value {
  95. BGPicker(
  96. title: "Low BG Limit",
  97. range: 40 ... 108,
  98. value: $speakLowBGLimit.value
  99. )
  100. }
  101. if speakProactiveLowBG.value {
  102. BGPicker(
  103. title: "Fast Drop Delta",
  104. range: 3 ... 20,
  105. value: $speakFastDropDelta.value
  106. )
  107. }
  108. Toggle("High", isOn: $speakHighBG.value.animation())
  109. if speakHighBG.value {
  110. BGPicker(
  111. title: "High BG Limit",
  112. range: 140 ... 300,
  113. value: $speakHighBGLimit.value
  114. )
  115. }
  116. }
  117. }
  118. }
  119. }
  120. }
  121. .preferredColorScheme(Storage.shared.appearanceMode.value.colorScheme)
  122. .navigationBarTitle("General Settings", displayMode: .inline)
  123. }
  124. private func markChartSettingsDirty() {
  125. Observable.shared.chartSettingsChanged.value = true
  126. }
  127. private static let sortedTimeZones: [TimeZone] = TimeZone.knownTimeZoneIdentifiers
  128. .compactMap { TimeZone(identifier: $0) }
  129. .sorted { $0.secondsFromGMT() < $1.secondsFromGMT() }
  130. private static func timeZoneLabel(_ tz: TimeZone) -> String {
  131. let offsetMinutes = tz.secondsFromGMT() / 60
  132. let sign = offsetMinutes >= 0 ? "+" : "-"
  133. let offsetString = String(format: "UTC%@%02d:%02d", sign, abs(offsetMinutes) / 60, abs(offsetMinutes) % 60)
  134. return "(\(offsetString)) \(tz.identifier)"
  135. }
  136. }