mirror of
https://github.com/yattee/yattee.git
synced 2026-02-20 01:39:46 +00:00
Yattee v2 rewrite
This commit is contained in:
115
Yattee/Views/Player/ChapterRow.swift
Normal file
115
Yattee/Views/Player/ChapterRow.swift
Normal file
@@ -0,0 +1,115 @@
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
Reference in New Issue
Block a user