mirror of
https://github.com/yattee/yattee.git
synced 2026-10-23 13:12: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.
118 lines
3.8 KiB
Swift
118 lines
3.8 KiB
Swift
//
|
|
// ExpandedPlayerSheet+VideoInfo.swift
|
|
// Yattee
|
|
//
|
|
// Video info, description, and comments functionality for the expanded player sheet.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
#if os(iOS) || os(macOS) || os(tvOS)
|
|
|
|
extension ExpandedPlayerSheet {
|
|
// MARK: - Scroll & Comments
|
|
|
|
/// Scrolls the content to the top.
|
|
func scrollToTop() {
|
|
withAnimation(.easeOut(duration: 0.25)) {
|
|
scrollPosition.scrollTo(y: 0)
|
|
}
|
|
}
|
|
|
|
/// Expands the comments overlay.
|
|
func expandComments() {
|
|
// Scroll player to top so video is fully visible
|
|
scrollPosition.scrollTo(y: 0)
|
|
// Use same animation as player sheet expand (0.3s, no bounce)
|
|
withAnimation(.smooth(duration: 0.3)) {
|
|
isCommentsExpanded = true
|
|
}
|
|
}
|
|
|
|
/// Collapses the comments overlay.
|
|
func collapseComments() {
|
|
navigationCoordinator?.commentsFrame = .zero
|
|
// Use same animation as player sheet dismiss (0.3s, no bounce)
|
|
withAnimation(.smooth(duration: 0.3)) {
|
|
isCommentsExpanded = false
|
|
commentsDismissOffset = 0
|
|
}
|
|
}
|
|
|
|
/// Starts preloading comments, cancelling any in-flight task.
|
|
func startPreloadingComments() {
|
|
commentsPreloadTask?.cancel()
|
|
commentsPreloadTask = Task { await preloadComments() }
|
|
}
|
|
|
|
/// Cancels any in-flight comments preload task.
|
|
func cancelCommentsPreload() {
|
|
commentsPreloadTask?.cancel()
|
|
commentsPreloadTask = nil
|
|
}
|
|
|
|
/// Handles comments dismiss offset during drag.
|
|
func handleCommentsDismissOffset(_ offset: CGFloat) {
|
|
// Don't apply real-time offset during drag - it causes feedback loop
|
|
// with scroll geometry. The scroll view bounces naturally with iOS rubber-banding.
|
|
}
|
|
|
|
/// Handles the end of comments dismiss gesture.
|
|
func handleCommentsDismissGestureEnded(_ finalOffset: CGFloat) {
|
|
let dismissThreshold: CGFloat = 30
|
|
if finalOffset >= dismissThreshold {
|
|
collapseComments()
|
|
}
|
|
// Below threshold - scroll view will rubber-band back naturally
|
|
}
|
|
|
|
/// Preloads comments for the current video.
|
|
func preloadComments() async {
|
|
guard let playerState, playerState.commentsState == .idle else { return }
|
|
|
|
// Check if comments pill is disabled in preset settings
|
|
let pillSettings = playerControlsLayout.effectivePlayerPillSettings
|
|
guard pillSettings.shouldLoadComments else {
|
|
playerState.commentsState = .disabled
|
|
return
|
|
}
|
|
|
|
guard let contentService = appEnvironment?.contentService,
|
|
let instancesManager = appEnvironment?.instancesManager else { return }
|
|
|
|
await playerState.loadComments(contentService: contentService, instancesManager: instancesManager)
|
|
}
|
|
|
|
// MARK: - Actions
|
|
|
|
/// Closes the current video and dismisses the player.
|
|
func closeVideo() {
|
|
// Mark as closing to hide tab accessory before dismissal
|
|
playerState?.isClosingVideo = true
|
|
|
|
// Clear the queue when closing video
|
|
appEnvironment?.queueManager.clearQueue()
|
|
|
|
// Reset panel state when closing player
|
|
appEnvironment?.settingsManager.landscapeDetailsPanelVisible = false
|
|
appEnvironment?.settingsManager.landscapeDetailsPanelPinned = false
|
|
|
|
// Stop player FIRST before dismissing window
|
|
// This ensures MPVRenderView and backend are properly cleaned up
|
|
// before the window's content view hierarchy is destroyed
|
|
playerService?.stop()
|
|
|
|
// Then dismiss player window (after backend is stopped)
|
|
navigationCoordinator?.isPlayerExpanded = false
|
|
}
|
|
|
|
/// Switches to a different stream.
|
|
func switchToStream(_ stream: Stream, audioStream: Stream? = nil) {
|
|
Task {
|
|
await playerService?.selectStreamManually(stream, audioStream: audioStream)
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|