diff --git a/Backports/ScrollContentBackground+Backport.swift b/Backports/ScrollContentBackground+Backport.swift new file mode 100644 index 00000000..9a26c6fe --- /dev/null +++ b/Backports/ScrollContentBackground+Backport.swift @@ -0,0 +1,12 @@ +import Foundation +import SwiftUI + +extension Backport where Content: View { + @ViewBuilder func scrollContentBackground(_ visibility: Bool) -> some View { + if #available(iOS 16.0, macOS 13.0, tvOS 16.0, *) { + content.scrollContentBackground(visibility ? .visible : .hidden) + } else { + content + } + } +} diff --git a/Model/NavigationModel.swift b/Model/NavigationModel.swift index 1caf2e21..cfea408c 100644 --- a/Model/NavigationModel.swift +++ b/Model/NavigationModel.swift @@ -96,23 +96,19 @@ final class NavigationModel: ObservableObject { player.hide() navigation.presentingChannel = false - let recent = RecentItem(from: channel) #if os(macOS) Windows.main.open() - #else - player.hide() #endif - DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { - recents.add(recent) + let recent = RecentItem(from: channel) + recents.add(RecentItem(from: channel)) - if navigationStyle == .sidebar { - navigation.sidebarSectionChanged.toggle() - navigation.tabSelection = .recentlyOpened(recent.tag) - } else { - withAnimation { - navigation.presentingChannel = true - } + if navigationStyle == .sidebar { + navigation.sidebarSectionChanged.toggle() + navigation.tabSelection = .recentlyOpened(recent.tag) + } else { + withAnimation { + navigation.presentingChannel = true } } } diff --git a/Model/Player/PlayerControlsModel.swift b/Model/Player/PlayerControlsModel.swift index e6779e8b..f8d10753 100644 --- a/Model/Player/PlayerControlsModel.swift +++ b/Model/Player/PlayerControlsModel.swift @@ -8,6 +8,7 @@ final class PlayerControlsModel: ObservableObject { @Published var isPlaying = true @Published var presentingControls = false { didSet { handlePresentationChange() } } @Published var presentingControlsOverlay = false { didSet { handleOverlayPresentationChange() } } + @Published var presentingDetailsOverlay = false @Published var timer: Timer? #if os(tvOS) @@ -21,6 +22,7 @@ final class PlayerControlsModel: ObservableObject { isPlaying: Bool = true, presentingControls: Bool = false, presentingControlsOverlay: Bool = false, + presentingDetailsOverlay: Bool = false, timer: Timer? = nil, player: PlayerModel? = nil ) { @@ -28,20 +30,22 @@ final class PlayerControlsModel: ObservableObject { self.isPlaying = isPlaying self.presentingControls = presentingControls self.presentingControlsOverlay = presentingControlsOverlay + self.presentingDetailsOverlay = presentingDetailsOverlay self.timer = timer self.player = player } func handlePresentationChange() { - if presentingControls { - DispatchQueue.main.async { [weak self] in - self?.player?.backend.startControlsUpdates() - self?.resetTimer() + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } + if self.presentingControls { + self.player?.backend.startControlsUpdates() + self.resetTimer() + } else { + self.player?.backend.stopControlsUpdates() + self.timer?.invalidate() + self.timer = nil } - } else { - player?.backend.stopControlsUpdates() - timer?.invalidate() - timer = nil } } @@ -54,6 +58,15 @@ final class PlayerControlsModel: ObservableObject { } } + var presentingOverlays: Bool { + presentingDetailsOverlay || presentingControlsOverlay + } + + func hideOverlays() { + presentingDetailsOverlay = false + presentingControlsOverlay = false + } + func show() { guard !(player?.currentItem.isNil ?? true) else { return diff --git a/Model/Player/PlayerModel.swift b/Model/Player/PlayerModel.swift index a3477ebd..6f254f49 100644 --- a/Model/Player/PlayerModel.swift +++ b/Model/Player/PlayerModel.swift @@ -98,7 +98,13 @@ final class PlayerModel: ObservableObject { var context: NSManagedObjectContext = PersistenceController.shared.container.viewContext var backgroundContext = PersistenceController.shared.container.newBackgroundContext() - @Published var playingFullScreen = false + #if os(tvOS) + static let fullScreenIsDefault = true + #else + static let fullScreenIsDefault = false + #endif + @Published var playingFullScreen = PlayerModel.fullScreenIsDefault + @Published var playingInPictureInPicture = false var pipController: AVPictureInPictureController? var pipDelegate = PiPDelegate() diff --git a/Shared/Navigation/ContentView.swift b/Shared/Navigation/ContentView.swift index 1989b89a..62a2534a 100644 --- a/Shared/Navigation/ContentView.swift +++ b/Shared/Navigation/ContentView.swift @@ -142,7 +142,7 @@ struct ContentView: View { .environmentObject(recents) .environmentObject(subscriptions) .environmentObject(thumbnailsModel) - .environment(\.navigationStyle, .sidebar) + .environment(\.navigationStyle, navigationStyle) } } diff --git a/Shared/Player/Controls/PlayerControls.swift b/Shared/Player/Controls/PlayerControls.swift index 4f5ffdde..5d1da19b 100644 --- a/Shared/Player/Controls/PlayerControls.swift +++ b/Shared/Player/Controls/PlayerControls.swift @@ -29,7 +29,7 @@ struct PlayerControls: View { } var body: some View { - ZStack(alignment: .topTrailing) { + ZStack(alignment: .topLeading) { VStack { ZStack(alignment: .center) { OpeningStream() @@ -39,19 +39,20 @@ struct PlayerControls: View { VStack(spacing: 4) { buttonsBar - if let video = player.currentVideo, player.playingFullScreen { - VStack(alignment: .leading, spacing: 2) { - Text(video.title) - .font(.caption.bold()) - - Text(video.author) - .font(.caption) - .foregroundColor(.secondary) + HStack { + if !player.currentVideo.isNil, player.playingFullScreen { + Button { + withAnimation(Self.animation) { + model.presentingDetailsOverlay = true + } + } label: { + ControlsBar(fullScreen: $model.presentingDetailsOverlay, presentingControls: false, detailsTogglePlayer: false, detailsToggleFullScreen: false) + .clipShape(RoundedRectangle(cornerRadius: 4)) + .frame(maxWidth: 300, alignment: .leading) + } + .buttonStyle(.plain) } - .padding(4) - .modifier(ControlBackgroundModifier()) - .clipShape(RoundedRectangle(cornerRadius: 2)) - .frame(maxWidth: .infinity, alignment: .leading) + Spacer() } Spacer() @@ -69,9 +70,6 @@ struct PlayerControls: View { .offset(y: -25) .zIndex(1) } - #if os(tvOS) - .offset(y: -50) - #endif .frame(maxWidth: 500) .padding(.bottom, 2) } @@ -79,7 +77,7 @@ struct PlayerControls: View { .padding(.top, 2) .padding(.horizontal, 2) } - .opacity(model.presentingControlsOverlay ? 1 : model.presentingControls ? 1 : 0) + .opacity(model.presentingOverlays ? 0 : model.presentingControls ? 1 : 0) } } #if os(tvOS) @@ -99,11 +97,16 @@ struct PlayerControls: View { ControlsOverlay() .frame(height: overlayHeight) .padding() - .modifier(ControlBackgroundModifier(enabled: true)) + .modifier(ControlBackgroundModifier()) .clipShape(RoundedRectangle(cornerRadius: 4)) - .offset(x: -2, y: 40) .opacity(model.presentingControlsOverlay ? 1 : 0) + VideoDetailsOverlay() + .frame(maxWidth: detailsWidth, maxHeight: 450) + .modifier(ControlBackgroundModifier()) + .clipShape(RoundedRectangle(cornerRadius: 4)) + .opacity(model.presentingDetailsOverlay ? 1 : 0) + Button { player.restoreLastSkippedSegment() } label: { @@ -124,13 +127,18 @@ struct PlayerControls: View { .offset(x: -2, y: -2) } .buttonStyle(.plain) - .opacity(model.presentingControls ? 0 : player.lastSkipped.isNil ? 0 : 1) + .opacity(model.presentingControls || model.presentingOverlays ? 0 : player.lastSkipped.isNil ? 0 : 1) } } var overlayHeight: Double { guard let player = player, player.playerSize.height.isFinite else { return 0 } - return [0, [player.playerSize.height - 80, 140].min()!].max()! + return [0, [player.playerSize.height - 40, 140].min()!].max()! + } + + var detailsWidth: Double { + guard let player = player, player.playerSize.width.isFinite else { return 200 } + return [player.playerSize.width, 600].min()! } @ViewBuilder var controlsBackground: some View { diff --git a/Shared/Player/Controls/VideoDetailsOverlay.swift b/Shared/Player/Controls/VideoDetailsOverlay.swift new file mode 100644 index 00000000..8644fc1e --- /dev/null +++ b/Shared/Player/Controls/VideoDetailsOverlay.swift @@ -0,0 +1,25 @@ +import Defaults +import SwiftUI + +struct VideoDetailsOverlay: View { + @EnvironmentObject private var controls + + var body: some View { + VideoDetails(sidebarQueue: false, fullScreen: fullScreenBinding) + } + + var fullScreenBinding: Binding { + .init(get: { + controls.presentingDetailsOverlay + }, set: { newValue in + controls.presentingDetailsOverlay = newValue + }) + } +} + +struct VideoDetailsOverlay_Previews: PreviewProvider { + static var previews: some View { + VideoDetailsOverlay() + .injectFixtureEnvironmentObjects() + } +} diff --git a/Shared/Player/PlayerGestures.swift b/Shared/Player/PlayerGestures.swift index 55e6477a..08afaa24 100644 --- a/Shared/Player/PlayerGestures.swift +++ b/Shared/Player/PlayerGestures.swift @@ -9,17 +9,7 @@ struct PlayerGestures: View { gestureRectangle .tapRecognizer( tapSensitivity: 0.2, - singleTapAction: { - if model.presentingControlsOverlay { - model.presentingControls = true - model.resetTimer() - withAnimation(PlayerControls.animation) { - model.presentingControlsOverlay = false - } - } else { - model.toggle() - } - }, + singleTapAction: { singleTapAction() }, doubleTapAction: { player.backend.seek(relative: .secondsInDefaultTimescale(-10)) }, @@ -31,17 +21,7 @@ struct PlayerGestures: View { gestureRectangle .tapRecognizer( tapSensitivity: 0.2, - singleTapAction: { - if model.presentingControlsOverlay { - model.presentingControls = true - model.resetTimer() - withAnimation(PlayerControls.animation) { - model.presentingControlsOverlay = false - } - } else { - model.toggle() - } - }, + singleTapAction: { singleTapAction() }, doubleTapAction: { player.backend.togglePlay() }, @@ -53,17 +33,7 @@ struct PlayerGestures: View { gestureRectangle .tapRecognizer( tapSensitivity: 0.2, - singleTapAction: { - if model.presentingControlsOverlay { - model.presentingControls = true - model.resetTimer() - withAnimation(PlayerControls.animation) { - model.presentingControlsOverlay = false - } - } else { - model.toggle() - } - }, + singleTapAction: { singleTapAction() }, doubleTapAction: { player.backend.seek(relative: .secondsInDefaultTimescale(10)) }, @@ -74,6 +44,16 @@ struct PlayerGestures: View { } } + func singleTapAction() { + if model.presentingOverlays { + withAnimation(PlayerControls.animation) { + model.hideOverlays() + } + } else { + model.toggle() + } + } + var gestureRectangle: some View { Color.clear .contentShape(Rectangle()) diff --git a/Shared/Player/PlayerQueueRow.swift b/Shared/Player/PlayerQueueRow.swift index a8cb1a6f..6153a0d4 100644 --- a/Shared/Player/PlayerQueueRow.swift +++ b/Shared/Player/PlayerQueueRow.swift @@ -26,34 +26,32 @@ struct PlayerQueueRow: View { } var body: some View { - Group { - Button { - player.prepareCurrentItemForHistory() + Button { + player.prepareCurrentItemForHistory() - player.avPlayerBackend.startPictureInPictureOnPlay = player.playingInPictureInPicture + player.avPlayerBackend.startPictureInPictureOnPlay = player.playingInPictureInPicture - player.videoBeingOpened = item.video + player.videoBeingOpened = item.video - if history { - player.playHistory(item, at: watchStoppedAt) - } else { - player.advanceToItem(item, at: watchStoppedAt) - } - - if fullScreen { - withAnimation { - fullScreen = false - } - } - - if closePiPOnNavigation, player.playingInPictureInPicture { - player.closePiP() - } - } label: { - VideoBanner(video: item.video, playbackTime: watchStoppedAt, videoDuration: watch?.videoDuration) + if history { + player.playHistory(item, at: watchStoppedAt) + } else { + player.advanceToItem(item, at: watchStoppedAt) } - .buttonStyle(.plain) + + if fullScreen { + withAnimation { + fullScreen = false + } + } + + if closePiPOnNavigation, player.playingInPictureInPicture { + player.closePiP() + } + } label: { + VideoBanner(video: item.video, playbackTime: watchStoppedAt, videoDuration: watch?.videoDuration) } + .buttonStyle(.plain) } private var watch: Watch? { diff --git a/Shared/Player/PlayerQueueView.swift b/Shared/Player/PlayerQueueView.swift index 05e7d284..419428a0 100644 --- a/Shared/Player/PlayerQueueView.swift +++ b/Shared/Player/PlayerQueueView.swift @@ -28,9 +28,10 @@ struct PlayerQueueView: View { playedPreviously } } + .listRowBackground(Color.clear) #if !os(iOS) - .padding(.vertical, 5) - .listRowInsets(EdgeInsets()) + .padding(.vertical, 5) + .listRowInsets(EdgeInsets()) #endif } @@ -38,6 +39,8 @@ struct PlayerQueueView: View { .listStyle(.inset) #elseif os(iOS) .listStyle(.grouped) + .backport + .scrollContentBackground(false) #else .listStyle(.plain) #endif diff --git a/Shared/Player/RelatedView.swift b/Shared/Player/RelatedView.swift index 36003640..f5651430 100644 --- a/Shared/Player/RelatedView.swift +++ b/Shared/Player/RelatedView.swift @@ -13,6 +13,7 @@ struct RelatedView: View { Section(header: Text("Related")) { ForEach(related) { video in PlayerQueueRow(item: PlayerQueueItem(video)) + .listRowBackground(Color.clear) .contextMenu { Section { Button { @@ -53,6 +54,8 @@ struct RelatedView: View { .listStyle(.inset) #elseif os(iOS) .listStyle(.grouped) + .backport + .scrollContentBackground(false) #else .listStyle(.plain) #endif diff --git a/Shared/Player/VideoDetails.swift b/Shared/Player/VideoDetails.swift index 10f1c0e4..746ded8b 100644 --- a/Shared/Player/VideoDetails.swift +++ b/Shared/Player/VideoDetails.swift @@ -108,7 +108,6 @@ struct VideoDetails: View { page.update(.moveToLast) } } - .edgesIgnoringSafeArea(.horizontal) .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topLeading) } @@ -178,18 +177,14 @@ struct VideoDetails: View { } case .chapters: ChaptersView() - .edgesIgnoringSafeArea(.horizontal) case .queue: PlayerQueueView(sidebarQueue: sidebarQueue, fullScreen: $fullScreen) - .edgesIgnoringSafeArea(.horizontal) case .related: RelatedView() - .edgesIgnoringSafeArea(.horizontal) case .comments: CommentsView(embedInScrollView: true) - .edgesIgnoringSafeArea(.horizontal) } } .contentShape(Rectangle()) @@ -209,16 +204,16 @@ struct VideoDetails: View { VStack(alignment: .leading, spacing: 10) { if !player.videoBeingOpened.isNil && (video.description.isNil || video.description!.isEmpty) { VStack(alignment: .leading, spacing: 0) { - ForEach(1 ... Int.random(in: 3 ... 5), id: \.self) { _ in - Text(String(repeating: Video.fixture.description!, count: Int.random(in: 1 ... 4))) - .redacted(reason: .placeholder) - } + Text(String(repeating: Video.fixture.description ?? "", count: Int.random(in: 1 ... 30))) + .redacted(reason: .placeholder) } } else if let description = video.description { Group { if #available(iOS 15.0, macOS 12.0, tvOS 15.0, *) { Text(description) + #if !os(tvOS) .textSelection(.enabled) + #endif } else { Text(description) } diff --git a/Shared/Player/VideoPlayerView.swift b/Shared/Player/VideoPlayerView.swift index 0e03babc..4cbd8723 100644 --- a/Shared/Player/VideoPlayerView.swift +++ b/Shared/Player/VideoPlayerView.swift @@ -119,6 +119,7 @@ struct VideoPlayerView: View { } viewVerticalOffset = Self.hiddenOffset stopOrientationUpdates() + player.controls.hideOverlays() } } #endif @@ -203,9 +204,9 @@ struct VideoPlayerView: View { hoveringPlayer = hovering hovering ? playerControls.show() : playerControls.hide() } - #if !os(macOS) - .gesture(playerDragGesture) - #else + #if os(iOS) + .gesture(isPlayerDragGestureEnabled ? playerDragGesture : nil) + #elseif os(macOS) .onAppear(perform: { NSEvent.addLocalMonitorForEvents(matching: [.mouseMoved]) { if !player.currentItem.isNil, hoveringPlayer { @@ -296,6 +297,9 @@ struct VideoPlayerView: View { .onChange(of: proxy.size) { _ in player.playerSize = proxy.size } + .onChange(of: player.controls.presentingOverlays) { _ in + player.playerSize = proxy.size + } }) #if os(iOS) .padding(.top, player.playingFullScreen && verticalSizeClass == .regular ? 20 : 0) @@ -351,6 +355,10 @@ struct VideoPlayerView: View { } } + var isPlayerDragGestureEnabled: Bool { + !player.controls.presentingDetailsOverlay && !player.controls.presentingDetailsOverlay + } + var controlsTopPadding: Double { guard fullScreenLayout else { return 0 } diff --git a/Shared/Views/ControlsBar.swift b/Shared/Views/ControlsBar.swift index 1799fb2a..5183181f 100644 --- a/Shared/Views/ControlsBar.swift +++ b/Shared/Views/ControlsBar.swift @@ -24,6 +24,7 @@ struct ControlsBar: View { var borderBottom = true var detailsTogglePlayer = true var detailsToggleFullScreen = false + var titleLineLimit = 2 var body: some View { HStack(spacing: 0) { @@ -69,7 +70,9 @@ struct ControlsBar: View { details .contentShape(Rectangle()) } + #if !os(tvOS) .keyboardShortcut("t") + #endif } else { details } @@ -139,7 +142,9 @@ struct ControlsBar: View { subscriptions.isSubscribing(video.channel.id) { Image(systemName: "star.circle.fill") + #if !os(tvOS) .background(Color.background) + #endif .clipShape(Circle()) .foregroundColor(.secondary) } @@ -166,7 +171,9 @@ struct ControlsBar: View { } } - ShareButton(contentItem: .init(video: model.currentVideo)) + #if !os(tvOS) + ShareButton(contentItem: .init(video: model.currentVideo)) + #endif Section { Button { @@ -215,12 +222,14 @@ struct ControlsBar: View { } } - VStack(alignment: .leading, spacing: 5) { + VStack(alignment: .leading, spacing: 0) { Text(model.currentVideo?.title ?? "Not playing") .font(.system(size: 14)) .fontWeight(.semibold) .foregroundColor(model.currentVideo.isNil ? .secondary : .accentColor) - .lineLimit(1) + .fixedSize(horizontal: false, vertical: true) + .lineLimit(titleLineLimit) + .multilineTextAlignment(.leading) if let video = model.currentVideo { HStack(spacing: 2) { diff --git a/Yattee.xcodeproj/project.pbxproj b/Yattee.xcodeproj/project.pbxproj index 9afb6144..28f4177a 100644 --- a/Yattee.xcodeproj/project.pbxproj +++ b/Yattee.xcodeproj/project.pbxproj @@ -714,6 +714,14 @@ 37E70927271CDDAE00D34DDE /* OpenSettingsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E70926271CDDAE00D34DDE /* OpenSettingsButton.swift */; }; 37E70928271CDDAE00D34DDE /* OpenSettingsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E70926271CDDAE00D34DDE /* OpenSettingsButton.swift */; }; 37E70929271CDDAE00D34DDE /* OpenSettingsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E70926271CDDAE00D34DDE /* OpenSettingsButton.swift */; }; + 37E80F3C287B107F00561799 /* VideoDetailsOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E80F3B287B107F00561799 /* VideoDetailsOverlay.swift */; }; + 37E80F3D287B107F00561799 /* VideoDetailsOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E80F3B287B107F00561799 /* VideoDetailsOverlay.swift */; }; + 37E80F40287B472300561799 /* ScrollContentBackground+Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E80F3F287B472300561799 /* ScrollContentBackground+Backport.swift */; }; + 37E80F43287B7AAF00561799 /* SwiftUIPager in Frameworks */ = {isa = PBXBuildFile; productRef = 37E80F42287B7AAF00561799 /* SwiftUIPager */; }; + 37E80F44287B7AB400561799 /* VideoDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B81AFE26D2CA3700675966 /* VideoDetails.swift */; }; + 37E80F45287B7AC000561799 /* ControlsBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372CFD14285F2E2A00B0B54B /* ControlsBar.swift */; }; + 37E80F46287B7AEC00561799 /* PlayerQueueView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CC3F4B270CFE1700608308 /* PlayerQueueView.swift */; }; + 37E80F47287B7B9400561799 /* VideoDetailsOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E80F3B287B107F00561799 /* VideoDetailsOverlay.swift */; }; 37E8B0EC27B326C00024006F /* TimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E8B0EB27B326C00024006F /* TimelineView.swift */; }; 37E8B0ED27B326C00024006F /* TimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E8B0EB27B326C00024006F /* TimelineView.swift */; }; 37E8B0EE27B326C00024006F /* TimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E8B0EB27B326C00024006F /* TimelineView.swift */; }; @@ -1179,6 +1187,8 @@ 37E64DD026D597EB00C71877 /* SubscriptionsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionsModel.swift; sourceTree = ""; }; 37E70922271CD43000D34DDE /* WelcomeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeScreen.swift; sourceTree = ""; }; 37E70926271CDDAE00D34DDE /* OpenSettingsButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenSettingsButton.swift; sourceTree = ""; }; + 37E80F3B287B107F00561799 /* VideoDetailsOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoDetailsOverlay.swift; sourceTree = ""; }; + 37E80F3F287B472300561799 /* ScrollContentBackground+Backport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ScrollContentBackground+Backport.swift"; sourceTree = ""; }; 37E8B0EB27B326C00024006F /* TimelineView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimelineView.swift; sourceTree = ""; }; 37E8B0EF27B326F30024006F /* Comparable+Clamped.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Comparable+Clamped.swift"; sourceTree = ""; }; 37EAD86A267B9C5600D9E01B /* SponsorBlockAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SponsorBlockAPI.swift; sourceTree = ""; }; @@ -1369,6 +1379,7 @@ 3772003A27E8EEBE00CB2475 /* CoreMedia.framework in Frameworks */, 372915E42687E33E00F5A35B /* Defaults in Frameworks */, 3772003B27E8EEC800CB2475 /* libbz2.tbd in Frameworks */, + 37E80F43287B7AAF00561799 /* SwiftUIPager in Frameworks */, 37BADCA9269A570B009BE4FB /* Alamofire in Frameworks */, 37D4B19D2671817900C925CA /* SwiftyJSON in Frameworks */, 3797757D268922D100DD52A8 /* Siesta in Frameworks */, @@ -1437,6 +1448,7 @@ 37F13B61285E43C000B137E4 /* ControlsOverlay.swift */, 37030FFE27B04DCC00ECDDAA /* PlayerControls.swift */, 37648B68286CF5F1003D330B /* TVControls.swift */, + 37E80F3B287B107F00561799 /* VideoDetailsOverlay.swift */, ); path = Controls; sourceTree = ""; @@ -1554,10 +1566,11 @@ children = ( 3722AEBD274DA401005EA4D6 /* Backport.swift */, 3722AEBB274DA396005EA4D6 /* Badge+Backport.swift */, - 3722AEBF274DAEB8005EA4D6 /* Tint+Backport.swift */, - 3727B74727872A500021C15E /* VisualEffectBlur-macOS.swift */, - 3727B74927872A920021C15E /* VisualEffectBlur-iOS.swift */, 37136CAB286273060095C0CF /* PersistentSystemOverlays+Backport.swift */, + 37E80F3F287B472300561799 /* ScrollContentBackground+Backport.swift */, + 3722AEBF274DAEB8005EA4D6 /* Tint+Backport.swift */, + 3727B74927872A920021C15E /* VisualEffectBlur-iOS.swift */, + 3727B74727872A500021C15E /* VisualEffectBlur-macOS.swift */, ); path = Backports; sourceTree = ""; @@ -2311,6 +2324,7 @@ 3765917D27237D2A009F956E /* PINCache */, 37CF8B8728535E6300B71E37 /* SDWebImage */, 372AA411286D06950000B1DC /* Repeat */, + 37E80F42287B7AAF00561799 /* SwiftUIPager */, ); productName = Yattee; productReference = 37D4B158267164AE00C925CA /* Yattee.app */; @@ -2674,6 +2688,7 @@ 378E50FF26FE8EEE00F49626 /* AccountsMenuView.swift in Sources */, 37169AA62729E2CC0011DE61 /* AccountsBridge.swift in Sources */, 37C7A1DA267CACF50010EAD6 /* TrendingCountry.swift in Sources */, + 37E80F40287B472300561799 /* ScrollContentBackground+Backport.swift in Sources */, 3727B74A27872A920021C15E /* VisualEffectBlur-iOS.swift in Sources */, 37977583268922F600DD52A8 /* InvidiousAPI.swift in Sources */, 37130A5F277657300033018A /* PersistenceController.swift in Sources */, @@ -2836,6 +2851,7 @@ 37BD07BB2698AB60003EBB87 /* AppSidebarNavigation.swift in Sources */, 37C0697A2725C09E00F7F6CB /* PlayerQueueItemBridge.swift in Sources */, 379B0253287A1CDF001015B5 /* OrientationTracker.swift in Sources */, + 37E80F3C287B107F00561799 /* VideoDetailsOverlay.swift in Sources */, 370B79C9286279810045DB77 /* NSObject+Swizzle.swift in Sources */, 37D4B0E42671614900C925CA /* YatteeApp.swift in Sources */, 37C3A241272359900087A57A /* Double+Format.swift in Sources */, @@ -2886,6 +2902,7 @@ 374C053C2724614F009BDDBE /* PlayerTVMenu.swift in Sources */, 377FC7DD267A081A00A6BBAF /* PopularView.swift in Sources */, 3784CDE327772EE40055BBF2 /* Watch.swift in Sources */, + 37E80F3D287B107F00561799 /* VideoDetailsOverlay.swift in Sources */, 37DD9DBB2785D60300539416 /* FramePreferenceKey.swift in Sources */, 375DFB5926F9DA010013F468 /* InstancesModel.swift in Sources */, 3705B183267B4E4900704544 /* TrendingCategory.swift in Sources */, @@ -3159,6 +3176,7 @@ 37732FF22703A26300F04329 /* AccountValidationStatus.swift in Sources */, 3756C2AC2861151C00E4B059 /* NetworkStateModel.swift in Sources */, 37A5DBCA285E371400CA4DD1 /* ControlBackgroundModifier.swift in Sources */, + 37E80F46287B7AEC00561799 /* PlayerQueueView.swift in Sources */, 37C0698427260B2100F7F6CB /* ThumbnailsModel.swift in Sources */, 37666BAA27023AF000F869E5 /* AccountSelectionView.swift in Sources */, 3765788B2685471400D4EA09 /* Playlist.swift in Sources */, @@ -3168,6 +3186,8 @@ 3752069B285E8DD300CA655F /* Chapter.swift in Sources */, 37B044B926F7AB9000E1419D /* SettingsView.swift in Sources */, 3743B86A27216D3600261544 /* ChannelCell.swift in Sources */, + 37E80F47287B7B9400561799 /* VideoDetailsOverlay.swift in Sources */, + 37E80F44287B7AB400561799 /* VideoDetails.swift in Sources */, 3751BA8527E6914F007B1A60 /* ReturnYouTubeDislikeAPI.swift in Sources */, 37030FFD27B0398000ECDDAA /* MPVClient.swift in Sources */, 37B767DD2677C3CA0098BAA8 /* PlayerModel.swift in Sources */, @@ -3180,6 +3200,7 @@ 371B7E682759786B00D21217 /* Comment+Fixtures.swift in Sources */, 37BE0BD126A0E2D50092E2DB /* VideoPlayerView.swift in Sources */, 37AAF27E26737323007FC770 /* PopularView.swift in Sources */, + 37E80F45287B7AC000561799 /* ControlsBar.swift in Sources */, 3743CA50270EFE3400E4D32B /* PlayerQueueRow.swift in Sources */, 376BE50827347B57009AD608 /* SettingsHeader.swift in Sources */, 37A9966026D6F9B9006E3224 /* FavoritesView.swift in Sources */, @@ -3900,7 +3921,7 @@ 37D4B178267164B000C925CA /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 71; @@ -3909,7 +3930,6 @@ ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = tvOS/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = Yattee; INFOPLIST_KEY_CFBundleName = "Yattee (Apple TV)"; INFOPLIST_KEY_CFBundleVersion = 1; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -3938,7 +3958,7 @@ 37D4B179267164B000C925CA /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 71; @@ -3947,7 +3967,6 @@ ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = tvOS/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = Yattee; INFOPLIST_KEY_CFBundleName = "Yattee (Apple TV)"; INFOPLIST_KEY_CFBundleVersion = 1; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -4470,6 +4489,11 @@ package = 37D4B19B2671817900C925CA /* XCRemoteSwiftPackageReference "SwiftyJSON" */; productName = SwiftyJSON; }; + 37E80F42287B7AAF00561799 /* SwiftUIPager */ = { + isa = XCSwiftPackageProductDependency; + package = 37A5DBC2285DFF5400CA4DD1 /* XCRemoteSwiftPackageReference "SwiftUIPager" */; + productName = SwiftUIPager; + }; 37FB28452722054C00A57617 /* SDWebImageSwiftUI */ = { isa = XCSwiftPackageProductDependency; package = 37FB28442722054B00A57617 /* XCRemoteSwiftPackageReference "SDWebImageSwiftUI" */;