Files
Spanish/Conjuga/Conjuga/Views/Practice/Books/BookLibraryView.swift
T
Trey T 51067e23fd Fix Books navigation — tapping a book no longer re-pushes the library
BookLibraryView is itself pushed from PracticeView's NavigationStack,
so the .navigationDestination(for: Book.self) it declared was a
non-root registration. Combined with NavigationLink(value: book), that
resolved the push to *both* the destination handler and the closure
that produced BookLibraryView originally — pushing the chapter list
underneath, then re-pushing the library on top. Hitting back popped
the library and revealed the chapter list, in the wrong order.

Switched both Library→ChapterList and ChapterList→Reader to closure-
based NavigationLinks. Destinations attach directly to the link, no
type-keyed registry involved.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 10:18:04 -05:00

100 lines
3.1 KiB
Swift

import SwiftUI
import SharedModels
import SwiftData
struct BookLibraryView: View {
@Query(sort: \Book.title) private var books: [Book]
var body: some View {
Group {
if books.isEmpty {
ContentUnavailableView(
"No Books",
systemImage: "books.vertical",
description: Text("Books bundled with the app will appear here.")
)
} else {
ScrollView {
LazyVStack(spacing: 12) {
ForEach(books) { book in
NavigationLink {
BookChapterListView(book: book)
} label: {
BookCard(book: book)
}
.tint(.primary)
}
}
.padding()
}
}
}
.navigationTitle("Books")
.navigationBarTitleDisplayMode(.inline)
}
}
private struct BookCard: View {
let book: Book
private var accentColor: Color {
Color(hex: book.accentColorHex) ?? .indigo
}
private var shortTitle: String {
// Trim "Volume X" subtitle if present most book titles are way too long.
if let colon = book.title.firstIndex(of: ":") {
return String(book.title[..<colon])
}
return book.title
}
var body: some View {
HStack(spacing: 14) {
RoundedRectangle(cornerRadius: 6)
.fill(accentColor.gradient)
.frame(width: 48, height: 64)
.overlay {
Image(systemName: "book.closed.fill")
.font(.title3)
.foregroundStyle(.white.opacity(0.9))
}
VStack(alignment: .leading, spacing: 4) {
Text(shortTitle)
.font(.subheadline.weight(.semibold))
.multilineTextAlignment(.leading)
if !book.author.isEmpty {
Text(book.author)
.font(.caption)
.foregroundStyle(.secondary)
}
Text("\(book.chapterCount) chapter\(book.chapterCount == 1 ? "" : "s")")
.font(.caption2)
.foregroundStyle(.tertiary)
}
Spacer()
Image(systemName: "chevron.right")
.font(.caption)
.foregroundStyle(.tertiary)
}
.padding(.horizontal, 16)
.padding(.vertical, 12)
.glassEffect(in: RoundedRectangle(cornerRadius: 14))
}
}
private extension Color {
init?(hex: String) {
var s = hex.trimmingCharacters(in: .whitespacesAndNewlines)
if s.hasPrefix("#") { s.removeFirst() }
guard s.count == 6, let v = UInt32(s, radix: 16) else { return nil }
let r = Double((v >> 16) & 0xFF) / 255.0
let g = Double((v >> 8) & 0xFF) / 255.0
let b = Double(v & 0xFF) / 255.0
self = Color(red: r, green: g, blue: b)
}
}