| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- //
- // RemoteSettingsView.swift
- // LoopFollow
- //
- // Created by Jonas Björkert on 2024-08-25.
- // Updated on 2024-09-16.
- // Copyright © 2024 Jon Fawcett. All rights reserved.
- //
- import SwiftUI
- import HealthKit
- struct RemoteSettingsView: View {
- @ObservedObject var viewModel: RemoteSettingsViewModel
- @Environment(\.presentationMode) var presentationMode
- @FocusState private var focusedField: Field?
- @State private var showAlert: Bool = false
- @State private var alertType: AlertType? = nil
- @State private var alertMessage: String? = nil
- enum Field: Hashable {
- case user
- case deviceToken
- case sharedSecret
- case apnsKey
- case teamId
- case keyId
- case bundleId
- case maxBolus
- }
- enum AlertType {
- case validation
- }
- var body: some View {
- NavigationView {
- Form {
- // Remote Type Section
- Section {
- Picker("Remote Type", selection: $viewModel.remoteType) {
- Text("None").tag(RemoteType.none)
- Text("Nightscout").tag(RemoteType.nightscout)
- Text("Trio Remote Control").tag(RemoteType.trc)
- }
- .pickerStyle(MenuPickerStyle())
- }
- // User Information Section
- if viewModel.remoteType != .none {
- Section(header: Text("User Information")) {
- HStack {
- Text("User")
- TextField("Enter User", text: $viewModel.user)
- .autocapitalization(.none)
- .disableAutocorrection(true)
- .focused($focusedField, equals: .user)
- .multilineTextAlignment(.trailing)
- }
- }
- }
- // Trio Remote Control Settings Section
- if viewModel.remoteType == .trc {
- Section(header: Text("Trio Remote Control Settings")) {
- HStack {
- Text("Shared Secret")
- TextField("Enter Shared Secret", text: $viewModel.sharedSecret)
- .autocapitalization(.none)
- .disableAutocorrection(true)
- .focused($focusedField, equals: .sharedSecret)
- .multilineTextAlignment(.trailing)
- }
- HStack {
- Text("APNS Key ID")
- TextField("Enter APNS Key ID", text: $viewModel.keyId)
- .autocapitalization(.none)
- .disableAutocorrection(true)
- .focused($focusedField, equals: .keyId)
- .multilineTextAlignment(.trailing)
- }
- VStack(alignment: .leading) {
- Text("APNS Key")
- TextEditor(text: $viewModel.apnsKey)
- .frame(height: 100)
- .autocapitalization(.none)
- .disableAutocorrection(true)
- .overlay(
- RoundedRectangle(cornerRadius: 8)
- .stroke(Color.gray.opacity(0.5), lineWidth: 1)
- )
- .focused($focusedField, equals: .apnsKey)
- }
- }
- // Guardrails Section
- Section(header: Text("Guardrails")) {
- HStack {
- Text("Max Bolus")
- Spacer()
- TextFieldWithToolBar(
- quantity: $viewModel.maxBolus,
- maxLength: 4,
- unit: HKUnit.internationalUnit(),
- allowDecimalSeparator: true,
- minValue: HKQuantity(unit: .internationalUnit(), doubleValue: 0.0),
- maxValue: HKQuantity(unit: .internationalUnit(), doubleValue: 10.0),
- onValidationError: { message in
- handleValidationError(message)
- }
- )
- .frame(width: 100)
- Text("U")
- .foregroundColor(.secondary)
- }
- HStack {
- Text("Max Carbs")
- Spacer()
- TextFieldWithToolBar(
- quantity: $viewModel.maxCarbs,
- maxLength: 4,
- unit: HKUnit.gram(),
- allowDecimalSeparator: true,
- minValue: HKQuantity(unit: .gram(), doubleValue: 0),
- maxValue: HKQuantity(unit: .gram(), doubleValue: 100),
- onValidationError: { message in
- handleValidationError(message)
- }
- )
- .frame(width: 100)
- Text("g")
- .foregroundColor(.secondary)
- }
- HStack {
- Text("Max Protein")
- Spacer()
- TextFieldWithToolBar(
- quantity: $viewModel.maxProtein,
- maxLength: 4,
- unit: HKUnit.gram(),
- allowDecimalSeparator: true,
- minValue: HKQuantity(unit: .gram(), doubleValue: 0),
- maxValue: HKQuantity(unit: .gram(), doubleValue: 100),
- onValidationError: { message in
- handleValidationError(message)
- }
- )
- .frame(width: 100)
- Text("g")
- .foregroundColor(.secondary)
- }
- HStack {
- Text("Max Fat")
- Spacer()
- TextFieldWithToolBar(
- quantity: $viewModel.maxFat,
- maxLength: 4,
- unit: HKUnit.gram(),
- allowDecimalSeparator: true,
- minValue: HKQuantity(unit: .gram(), doubleValue: 0),
- maxValue: HKQuantity(unit: .gram(), doubleValue: 100),
- onValidationError: { message in
- handleValidationError(message)
- }
- )
- .frame(width: 100)
- Text("g")
- .foregroundColor(.secondary)
- }
- }
- }
- }
- .navigationBarTitle("Remote Settings", displayMode: .inline)
- .toolbar {
- ToolbarItem(placement: .navigationBarTrailing) {
- Button("Done") {
- presentationMode.wrappedValue.dismiss()
- }
- }
- }
- .onTapGesture {
- focusedField = nil
- }
- .alert(isPresented: $showAlert) {
- switch alertType {
- case .validation:
- return Alert(
- title: Text("Validation Error"),
- message: Text(alertMessage ?? "Invalid input."),
- dismissButton: .default(Text("OK"))
- )
- case .none:
- return Alert(title: Text("Unknown Alert"))
- }
- }
- }
- }
- private func handleValidationError(_ message: String) {
- alertMessage = message
- alertType = .validation
- showAlert = true
- }
- }
|