Start version 2

This commit is contained in:
Christoph Hagen
2022-06-10 21:20:49 +02:00
parent c119885743
commit 093d82893b
93 changed files with 2604 additions and 6509 deletions

View File

@@ -0,0 +1,31 @@
import SwiftUI
import SFSafeSymbols
struct CapNameEntryView: View {
@Binding
var name: String
var body: some View {
TextField("Name", text: $name, prompt: Text("Enter name..."))
.padding(7)
.padding(.horizontal, 25)
.background(Color(.systemGray5))
.cornerRadius(8)
.overlay(
HStack {
Image(systemSymbol: .squareAndPencil)
.foregroundColor(.gray)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
.padding(.leading, 8)
}
)
}
}
struct CapNameEntryView_Previews: PreviewProvider {
static var previews: some View {
CapNameEntryView(name: .constant(""))
.previewLayout(.fixed(width: 375, height: 50))
}
}

View File

@@ -0,0 +1,77 @@
import SwiftUI
import CachedAsyncImage
struct CapRowView: View {
private let imageSize: CGFloat = 70
private let sufficientImageCount = 10
let cap: Cap
let match: Float?
@EnvironmentObject
var database: Database
var imageUrl: URL {
database.serverUrl.appendingPathComponent(cap.mainImagePath)
}
var imageCountText: String {
guard cap.imageCount != 1 else {
return "\(cap.id) (1 image)"
}
return "\(cap.id) (\(cap.imageCount) images)"
}
var body: some View {
HStack(alignment: .center) {
VStack(alignment: .leading, spacing: 0) {
HStack(spacing: 8) {
Text(imageCountText)
.font(.footnote)
if !cap.classifiable(by: database.classifierVersion) {
Text("📵")
}
if cap.imageCount < sufficientImageCount {
Text("⚠️")
}
if database.hasPendingUpdates(for: cap.id) {
Text("")
}
if database.hasPendingOperations(for: cap.id) {
ProgressView()
}
}
.padding(.top, 0)
.font(.footnote)
Text(cap.name)
.font(.headline)
.padding(.bottom, 3)
if let match = match {
Text("\(Int((match * 100).rounded())) % match")
.font(.footnote)
}
}//.padding(.vertical)
Spacer()
CachedAsyncImage(url: imageUrl, urlCache: database.imageCache) { image in
image.resizable()
} placeholder: {
ProgressView()
}
.frame(width: imageSize, height: imageSize)
.cornerRadius(imageSize / 2)
}
}
}
struct CapRowView_Previews: PreviewProvider {
static var previews: some View {
CapRowView(cap: Cap(id: 123, name: "My new cap"),
match: 0.13)
.previewLayout(.fixed(width: 375, height: 80))
.environmentObject(Database.mock)
}
}

13
Caps/Views/GridView.swift Normal file
View File

@@ -0,0 +1,13 @@
import SwiftUI
struct GridView: View {
var body: some View {
Text("Grid view")
}
}
struct GridView_Previews: PreviewProvider {
static var previews: some View {
GridView()
}
}

View File

@@ -0,0 +1,40 @@
import SwiftUI
import SFSafeSymbols
struct SearchField: View {
@Binding
var searchString: String
var body: some View {
TextField("Search", text: $searchString, prompt: Text("Search..."))
.padding(7)
.padding(.horizontal, 25)
.background(Color(.systemGray5))
.cornerRadius(8)
.overlay(
HStack {
Image(systemSymbol: .magnifyingglass)
.foregroundColor(.gray)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
.padding(.leading, 8)
if searchString != "" {
Button(action: {
self.searchString = ""
}) {
Image(systemSymbol: .multiplyCircleFill)
.foregroundColor(.gray)
.padding(.trailing, 8)
}
}
}
)
}
}
struct SearchField_Previews: PreviewProvider {
static var previews: some View {
SearchField(searchString: .constant(""))
.previewLayout(.fixed(width: 375, height: 50))
}
}

View File

@@ -0,0 +1,30 @@
//
// SettingsStatisticRow.swift
// Caps
//
// Created by CH on 26.05.22.
//
import SwiftUI
struct SettingsStatisticRow: View {
let label: String
let value: String
var body: some View {
HStack {
Text(label)
Spacer()
Text(value)
}
}
}
struct SettingsStatisticRow_Previews: PreviewProvider {
static var previews: some View {
SettingsStatisticRow(label: "Label", value: "Value")
.previewLayout(.fixed(width: 375, height: 40))
}
}

View File

@@ -0,0 +1,74 @@
import SwiftUI
struct SettingsView: View {
@EnvironmentObject
var database: Database
@Binding
var isPresented: Bool
var body: some View {
VStack(alignment: .leading, spacing: 3) {
HStack {
Text("Settings")
.font(.title2)
.bold()
Spacer()
Button(action: hide) {
Image(systemSymbol: .xmarkCircleFill)
.foregroundColor(.gray)
.font(.system(size: 26))
}
}
Text("Statistics")
.font(.footnote)
.textCase(.uppercase)
.foregroundColor(.secondary)
.padding(.top)
Group {
SettingsStatisticRow(label: "Caps", value: "\(database.numberOfCaps)")
SettingsStatisticRow(label: "Total images", value: "\(database.numberOfImages)")
SettingsStatisticRow(label: "Images per cap", value: String(format: "%.1f", database.averageImageCount))
}.padding(.horizontal)
Text("Classifier")
.font(.footnote)
.textCase(.uppercase)
.foregroundColor(.secondary)
.padding(.top)
Group {
SettingsStatisticRow(label: "Version", value: "\(database.classifierVersion)")
SettingsStatisticRow(label: "Recognized caps", value: "\(database.classifierClassCount)")
}.padding(.horizontal)
Text("Storage")
.font(.footnote)
.textCase(.uppercase)
.foregroundColor(.secondary)
.padding(.top)
Group {
SettingsStatisticRow(label: "Image cache", value: byteString(database.imageCacheSize))
SettingsStatisticRow(label: "Database", value: byteString(database.databaseSize))
SettingsStatisticRow(label: "Classifier", value: byteString(database.classifierSize))
}.padding(.horizontal)
Spacer()
}
.padding(.horizontal)
}
private func hide() {
isPresented = false
}
private func byteString(_ count: Int) -> String {
ByteCountFormatter.string(fromByteCount: Int64(count), countStyle: .file)
}
}
struct SettingsView_Previews: PreviewProvider {
static var previews: some View {
SettingsView(isPresented: .constant(true))
.environmentObject(Database.mock)
.previewLayout(.fixed(width: 375, height: 330))
}
}

View File

@@ -0,0 +1,33 @@
import SwiftUI
struct SortCaseRowView: View {
@Binding
var selectedType: SortCriteria
let type: SortCriteria
var body: some View {
Button(action: { selectedType = type}) {
HStack {
Text(type.text)
.foregroundColor(.primary)
Spacer()
if selectedType == type {
Image(systemSymbol: .checkmark)
}
}
.padding(.horizontal)
.padding(.vertical, 8)
.background(Color(UIColor.systemGroupedBackground))
.cornerRadius(8)
}
}
}
struct SortCaseRowView_Previews: PreviewProvider {
static var previews: some View {
SortCaseRowView(selectedType: .constant(.id), type: .id)
.previewLayout(.fixed(width: 375, height: 50))
}
}

View File

@@ -0,0 +1,95 @@
import SwiftUI
private extension Binding where Value == SortCriteria {
func value() -> Binding<Int> {
return Binding<Int>(get:{ self.wrappedValue.rawValue },
set: { self.wrappedValue = .init(rawValue: $0)!})
}
}
struct SortSelectionView: View {
let hasMatches: Bool
@Binding
var isPresented: Bool
@Binding
var sortType: SortCriteria
@Binding
var sortAscending: Bool
@Binding
var showGridView: Bool
var body: some View {
VStack(alignment: .leading, spacing: 3) {
HStack {
Text("List Settings").font(.title2).bold()
Spacer()
Button(action: { isPresented = false }) {
Image(systemSymbol: .xmarkCircleFill)
.foregroundColor(.gray)
.font(.system(size: 26))
}
}
.padding(.bottom)
Text("Sort by")
.font(.footnote)
.textCase(.uppercase)
.foregroundColor(.secondary)
Picker("Sort type", selection: $sortType.value()) {
Text(SortCriteria.id.text).tag(SortCriteria.id.rawValue)
Text(SortCriteria.name.text).tag(SortCriteria.name.rawValue)
Text(SortCriteria.count.text).tag(SortCriteria.count.rawValue)
if hasMatches {
Text(SortCriteria.match.text).tag(SortCriteria.match.rawValue)
}
}
.pickerStyle(SegmentedPickerStyle())
.padding(.bottom)
Text("Sort order")
.font(.footnote)
.textCase(.uppercase)
.foregroundColor(.secondary)
Picker("Sort order", selection: $sortAscending) {
Text("Ascending").tag(true)
Text("Descending").tag(false)
}
.pickerStyle(SegmentedPickerStyle())
.padding(.bottom)
HStack {
Spacer()
Button(action: showGrid) {
HStack {
Image(systemSymbol: .circleHexagongrid)
Text("Show grid")
}
}.padding()
Spacer()
}
Spacer()
}
.padding(.horizontal)
}
private func showGrid() {
showGridView = true
isPresented = false
}
}
struct SortSelectionView_Previews: PreviewProvider {
static var previews: some View {
SortSelectionView(
hasMatches: true,
isPresented: .constant(true),
sortType: .constant(.id),
sortAscending: .constant(false),
showGridView: .constant(false))
.previewLayout(.fixed(width: 375, height: 250))
}
}