58 lines
1.8 KiB
Swift
58 lines
1.8 KiB
Swift
import SwiftUI
|
|
|
|
struct BookCoverCard: View {
|
|
let book: BookRemote
|
|
let imageCache: ImageCacheProtocol
|
|
|
|
var body: some View {
|
|
VStack(spacing: 6) {
|
|
RemoteImageView(url: book.coverImageUrl, imageCache: imageCache)
|
|
.frame(width: 98, height: 145)
|
|
.clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
|
|
.overlay {
|
|
RoundedRectangle(cornerRadius: 10)
|
|
.stroke(Color.black.opacity(0.08), lineWidth: 1)
|
|
}
|
|
.shadow(color: .black.opacity(0.16), radius: 6, y: 4)
|
|
|
|
Text(book.title)
|
|
.font(.caption)
|
|
.lineLimit(1)
|
|
.foregroundStyle(.primary)
|
|
}
|
|
.accessibilityElement(children: .combine)
|
|
.accessibilityLabel("\(book.title), \(book.authors.joined(separator: ", "))")
|
|
}
|
|
}
|
|
|
|
private struct RemoteImageView: View {
|
|
let url: URL?
|
|
let imageCache: ImageCacheProtocol
|
|
|
|
@State private var image: UIImage?
|
|
|
|
var body: some View {
|
|
ZStack {
|
|
RoundedRectangle(cornerRadius: 10)
|
|
.fill(LinearGradient(colors: [Color.gray.opacity(0.22), Color.gray.opacity(0.35)], startPoint: .top, endPoint: .bottom))
|
|
if let image {
|
|
Image(uiImage: image)
|
|
.resizable()
|
|
.scaledToFill()
|
|
} else {
|
|
Image(systemName: "book.closed")
|
|
.font(.title3)
|
|
.foregroundStyle(Color.black.opacity(0.35))
|
|
}
|
|
}
|
|
.task(id: url) {
|
|
guard let url else { return }
|
|
do {
|
|
image = try await imageCache.image(for: url)
|
|
} catch {
|
|
image = nil
|
|
}
|
|
}
|
|
}
|
|
}
|