diff --git a/Shared/Player/PlayerBackendView.swift b/Shared/Player/PlayerBackendView.swift index 6c75f91d..ddd6a201 100644 --- a/Shared/Player/PlayerBackendView.swift +++ b/Shared/Player/PlayerBackendView.swift @@ -43,7 +43,7 @@ struct PlayerBackendView: View { Color.clear .onAppear { player.playerSize = proxy.size } .onChange(of: proxy.size) { _ in player.playerSize = proxy.size } - .onChange(of: player.controls.presentingOverlays) { _ in player.playerSize = proxy.size } + .onChange(of: player.currentItem?.id) { _ in player.playerSize = proxy.size } }) #if !os(tvOS) diff --git a/Shared/Player/PlayerDragGesture.swift b/Shared/Player/PlayerDragGesture.swift index 69c0c965..a2046711 100644 --- a/Shared/Player/PlayerDragGesture.swift +++ b/Shared/Player/PlayerDragGesture.swift @@ -56,7 +56,6 @@ extension VideoPlayerView { player.seek.gestureStart = time } let timeSeek = (time / player.playerSize.width) * horizontalDrag * seekGestureSpeed - player.seek.gestureSeek = timeSeek } return @@ -80,6 +79,54 @@ extension VideoPlayerView { } } + var detailsDragGesture: some Gesture { + DragGesture(minimumDistance: 30) + .onChanged { value in + handleDetailsDragChange(value) + } + .onEnded { value in + handleDetailsDragEnd(value) + } + } + + private func handleDetailsDragChange(_ value: DragGesture.Value) { + let maxOffset = -player.playerSize.height + + // Continuous drag update for smooth movement of VideoDetails + if fullScreenDetails { + // Allow only downward dragging when in fullscreen + if value.translation.height > 0 { + detailViewDragOffset = min(value.translation.height, abs(maxOffset)) + } + } else { + // Allow only upward dragging when not in fullscreen + if value.translation.height < 0 { + detailViewDragOffset = max(value.translation.height, maxOffset) + } + } + } + + private func handleDetailsDragEnd(_ value: DragGesture.Value) { + if value.translation.height < -50, !fullScreenDetails { + // Swipe up to enter fullscreen + withAnimation(Constants.overlayAnimation) { + fullScreenDetails = true + detailViewDragOffset = 0 + } + } else if value.translation.height > 50, fullScreenDetails { + // Swipe down to exit fullscreen + withAnimation(Constants.overlayAnimation) { + fullScreenDetails = false + detailViewDragOffset = 0 + } + } else { + // Reset offset if drag was not significant + withAnimation(Constants.overlayAnimation) { + detailViewDragOffset = 0 + } + } + } + func onPlayerDragGestureEnded() { if horizontalPlayerGestureEnabled, isHorizontalDrag { isHorizontalDrag = false @@ -108,7 +155,6 @@ extension VideoPlayerView { } } - // Function to temporarily disable the toggle gesture after a fullscreen change private func disableGestureTemporarily() { disableToggleGesture = true DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { diff --git a/Shared/Player/Video Details/VideoDetails.swift b/Shared/Player/Video Details/VideoDetails.swift index 173fd830..33ee0927 100644 --- a/Shared/Player/Video Details/VideoDetails.swift +++ b/Shared/Player/Video Details/VideoDetails.swift @@ -223,7 +223,7 @@ struct VideoDetails: View { .frame(maxWidth: .infinity, alignment: .leading) .contentShape(Rectangle()) .padding(.horizontal, 16) - // swiftlint:disable trailing_closure + // TODO: when setting tvOS minimum to 16, the platform modifier can be removed #if !os(tvOS) .simultaneousGesture( // Simultaneous gesture to prioritize button tap @@ -234,7 +234,7 @@ struct VideoDetails: View { } ) #endif - // swiftlint:enable trailing_closure + if VideoActions().isAnyActionVisible() { VideoActions(video: player.videoForDisplay) .padding(.vertical, 5) diff --git a/Shared/Player/VideoPlayerView.swift b/Shared/Player/VideoPlayerView.swift index be678a8c..83afc008 100644 --- a/Shared/Player/VideoPlayerView.swift +++ b/Shared/Player/VideoPlayerView.swift @@ -24,13 +24,12 @@ struct VideoPlayerView: View { #if os(macOS) 335 #else - 200 + 140 #endif } @State private var playerSize: CGSize = .zero { didSet { updateSidebarQueue() } } @State private var hoveringPlayer = false - @State private var fullScreenDetails = false @State private var sidebarQueue = defaultSidebarQueueValue @Environment(\.colorScheme) private var colorScheme @@ -51,12 +50,14 @@ struct VideoPlayerView: View { @State var isHorizontalDrag = false @State var isVerticalDrag = false @State var viewDragOffset = Self.hiddenOffset + @State var detailViewDragOffset: Double = 0 // swiftlint:enable private_swiftui_state #endif // swiftlint:disable private_swiftui_state @State var disableToggleGesture = false + @State var fullScreenDetails = false // swiftlint:enable private_swiftui_state @ObservedObject var player = PlayerModel.shared // swiftlint:disable:this swiftui_state_private @@ -307,6 +308,8 @@ struct VideoPlayerView: View { #endif .id(player.currentVideo?.cacheKey) .transition(.opacity) + .offset(y: detailViewDragOffset) + .gesture(detailsDragGesture) } else { VStack {} }