Files
yattee/Yattee/Views/Player/Panels/RelatedPanelPage.swift
Arkadiusz Fal d5285b2b83 Add unified page-based details panel on existing layouts
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.
2026-09-21 23:20:45 +02:00

62 lines
2.0 KiB
Swift

//
// RelatedPanelPage.swift
// Yattee
//
// Details panel page listing videos related to the current one.
//
import SwiftUI
#if os(iOS) || os(macOS)
struct RelatedPanelPage: View {
let context: PanelPageContext
var scrollConfig: PanelScrollConfig = PanelScrollConfig()
private var relatedVideos: [Video] { context.video.relatedVideos ?? [] }
var body: some View {
PanelPageScrollView(config: scrollConfig) {
if relatedVideos.isEmpty {
emptyState
} else {
LazyVStack(spacing: 12) {
ForEach(Array(relatedVideos.enumerated()), id: \.element.id) { index, relatedVideo in
VideoRowView(video: relatedVideo, style: .regular)
.tappableVideo(
relatedVideo,
queueSource: .manual,
sourceLabel: String(localized: "videoInfo.section.relatedVideos"),
videoList: relatedVideos,
videoIndex: index,
loadMoreVideos: nil
)
if index < relatedVideos.count - 1 {
Divider()
.padding(.leading, VideoRowStyle.regular.thumbnailWidth + 12)
}
}
}
.padding(.horizontal)
.padding(.top, 8)
}
}
}
private var emptyState: some View {
VStack(spacing: 8) {
Image(systemName: "rectangle.stack.badge.play")
.font(.title2)
.foregroundStyle(.secondary)
Text(String(localized: "videoInfo.section.relatedVideos"))
.font(.subheadline)
.foregroundStyle(.secondary)
}
.frame(maxWidth: .infinity)
.padding(.vertical, 32)
}
}
#endif