Player overlaying other views and swipe gesture (fix #44, #130)

This commit is contained in:
Arkadiusz Fal 2022-05-28 23:41:23 +02:00
parent 026a65bfd7
commit a71a7760be
17 changed files with 187 additions and 209 deletions

View File

@ -1,5 +1,6 @@
import AVFAudio import AVFAudio
import CoreMedia import CoreMedia
import Defaults
import Foundation import Foundation
import Logging import Logging
import SwiftUI import SwiftUI
@ -247,7 +248,13 @@ final class MPVBackend: PlayerBackend {
client?.stop() client?.stop()
} }
func enterFullScreen() {} func enterFullScreen() {
model.toggleFullscreen(controls?.playingFullscreen ?? false)
if Defaults[.lockLandscapeWhenEnteringFullscreen] {
Orientation.lockOrientation(.landscape, andRotateTo: UIDevice.current.orientation.isLandscape ? nil : .landscapeRight)
}
}
func exitFullScreen() {} func exitFullScreen() {}

View File

@ -134,11 +134,11 @@ final class MPVClient: ObservableObject {
} }
var currentTime: CMTime { var currentTime: CMTime {
CMTime.secondsInDefaultTimescale(getDouble("time-pos")) CMTime.secondsInDefaultTimescale(mpv.isNil ? -1 : getDouble("time-pos"))
} }
var duration: CMTime { var duration: CMTime {
CMTime.secondsInDefaultTimescale(getDouble("duration")) CMTime.secondsInDefaultTimescale(mpv.isNil ? -1 : getDouble("duration"))
} }
func seek(relative time: CMTime, completionHandler: ((Bool) -> Void)? = nil) { func seek(relative time: CMTime, completionHandler: ((Bool) -> Void)? = nil) {

View File

@ -57,8 +57,6 @@ final class PlayerModel: ObservableObject {
@Published var preservedTime: CMTime? @Published var preservedTime: CMTime?
@Published var playerNavigationLinkActive = false { didSet { handleNavigationViewPlayerPresentationChange() } }
@Published var sponsorBlock = SponsorBlockAPI() @Published var sponsorBlock = SponsorBlockAPI()
@Published var segmentRestorationTime: CMTime? @Published var segmentRestorationTime: CMTime?
@Published var lastSkipped: Segment? { didSet { rebuildTVMenu() } } @Published var lastSkipped: Segment? { didSet { rebuildTVMenu() } }
@ -120,23 +118,24 @@ final class PlayerModel: ObservableObject {
} }
func show() { func show() {
guard !presentingPlayer else { #if os(macOS)
#if os(macOS) if presentingPlayer {
Windows.player.focus() Windows.player.focus()
#endif return
return }
} #endif
presentingPlayer = true
#if os(macOS) #if os(macOS)
Windows.player.open() Windows.player.open()
Windows.player.focus() Windows.player.focus()
#endif #endif
presentingPlayer = true
} }
func hide() { func hide() {
controls.playingFullscreen = false controls.playingFullscreen = false
presentingPlayer = false presentingPlayer = false
playerNavigationLinkActive = false
#if os(iOS) #if os(iOS)
if Defaults[.lockPortraitWhenBrowsing] { if Defaults[.lockPortraitWhenBrowsing] {
@ -206,18 +205,25 @@ final class PlayerModel: ObservableObject {
backend.pause() backend.pause()
} }
func play(_ video: Video, at time: CMTime? = nil, inNavigationView: Bool = false) { func play(_ video: Video, at time: CMTime? = nil) {
playNow(video, at: time) var delay = 0.0
#if !os(macOS)
delay = 0.3
#endif
DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in
guard let self = self else {
return
}
self.playNow(video, at: time)
}
guard !playingInPictureInPicture else { guard !playingInPictureInPicture else {
return return
} }
if inNavigationView { show()
playerNavigationLinkActive = true
} else {
show()
}
} }
func playStream( func playStream(
@ -297,7 +303,18 @@ final class PlayerModel: ObservableObject {
} }
private func handlePresentationChange() { private func handlePresentationChange() {
backend.setNeedsDrawing(presentingPlayer) var delay = 0.0
#if os(iOS)
if presentingPlayer {
delay = 0.2
}
#endif
DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in
self?.backend.setNeedsDrawing(self?.presentingPlayer ?? false)
}
controls.hide() controls.hide()
#if !os(macOS) #if !os(macOS)
@ -323,17 +340,6 @@ final class PlayerModel: ObservableObject {
} }
} }
private func handleNavigationViewPlayerPresentationChange() {
backend.setNeedsDrawing(playerNavigationLinkActive)
controls.hide()
if pauseOnHidingPlayer, !playingInPictureInPicture, !playerNavigationLinkActive {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.pause()
}
}
}
func changeActiveBackend(from: PlayerBackendType, to: PlayerBackendType) { func changeActiveBackend(from: PlayerBackendType, to: PlayerBackendType) {
Defaults[.activeBackend] = to Defaults[.activeBackend] = to
self.activeBackend = to self.activeBackend = to

View File

@ -8,7 +8,7 @@ extension PlayerModel {
currentItem?.video currentItem?.video
} }
func play(_ videos: [Video], shuffling: Bool = false, inNavigationView: Bool = false) { func play(_ videos: [Video], shuffling: Bool = false) {
let videosToPlay = shuffling ? videos.shuffled() : videos let videosToPlay = shuffling ? videos.shuffled() : videos
guard let first = videosToPlay.first else { guard let first = videosToPlay.first else {
@ -27,11 +27,7 @@ extension PlayerModel {
} }
} }
if inNavigationView { show()
playerNavigationLinkActive = true
} else {
show()
}
} }
func playNext(_ video: Video) { func playNext(_ video: Video) {

View File

@ -1,10 +1,6 @@
import Foundation import Foundation
import SwiftUI import SwiftUI
private struct InNavigationViewKey: EnvironmentKey {
static let defaultValue = false
}
private struct InChannelViewKey: EnvironmentKey { private struct InChannelViewKey: EnvironmentKey {
static let defaultValue = false static let defaultValue = false
} }
@ -40,11 +36,6 @@ private struct ScrollViewBottomPaddingKey: EnvironmentKey {
} }
extension EnvironmentValues { extension EnvironmentValues {
var inNavigationView: Bool {
get { self[InNavigationViewKey.self] }
set { self[InNavigationViewKey.self] = newValue }
}
var inChannelView: Bool { var inChannelView: Bool {
get { self[InChannelViewKey.self] } get { self[InChannelViewKey.self] }
set { self[InChannelViewKey.self] = newValue } set { self[InChannelViewKey.self] = newValue }

View File

@ -63,24 +63,6 @@ struct AppSidebarNavigation: View {
} }
} }
.environment(\.navigationStyle, .sidebar) .environment(\.navigationStyle, .sidebar)
#if os(iOS)
.background(
EmptyView().fullScreenCover(isPresented: $player.presentingPlayer) {
VideoPlayerView()
.environmentObject(accounts)
.environmentObject(comments)
.environmentObject(instances)
.environmentObject(navigation)
.environmentObject(player)
.environmentObject(playerControls)
.environmentObject(playlists)
.environmentObject(recents)
.environmentObject(subscriptions)
.environmentObject(thumbnailsModel)
.environment(\.navigationStyle, .sidebar)
}
)
#endif
} }
var toolbarContent: some ToolbarContent { var toolbarContent: some ToolbarContent {

View File

@ -51,14 +51,11 @@ struct AppTabNavigation: View {
ChannelVideosView(channel: channel) ChannelVideosView(channel: channel)
.environment(\.managedObjectContext, persistenceController.container.viewContext) .environment(\.managedObjectContext, persistenceController.container.viewContext)
.environment(\.inChannelView, true) .environment(\.inChannelView, true)
.environment(\.inNavigationView, true)
.environmentObject(accounts) .environmentObject(accounts)
.environmentObject(navigation) .environmentObject(navigation)
.environmentObject(player) .environmentObject(player)
.environmentObject(subscriptions) .environmentObject(subscriptions)
.environmentObject(thumbnailsModel) .environmentObject(thumbnailsModel)
.background(playerNavigationLink)
} }
} }
} }
@ -69,25 +66,15 @@ struct AppTabNavigation: View {
NavigationView { NavigationView {
ChannelPlaylistView(playlist: playlist) ChannelPlaylistView(playlist: playlist)
.environment(\.managedObjectContext, persistenceController.container.viewContext) .environment(\.managedObjectContext, persistenceController.container.viewContext)
.environment(\.inNavigationView, true)
.environmentObject(accounts) .environmentObject(accounts)
.environmentObject(navigation) .environmentObject(navigation)
.environmentObject(player) .environmentObject(player)
.environmentObject(subscriptions) .environmentObject(subscriptions)
.environmentObject(thumbnailsModel) .environmentObject(thumbnailsModel)
.background(playerNavigationLink)
} }
} }
} }
) )
.background(
EmptyView().fullScreenCover(isPresented: $player.presentingPlayer) {
videoPlayer
.environment(\.managedObjectContext, persistenceController.container.viewContext)
.environment(\.navigationStyle, .tab)
}
)
} }
private var favoritesNavigationView: some View { private var favoritesNavigationView: some View {
@ -172,15 +159,6 @@ struct AppTabNavigation: View {
.tag(TabSelection.search) .tag(TabSelection.search)
} }
private var playerNavigationLink: some View {
NavigationLink(isActive: $player.playerNavigationLinkActive, destination: {
videoPlayer
.environment(\.inNavigationView, true)
}) {
EmptyView()
}
}
private var videoPlayer: some View { private var videoPlayer: some View {
VideoPlayerView() VideoPlayerView()
.environmentObject(accounts) .environmentObject(accounts)

View File

@ -57,50 +57,54 @@ struct ContentView: View {
.environmentObject(subscriptions) .environmentObject(subscriptions)
.environmentObject(thumbnailsModel) .environmentObject(thumbnailsModel)
// iOS 14 has problem with multiple sheets in one view #if !os(macOS)
// but it's ok when it's in background .overlay(videoPlayer)
.background(
EmptyView().sheet(isPresented: $navigation.presentingWelcomeScreen) {
WelcomeScreen()
.environmentObject(accounts)
.environmentObject(navigation)
}
)
#if !os(tvOS)
.onOpenURL { OpenURLHandler(accounts: accounts, player: player).handle($0) }
.background(
EmptyView().sheet(isPresented: $navigation.presentingAddToPlaylist) {
AddToPlaylistView(video: navigation.videoToAddToPlaylist)
.environmentObject(playlists)
}
)
.background(
EmptyView().sheet(isPresented: $navigation.presentingPlaylistForm) {
PlaylistFormView(playlist: $navigation.editedPlaylist)
.environmentObject(accounts)
.environmentObject(playlists)
}
)
.background(
EmptyView().sheet(isPresented: $navigation.presentingSettings, onDismiss: openWelcomeScreenIfAccountEmpty) {
SettingsView()
.environmentObject(accounts)
.environmentObject(instances)
.environmentObject(player)
}
)
#endif #endif
.alert(isPresented: $navigation.presentingUnsubscribeAlert) {
Alert( // iOS 14 has problem with multiple sheets in one view
title: Text( // but it's ok when it's in background
"Are you sure you want to unsubscribe from \(navigation.channelToUnsubscribe.name)?" .background(
), EmptyView().sheet(isPresented: $navigation.presentingWelcomeScreen) {
primaryButton: .destructive(Text("Unsubscribe")) { WelcomeScreen()
subscriptions.unsubscribe(navigation.channelToUnsubscribe.id) .environmentObject(accounts)
}, .environmentObject(navigation)
secondaryButton: .cancel() }
) )
} #if !os(tvOS)
.onOpenURL { OpenURLHandler(accounts: accounts, player: player).handle($0) }
.background(
EmptyView().sheet(isPresented: $navigation.presentingAddToPlaylist) {
AddToPlaylistView(video: navigation.videoToAddToPlaylist)
.environmentObject(playlists)
}
)
.background(
EmptyView().sheet(isPresented: $navigation.presentingPlaylistForm) {
PlaylistFormView(playlist: $navigation.editedPlaylist)
.environmentObject(accounts)
.environmentObject(playlists)
}
)
.background(
EmptyView().sheet(isPresented: $navigation.presentingSettings, onDismiss: openWelcomeScreenIfAccountEmpty) {
SettingsView()
.environmentObject(accounts)
.environmentObject(instances)
.environmentObject(player)
}
)
#endif
.alert(isPresented: $navigation.presentingUnsubscribeAlert) {
Alert(
title: Text(
"Are you sure you want to unsubscribe from \(navigation.channelToUnsubscribe.name)?"
),
primaryButton: .destructive(Text("Unsubscribe")) {
subscriptions.unsubscribe(navigation.channelToUnsubscribe.id)
},
secondaryButton: .cancel()
)
}
} }
func configure() { func configure() {
@ -222,6 +226,21 @@ struct ContentView: View {
navigation.presentingWelcomeScreen = true navigation.presentingWelcomeScreen = true
} }
var videoPlayer: some View {
VideoPlayerView()
.environmentObject(accounts)
.environmentObject(comments)
.environmentObject(instances)
.environmentObject(navigation)
.environmentObject(player)
.environmentObject(playerControls)
.environmentObject(playlists)
.environmentObject(recents)
.environmentObject(subscriptions)
.environmentObject(thumbnailsModel)
.environment(\.navigationStyle, .sidebar)
}
} }
struct ContentView_Previews: PreviewProvider { struct ContentView_Previews: PreviewProvider {

View File

@ -178,11 +178,7 @@ extension AppleAVPlayerViewController: AVPlayerViewControllerDelegate {
restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void
) { ) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
if self.navigationModel.presentingChannel { self.playerModel.show()
self.playerModel.playerNavigationLinkActive = true
} else {
self.playerModel.show()
}
#if os(tvOS) #if os(tvOS)
if self.playerModel.playingInPictureInPicture { if self.playerModel.playingInPictureInPicture {
@ -198,7 +194,6 @@ extension AppleAVPlayerViewController: AVPlayerViewControllerDelegate {
func playerViewControllerWillStartPictureInPicture(_: AVPlayerViewController) { func playerViewControllerWillStartPictureInPicture(_: AVPlayerViewController) {
playerModel.playingInPictureInPicture = true playerModel.playingInPictureInPicture = true
playerModel.playerNavigationLinkActive = false
} }
func playerViewControllerWillStopPictureInPicture(_: AVPlayerViewController) { func playerViewControllerWillStopPictureInPicture(_: AVPlayerViewController) {

View File

@ -240,7 +240,7 @@ struct PlayerControls: View {
player.hide() player.hide()
player.closePiP() player.closePiP()
var delay = 0.3 var delay = 0.2
#if os(macOS) #if os(macOS)
delay = 0.0 delay = 0.0
#endif #endif

View File

@ -21,7 +21,6 @@ struct VideoDetails: View {
@State private var currentPage = Page.info @State private var currentPage = Page.info
@Environment(\.presentationMode) private var presentationMode @Environment(\.presentationMode) private var presentationMode
@Environment(\.inNavigationView) private var inNavigationView
@Environment(\.navigationStyle) private var navigationStyle @Environment(\.navigationStyle) private var navigationStyle
@EnvironmentObject<AccountsModel> private var accounts @EnvironmentObject<AccountsModel> private var accounts
@ -112,7 +111,6 @@ struct VideoDetails: View {
.edgesIgnoringSafeArea(.horizontal) .edgesIgnoringSafeArea(.horizontal)
} }
} }
.padding(.top, inNavigationView && fullScreen ? 10 : 0)
.onAppear { .onAppear {
if video.isNil && !sidebarQueue { if video.isNil && !sidebarQueue {
currentPage = .queue currentPage = .queue

View File

@ -37,6 +37,10 @@ struct VideoPlayerView: View {
var mouseLocation: CGPoint { NSEvent.mouseLocation } var mouseLocation: CGPoint { NSEvent.mouseLocation }
#endif #endif
#if !os(macOS)
@State private var playerOffset = UIScreen.main.bounds.height
#endif
@EnvironmentObject<AccountsModel> private var accounts @EnvironmentObject<AccountsModel> private var accounts
@EnvironmentObject<PlayerControlsModel> private var playerControls @EnvironmentObject<PlayerControlsModel> private var playerControls
@EnvironmentObject<PlayerModel> private var player @EnvironmentObject<PlayerModel> private var player
@ -54,10 +58,6 @@ struct VideoPlayerView: View {
content content
.onAppear { .onAppear {
playerSize = geometry.size playerSize = geometry.size
#if os(iOS)
configureOrientationUpdatesBasedOnAccelerometer()
#endif
} }
} }
.onChange(of: geometry.size) { size in .onChange(of: geometry.size) { size in
@ -70,22 +70,10 @@ struct VideoPlayerView: View {
.onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in .onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
handleOrientationDidChangeNotification() handleOrientationDidChangeNotification()
} }
.onDisappear {
guard !playerControls.playingFullscreen else {
return // swiftlint:disable:this implicit_return
}
if Defaults[.lockPortraitWhenBrowsing] {
Orientation.lockOrientation(.portrait, andRotateTo: .portrait)
} else {
Orientation.lockOrientation(.allButUpsideDown)
}
motionManager?.stopAccelerometerUpdates()
motionManager = nil
}
#endif #endif
} }
.offset(y: playerOffset)
.animation(.easeIn(duration: 0.2), value: playerOffset)
#endif #endif
} }
@ -138,6 +126,59 @@ struct VideoPlayerView: View {
hoveringPlayer = hovering hoveringPlayer = hovering
hovering ? playerControls.show() : playerControls.hide() hovering ? playerControls.show() : playerControls.hide()
} }
#if !os(tvOS)
.onChange(of: player.presentingPlayer) { newValue in
if newValue {
playerOffset = 0
#if os(iOS)
configureOrientationUpdatesBasedOnAccelerometer()
#endif
} else {
#if !os(macOS)
playerOffset = UIScreen.main.bounds.height
#if os(iOS)
if Defaults[.lockPortraitWhenBrowsing] {
Orientation.lockOrientation(.portrait, andRotateTo: .portrait)
} else {
Orientation.lockOrientation(.allButUpsideDown)
}
motionManager?.stopAccelerometerUpdates()
motionManager = nil
#endif
#endif
}
}
.gesture(
DragGesture(minimumDistance: 0)
.onChanged { value in
guard !fullScreenLayout else {
return
}
player.backend.setNeedsDrawing(false)
let drag = value.translation.height
guard drag > 0 else {
return
}
withAnimation(.easeIn(duration: 0.2)) {
playerOffset = drag
}
}
.onEnded { _ in
if playerOffset > 100 {
player.backend.setNeedsDrawing(true)
player.hide()
} else {
player.show()
playerOffset = 0
}
}
)
#endif
#if os(macOS) #if os(macOS)
.onAppear(perform: { .onAppear(perform: {
NSEvent.addLocalMonitorForEvents(matching: [.mouseMoved]) { NSEvent.addLocalMonitorForEvents(matching: [.mouseMoved]) {
@ -406,7 +447,8 @@ struct VideoPlayerView: View {
} else { } else {
guard abs(acceleration.z) <= 0.74, guard abs(acceleration.z) <= 0.74,
player.lockedOrientation.isNil, player.lockedOrientation.isNil,
enterFullscreenInLandscape enterFullscreenInLandscape,
!lockLandscapeOnRotation
else { else {
return return
} }
@ -421,6 +463,7 @@ struct VideoPlayerView: View {
} }
private func handleOrientationDidChangeNotification() { private func handleOrientationDidChangeNotification() {
playerOffset = playerOffset == 0 ? 0 : UIScreen.main.bounds.height
let newOrientation = UIApplication.shared.windows.first?.windowScene?.interfaceOrientation let newOrientation = UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
if newOrientation?.isLandscape ?? false, if newOrientation?.isLandscape ?? false,
player.presentingPlayer, player.presentingPlayer,

View File

@ -6,7 +6,6 @@ import SwiftUI
struct VideoCell: View { struct VideoCell: View {
private var video: Video private var video: Video
@Environment(\.inNavigationView) private var inNavigationView
@Environment(\.navigationStyle) private var navigationStyle @Environment(\.navigationStyle) private var navigationStyle
#if os(iOS) #if os(iOS)
@ -46,11 +45,8 @@ struct VideoCell: View {
.buttonStyle(.plain) .buttonStyle(.plain)
.contentShape(RoundedRectangle(cornerRadius: thumbnailRoundingCornerRadius)) .contentShape(RoundedRectangle(cornerRadius: thumbnailRoundingCornerRadius))
.contextMenu { .contextMenu {
VideoContextMenuView( VideoContextMenuView(video: video)
video: video, .environmentObject(accounts)
playerNavigationLinkActive: $player.playerNavigationLinkActive
)
.environmentObject(accounts)
} }
} }
@ -93,7 +89,7 @@ struct VideoCell: View {
player.avPlayerBackend.startPictureInPictureOnPlay = player.playingInPictureInPicture player.avPlayerBackend.startPictureInPictureOnPlay = player.playingInPictureInPicture
player.play(video, at: playAt, inNavigationView: inNavigationView) player.play(video, at: playAt)
} }
} }

View File

@ -10,7 +10,6 @@ struct ChannelPlaylistView: View {
@StateObject private var store = Store<ChannelPlaylist>() @StateObject private var store = Store<ChannelPlaylist>()
@Environment(\.colorScheme) private var colorScheme @Environment(\.colorScheme) private var colorScheme
@Environment(\.inNavigationView) private var inNavigationView
@EnvironmentObject<AccountsModel> private var accounts @EnvironmentObject<AccountsModel> private var accounts
@EnvironmentObject<PlayerModel> private var player @EnvironmentObject<PlayerModel> private var player
@ -24,19 +23,9 @@ struct ChannelPlaylistView: View {
} }
var body: some View { var body: some View {
#if os(iOS) BrowserPlayerControls {
if inNavigationView { content
content }
} else {
BrowserPlayerControls {
content
}
}
#else
BrowserPlayerControls {
content
}
#endif
} }
var content: some View { var content: some View {
@ -94,9 +83,6 @@ struct ChannelPlaylistView: View {
} }
} }
.navigationTitle(playlist.title) .navigationTitle(playlist.title)
#if os(iOS)
.navigationBarHidden(player.playerNavigationLinkActive)
#endif
#endif #endif
} }
@ -110,7 +96,7 @@ struct ChannelPlaylistView: View {
private var playButton: some View { private var playButton: some View {
Button { Button {
player.play(videos, inNavigationView: inNavigationView) player.play(videos)
} label: { } label: {
Label("Play All", systemImage: "play") Label("Play All", systemImage: "play")
} }
@ -118,7 +104,7 @@ struct ChannelPlaylistView: View {
private var shuffleButton: some View { private var shuffleButton: some View {
Button { Button {
player.play(videos, shuffling: true, inNavigationView: inNavigationView) player.play(videos, shuffling: true)
} label: { } label: {
Label("Shuffle", systemImage: "shuffle") Label("Shuffle", systemImage: "shuffle")
} }

View File

@ -13,7 +13,6 @@ struct ChannelVideosView: View {
@Environment(\.colorScheme) private var colorScheme @Environment(\.colorScheme) private var colorScheme
#if os(iOS) #if os(iOS)
@Environment(\.inNavigationView) private var inNavigationView
@Environment(\.horizontalSizeClass) private var horizontalSizeClass @Environment(\.horizontalSizeClass) private var horizontalSizeClass
@EnvironmentObject<PlayerModel> private var player @EnvironmentObject<PlayerModel> private var player
#endif #endif
@ -29,19 +28,9 @@ struct ChannelVideosView: View {
} }
var body: some View { var body: some View {
#if os(iOS) BrowserPlayerControls {
if inNavigationView { content
content }
} else {
BrowserPlayerControls {
content
}
}
#else
BrowserPlayerControls {
content
}
#endif
} }
var content: some View { var content: some View {
@ -115,9 +104,6 @@ struct ChannelVideosView: View {
resource.load() resource.load()
} }
} }
#if os(iOS)
.navigationBarHidden(player.playerNavigationLinkActive)
#endif
.navigationTitle(navigationTitle) .navigationTitle(navigationTitle)
return Group { return Group {

View File

@ -4,7 +4,6 @@ import SwiftUI
struct PlaylistVideosView: View { struct PlaylistVideosView: View {
let playlist: Playlist let playlist: Playlist
@Environment(\.inNavigationView) private var inNavigationView
@EnvironmentObject<PlayerModel> private var player @EnvironmentObject<PlayerModel> private var player
@EnvironmentObject<PlaylistsModel> private var model @EnvironmentObject<PlaylistsModel> private var model
@ -66,13 +65,13 @@ struct PlaylistVideosView: View {
FavoriteButton(item: FavoriteItem(section: .channelPlaylist(playlist.id, playlist.title))) FavoriteButton(item: FavoriteItem(section: .channelPlaylist(playlist.id, playlist.title)))
Button { Button {
player.play(videos, inNavigationView: inNavigationView) player.play(videos)
} label: { } label: {
Label("Play All", systemImage: "play") Label("Play All", systemImage: "play")
} }
Button { Button {
player.play(videos, shuffling: true, inNavigationView: inNavigationView) player.play(videos, shuffling: true)
} label: { } label: {
Label("Shuffle", systemImage: "shuffle") Label("Shuffle", systemImage: "shuffle")
} }

View File

@ -5,9 +5,6 @@ import SwiftUI
struct VideoContextMenuView: View { struct VideoContextMenuView: View {
let video: Video let video: Video
@Binding var playerNavigationLinkActive: Bool
@Environment(\.inNavigationView) private var inNavigationView
@Environment(\.inChannelView) private var inChannelView @Environment(\.inChannelView) private var inChannelView
@Environment(\.inChannelPlaylistView) private var inChannelPlaylistView @Environment(\.inChannelPlaylistView) private var inChannelPlaylistView
@Environment(\.navigationStyle) private var navigationStyle @Environment(\.navigationStyle) private var navigationStyle
@ -26,9 +23,8 @@ struct VideoContextMenuView: View {
private var viewContext: NSManagedObjectContext = PersistenceController.shared.container.viewContext private var viewContext: NSManagedObjectContext = PersistenceController.shared.container.viewContext
init(video: Video, playerNavigationLinkActive: Binding<Bool>) { init(video: Video) {
self.video = video self.video = video
_playerNavigationLinkActive = playerNavigationLinkActive
_watchRequest = video.watchFetchRequest _watchRequest = video.watchFetchRequest
} }
@ -111,7 +107,7 @@ struct VideoContextMenuView: View {
private var continueButton: some View { private var continueButton: some View {
Button { Button {
player.play(video, at: .secondsInDefaultTimescale(watch!.stoppedAt), inNavigationView: inNavigationView) player.play(video, at: .secondsInDefaultTimescale(watch!.stoppedAt))
} label: { } label: {
Label("Continue from \(watch!.stoppedAt.formattedAsPlaybackTime() ?? "where I left off")", systemImage: "playpause") Label("Continue from \(watch!.stoppedAt.formattedAsPlaybackTime() ?? "where I left off")", systemImage: "playpause")
} }
@ -131,7 +127,7 @@ struct VideoContextMenuView: View {
private var playNowButton: some View { private var playNowButton: some View {
Button { Button {
player.play(video, inNavigationView: inNavigationView) player.play(video)
} label: { } label: {
Label("Play Now", systemImage: "play") Label("Play Now", systemImage: "play")
} }