116 lines
4.0 KiB
Swift
116 lines
4.0 KiB
Swift
import SwiftUI
|
|
|
|
struct AddBooksView: View {
|
|
@EnvironmentObject private var router: AppRouter
|
|
@ObservedObject var viewModel: AddBooksViewModel
|
|
|
|
var body: some View {
|
|
VStack(spacing: 12) {
|
|
Picker("Mode", selection: $viewModel.mode) {
|
|
ForEach(AddBooksViewModel.Mode.allCases, id: \.self) { mode in
|
|
Text(mode.title).tag(mode)
|
|
}
|
|
}
|
|
.pickerStyle(.segmented)
|
|
|
|
Group {
|
|
switch viewModel.mode {
|
|
case .title:
|
|
titleSearch
|
|
case .scan:
|
|
scanSearch
|
|
case .filter:
|
|
filterSearch
|
|
}
|
|
}
|
|
|
|
if let error = viewModel.errorMessage {
|
|
NetworkErrorView(message: error) {
|
|
Task { await viewModel.searchByTitle() }
|
|
}
|
|
}
|
|
|
|
List(viewModel.results, id: \.id) { book in
|
|
Button {
|
|
router.path.append(.detail(book))
|
|
} label: {
|
|
HStack(spacing: 12) {
|
|
AsyncImage(url: book.coverImageUrl) { phase in
|
|
if let image = phase.image {
|
|
image.resizable().scaledToFill()
|
|
} else {
|
|
RoundedRectangle(cornerRadius: 8).fill(.gray.opacity(0.2))
|
|
}
|
|
}
|
|
.frame(width: 48, height: 72)
|
|
.clipShape(RoundedRectangle(cornerRadius: 8))
|
|
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
Text(book.title)
|
|
.font(.headline)
|
|
.lineLimit(2)
|
|
Text(book.authors.joined(separator: ", "))
|
|
.font(.subheadline)
|
|
.foregroundStyle(.secondary)
|
|
.lineLimit(1)
|
|
if let year = book.publishedYear {
|
|
Text("\(year)")
|
|
.font(.caption)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.listStyle(.plain)
|
|
.overlay {
|
|
if viewModel.results.isEmpty, !viewModel.isLoading {
|
|
Text(String(localized: "common.empty"))
|
|
.font(.subheadline)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
}
|
|
}
|
|
.padding(.horizontal, 16)
|
|
.navigationTitle(String(localized: "add.title"))
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.overlay {
|
|
if viewModel.isLoading {
|
|
ProgressView()
|
|
.controlSize(.large)
|
|
}
|
|
}
|
|
}
|
|
|
|
private var titleSearch: some View {
|
|
TextField(String(localized: "add.searchPlaceholder"), text: $viewModel.titleQuery)
|
|
.textFieldStyle(.roundedBorder)
|
|
.onChange(of: viewModel.titleQuery) { _, _ in
|
|
viewModel.titleChanged()
|
|
}
|
|
}
|
|
|
|
private var scanSearch: some View {
|
|
BarcodeScannerView { isbn in
|
|
Task { await viewModel.searchByISBN(isbn) }
|
|
}
|
|
.frame(height: 260)
|
|
.clipShape(RoundedRectangle(cornerRadius: 16))
|
|
}
|
|
|
|
private var filterSearch: some View {
|
|
VStack(spacing: 8) {
|
|
TextField("Title", text: $viewModel.filterTitle)
|
|
.textFieldStyle(.roundedBorder)
|
|
TextField("YYYY", text: $viewModel.filterYear)
|
|
.textFieldStyle(.roundedBorder)
|
|
.keyboardType(.numberPad)
|
|
Button("Apply") {
|
|
Task { await viewModel.applyFilter() }
|
|
}
|
|
.buttonStyle(.borderedProminent)
|
|
.frame(maxWidth: .infinity, alignment: .trailing)
|
|
}
|
|
}
|
|
}
|