| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- //
- // AlgorithmSettingsSubstepView.swift
- // Trio
- //
- // Created by Cengiz Deniz on 15.04.25.
- //
- import SwiftUI
- struct AlgorithmSettingsSubstepView<Substep: AlgorithmSubstepProtocol & RawRepresentable>: View where Substep.RawValue == Int {
- @Bindable var state: Onboarding.StateModel
- let substep: Substep
- @State private var shouldDisplayPicker: Bool = false
- @State private var decimalPlaceholder: Decimal = 0.0
- @State private var booleanPlaceholder: Bool = false
- private let settingsProvider = PickerSettingsProvider.shared
- var body: some View {
- VStack(alignment: .leading, spacing: 16) {
- Text(substep.title)
- .padding(.horizontal)
- .font(.title3)
- .bold()
- Text(substep.hint(units: state.units))
- .font(.subheadline)
- .multilineTextAlignment(.leading)
- .padding(.horizontal)
- .foregroundStyle(Color.secondary)
- if let step = substep.toAlgorithmSubstep() {
- switch step {
- case .autosensMin:
- algorithmSettingsInput(
- label: step.title,
- displayPicker: $shouldDisplayPicker,
- setting: settingsProvider.settings.autosensMin,
- decimalValue: $state.autosensMin,
- booleanValue: $booleanPlaceholder,
- type: OnboardingInputSectionType.decimal
- )
- case .autosensMax:
- algorithmSettingsInput(
- label: substep.title,
- displayPicker: $shouldDisplayPicker,
- setting: settingsProvider.settings.autosensMax,
- decimalValue: $state.autosensMax,
- booleanValue: $booleanPlaceholder,
- type: OnboardingInputSectionType.decimal
- )
- case .rewindResetsAutosens:
- algorithmSettingsInput(
- label: substep.title,
- displayPicker: $shouldDisplayPicker,
- setting: nil,
- decimalValue: $decimalPlaceholder,
- booleanValue: $state.rewindResetsAutosens,
- type: OnboardingInputSectionType.boolean
- )
- case .enableSMBAlways:
- algorithmSettingsInput(
- label: substep.title,
- displayPicker: $shouldDisplayPicker,
- setting: nil,
- decimalValue: $decimalPlaceholder,
- booleanValue: $state.enableSMBAlways,
- type: OnboardingInputSectionType.boolean
- )
- case .enableSMBWithCOB:
- algorithmSettingsInput(
- label: substep.title,
- displayPicker: $shouldDisplayPicker,
- setting: nil,
- decimalValue: $decimalPlaceholder,
- booleanValue: $state.enableSMBWithCOB,
- type: OnboardingInputSectionType.boolean,
- disabled: state.enableSMBAlways
- )
- case .enableSMBWithTempTarget:
- algorithmSettingsInput(
- label: substep.title,
- displayPicker: $shouldDisplayPicker,
- setting: nil,
- decimalValue: $decimalPlaceholder,
- booleanValue: $state.enableSMBWithTempTarget,
- type: OnboardingInputSectionType.boolean,
- disabled: state.enableSMBAlways
- )
- case .enableSMBAfterCarbs:
- algorithmSettingsInput(
- label: substep.title,
- displayPicker: $shouldDisplayPicker,
- setting: nil,
- decimalValue: $decimalPlaceholder,
- booleanValue: $state.enableSMBAfterCarbs,
- type: OnboardingInputSectionType.boolean,
- disabled: state.enableSMBAlways
- )
- case .enableSMBWithHighGlucoseTarget:
- algorithmSettingsInput(
- label: substep.title,
- displayPicker: $shouldDisplayPicker,
- setting: nil,
- decimalValue: $decimalPlaceholder,
- booleanValue: $state.enableSMBWithHighGlucoseTarget,
- type: OnboardingInputSectionType.boolean,
- disabled: state.enableSMBAlways
- )
- if state.enableSMBWithHighGlucoseTarget {
- algorithmSettingsInput(
- label: String(localized: "High Glucose Target"),
- displayPicker: $shouldDisplayPicker,
- setting: settingsProvider.settings.enableSMB_high_bg_target,
- decimalValue: $state.highGlucoseTarget,
- booleanValue: $booleanPlaceholder,
- type: OnboardingInputSectionType.decimal,
- disabled: state.enableSMBAlways
- )
- }
- case .allowSMBWithHighTempTarget:
- algorithmSettingsInput(
- label: substep.title,
- displayPicker: $shouldDisplayPicker,
- setting: nil,
- decimalValue: $decimalPlaceholder,
- booleanValue: $state.allowSMBWithHighTempTarget,
- type: OnboardingInputSectionType.boolean
- )
- case .enableUAM:
- algorithmSettingsInput(
- label: substep.title,
- displayPicker: $shouldDisplayPicker,
- setting: nil,
- decimalValue: $decimalPlaceholder,
- booleanValue: $state.enableUAM,
- type: OnboardingInputSectionType.boolean
- )
- case .maxSMBMinutes:
- algorithmSettingsInput(
- label: substep.title,
- displayPicker: $shouldDisplayPicker,
- setting: settingsProvider.settings.maxSMBBasalMinutes,
- decimalValue: $state.maxSMBMinutes,
- booleanValue: $booleanPlaceholder,
- type: OnboardingInputSectionType.decimal
- )
- case .maxUAMMinutes:
- algorithmSettingsInput(
- label: substep.title,
- displayPicker: $shouldDisplayPicker,
- setting: settingsProvider.settings.maxUAMSMBBasalMinutes,
- decimalValue: $state.maxUAMMinutes,
- booleanValue: $booleanPlaceholder,
- type: OnboardingInputSectionType.decimal
- )
- case .maxDeltaGlucoseThreshold:
- algorithmSettingsInput(
- label: substep.title,
- displayPicker: $shouldDisplayPicker,
- setting: settingsProvider.settings.maxDeltaBGthreshold,
- decimalValue: $state.maxDeltaGlucoseThreshold,
- booleanValue: $booleanPlaceholder,
- type: OnboardingInputSectionType.decimal
- )
- case .highTempTargetRaisesSensitivity:
- algorithmSettingsInput(
- label: substep.title,
- displayPicker: $shouldDisplayPicker,
- setting: nil,
- decimalValue: $decimalPlaceholder,
- booleanValue: $state.highTempTargetRaisesSensitivity,
- type: OnboardingInputSectionType.boolean
- )
- case .lowTempTargetLowersSensitivity:
- algorithmSettingsInput(
- label: substep.title,
- displayPicker: $shouldDisplayPicker,
- setting: nil,
- decimalValue: $decimalPlaceholder,
- booleanValue: $state.lowTempTargetLowersSensitivity,
- type: OnboardingInputSectionType.boolean
- )
- case .sensitivityRaisesTarget:
- algorithmSettingsInput(
- label: substep.title,
- displayPicker: $shouldDisplayPicker,
- setting: nil,
- decimalValue: $decimalPlaceholder,
- booleanValue: $state.sensitivityRaisesTarget,
- type: OnboardingInputSectionType.boolean
- )
- case .resistanceLowersTarget:
- algorithmSettingsInput(
- label: substep.title,
- displayPicker: $shouldDisplayPicker,
- setting: nil,
- decimalValue: $decimalPlaceholder,
- booleanValue: $state.resistanceLowersTarget,
- type: OnboardingInputSectionType.boolean
- )
- case .halfBasalTarget:
- algorithmSettingsInput(
- label: substep.title,
- displayPicker: $shouldDisplayPicker,
- setting: settingsProvider.settings.halfBasalExerciseTarget,
- decimalValue: $state.halfBasalTarget,
- booleanValue: $booleanPlaceholder,
- type: OnboardingInputSectionType.decimal
- )
- }
- }
- substep.description(units: state.units).eraseToAnyView()
- .font(.footnote)
- .foregroundStyle(.secondary)
- .padding(.horizontal)
- .multilineTextAlignment(.leading)
- }
- .onAppear {
- // Ensure picker view is closed, when switching between setting steps
- shouldDisplayPicker = false
- }
- }
- @ViewBuilder private func algorithmSettingsInput(
- label: String,
- displayPicker: Binding<Bool>,
- setting: PickerSetting?,
- decimalValue: Binding<Decimal>,
- booleanValue: Binding<Bool>,
- type: OnboardingInputSectionType,
- disabled: Bool = false /// parameter only relevant for `Enable SMB Always` dependent settings
- ) -> some View {
- VStack {
- VStack {
- switch type {
- case .boolean:
- Toggle(isOn: booleanValue) {
- Text(label)
- }.tint(Color.accentColor)
- .disabled(disabled)
- case .decimal:
- Group {
- HStack {
- Text(label)
- Spacer()
- displayText(for: substep, decimalValue: decimalValue.wrappedValue, units: state.units)
- .foregroundColor(!displayPicker.wrappedValue ? .primary : .accentColor)
- .onTapGesture {
- displayPicker.wrappedValue.toggle()
- }
- }.disabled(disabled)
- if displayPicker.wrappedValue {
- Picker(selection: decimalValue, label: Text(label)) {
- if let setting = setting {
- ForEach(
- settingsProvider.generatePickerValues(from: setting, units: state.units),
- id: \.self
- ) { value in
- displayText(for: substep, decimalValue: value, units: state.units).tag(value)
- }
- }
- }
- .disabled(disabled)
- .pickerStyle(WheelPickerStyle())
- .frame(maxWidth: .infinity)
- }
- }
- }
- }
- .padding()
- .background(Color.chart.opacity(0.65))
- .cornerRadius(10)
- }
- }
- private func displayText(for substep: Substep, decimalValue: Decimal, units: GlucoseUnits) -> Text {
- guard let step = substep.toAlgorithmSubstep() else {
- return Text(decimalValue.description)
- }
- switch step {
- case .autosensMax,
- .autosensMin,
- .maxDeltaGlucoseThreshold:
- return Text("\(decimalValue * 100) \(String(localized: "%"))")
- case .enableSMBWithHighGlucoseTarget,
- .halfBasalTarget:
- let displayValue = units == .mmolL ? decimalValue.asMmolL : decimalValue
- return Text("\(displayValue.description) \(units.rawValue)")
- case .maxSMBMinutes,
- .maxUAMMinutes:
- return Text("\(decimalValue) \(String(localized: "min"))")
- default:
- return Text("") // not needed, because input type is boolean
- }
- }
- }
|