mirror of
https://github.com/yattee/yattee.git
synced 2026-10-23 21:22:53 +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.
129 lines
4.2 KiB
Swift
129 lines
4.2 KiB
Swift
//
|
|
// PlayerPanelContentView.swift
|
|
// Yattee
|
|
//
|
|
// Unified details panel content: page bar plus the selected page. Hosted by
|
|
// the portrait panel, the floating panel, and (later) the corner layout
|
|
// regions.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
#if os(iOS) || os(macOS)
|
|
|
|
struct PlayerPanelContentView: View {
|
|
let panelSettings: PanelSettings
|
|
|
|
/// Region filter for corner mode; `nil` shows pages from all regions.
|
|
var region: PanelRegion? = nil
|
|
|
|
@Binding var selectedPage: PanelPageType
|
|
let context: PanelPageContext
|
|
var scrollConfig: PanelScrollConfig = PanelScrollConfig()
|
|
|
|
/// Extra space above the page bar (e.g. below the portrait drag handle).
|
|
var pageBarTopPadding: CGFloat = 0
|
|
|
|
private var appEnvironment: AppEnvironment? { context.appEnvironment }
|
|
private var playerState: PlayerState? { appEnvironment?.playerService.state }
|
|
|
|
// MARK: - Page Visibility
|
|
|
|
/// Config enables a page; runtime hides pages with nothing to show.
|
|
private func isRuntimeVisible(_ pageType: PanelPageType) -> Bool {
|
|
switch pageType {
|
|
case .info:
|
|
return true
|
|
case .comments:
|
|
return !context.video.isFromMediaSource
|
|
&& playerState?.commentsState != .disabled
|
|
case .related:
|
|
return !(context.video.relatedVideos ?? []).isEmpty
|
|
case .chapters:
|
|
return !(playerState?.chapters ?? []).isEmpty
|
|
case .queue:
|
|
return appEnvironment?.settingsManager.queueEnabled ?? true
|
|
case .tracks:
|
|
return true
|
|
}
|
|
}
|
|
|
|
/// Enabled pages that currently have content, in configured order.
|
|
private var visiblePages: [PanelPageConfiguration] {
|
|
panelSettings.enabledPages(in: region).filter { isRuntimeVisible($0.pageType) }
|
|
}
|
|
|
|
/// The page to render: the selection when visible, else the first page.
|
|
private var effectivePage: PanelPageConfiguration? {
|
|
visiblePages.first { $0.pageType == selectedPage } ?? visiblePages.first
|
|
}
|
|
|
|
// MARK: - Body
|
|
|
|
var body: some View {
|
|
let pages = visiblePages
|
|
|
|
VStack(spacing: 0) {
|
|
if pages.count >= 2 {
|
|
PanelPageBar(
|
|
pages: pages,
|
|
selectedPage: $selectedPage,
|
|
accentColor: context.accentColor,
|
|
showLabels: panelSettings.effectiveShowPageBarLabels
|
|
)
|
|
.padding(.top, pageBarTopPadding)
|
|
}
|
|
|
|
if let page = effectivePage {
|
|
pageContent(page)
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
} else {
|
|
Spacer()
|
|
}
|
|
}
|
|
.onChange(of: selectedPage) { _, _ in
|
|
// Each page owns its scroll position; reset the host's offset so
|
|
// pills and the scroll-to-top button start from the top state.
|
|
scrollConfig.scrollOffsetChanged?(0)
|
|
}
|
|
.onChange(of: context.video.id) { _, _ in
|
|
// New video: fall back to the first configured page.
|
|
if let first = visiblePages.first?.pageType {
|
|
selectedPage = first
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Pages
|
|
|
|
@ViewBuilder
|
|
private func pageContent(_ page: PanelPageConfiguration) -> some View {
|
|
switch page.pageType {
|
|
case .info:
|
|
InfoPanelPage(
|
|
context: context,
|
|
settings: page.infoSettings,
|
|
scrollConfig: scrollConfig
|
|
)
|
|
.id(PanelPageType.info)
|
|
case .comments:
|
|
CommentsPanelPage(context: context, scrollConfig: scrollConfig)
|
|
.id(PanelPageType.comments)
|
|
case .related:
|
|
RelatedPanelPage(context: context, scrollConfig: scrollConfig)
|
|
.id(PanelPageType.related)
|
|
case .chapters:
|
|
ChaptersPanelPage(context: context, scrollConfig: scrollConfig)
|
|
.id(PanelPageType.chapters)
|
|
case .queue:
|
|
QueuePanelPage(context: context, bottomInset: scrollConfig.bottomInset)
|
|
.id(PanelPageType.queue)
|
|
case .tracks:
|
|
TracksPanelPage(context: context, bottomInset: scrollConfig.bottomInset)
|
|
.id(PanelPageType.tracks)
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|