95 lines
2.9 KiB
Swift
95 lines
2.9 KiB
Swift
import SwiftUI
|
|
|
|
struct ContentView: View {
|
|
@StateObject private var viewModel = LibraryViewModel()
|
|
@State private var selectedItem: BookItem?
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
VStack(alignment: .leading, spacing: 16) {
|
|
header
|
|
|
|
if viewModel.items.isEmpty {
|
|
emptyState
|
|
} else {
|
|
bookList
|
|
}
|
|
|
|
Spacer()
|
|
}
|
|
.padding(20)
|
|
.navigationTitle("Vorleser")
|
|
.toolbar {
|
|
ToolbarItem(placement: .topBarTrailing) {
|
|
Button("Import EPUB") {
|
|
viewModel.isImporting = true
|
|
}
|
|
}
|
|
}
|
|
.sheet(isPresented: $viewModel.isImporting) {
|
|
DocumentPicker(onPick: { url in
|
|
viewModel.isImporting = false
|
|
viewModel.importPicked(url: url)
|
|
}, onCancel: {
|
|
viewModel.isImporting = false
|
|
})
|
|
}
|
|
.alert("Import failed", isPresented: .constant(viewModel.lastErrorMessage != nil), actions: {
|
|
Button("OK", role: .cancel) {
|
|
viewModel.lastErrorMessage = nil
|
|
}
|
|
}, message: {
|
|
Text(viewModel.lastErrorMessage ?? "")
|
|
})
|
|
}
|
|
}
|
|
|
|
private var header: some View {
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
Text("Local EPUB reader")
|
|
.font(.title2.weight(.semibold))
|
|
Text("On-device, high-quality narration powered by CoreML.")
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
}
|
|
|
|
private var emptyState: some View {
|
|
VStack(alignment: .leading, spacing: 12) {
|
|
Text("No books yet")
|
|
.font(.headline)
|
|
Text("Import an EPUB from Files or iCloud to start preparing narration.")
|
|
.foregroundStyle(.secondary)
|
|
Button("Import EPUB") {
|
|
viewModel.isImporting = true
|
|
}
|
|
.buttonStyle(.borderedProminent)
|
|
}
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.padding(16)
|
|
.background(.thinMaterial)
|
|
.clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
|
|
}
|
|
|
|
private var bookList: some View {
|
|
List(viewModel.items) { item in
|
|
NavigationLink(value: item) {
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
Text(item.title)
|
|
.font(.headline)
|
|
Text(item.sourceURL.lastPathComponent)
|
|
.font(.caption)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
}
|
|
}
|
|
.listStyle(.plain)
|
|
.navigationDestination(for: BookItem.self) { item in
|
|
ReaderView(item: item)
|
|
}
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
ContentView()
|
|
}
|