mirror of
https://github.com/yattee/yattee.git
synced 2026-02-20 09:49:46 +00:00
116 lines
3.3 KiB
Swift
116 lines
3.3 KiB
Swift
//
|
|
// ChapterRow.swift
|
|
// Yattee
|
|
//
|
|
// Row view for displaying a video chapter.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
struct ChapterRow: View {
|
|
let chapter: VideoChapter
|
|
let isActive: Bool
|
|
let storyboard: Storyboard?
|
|
let onTap: () -> Void
|
|
|
|
@State private var thumbnail: PlatformImage?
|
|
|
|
var body: some View {
|
|
Button(action: onTap) {
|
|
HStack(spacing: 12) {
|
|
// Thumbnail from storyboard
|
|
Group {
|
|
if let thumbnail {
|
|
#if os(macOS)
|
|
Image(nsImage: thumbnail)
|
|
.resizable()
|
|
.aspectRatio(contentMode: .fill)
|
|
#else
|
|
Image(uiImage: thumbnail)
|
|
.resizable()
|
|
.aspectRatio(contentMode: .fill)
|
|
#endif
|
|
} else {
|
|
Rectangle()
|
|
.fill(.quaternary)
|
|
.overlay {
|
|
Image(systemName: "film")
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
}
|
|
}
|
|
.frame(width: 80, height: 45)
|
|
.clipShape(RoundedRectangle(cornerRadius: 4))
|
|
|
|
// Info
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
Text(chapter.title)
|
|
.font(.subheadline)
|
|
.fontWeight(isActive ? .semibold : .regular)
|
|
.lineLimit(2)
|
|
|
|
Text(chapter.formattedStartTime)
|
|
.font(.caption)
|
|
.foregroundStyle(.secondary)
|
|
.monospacedDigit()
|
|
}
|
|
|
|
Spacer()
|
|
|
|
// Active indicator
|
|
if isActive {
|
|
Image(systemName: "play.fill")
|
|
.font(.caption)
|
|
.foregroundStyle(Color.accentColor)
|
|
}
|
|
}
|
|
.padding(.vertical, 4)
|
|
}
|
|
.buttonStyle(.plain)
|
|
.listRowBackground(isActive ? Color.accentColor.opacity(0.1) : nil)
|
|
.task {
|
|
await loadThumbnail()
|
|
}
|
|
}
|
|
|
|
private func loadThumbnail() async {
|
|
guard let storyboard else { return }
|
|
|
|
let service = StoryboardService.shared
|
|
|
|
// Try cached first
|
|
if let cached = await service.thumbnail(for: chapter.startTime, from: storyboard) {
|
|
thumbnail = cached
|
|
return
|
|
}
|
|
|
|
// Load the sheet
|
|
await service.preloadNearbySheets(around: chapter.startTime, from: storyboard)
|
|
|
|
// Try again after loading
|
|
if let loaded = await service.thumbnail(for: chapter.startTime, from: storyboard) {
|
|
thumbnail = loaded
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Preview
|
|
|
|
#Preview {
|
|
List {
|
|
ChapterRow(
|
|
chapter: VideoChapter(title: "Introduction", startTime: 0),
|
|
isActive: true,
|
|
storyboard: nil,
|
|
onTap: {}
|
|
)
|
|
ChapterRow(
|
|
chapter: VideoChapter(title: "Main Content", startTime: 60),
|
|
isActive: false,
|
|
storyboard: nil,
|
|
onTap: {}
|
|
)
|
|
}
|
|
.listStyle(.plain)
|
|
}
|