mirror of
https://github.com/yattee/yattee.git
synced 2026-10-28 07:27:18 +00:00
Phase 1 of the player panels redesign: - New PanelSettings model (pages, regions, layout mode, default visibility) stored as an optional field on PlayerControlsLayout with an effectivePanelSettings accessor; lenient Codable so future page types and settings payloads never corrupt saved presets. - New Views/Player/Panels content engine: PlayerPanelContentView renders a page bar plus the selected page (info, comments, related, chapters, queue, tracks), with runtime hiding of empty pages. - PortraitDetailsPanel and FloatingDetailsPanel now host the shared content instead of their duplicated info bodies; drag machinery, pills, and fades are untouched. - Comments list extracted into CommentsListContent shared by the overlay and the comments page; comments pill routes to the comments page (expanding the portrait panel) when the page is enabled, and falls back to the overlay when it is not. - Tracks page embeds QualitySelectorView via a new isEmbedded mode. - Deleted the unused video info bodies in ExpandedPlayerSheet+VideoInfo and the dead PlayerInfoTab model.
64 lines
2.0 KiB
Swift
64 lines
2.0 KiB
Swift
//
|
|
// ChaptersPanelPage.swift
|
|
// Yattee
|
|
//
|
|
// Details panel page listing video chapters; tapping seeks.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
#if os(iOS) || os(macOS)
|
|
|
|
struct ChaptersPanelPage: View {
|
|
let context: PanelPageContext
|
|
var scrollConfig: PanelScrollConfig = PanelScrollConfig()
|
|
|
|
private var appEnvironment: AppEnvironment? { context.appEnvironment }
|
|
private var playerService: PlayerService? { appEnvironment?.playerService }
|
|
private var playerState: PlayerState? { playerService?.state }
|
|
|
|
private var chapters: [VideoChapter] { playerState?.chapters ?? [] }
|
|
private var currentTime: TimeInterval { playerState?.currentTime ?? 0 }
|
|
private var storyboard: Storyboard? { playerState?.preferredStoryboard }
|
|
|
|
var body: some View {
|
|
PanelPageScrollView(config: scrollConfig) {
|
|
LazyVStack(spacing: 0) {
|
|
ForEach(chapters) { chapter in
|
|
ChapterRow(
|
|
chapter: chapter,
|
|
isActive: isChapterActive(chapter),
|
|
storyboard: storyboard,
|
|
onTap: {
|
|
Task {
|
|
await playerService?.seek(to: chapter.startTime)
|
|
}
|
|
}
|
|
)
|
|
}
|
|
}
|
|
.padding(.horizontal)
|
|
.padding(.top, 8)
|
|
}
|
|
.task(id: context.video.id) {
|
|
await preloadChapterSheets()
|
|
}
|
|
}
|
|
|
|
private func isChapterActive(_ chapter: VideoChapter) -> Bool {
|
|
let nextChapter = chapters.first { $0.startTime > chapter.startTime }
|
|
let endTime = nextChapter?.startTime ?? .infinity
|
|
return currentTime >= chapter.startTime && currentTime < endTime
|
|
}
|
|
|
|
private func preloadChapterSheets() async {
|
|
guard let storyboard else { return }
|
|
let service = StoryboardService.shared
|
|
for chapter in chapters {
|
|
await service.loadSheet(for: chapter.startTime, from: storyboard)
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|