mirror of
https://github.com/yattee/yattee.git
synced 2026-10-28 23:47:17 +00:00
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.
This commit is contained in:
@@ -223,6 +223,48 @@ struct ExpandedCommentsView: View {
|
||||
|
||||
@ViewBuilder
|
||||
private var commentsContent: some View {
|
||||
CommentsListContent(videoID: videoID)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var closeButton: some View {
|
||||
Button(action: onClose) {
|
||||
Image(systemName: isDismissThresholdReached ? "chevron.down" : "xmark")
|
||||
.font(.system(size: 18, weight: .semibold))
|
||||
.foregroundStyle(isDismissThresholdReached ? .white : .primary)
|
||||
.frame(width: 32, height: 32)
|
||||
.padding(10)
|
||||
.contentShape(Circle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.glassBackground(
|
||||
isDismissThresholdReached ? .tinted(.blue) : .regular,
|
||||
in: .circle,
|
||||
fallback: isDismissThresholdReached ? .ultraThinMaterial : .thinMaterial
|
||||
)
|
||||
.contentShape(Circle())
|
||||
.animation(.easeInOut(duration: 0.15), value: isDismissThresholdReached)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Comments List Content
|
||||
|
||||
/// Comments list states and rows driven by `PlayerState`, shared between the
|
||||
/// expanded comments overlay and the details panel comments page.
|
||||
struct CommentsListContent: View {
|
||||
@Environment(\.appEnvironment) private var appEnvironment
|
||||
|
||||
let videoID: String
|
||||
|
||||
private var playerState: PlayerState? { appEnvironment?.playerService.state }
|
||||
private var contentService: ContentService? { appEnvironment?.contentService }
|
||||
private var instancesManager: InstancesManager? { appEnvironment?.instancesManager }
|
||||
|
||||
private var comments: [Comment] { playerState?.comments ?? [] }
|
||||
private var commentsState: CommentsLoadState { playerState?.commentsState ?? .idle }
|
||||
private var videoSource: ContentSource? { playerState?.currentVideo?.id.source }
|
||||
|
||||
var body: some View {
|
||||
switch commentsState {
|
||||
case .idle, .loading:
|
||||
HStack {
|
||||
@@ -307,26 +349,6 @@ struct ExpandedCommentsView: View {
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var closeButton: some View {
|
||||
Button(action: onClose) {
|
||||
Image(systemName: isDismissThresholdReached ? "chevron.down" : "xmark")
|
||||
.font(.system(size: 18, weight: .semibold))
|
||||
.foregroundStyle(isDismissThresholdReached ? .white : .primary)
|
||||
.frame(width: 32, height: 32)
|
||||
.padding(10)
|
||||
.contentShape(Circle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.glassBackground(
|
||||
isDismissThresholdReached ? .tinted(.blue) : .regular,
|
||||
in: .circle,
|
||||
fallback: isDismissThresholdReached ? .ultraThinMaterial : .thinMaterial
|
||||
)
|
||||
.contentShape(Circle())
|
||||
.animation(.easeInOut(duration: 0.15), value: isDismissThresholdReached)
|
||||
}
|
||||
|
||||
private func loadComments() async {
|
||||
guard let playerState, let contentService, let instancesManager else { return }
|
||||
await playerState.loadComments(contentService: contentService, instancesManager: instancesManager)
|
||||
|
||||
Reference in New Issue
Block a user