| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- //
- // CarbEntryEditorView.swift
- // FreeAPS
- //
- // Created by Marvin Polscheit on 15.01.25.
- //
- import CoreData
- import SwiftUI
- struct CarbEntryEditorView: View {
- @Environment(\.dismiss) private var dismiss
- @Environment(\.colorScheme) var colorScheme
- @Environment(AppState.self) var appState
- var state: History.StateModel
- let carbEntry: CarbEntryStored
- /*
- This is the objectID of the entry that the user is editing. It is NOT always the `carbEntry: CarbEntryStored` that we pass to the `CarbEntryEditorView`.
- We need this because FPUs and carbs are treated completely different and that complicates the update process.
- */
- @State private var entryToEdit: NSManagedObjectID?
- @State private var editedCarbs: Decimal
- @State private var editedFat: Decimal
- @State private var editedProtein: Decimal
- @State private var editedNote: String
- @State private var isFPU: Bool
- @State private var editedDate: Date
- init(state: History.StateModel, carbEntry: CarbEntryStored) {
- self.state = state
- self.carbEntry = carbEntry
- _editedCarbs = State(initialValue: 0) // gets updated in the task block
- _editedFat = State(initialValue: 0) // gets updated in the task block
- _editedProtein = State(initialValue: 0) // gets updated in the task block
- _editedNote = State(initialValue: carbEntry.note ?? "")
- _isFPU = State(initialValue: carbEntry.isFPU)
- _entryToEdit = State(initialValue: nil)
- _editedDate = State(initialValue: Date())
- }
- private var mealFormatter: NumberFormatter {
- let formatter = NumberFormatter()
- formatter.numberStyle = .decimal
- formatter.maximumIntegerDigits = 3
- formatter.maximumFractionDigits = 0
- return formatter
- }
- private var carbLimitExceeded: Bool {
- editedCarbs > state.settingsManager.settings.maxCarbs
- }
- private var fatLimitExceeded: Bool {
- editedFat > state.settingsManager.settings.maxFat
- }
- private var proteinLimitExceeded: Bool {
- editedProtein > state.settingsManager.settings.maxProtein
- }
- private var limitExceeded: Bool {
- carbLimitExceeded || fatLimitExceeded || proteinLimitExceeded
- }
- private var isButtonDisabled: Bool {
- editedCarbs == 0 && editedFat == 0 && editedProtein == 0
- }
- private var buttonLabel: some View {
- if carbLimitExceeded {
- return Text("Max Carbs of \(state.settingsManager.settings.maxCarbs.description) g Exceeded")
- } else if fatLimitExceeded {
- return Text("Max Fat of \(state.settingsManager.settings.maxFat.description) g Exceeded")
- } else if proteinLimitExceeded {
- return Text("Max Protein of \(state.settingsManager.settings.maxProtein.description) g Exceeded")
- }
- return Text("Save and Update")
- }
- private var buttonBackgroundColor: Color {
- var treatmentButtonBackground = Color(.systemBlue)
- if limitExceeded {
- treatmentButtonBackground = Color(.systemRed)
- } else if isButtonDisabled {
- treatmentButtonBackground = Color(.systemGray)
- }
- return treatmentButtonBackground
- }
- var stickyButton: some View {
- ZStack {
- Rectangle()
- .frame(width: UIScreen.main.bounds.width, height: 65)
- .foregroundStyle(colorScheme == .dark ? Color.bgDarkerDarkBlue : Color.white)
- .background(.thinMaterial)
- .opacity(0.8)
- .clipShape(Rectangle())
- Button(
- action: {
- guard let entryToEdit = entryToEdit else { return }
- state.updateEntry(
- entryToEdit,
- newCarbs: editedCarbs,
- newFat: editedFat,
- newProtein: editedProtein,
- newNote: editedNote,
- newDate: editedDate
- )
- dismiss()
- }, label: {
- buttonLabel
- .font(.headline)
- .frame(maxWidth: .infinity, maxHeight: .infinity)
- .padding(10)
- }
- )
- .frame(width: UIScreen.main.bounds.width * 0.9, height: 40, alignment: .center)
- .disabled(isButtonDisabled)
- .background(buttonBackgroundColor)
- .tint(.white)
- .clipShape(RoundedRectangle(cornerRadius: 8))
- }
- }
- var body: some View {
- NavigationView {
- Form {
- Section {
- HStack {
- Text("Carbs")
- TextFieldWithToolBar(
- text: $editedCarbs,
- placeholder: "0",
- keyboardType: .numberPad,
- numberFormatter: mealFormatter,
- unitsText: String(localized: "g", comment: "Units for carbs")
- )
- }
- if state.settingsManager.settings.useFPUconversion {
- HStack {
- Text("Fat")
- TextFieldWithToolBar(
- text: $editedFat,
- placeholder: "0",
- keyboardType: .numberPad,
- numberFormatter: mealFormatter,
- unitsText: String(localized: "g", comment: "Units for carbs")
- )
- }
- HStack {
- Text("Protein")
- TextFieldWithToolBar(
- text: $editedProtein,
- placeholder: "0",
- keyboardType: .numberPad,
- numberFormatter: mealFormatter,
- unitsText: String(localized: "g", comment: "Units for carbs")
- )
- }
- }
- HStack {
- Image(systemName: "square.and.pencil")
- TextFieldWithToolBarString(text: $editedNote, placeholder: String(localized: "Note..."), maxLength: 25)
- }
- }.listRowBackground(Color.chart)
- Section {
- DatePicker(
- "Time",
- selection: $editedDate,
- displayedComponents: [.date, .hourAndMinute]
- )
- }.listRowBackground(Color.chart)
- }
- .safeAreaInset(
- edge: .bottom,
- spacing: 30
- ) {
- stickyButton
- }
- .scrollContentBackground(.hidden)
- .background(appState.trioBackgroundColor(for: colorScheme))
- .navigationTitle("Edit Meal")
- .navigationBarTitleDisplayMode(.inline)
- .toolbar {
- ToolbarItem(placement: .topBarLeading) {
- Button("Cancel") {
- dismiss()
- }
- }
- }
- }
- .task {
- /*
- User taps on a FPU entry in the DataTable list. There are two cases:
- - the User has entered this FPU entry WITH carbs
- - the User has entered this FPU entry WITHOUT carbs
- In the first case, we simply need to load the corresponding carb entry. For this case THIS is the entry we want to edit.
- In the second case, we need to load the zero-carb entry that actualy holds the FPU values (and the carbs). For this case THIS is the entry we want to edit.
- */
- if carbEntry.isFPU {
- if let result = await state.handleFPUEntry(carbEntry.objectID) {
- editedCarbs = result.entryValues?.carbs ?? 0
- editedFat = result.entryValues?.fat ?? 0
- editedProtein = result.entryValues?.protein ?? 0
- editedNote = result.entryValues?.note ?? ""
- entryToEdit = result.entryID
- editedDate = result.entryValues?.date ?? Date()
- }
- /*
- User taps on a carb entry in the DataTable list. There are again two cases which don't need explicit handling:
- - the User has only entered carbs
- - the User has entered carbs with FPU
- In both cases, we need to simply load the carb entry that holds all the necessary values for us. This is the entry we want to edit.
- */
- } else {
- if let values = await state.loadEntryValues(from: carbEntry.objectID) {
- editedCarbs = values.carbs
- editedFat = values.fat
- editedProtein = values.protein
- editedNote = values.note
- editedDate = values.date
- entryToEdit = carbEntry.objectID
- }
- }
- }
- }
- }
|