Просмотр исходного кода

Swipe to delete, and move add buttons

Co-Authored-By: Deniz Cengiz <48965855+dnzxy@users.noreply.github.com>
Brian Wieder 2 лет назад
Родитель
Сommit
c8de2f2d46

+ 0 - 2
FreeAPS/Sources/Modules/DataTable/DataTableDataFlow.swift

@@ -141,8 +141,6 @@ enum DataTable {
 
                 if isExternal ?? false {
                     bolusText += " " + NSLocalizedString("External", comment: "External Insulin")
-                } else if isSMB ?? false {
-                    bolusText += " " + NSLocalizedString("SMB", comment: "SMB")
                 }
 
                 return numberFormatter

+ 2 - 2
FreeAPS/Sources/Modules/DataTable/DataTableStateModel.swift

@@ -156,8 +156,8 @@ extension DataTable {
                 .store(in: &lifetime)
         }
 
-        func deleteGlucose(at index: Int) {
-            let id = glucose[index].id
+        func deleteGlucose(_ glucose: Glucose) {
+            let id = glucose.id
             provider.deleteGlucose(id: id)
 
             let fetchRequest: NSFetchRequest<NSFetchRequestResult>

+ 138 - 118
FreeAPS/Sources/Modules/DataTable/View/DataTableRootView.swift

@@ -7,10 +7,11 @@ extension DataTable {
         let resolver: Resolver
         @StateObject var state = StateModel()
 
-        @State private var isRemoveCarbsAlertPresented = false
-        @State private var removeCarbsAlert: Alert?
-        @State private var isRemoveInsulinAlertPresented = false
-        @State private var removeInsulinAlert: Alert?
+        @State private var isRemoveHistoryItemAlertPresented: Bool = false
+        @State private var alertTitle: String = ""
+        @State private var alertMessage: String = ""
+        @State private var alertTreatmentToDelete: Treatment?
+        @State private var alertGlucoseToDelete: Glucose?
         @State private var showManualGlucose = false
         @State private var showExternalInsulin = false
         @State private var isAmountUnconfirmed = true
@@ -64,7 +65,7 @@ extension DataTable {
             .navigationBarTitleDisplayMode(.automatic)
             .navigationBarItems(
                 leading: Button("Close", action: state.hideModal),
-                trailing: state.mode == .glucose ? EditButton().asAny() : EmptyView().asAny()
+                trailing: state.mode == .glucose ? addGlucoseButton.asAny() : addInsulinButton.asAny()
             )
             .sheet(isPresented: $showExternalInsulin, onDismiss: {
                 if isAmountUnconfirmed {
@@ -79,24 +80,35 @@ extension DataTable {
             }
         }
 
-        private var treatmentsList: some View {
-            List {
-                HStack {
-                    Spacer()
-                    Button(action: { showExternalInsulin = true
-                        state.externalInsulinDate = Date() }, label: {
-                        HStack {
-                            Text("Add")
-                                .foregroundColor(Color.secondary)
-                                .font(.caption)
-
-                            Image(systemName: "syringe")
-                                .foregroundColor(Color.accentColor)
-                        }.frame(maxWidth: .infinity, alignment: .trailing)
+        private var addInsulinButton: some View {
+            Button(action: { showExternalInsulin = true
+                state.externalInsulinDate = Date() }, label: {
+                Text("Add")
+                    .foregroundColor(Color.secondary)
+                    .font(.caption)
+                Image(systemName: "syringe")
+                    .foregroundColor(Color.accentColor)
+            }).buttonStyle(.borderless)
+        }
 
-                    }).buttonStyle(.borderless)
+        private var addGlucoseButton: some View {
+            Button(
+                action: {
+                    showManualGlucose = true
+                    state.manualGlucose = 0
+                },
+                label: {
+                    Text("Add")
+                        .foregroundColor(Color.secondary)
+                        .font(.caption)
+                    Image(systemName: "drop")
+                        .foregroundColor(Color.accentColor)
                 }
+            ).buttonStyle(.borderless)
+        }
 
+        private var treatmentsList: some View {
+            List {
                 if !state.treatments.isEmpty {
                     ForEach(state.treatments) { item in
                         treatmentView(item)
@@ -111,24 +123,10 @@ extension DataTable {
 
         private var glucoseList: some View {
             List {
-                HStack {
-                    Text("Time").foregroundStyle(.secondary)
-                    Spacer()
-                    Text(state.units.rawValue).foregroundStyle(.secondary)
-                    Button(
-                        action: { 
-                            showManualGlucose = true
-                            state.manualGlucose = 0
-                        },
-                        label: { Image(systemName: "plus.circle.fill").foregroundStyle(.secondary)
-                        }
-                    ).buttonStyle(.borderless)
-                }
                 if !state.glucose.isEmpty {
                     ForEach(state.glucose) { item in
                         glucoseView(item)
                     }
-                    .onDelete(perform: deleteGlucose)
                 } else {
                     HStack {
                         Text(NSLocalizedString("No data.", comment: "No data text when no entries in history list"))
@@ -181,84 +179,75 @@ extension DataTable {
 
         @ViewBuilder private func treatmentView(_ item: Treatment) -> some View {
             HStack {
-                Image(systemName: "circle.fill").foregroundColor(item.color)
-                Text(dateFormatter.string(from: item.date))
-                    .moveDisabled(true)
-                Text(item.type.name)
+                if item.type == .bolus || item.type == .carbs {
+                    Image(systemName: "circle.fill").foregroundColor(item.color).padding(.vertical)
+                } else {
+                    Image(systemName: "circle.fill").foregroundColor(item.color)
+                }
+                Text((item.isSMB ?? false) ? "SMB" : item.type.name)
                 Text(item.amountText).foregroundColor(.secondary)
                 if let duration = item.durationText {
                     Text(duration).foregroundColor(.secondary)
                 }
+                Spacer()
+                Text(dateFormatter.string(from: item.date))
+                    .moveDisabled(true)
+            }
+            .swipeActions {
+                // Only allow swipe to delete if a carb, fpu, or bolus entry.
+                if item.type == .carbs || item.type == .fpus || item.type == .bolus {
+                    Button(
+                        "Delete",
+                        systemImage: "trash.fill",
+                        role: .none,
+                        action: {
+                            alertTreatmentToDelete = item
 
-                if item.type == .carbs {
-                    if item.note != "" {
-                        Spacer()
-                        Text(item.note ?? "").foregroundColor(.brown)
-                    }
-                    Spacer()
-                    Image(systemName: "xmark.circle").foregroundColor(.secondary)
-                        .contentShape(Rectangle())
-                        .padding(.vertical)
-                        .onTapGesture {
-                            removeCarbsAlert = Alert(
-                                title: Text("Delete carbs?"),
-                                message: Text(item.amountText),
-                                primaryButton: .destructive(
-                                    Text("Delete"),
-                                    action: { state.deleteCarbs(item) }
-                                ),
-                                secondaryButton: .cancel()
-                            )
-                            isRemoveCarbsAlertPresented = true
-                        }
-                        .alert(isPresented: $isRemoveCarbsAlertPresented) {
-                            removeCarbsAlert!
-                        }
-                }
+                            if item.type == .carbs {
+                                alertTitle = "Delete Carbs?"
+                                alertMessage = dateFormatter.string(from: item.date) + ", " + item.amountText
+                            } else if item.type == .fpus {
+                                alertTitle = "Delete Carb Equivalents?"
+                                alertMessage = "All FPUs of the meal will be deleted."
+                            } else {
+                                // item is insulin treatment; item.type == .bolus
+                                alertTitle = "Delete Insulin?"
+                                alertMessage = dateFormatter.string(from: item.date) + ", " + item.amountText
 
-                if item.type == .fpus {
-                    Spacer()
-                    Image(systemName: "xmark.circle").foregroundColor(.secondary)
-                        .contentShape(Rectangle())
-                        .padding(.vertical)
-                        .onTapGesture {
-                            removeCarbsAlert = Alert(
-                                title: Text("Delete carb equivalents?"),
-                                message: Text(""), // Temporary fix. New to fix real amount of carb equivalents later
-                                primaryButton: .destructive(
-                                    Text("Delete"),
-                                    action: { state.deleteCarbs(item) }
-                                ),
-                                secondaryButton: .cancel()
-                            )
-                            isRemoveCarbsAlertPresented = true
-                        }
-                        .alert(isPresented: $isRemoveCarbsAlertPresented) {
-                            removeCarbsAlert!
+                                if item.isSMB ?? false {
+                                    // Add text snippet, so that alert message is more descriptive for SMBs
+                                    alertMessage += " SMB"
+                                }
+                            }
+
+                            isRemoveHistoryItemAlertPresented = true
                         }
+                    ).tint(.red)
                 }
+            }
+            .disabled(
+                item.type == .tempBasal || item.type == .tempTarget || item.type == .resume || item
+                    .type == .suspend
+            )
+            .alert(
+                Text(NSLocalizedString(alertTitle, comment: "")),
+                isPresented: $isRemoveHistoryItemAlertPresented
+            ) {
+                Button("Cancel", role: .cancel) {}
+                Button("Delete", role: .destructive) {
+                    guard let treatmentToDelete = alertTreatmentToDelete else {
+                        debug(.default, "Cannot gracefully unwrap alertTreatmentToDelete!")
+                        return
+                    }
 
-                if item.type == .bolus {
-                    Spacer()
-                    Image(systemName: "xmark.circle").foregroundColor(.secondary)
-                        .contentShape(Rectangle())
-                        .padding(.vertical)
-                        .onTapGesture {
-                            removeInsulinAlert = Alert(
-                                title: Text("Delete insulin?"),
-                                message: Text(item.amountText),
-                                primaryButton: .destructive(
-                                    Text("Delete"),
-                                    action: { state.deleteInsulin(item) }
-                                ),
-                                secondaryButton: .cancel()
-                            )
-                            isRemoveInsulinAlertPresented = true
-                        }
-                        .alert(isPresented: $isRemoveInsulinAlertPresented) {
-                            removeInsulinAlert!
-                        }
+                    if treatmentToDelete.type == .carbs || treatmentToDelete.type == .fpus {
+                        state.deleteCarbs(treatmentToDelete)
+                    } else {
+                        state.deleteInsulin(treatmentToDelete)
+                    }
                 }
+            } message: {
+                Text("\n" + NSLocalizedString(alertMessage, comment: ""))
             }
         }
 
@@ -327,24 +316,55 @@ extension DataTable {
         }
 
         @ViewBuilder private func glucoseView(_ item: Glucose) -> some View {
-            VStack(alignment: .leading, spacing: 4) {
-                HStack {
-                    Text(dateFormatter.string(from: item.glucose.dateString))
-                    Spacer()
-                    Text(item.glucose.glucose.map {
-                        glucoseFormatter.string(from: Double(
-                            state.units == .mmolL ? $0.asMmolL : Decimal($0)
-                        ) as NSNumber)!
-                    } ?? "--")
-                    Text(state.units.rawValue)
-                    Text(item.glucose.direction?.symbol ?? "--")
-                }
-                Text("ID: " + item.glucose.id).font(.caption2).foregroundColor(.secondary)
+            HStack {
+                Text(item.glucose.glucose.map {
+                    glucoseFormatter.string(from: Double(
+                        state.units == .mmolL ? $0.asMmolL : Decimal($0)
+                    ) as NSNumber)!
+                } ?? "--")
+                Text(item.glucose.direction?.symbol ?? "--")
+                Spacer()
+
+                Text(dateFormatter.string(from: item.glucose.dateString))
             }
-        }
+            .swipeActions {
+                Button(
+                    "Delete",
+                    systemImage: "trash.fill",
+                    role: .none,
+                    action: {
+                        alertGlucoseToDelete = item
 
-        private func deleteGlucose(at offsets: IndexSet) {
-            state.deleteGlucose(at: offsets[offsets.startIndex])
+                        let valueText = glucoseFormatter.string(from: Double(
+                            state.units == .mmolL ? Double(item.glucose.value.asMmolL) : item.glucose.value
+                        ) as NSNumber)! + " " + state.units.rawValue
+
+                        alertTitle = "Delete Glucose?"
+                        alertMessage = dateFormatter.string(from: item.glucose.dateString) + ", " + valueText
+
+                        isRemoveHistoryItemAlertPresented = true
+                    }
+                ).tint(.red)
+            }
+            .alert(
+                Text(NSLocalizedString(alertTitle, comment: "")),
+                isPresented: $isRemoveHistoryItemAlertPresented
+            ) {
+                Button("Cancel", role: .cancel) {}
+                Button("Delete", role: .destructive) {
+                    // gracefully unwrap value here.
+                    // value cannot ever really be nil because it is an existing(!) table entry
+                    // but just to be sure.
+                    guard let glucoseToDelete = alertGlucoseToDelete else {
+                        print("Cannot gracefully unwrap alertTreatmentToDelete!")
+                        return
+                    }
+
+                    state.deleteGlucose(glucoseToDelete)
+                }
+            } message: {
+                Text("\n" + NSLocalizedString(alertMessage, comment: ""))
+            }
         }
     }
 }