diff --git a/Pearvidious.xcodeproj/project.pbxproj b/Pearvidious.xcodeproj/project.pbxproj index 80769175..6704f14f 100644 --- a/Pearvidious.xcodeproj/project.pbxproj +++ b/Pearvidious.xcodeproj/project.pbxproj @@ -82,6 +82,7 @@ 372915E72687E3B900F5A35B /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372915E52687E3B900F5A35B /* Defaults.swift */; }; 372915E82687E3B900F5A35B /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372915E52687E3B900F5A35B /* Defaults.swift */; }; 3730D8A02712E2B70020ED53 /* NowPlayingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3730D89F2712E2B70020ED53 /* NowPlayingView.swift */; }; + 3730F75A2733481E00F385FC /* RelatedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373197D82732015300EF734F /* RelatedView.swift */; }; 373197D92732015300EF734F /* RelatedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373197D82732015300EF734F /* RelatedView.swift */; }; 373197DA2732060100EF734F /* RelatedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373197D82732015300EF734F /* RelatedView.swift */; }; 37319F0527103F94004ECCD0 /* PlayerQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37319F0427103F94004ECCD0 /* PlayerQueue.swift */; }; @@ -1876,6 +1877,7 @@ 3700155C271B0D4D0049C794 /* PipedAPI.swift in Sources */, 37D4B19826717E1500C925CA /* Video.swift in Sources */, 37599F31272B42810087F250 /* FavoriteItem.swift in Sources */, + 3730F75A2733481E00F385FC /* RelatedView.swift in Sources */, 375168D72700FDB8008F96A6 /* Debounce.swift in Sources */, 37D526DF2720AC4400ED2F5E /* VideosAPI.swift in Sources */, 37D4B0E52671614900C925CA /* PearvidiousApp.swift in Sources */, diff --git a/Shared/Defaults.swift b/Shared/Defaults.swift index a98ef65b..326c3630 100644 --- a/Shared/Defaults.swift +++ b/Shared/Defaults.swift @@ -1,7 +1,19 @@ import Defaults import Foundation -extension Defaults.Keys { +enum PlayerSidebarSetting: String, CaseIterable, Defaults.Serializable { + case always, whenFits, never + + static var defaultValue: Self { + #if os(macOS) + .always + #else + .whenFits + #endif + } +} + +extension Defaults.Keys { static let invidiousInstanceID = "default-invidious-instance" static let pipedInstanceID = "default-piped-instance" static let privateAccountID = "default-private-invidious-account" @@ -40,6 +52,7 @@ extension Defaults.Keys { ]) static let quality = Key("quality", default: .hd720pFirstThenBest) + static let playerSidebar = Key("playerSidebar", default: PlayerSidebarSetting.defaultValue) static let recentlyOpened = Key<[RecentItem]>("recentlyOpened", default: []) diff --git a/Shared/Player/PlayerQueueView.swift b/Shared/Player/PlayerQueueView.swift index 57bc4129..e8eb99ae 100644 --- a/Shared/Player/PlayerQueueView.swift +++ b/Shared/Player/PlayerQueueView.swift @@ -2,6 +2,7 @@ import Foundation import SwiftUI struct PlayerQueueView: View { + @Binding var sidebarQueue: Bool @Binding var fullScreen: Bool @EnvironmentObject private var player @@ -10,7 +11,9 @@ struct PlayerQueueView: View { List { Group { playingNext - related + if sidebarQueue { + related + } playedPreviously } #if !os(iOS) @@ -119,7 +122,7 @@ struct PlayerQueueView: View { struct PlayerQueueView_Previews: PreviewProvider { static var previews: some View { VStack { - PlayerQueueView(fullScreen: .constant(true)) + PlayerQueueView(sidebarQueue: .constant(true), fullScreen: .constant(true)) } .injectFixtureEnvironmentObjects() } diff --git a/Shared/Player/VideoDetails.swift b/Shared/Player/VideoDetails.swift index 996cdc19..40d1b926 100644 --- a/Shared/Player/VideoDetails.swift +++ b/Shared/Player/VideoDetails.swift @@ -58,7 +58,7 @@ struct VideoDetails: View { } .padding(.horizontal) - if !video.isNil, !sidebarQueue { + if !sidebarQueue { pagePicker .padding(.horizontal) } @@ -87,25 +87,19 @@ struct VideoDetails: View { detailsPage } case .queue: - PlayerQueueView(fullScreen: $fullScreen) + PlayerQueueView(sidebarQueue: $sidebarQueue, fullScreen: $fullScreen) .edgesIgnoringSafeArea(.horizontal) case .related: - #if os(macOS) - EmptyView() - #else - RelatedView() - .edgesIgnoringSafeArea(.horizontal) - #endif + RelatedView() + .edgesIgnoringSafeArea(.horizontal) } } .padding(.top, inNavigationView && fullScreen ? 10 : 0) .onAppear { - #if !os(macOS) - if video.isNil { - currentPage = .queue - } - #endif + if video.isNil { + currentPage = .queue + } guard video != nil, accounts.app.supportsSubscriptions else { subscribed = false @@ -115,15 +109,13 @@ struct VideoDetails: View { subscribed = subscriptions.isSubscribing(video!.channel.id) } .onChange(of: sidebarQueue) { queue in - #if !os(macOS) - if queue { - if currentPage == .queue { - currentPage = .details - } - } else { - currentPage = .queue + if queue { + if currentPage == .queue { + currentPage = .details } - #endif + } else if video.isNil { + currentPage = .queue + } } .edgesIgnoringSafeArea(.horizontal) .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading) @@ -134,9 +126,7 @@ struct VideoDetails: View { if video != nil { Text(video!.title) .onAppear { - #if !os(macOS) - currentPage = .details - #endif + currentPage = .details } .font(.title2.bold()) @@ -225,11 +215,13 @@ struct VideoDetails: View { var pagePicker: some View { Picker("Page", selection: $currentPage) { - Text("Details").tag(Page.details) - Text("Related").tag(Page.related) + if !video.isNil { + Text("Details").tag(Page.details) + Text("Related").tag(Page.related) + } Text("Queue").tag(Page.queue) } - + .labelsHidden() .pickerStyle(.segmented) .onDisappear { currentPage = .details diff --git a/Shared/Player/VideoPlayerView.swift b/Shared/Player/VideoPlayerView.swift index c68ecec1..cb28c075 100644 --- a/Shared/Player/VideoPlayerView.swift +++ b/Shared/Player/VideoPlayerView.swift @@ -13,34 +13,37 @@ struct VideoPlayerView: View { #endif } + @State private var playerSize: CGSize = .zero @State private var fullScreen = false #if os(iOS) @Environment(\.dismiss) private var dismiss @Environment(\.horizontalSizeClass) private var horizontalSizeClass @Environment(\.verticalSizeClass) private var verticalSizeClass - - private var idiom: UIUserInterfaceIdiom { - UIDevice.current.userInterfaceIdiom - } #endif @EnvironmentObject private var player var body: some View { - #if os(macOS) - HSplitView { - content - } - .frame(idealWidth: 1000, maxWidth: 1100, minHeight: 700) - #else - HStack(spacing: 0) { - content - } - #if os(iOS) + GeometryReader { geometry in + #if os(macOS) + HSplitView { + content + } + .frame(idealWidth: 1000, maxWidth: 1100, minHeight: 700) + #else + HStack(spacing: 0) { + content + } + .onAppear { + self.playerSize = geometry.size + } + .onChange(of: geometry.size) { size in + self.playerSize = size + } .navigationBarHidden(true) #endif - #endif + } } var content: some View { @@ -92,7 +95,7 @@ struct VideoPlayerView: View { } #else - VideoDetails(fullScreen: $fullScreen) + VideoDetails(sidebarQueue: sidebarQueueBinding, fullScreen: $fullScreen) #endif } .background() @@ -105,12 +108,14 @@ struct VideoPlayerView: View { #endif #if os(iOS) if sidebarQueue { - PlayerQueueView(fullScreen: $fullScreen) + PlayerQueueView(sidebarQueue: .constant(true), fullScreen: $fullScreen) .frame(maxWidth: 350) } #elseif os(macOS) - PlayerQueueView(fullScreen: $fullScreen) - .frame(minWidth: 250) + if Defaults[.playerSidebar] != .never { + PlayerQueueView(sidebarQueue: sidebarQueueBinding, fullScreen: $fullScreen) + .frame(minWidth: 250) + } #endif } .onDisappear { @@ -140,18 +145,23 @@ struct VideoPlayerView: View { .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: geometry.size.width / VideoPlayerView.defaultAspectRatio) } - #if os(iOS) - var sidebarQueue: Bool { - horizontalSizeClass == .regular && idiom == .pad + var sidebarQueue: Bool { + switch Defaults[.playerSidebar] { + case .never: + return false + case .always: + return true + case .whenFits: + return playerSize.width > 900 } + } - var sidebarQueueBinding: Binding { - Binding( - get: { self.sidebarQueue }, - set: { _ in } - ) - } - #endif + var sidebarQueueBinding: Binding { + Binding( + get: { sidebarQueue }, + set: { _ in } + ) + } } struct VideoPlayerView_Previews: PreviewProvider { diff --git a/Shared/Settings/PlaybackSettings.swift b/Shared/Settings/PlaybackSettings.swift index d1b15da6..5777dc3f 100644 --- a/Shared/Settings/PlaybackSettings.swift +++ b/Shared/Settings/PlaybackSettings.swift @@ -3,6 +3,13 @@ import SwiftUI struct PlaybackSettings: View { @Default(.quality) private var quality + @Default(.playerSidebar) private var playerSidebar + + #if os(iOS) + private var idiom: UIUserInterfaceIdiom { + UIDevice.current.userInterfaceIdiom + } + #endif var body: some View { Section(header: Text("Quality")) { @@ -18,9 +25,40 @@ struct PlaybackSettings: View { #elseif os(tvOS) .pickerStyle(.inline) #endif + } - #if os(macOS) - Spacer() + #if os(iOS) + if idiom == .pad { + playerSidebarSection + } + #elseif os(macOS) + playerSidebarSection + #endif + + #if os(macOS) + Spacer() + #endif + } + + private var playerSidebarSection: some View { + Section(header: Text("Player Sidebar")) { + Picker("Player Sidebar", selection: $playerSidebar) { + #if os(macOS) + Text("Show").tag(PlayerSidebarSetting.always) + #endif + + #if os(iOS) + Text("Show when space permits").tag(PlayerSidebarSetting.whenFits) + #endif + + Text("Hide").tag(PlayerSidebarSetting.never) + } + .labelsHidden() + + #if os(iOS) + .pickerStyle(.automatic) + #elseif os(tvOS) + .pickerStyle(.inline) #endif } } diff --git a/Shared/Settings/SettingsView.swift b/Shared/Settings/SettingsView.swift index a36459c2..1da71af1 100644 --- a/Shared/Settings/SettingsView.swift +++ b/Shared/Settings/SettingsView.swift @@ -5,7 +5,7 @@ import SwiftUI struct SettingsView: View { #if os(macOS) private enum Tabs: Hashable { - case playback, services, instances + case instances, playback, services } #endif @@ -24,14 +24,6 @@ struct SettingsView: View { } .tag(Tabs.instances) - Form { - ServicesSettings() - } - .tabItem { - Label("Services", systemImage: "puzzlepiece.extension") - } - .tag(Tabs.services) - Form { PlaybackSettings() } @@ -39,6 +31,14 @@ struct SettingsView: View { Label("Playback", systemImage: "play.rectangle.on.rectangle.fill") } .tag(Tabs.playback) + + Form { + ServicesSettings() + } + .tabItem { + Label("Services", systemImage: "puzzlepiece.extension") + } + .tag(Tabs.services) } .padding(20) .frame(width: 400, height: 310) @@ -49,8 +49,8 @@ struct SettingsView: View { AccountSelectionView() #endif InstancesSettings() - ServicesSettings() PlaybackSettings() + ServicesSettings() } .navigationTitle("Settings") .toolbar {