Add view of all images for a cap
This commit is contained in:
parent
fb90a4847e
commit
9cf1c236e0
@ -7,6 +7,7 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
88C1511C29A11ADF0080EF4F /* CapImagesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88C1511B29A11ADF0080EF4F /* CapImagesView.swift */; };
|
||||
88DBE72E285495B100D1573B /* FancyTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88DBE72D285495B100D1573B /* FancyTextField.swift */; };
|
||||
E20D104A285612AF0019BD91 /* ImageGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = E20D1049285612AF0019BD91 /* ImageGrid.swift */; };
|
||||
E20D104C28563DB10019BD91 /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = E20D104B28563DB10019BD91 /* ImageCache.swift */; };
|
||||
@ -50,6 +51,7 @@
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
88C1511B29A11ADF0080EF4F /* CapImagesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CapImagesView.swift; sourceTree = "<group>"; };
|
||||
88DBE72D285495B100D1573B /* FancyTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FancyTextField.swift; sourceTree = "<group>"; };
|
||||
E20D1049285612AF0019BD91 /* ImageGrid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageGrid.swift; sourceTree = "<group>"; };
|
||||
E20D104B28563DB10019BD91 /* ImageCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCache.swift; sourceTree = "<group>"; };
|
||||
@ -187,6 +189,7 @@
|
||||
E2EA00E0283F658E00F7B269 /* SettingsView.swift */,
|
||||
E2EA00CB283EB43E00F7B269 /* SortCaseRowView.swift */,
|
||||
E2EA00C6283EAA0100F7B269 /* SortSelectionView.swift */,
|
||||
88C1511B29A11ADF0080EF4F /* CapImagesView.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
@ -285,6 +288,7 @@
|
||||
files = (
|
||||
E20D10582858CEBD0019BD91 /* IconButton.swift in Sources */,
|
||||
E25AAC7E283D855D006E9E7F /* ContentView.swift in Sources */,
|
||||
88C1511C29A11ADF0080EF4F /* CapImagesView.swift in Sources */,
|
||||
E2EA00F328438E6B00F7B269 /* CapNameEntryView.swift in Sources */,
|
||||
E25AAC8B283D868D006E9E7F /* Classifier.swift in Sources */,
|
||||
E20D10562858CDFA0019BD91 /* View+Extensions.swift in Sources */,
|
||||
@ -457,7 +461,7 @@
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.4;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@ -489,7 +493,7 @@
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.4;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@ -532,7 +536,7 @@
|
||||
repositoryURL = "https://github.com/SFSafeSymbols/SFSafeSymbols";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 3.0.0;
|
||||
minimumVersion = 4.0.0;
|
||||
};
|
||||
};
|
||||
E2EA00C8283EACB200F7B269 /* XCRemoteSwiftPackageReference "bottom-sheet" */ = {
|
||||
|
@ -14,8 +14,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/SFSafeSymbols/SFSafeSymbols",
|
||||
"state" : {
|
||||
"revision" : "c8c33d947d8a1c883aa19fd24e14fd738b06e369",
|
||||
"version" : "3.3.2"
|
||||
"revision" : "7cca2d60925876b5953a2cf7341cd80fbeac983c",
|
||||
"version" : "4.1.1"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
Binary file not shown.
@ -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 {
|
||||
|
@ -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.
|
||||
|
@ -6,3 +6,8 @@ struct CapImage: Codable, Equatable, Hashable {
|
||||
|
||||
let version: Int
|
||||
}
|
||||
|
||||
extension CapImage: Identifiable {
|
||||
|
||||
var id: Int { version }
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
75
Caps/Views/CapImagesView.swift
Normal file
75
Caps/Views/CapImagesView.swift
Normal 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))
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user