128 lines
4.9 KiB
Swift
128 lines
4.9 KiB
Swift
//
|
|
// Swiftfin is subject to the terms of the Mozilla Public
|
|
// License, v2.0. If a copy of the MPL was not distributed with this
|
|
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
|
//
|
|
// Copyright (c) 2024 Jellyfin & Jellyfin Contributors
|
|
//
|
|
|
|
import Defaults
|
|
import SwiftUI
|
|
|
|
extension VideoPlayer {
|
|
|
|
struct MainOverlay: View {
|
|
|
|
@Default(.VideoPlayer.Overlay.playbackButtonType)
|
|
private var playbackButtonType
|
|
|
|
@Environment(\.currentOverlayType)
|
|
@Binding
|
|
private var currentOverlayType
|
|
@Environment(\.isPresentingOverlay)
|
|
@Binding
|
|
private var isPresentingOverlay
|
|
@Environment(\.isScrubbing)
|
|
@Binding
|
|
private var isScrubbing: Bool
|
|
@Environment(\.safeAreaInsets)
|
|
private var safeAreaInsets
|
|
|
|
@EnvironmentObject
|
|
private var splitContentViewProxy: SplitContentViewProxy
|
|
|
|
@StateObject
|
|
private var overlayTimer: TimerProxy = .init()
|
|
|
|
var body: some View {
|
|
ZStack {
|
|
VStack {
|
|
Overlay.TopBarView()
|
|
.if(UIDevice.hasNotch) { view in
|
|
view.padding(safeAreaInsets.mutating(\.trailing, with: 0))
|
|
.padding(.trailing, splitContentViewProxy.isPresentingSplitView ? 0 : safeAreaInsets.trailing)
|
|
}
|
|
.if(UIDevice.isPad) { view in
|
|
view.padding(.top)
|
|
.padding(.horizontal)
|
|
}
|
|
.background {
|
|
LinearGradient(
|
|
stops: [
|
|
.init(color: .black.opacity(0.9), location: 0),
|
|
.init(color: .clear, location: 1),
|
|
],
|
|
startPoint: .top,
|
|
endPoint: .bottom
|
|
)
|
|
.visible(playbackButtonType == .compact)
|
|
}
|
|
.visible(!isScrubbing && isPresentingOverlay)
|
|
|
|
Spacer()
|
|
.allowsHitTesting(false)
|
|
|
|
Overlay.BottomBarView()
|
|
.if(UIDevice.hasNotch) { view in
|
|
view.padding(safeAreaInsets.mutating(\.trailing, with: 0))
|
|
.padding(.trailing, splitContentViewProxy.isPresentingSplitView ? 0 : safeAreaInsets.trailing)
|
|
}
|
|
.if(UIDevice.isPad) { view in
|
|
view.padding(.bottom)
|
|
.padding(.horizontal)
|
|
}
|
|
.background {
|
|
LinearGradient(
|
|
stops: [
|
|
.init(color: .clear, location: 0),
|
|
.init(color: .black.opacity(0.5), location: 0.5),
|
|
.init(color: .black.opacity(0.5), location: 1),
|
|
],
|
|
startPoint: .top,
|
|
endPoint: .bottom
|
|
)
|
|
.visible(isScrubbing || playbackButtonType == .compact)
|
|
}
|
|
.background {
|
|
Color.clear
|
|
.allowsHitTesting(true)
|
|
.contentShape(Rectangle())
|
|
.allowsHitTesting(true)
|
|
}
|
|
.visible(isScrubbing || isPresentingOverlay)
|
|
}
|
|
|
|
if playbackButtonType == .large {
|
|
Overlay.LargePlaybackButtons()
|
|
.visible(!isScrubbing && isPresentingOverlay)
|
|
}
|
|
}
|
|
.environmentObject(overlayTimer)
|
|
.background {
|
|
Color.black
|
|
.opacity(!isScrubbing && playbackButtonType == .large && isPresentingOverlay ? 0.5 : 0)
|
|
.allowsHitTesting(false)
|
|
}
|
|
.animation(.linear(duration: 0.1), value: isScrubbing)
|
|
.onChange(of: isPresentingOverlay) { newValue in
|
|
guard newValue, !isScrubbing else { return }
|
|
overlayTimer.start(5)
|
|
}
|
|
.onChange(of: isScrubbing) { newValue in
|
|
if newValue {
|
|
overlayTimer.stop()
|
|
} else {
|
|
overlayTimer.start(5)
|
|
}
|
|
}
|
|
.onChange(of: overlayTimer.isActive) { newValue in
|
|
guard !newValue, !isScrubbing else { return }
|
|
|
|
withAnimation(.linear(duration: 0.3)) {
|
|
isPresentingOverlay = false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|