G7SettingsView.swift 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. //
  2. // G7SettingsView.swift
  3. // CGMBLEKitUI
  4. //
  5. // Created by Pete Schwamb on 9/25/22.
  6. // Copyright © 2022 LoopKit Authors. All rights reserved.
  7. //
  8. import Foundation
  9. import SwiftUI
  10. import G7SensorKit
  11. import LoopKitUI
  12. struct G7SettingsView: View {
  13. private var durationFormatter: RelativeDateTimeFormatter = {
  14. let formatter = RelativeDateTimeFormatter()
  15. formatter.unitsStyle = .full
  16. return formatter
  17. }()
  18. @Environment(\.guidanceColors) private var guidanceColors
  19. @Environment(\.glucoseTintColor) private var glucoseTintColor
  20. var didFinish: (() -> Void)
  21. var deleteCGM: (() -> Void)
  22. @ObservedObject var viewModel: G7SettingsViewModel
  23. @State private var showingDeletionSheet = false
  24. init(didFinish: @escaping () -> Void, deleteCGM: @escaping () -> Void, viewModel: G7SettingsViewModel) {
  25. self.didFinish = didFinish
  26. self.deleteCGM = deleteCGM
  27. self.viewModel = viewModel
  28. }
  29. private var timeFormatter: DateFormatter = {
  30. let formatter = DateFormatter()
  31. formatter.dateStyle = .short
  32. formatter.timeStyle = .short
  33. return formatter
  34. }()
  35. var body: some View {
  36. List {
  37. Section() {
  38. VStack {
  39. headerImage
  40. progressBar
  41. }
  42. }
  43. if let activatedAt = viewModel.activatedAt {
  44. HStack {
  45. Text(LocalizedString("Sensor Start", comment: "title for g7 settings row showing sensor start time"))
  46. Spacer()
  47. Text(timeFormatter.string(from: activatedAt))
  48. .foregroundColor(.secondary)
  49. }
  50. HStack {
  51. Text(LocalizedString("Sensor Expiration", comment: "title for g7 settings row showing sensor expiration time"))
  52. Spacer()
  53. Text(timeFormatter.string(from: activatedAt.addingTimeInterval(G7Sensor.lifetime)))
  54. .foregroundColor(.secondary)
  55. }
  56. HStack {
  57. Text(LocalizedString("Grace Period End", comment: "title for g7 settings row showing sensor grace period end time"))
  58. Spacer()
  59. Text(timeFormatter.string(from: activatedAt.addingTimeInterval(G7Sensor.lifetime + G7Sensor.gracePeriod)))
  60. .foregroundColor(.secondary)
  61. }
  62. }
  63. Section(LocalizedString("Last Reading", comment: "Title")) {
  64. LabeledValueView(label: LocalizedString("Glucose", comment: "Field label"),
  65. value: viewModel.lastGlucoseString)
  66. LabeledDateView(label: LocalizedString("Time", comment: "Field label"),
  67. date: viewModel.latestReadingTimestamp,
  68. dateFormatter: viewModel.dateFormatter)
  69. LabeledValueView(label: LocalizedString("Trend", comment: "Field label"),
  70. value: viewModel.lastGlucoseTrendString)
  71. }
  72. Section(LocalizedString("Bluetooth", comment: "Title")) {
  73. if let name = viewModel.sensorName {
  74. HStack {
  75. Text(LocalizedString("Name", comment: "title for g7 settings row showing BLE Name"))
  76. Spacer()
  77. Text(name)
  78. .foregroundColor(.secondary)
  79. }
  80. }
  81. if viewModel.scanning {
  82. HStack {
  83. Text(LocalizedString("Scanning", comment: "title for g7 settings connection status when scanning"))
  84. Spacer()
  85. SwiftUI.ProgressView()
  86. }
  87. } else {
  88. if viewModel.connected {
  89. Text(LocalizedString("Connected", comment: "title for g7 settings connection status when connected"))
  90. } else {
  91. HStack {
  92. Text(LocalizedString("Connecting", comment: "title for g7 settings connection status when connecting"))
  93. Spacer()
  94. SwiftUI.ProgressView()
  95. }
  96. }
  97. }
  98. if let lastConnect = viewModel.lastConnect {
  99. LabeledValueView(label: LocalizedString("Last Connect", comment: "title for g7 settings row showing sensor last connect time"),
  100. value: timeFormatter.string(from: lastConnect))
  101. }
  102. }
  103. Section(LocalizedString("Configuration", comment: "Title")) {
  104. HStack {
  105. Toggle(LocalizedString("Upload Readings", comment: "title for g7 config settings to upload readings"), isOn: $viewModel.uploadReadings)
  106. }
  107. }
  108. Section () {
  109. if !self.viewModel.scanning {
  110. Button(LocalizedString("Scan for new sensor", comment: "Button text"), action: {
  111. self.viewModel.scanForNewSensor()
  112. })
  113. }
  114. deleteCGMButton
  115. }
  116. }
  117. .insetGroupedListStyle()
  118. .navigationBarItems(trailing: doneButton)
  119. .navigationBarTitle(LocalizedString("Dexcom G7", comment: "Navigation bar title for G7SettingsView"))
  120. }
  121. private var deleteCGMButton: some View {
  122. Button(action: {
  123. showingDeletionSheet = true
  124. }, label: {
  125. Text(LocalizedString("Delete CGM", comment: "Button label for removing CGM"))
  126. .foregroundColor(.red)
  127. }).actionSheet(isPresented: $showingDeletionSheet) {
  128. ActionSheet(
  129. title: Text("Are you sure you want to delete this CGM?"),
  130. buttons: [
  131. .destructive(Text("Delete CGM")) {
  132. self.deleteCGM()
  133. },
  134. .cancel(),
  135. ]
  136. )
  137. }
  138. }
  139. private var headerImage: some View {
  140. VStack(alignment: .center) {
  141. Image(frameworkImage: "g7")
  142. .resizable()
  143. .aspectRatio(contentMode: ContentMode.fit)
  144. .frame(height: 150)
  145. .padding(.horizontal)
  146. }.frame(maxWidth: .infinity)
  147. }
  148. @ViewBuilder
  149. private var progressBar: some View {
  150. VStack(alignment: .leading, spacing: 4) {
  151. HStack(alignment: .firstTextBaseline) {
  152. Text(viewModel.progressBarState.label)
  153. .font(.system(size: 17))
  154. .foregroundColor(color(for: viewModel.progressBarState.labelColor))
  155. Spacer()
  156. if let referenceDate = viewModel.progressReferenceDate {
  157. Text(durationFormatter.localizedString(for: referenceDate, relativeTo: Date()))
  158. .foregroundColor(.secondary)
  159. }
  160. }
  161. ProgressView(value: viewModel.progressBarProgress)
  162. .accentColor(color(for: viewModel.progressBarColorStyle))
  163. }
  164. }
  165. private func color(for colorStyle: ColorStyle) -> Color {
  166. switch colorStyle {
  167. case .glucose:
  168. return glucoseTintColor
  169. case .warning:
  170. return guidanceColors.warning
  171. case .critical:
  172. return guidanceColors.critical
  173. case .normal:
  174. return .primary
  175. case .dimmed:
  176. return .secondary
  177. }
  178. }
  179. private var doneButton: some View {
  180. Button("Done", action: {
  181. self.didFinish()
  182. })
  183. }
  184. }