NightscoutConfigRootView.swift 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. import CoreData
  2. import SwiftUI
  3. import Swinject
  4. extension NightscoutConfig {
  5. struct RootView: BaseView {
  6. let resolver: Resolver
  7. @StateObject var state = StateModel()
  8. @State var importAlert: Alert?
  9. @State var isImportAlertPresented = false
  10. @State var importedHasRun = false
  11. @Environment(\.colorScheme) var colorScheme
  12. var color: LinearGradient {
  13. colorScheme == .dark ? LinearGradient(
  14. gradient: Gradient(colors: [
  15. Color("Background_1"),
  16. Color("Background_1"),
  17. Color("Background_2")
  18. // Color("Background_1")
  19. ]),
  20. startPoint: .top,
  21. endPoint: .bottom
  22. )
  23. :
  24. LinearGradient(
  25. gradient: Gradient(colors: [Color.gray.opacity(0.1)]),
  26. startPoint: .top,
  27. endPoint: .bottom
  28. )
  29. }
  30. @FetchRequest(
  31. entity: ImportError.entity(),
  32. sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)], predicate: NSPredicate(
  33. format: "date > %@", Date().addingTimeInterval(-1.minutes.timeInterval) as NSDate
  34. )
  35. ) var fetchedErrors: FetchedResults<ImportError>
  36. private var portFormater: NumberFormatter {
  37. let formatter = NumberFormatter()
  38. formatter.allowsFloats = false
  39. return formatter
  40. }
  41. var body: some View {
  42. Form {
  43. Section {
  44. TextField("URL", text: $state.url)
  45. .disableAutocorrection(true)
  46. .textContentType(.URL)
  47. .autocapitalization(.none)
  48. .keyboardType(.URL)
  49. SecureField("API secret", text: $state.secret)
  50. .disableAutocorrection(true)
  51. .autocapitalization(.none)
  52. .textContentType(.password)
  53. .keyboardType(.asciiCapable)
  54. if !state.message.isEmpty {
  55. Text(state.message)
  56. }
  57. if state.connecting {
  58. HStack {
  59. Text("Connecting...")
  60. Spacer()
  61. ProgressView()
  62. }
  63. }
  64. }
  65. Section {
  66. Button("Connect") { state.connect() }
  67. .disabled(state.url.isEmpty || state.connecting)
  68. Button("Delete") { state.delete() }.foregroundColor(.red).disabled(state.connecting)
  69. }
  70. Section {
  71. Toggle("Upload", isOn: $state.isUploadEnabled)
  72. if state.isUploadEnabled {
  73. Toggle("Statistics", isOn: $state.uploadStats)
  74. HStack(alignment: .top) {
  75. Image(systemName: "pencil.circle.fill")
  76. VStack {
  77. Text(
  78. "This enables uploading of statistics.json to Nightscout, which can be used by the Community Statistics and Demographics Project.\n\nParticipation in Community Statistics is opt-in, and requires separate registration at:\n"
  79. )
  80. .font(.caption)
  81. Text(
  82. "https://iaps-stats.hub.org"
  83. )
  84. .font(.caption)
  85. .multilineTextAlignment(.center)
  86. }
  87. }
  88. .foregroundColor(Color.secondary)
  89. Toggle("Glucose", isOn: $state.uploadGlucose)
  90. }
  91. } header: {
  92. Text("Allow Uploads")
  93. }
  94. Section {
  95. Button("Import settings from Nightscout") {
  96. importAlert = Alert(
  97. title: Text("Import settings?"),
  98. message: Text(
  99. "\n" +
  100. NSLocalizedString(
  101. "This will replace some or all of your current pump settings. Are you sure you want to import profile settings from Nightscout?",
  102. comment: "Profile Import Alert"
  103. ) +
  104. "\n"
  105. ),
  106. primaryButton: .destructive(
  107. Text("Yes, Import"),
  108. action: {
  109. state.importSettings()
  110. importedHasRun = true
  111. }
  112. ),
  113. secondaryButton: .cancel()
  114. )
  115. isImportAlertPresented.toggle()
  116. }.disabled(state.url.isEmpty || state.connecting)
  117. } header: { Text("Import from Nightscout") }
  118. .alert(isPresented: $importedHasRun) {
  119. Alert(
  120. title: Text((fetchedErrors.first?.error ?? "").count < 4 ? "Settings imported" : "Import Error"),
  121. message: Text(
  122. (fetchedErrors.first?.error ?? "").count < 4 ?
  123. NSLocalizedString(
  124. "\nNow please verify all of your new settings thoroughly:\n\n* Basal Settings\n * Carb Ratios\n * Glucose Targets\n * Insulin Sensitivities\n * DIA\n\n in iAPS Settings > Configuration.\n\nBad or invalid profile settings could have disatrous effects.",
  125. comment: "Imported Profiles Alert"
  126. ) :
  127. NSLocalizedString(fetchedErrors.first?.error ?? "", comment: "Import Error")
  128. ),
  129. primaryButton: .destructive(
  130. Text("OK")
  131. ),
  132. secondaryButton: .cancel()
  133. )
  134. }
  135. Section {
  136. Toggle("Use local glucose server", isOn: $state.useLocalSource)
  137. HStack {
  138. Text("Port")
  139. DecimalTextField("", value: $state.localPort, formatter: portFormater)
  140. }
  141. } header: { Text("Local glucose source") }
  142. Section {
  143. Button("Backfill glucose") { state.backfillGlucose() }
  144. .disabled(state.url.isEmpty || state.connecting || state.backfilling)
  145. }
  146. Section {
  147. Toggle("Remote control", isOn: $state.allowAnnouncements)
  148. } header: { Text("Allow Remote control of iAPS") }
  149. }
  150. .scrollContentBackground(.hidden).background(color)
  151. .onAppear(perform: configureView)
  152. .navigationBarTitle("Nightscout Config")
  153. .navigationBarTitleDisplayMode(.automatic)
  154. .alert(isPresented: $isImportAlertPresented) {
  155. importAlert!
  156. }
  157. }
  158. }
  159. }