From 37fb3b4a880db9032db1808fe49641e0009a0c32 Mon Sep 17 00:00:00 2001 From: Christoph Hagen Date: Mon, 13 Mar 2023 11:07:22 +0100 Subject: [PATCH] Allow cap deletion --- Caps/ContentView.swift | 29 +++++++++++++++++++---- Caps/Data/Database.swift | 51 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 71 insertions(+), 9 deletions(-) diff --git a/Caps/ContentView.swift b/Caps/ContentView.swift index 9e4a197..2de00e4 100644 --- a/Caps/ContentView.swift +++ b/Caps/ContentView.swift @@ -45,6 +45,9 @@ struct ContentView: View { @State var showUpdateCapNameAlert = false + + @State + var showDeleteCapAlert = false @State var updatedCapName = "" @@ -138,10 +141,13 @@ struct ContentView: View { Label("Rename", systemSymbol: .pencil) } .tint(.purple) - Button(role: .destructive) { startDeleteCap(cap: cap) + Button { + selectedCapId = cap.id + showDeleteCapAlert = true } label: { Label("Delete", systemSymbol: .trashCircleFill) } + .tint(.red) } .swipeActions(edge: .leading) { Button { @@ -149,7 +155,7 @@ struct ContentView: View { } label: { Label("Images", systemSymbol: .photoStack) } - .tint(.purple) + .tint(.blue) } } .refreshable { @@ -288,6 +294,14 @@ struct ContentView: View { primaryButton: .default(Text("Download"), action: downloadClassifier), secondaryButton: .cancel()) } + .alert(Text("Delete cap"), + isPresented: $showDeleteCapAlert, + actions: { + Button("Delete", role: .destructive, action: saveNewCapName) + Button("Cancel", role: .cancel, action: {}) + }, message: { + Text("Confirm the deletion of cap \(selectedCapId ?? 0)") + }) .alert("Update name", isPresented: $showUpdateCapNameAlert, actions: { TextField("Name", text: $updatedCapName) Button("Update", action: saveNewCapName) @@ -405,8 +419,15 @@ struct ContentView: View { showImageOverviewForCap = true } - private func startDeleteCap(cap: Cap) { - // TODO: Implement + private func startDeleteCap() { + guard let cap = selectedCapId else { + return + } + Task { + guard await database.delete(cap: cap) else { + return + } + } } } diff --git a/Caps/Data/Database.swift b/Caps/Data/Database.swift index ca6417c..560b718 100644 --- a/Caps/Data/Database.swift +++ b/Caps/Data/Database.swift @@ -35,7 +35,11 @@ final class Database: ObservableObject { } var nextCapId: Int { - (caps.values.max()?.id ?? 0) + 1 + var next = 1 + while caps[next] != nil { + next += 1 + } + return next } @AppStorage("changed") @@ -621,15 +625,15 @@ final class Database: ObservableObject { func delete(image: CapImage) async -> Bool { guard hasServerAuthentication else { - log("No authorization to set main image") + log("No authorization to delete cap image") return false } - guard var cap = cap(for: image.cap) else { - log("No cap \(image.cap) to set main image") + guard let cap = cap(for: image.cap) else { + log("No cap \(image.cap) to delete cap image") return false } guard image.version < cap.imageCount else { - log("Invalid main image \(image.version) for \(cap.id) with only \(cap.imageCount) images") + log("Invalid image \(image.version) to delete for \(cap.id) with only \(cap.imageCount) images") return false } @@ -667,6 +671,43 @@ final class Database: ObservableObject { } } + func delete(cap: Int) async -> Bool { + guard hasServerAuthentication else { + log("No authorization to delete cap") + return false + } + guard caps[cap] != nil else { + log("No cap \(cap) to delete") + return false + } + + let url = serverUrl.appendingPathComponent("delete/\(cap)") + var request = URLRequest(url: url) + request.httpMethod = "POST" + request.addValue(serverAuthenticationKey, forHTTPHeaderField: "key") + do { + let (_, response) = try await URLSession.shared.data(for: request) + guard let httpResponse = response as? HTTPURLResponse else { + log("Unexpected response deleting cap \(cap): \(response)") + return false + } + guard httpResponse.statusCode == 200 else { + log("Failed to delete cap \(cap): Response \(httpResponse.statusCode)") + return false + } + + // Delete cached images + images.removeCachedImages(for: cap) + // Delete cap + caps[cap] = nil + log("Deleted cap \(cap)") + return true + } catch { + log("Failed to delete cap \(cap): \(error)") + return false + } + } + // MARK: Classification /// The compiled recognition model on disk