Add view of all images for a cap

This commit is contained in:
Christoph Hagen
2023-02-19 00:38:52 +01:00
parent fb90a4847e
commit 9cf1c236e0
8 changed files with 134 additions and 10 deletions

View File

@ -54,6 +54,12 @@ struct ContentView: View {
@State
private var selectedCapId: Int?
@State
var showImageOverviewForCap = false
@State
private var selectedCapToShowImages: Cap?
var filteredCaps: [Cap] {
let text = searchString
@ -125,7 +131,7 @@ struct ContentView: View {
.onTapGesture {
didTap(cap: cap)
}
.swipeActions() {
.swipeActions(edge: .trailing) {
Button {
showRenameWindow(for: cap)
} label: {
@ -133,6 +139,14 @@ struct ContentView: View {
}
.tint(.purple)
}
.swipeActions(edge: .leading) {
Button {
showAllImages(for: cap)
} label: {
Label("Images", systemSymbol: .photoStack)
}
.tint(.purple)
}
}
.refreshable {
refresh()
@ -260,7 +274,11 @@ struct ContentView: View {
}
.sheet(isPresented: $showGridView) {
GridView(isPresented: $showGridView, database: database)
}.alert(isPresented: $showNewClassifierAlert) {
}
.bottomSheet(isPresented: $showImageOverviewForCap, height: 400) {
CapImagesView(cap: $selectedCapToShowImages, database: database, isPresented: $showImageOverviewForCap)
}
.alert(isPresented: $showNewClassifierAlert) {
Alert(title: Text("New classifier available"),
message: Text("Classifier \(database.serverClassifierVersion) is available. You have version \(database.classifierVersion). Do you want to download it now?"),
primaryButton: .default(Text("Download"), action: downloadClassifier),
@ -377,6 +395,11 @@ struct ContentView: View {
return
}
}
private func showAllImages(for cap: Cap) {
selectedCapToShowImages = cap
showImageOverviewForCap = true
}
}
struct ContentView_Previews: PreviewProvider {

View File

@ -24,12 +24,20 @@ struct Cap {
/// The subpath to the main image on the server
var mainImagePath: String {
String(format: "images/%04d/%04d-%02d.jpg", id, id, mainImage)
imagePath(version: mainImage)
}
func imagePath(version: Int) -> String {
String(format: "images/%04d/%04d-%02d.jpg", id, id, version)
}
var image: CapImage {
.init(cap: id, version: mainImage)
}
func image(version: Int) -> CapImage {
.init(cap: id, version: version)
}
/**
Create a new cap.

View File

@ -6,3 +6,8 @@ struct CapImage: Codable, Equatable, Hashable {
let version: Int
}
extension CapImage: Identifiable {
var id: Int { version }
}

View File

@ -241,7 +241,7 @@ final class Database: ObservableObject {
@discardableResult
func downloadCaps() async -> Bool {
print("Downloading cap data")
print("Downloading cap data from \(serverDbUrl)")
let data: Data
let response: URLResponse
do {
@ -250,7 +250,11 @@ final class Database: ObservableObject {
print("Failed to download classifier version: \(error)")
return false
}
guard (response as? HTTPURLResponse)?.statusCode == 200 else {
guard let httpResponse = response as? HTTPURLResponse else {
return false
}
guard httpResponse.statusCode == 200 else {
print("Failed to download caps: \(httpResponse.statusCode)")
return false
}
@ -261,6 +265,7 @@ final class Database: ObservableObject {
print("Failed to decode server database: \(error)")
return false
}
print("Downloaded \(capData) caps")
var inserts = 0
var updates = 0
for cap in capData {
@ -358,6 +363,10 @@ final class Database: ObservableObject {
func hasPendingOperations(for cap: Int) -> Bool {
return false
}
func cap(for id: Int) -> Cap? {
caps[id]
}
// MARK: Adding new data

View File

@ -0,0 +1,75 @@
import SwiftUI
struct CapImagesView: View {
private let imageSize: CGFloat = 70
@Binding
var cap: Cap?
@Binding
var isPresented: Bool
init(cap: Binding<Cap?>, database: Database, isPresented: Binding<Bool>) {
self.database = database
self._cap = cap
self._isPresented = isPresented
}
let database: Database
var images: [CapImage] {
guard let cap else {
return []
}
return (0..<cap.imageCount).map {
cap.image(version: $0)
}
}
var title: String {
guard let cap else {
return "Images"
}
return "Cap \(cap.id)"
}
var body: some View {
GeometryReader { geo in
VStack {
HStack {
Text(title).font(.title2).bold()
Spacer()
Button(action: { isPresented = false }) {
Image(systemSymbol: .xmarkCircleFill)
.foregroundColor(.gray)
.font(.system(size: 26))
}
}
let gridItem = GridItem(.flexible(), spacing: 10, alignment: .leading)
ScrollView(.vertical) {
LazyVGrid(columns: [gridItem, gridItem, gridItem, gridItem]) {
ForEach(images) { item in
CachedCapImage(
item,
check: { database.images.cachedImage(item) },
fetch: { await database.images.image(item) },
content: { $0.resizable() },
placeholder: { ProgressView() })
.frame(width: imageSize,
height: imageSize)
.clipShape(Circle())
}
}
}
}
.padding(.horizontal)
}
}
}
struct CapImagesView_Previews: PreviewProvider {
static var previews: some View {
CapImagesView(cap: .constant(.init(id: 123, name: "Some")), database: .mock, isPresented: .constant(true))
}
}