diff --git a/Shared/EnvironmentValues.swift b/Shared/EnvironmentValues.swift index 1577bfe7..7509ef07 100644 --- a/Shared/EnvironmentValues.swift +++ b/Shared/EnvironmentValues.swift @@ -34,6 +34,10 @@ private struct InQueueListingKey: EnvironmentKey { static let defaultValue = false } +private struct NoListingDividersKey: EnvironmentKey { + static let defaultValue = false +} + enum ListingStyle: String, CaseIterable, Defaults.Serializable { case cells case list @@ -113,4 +117,8 @@ extension EnvironmentValues { set { self[InQueueListingKey.self] = newValue } } + var noListingDividers: Bool { + get { self[NoListingDividersKey.self] } + set { self[NoListingDividersKey.self] = newValue } + } } diff --git a/Shared/Home/HomeView.swift b/Shared/Home/HomeView.swift index c804087a..fd3657ec 100644 --- a/Shared/Home/HomeView.swift +++ b/Shared/Home/HomeView.swift @@ -62,15 +62,19 @@ struct HomeView: View { } #endif } - #if os(iOS) - .padding(.top, RefreshControl.navigationBarTitleDisplayMode == .inline ? 15 : 0) - #else .padding(.top, 15) - #endif #if os(tvOS) - .padding(.horizontal, 40) + .padding(.horizontal, 40) #else - .padding(.horizontal, 15) + .padding(.horizontal, 15) + #endif + + QueueView() + .padding(.vertical, 15) + #if os(tvOS) + .padding(.horizontal, 40) + #else + .padding(.horizontal, 15) #endif if !accounts.current.isNil, showFavoritesInHome { diff --git a/Shared/Home/QueueView.swift b/Shared/Home/QueueView.swift new file mode 100644 index 00000000..1aae75f5 --- /dev/null +++ b/Shared/Home/QueueView.swift @@ -0,0 +1,69 @@ +import SwiftUI + +struct QueueView: View { + @State private var expanded = false + + @ObservedObject private var player = PlayerModel.shared + + var body: some View { + LazyVStack { + if !items.isEmpty { + HStack { + sectionLabel("Next in queue") + Button { + withAnimation { + expanded.toggle() + } + } label: { + Label("Show more", systemImage: expanded ? "chevron.up" : "chevron.down") + .animation(nil, value: expanded) + .foregroundColor(.accentColor) + .imageScale(.large) + .labelStyle(.iconOnly) + } + .opacity(items.count > 1 ? 1 : 0) + } + ForEach(limitedItems) { item in + ContentItemView(item: .init(video: item.video)) + .environment(\.listingStyle, .list) + .environment(\.inQueueListing, true) + .environment(\.noListingDividers, limit == 1) + .transition(.opacity) + } + } + } + } + + var limitedItems: [PlayerQueueItem] { + if let limit { + return Array(items.prefix(limit)) + } + + return items + } + + var items: [PlayerQueueItem] { + player.queue + } + + var limit: Int? { + if !expanded { + return 1 + } + + return nil + } + + func sectionLabel(_ label: String) -> some View { + Text(label.localized()) + .font(.title3.bold()) + .frame(maxWidth: .infinity, alignment: .leading) + .foregroundColor(.secondary) + } +} + +struct QueueView_Previews: PreviewProvider { + static var previews: some View { + QueueView() + } +} diff --git a/Shared/Videos/VideoBanner.swift b/Shared/Videos/VideoBanner.swift index db0fb887..a054eead 100644 --- a/Shared/Videos/VideoBanner.swift +++ b/Shared/Videos/VideoBanner.swift @@ -254,7 +254,7 @@ struct VideoBanner: View { PlayingIndicatorView(video: video, height: 10) .frame(width: 12, alignment: .trailing) .padding(.trailing, 3) - .padding(.bottom, timeLabel == nil ? 3 : 0) + .padding(.bottom, timeLabel == nil ? 3 : -5) if let timeLabel { Text(timeLabel) diff --git a/Shared/Views/ContentItemView.swift b/Shared/Views/ContentItemView.swift index f2932e64..400fada1 100644 --- a/Shared/Views/ContentItemView.swift +++ b/Shared/Views/ContentItemView.swift @@ -4,6 +4,7 @@ import SwiftUI struct ContentItemView: View { let item: ContentItem @Environment(\.listingStyle) private var listingStyle + @Environment(\.noListingDividers) private var noListingDividers var body: some View { Group { @@ -25,16 +26,19 @@ struct ContentItemView: View { if listingStyle == .cells { VideoCell(video: video) } else { - PlayerQueueRow(item: .init(video)) + let item = PlayerQueueItem(video) + PlayerQueueRow(item: item) .contextMenu { VideoContextMenuView(video: video) } + .id(item.id) #if os(tvOS) .padding(.horizontal, 30) #endif #if !os(tvOS) Divider() + .opacity(noListingDividers ? 0 : 1) #endif } } diff --git a/Yattee.xcodeproj/project.pbxproj b/Yattee.xcodeproj/project.pbxproj index 1588b5c0..fd02d237 100644 --- a/Yattee.xcodeproj/project.pbxproj +++ b/Yattee.xcodeproj/project.pbxproj @@ -819,6 +819,9 @@ 37C89322294532220032AFD3 /* PlayerOverlayModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C89321294532220032AFD3 /* PlayerOverlayModifier.swift */; }; 37C89323294532220032AFD3 /* PlayerOverlayModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C89321294532220032AFD3 /* PlayerOverlayModifier.swift */; }; 37C89324294532220032AFD3 /* PlayerOverlayModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C89321294532220032AFD3 /* PlayerOverlayModifier.swift */; }; + 37C8E701294FC97D00EEAB14 /* QueueView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C8E700294FC97D00EEAB14 /* QueueView.swift */; }; + 37C8E702294FC97D00EEAB14 /* QueueView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C8E700294FC97D00EEAB14 /* QueueView.swift */; }; + 37C8E703294FC97D00EEAB14 /* QueueView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C8E700294FC97D00EEAB14 /* QueueView.swift */; }; 37CC3F45270CE30600608308 /* PlayerQueueItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CC3F44270CE30600608308 /* PlayerQueueItem.swift */; }; 37CC3F46270CE30600608308 /* PlayerQueueItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CC3F44270CE30600608308 /* PlayerQueueItem.swift */; }; 37CC3F47270CE30600608308 /* PlayerQueueItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CC3F44270CE30600608308 /* PlayerQueueItem.swift */; }; @@ -1409,6 +1412,7 @@ 37C3A250272366440087A57A /* ChannelPlaylistView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelPlaylistView.swift; sourceTree = ""; }; 37C7A1D4267BFD9D0010EAD6 /* SponsorBlockSegment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SponsorBlockSegment.swift; sourceTree = ""; }; 37C89321294532220032AFD3 /* PlayerOverlayModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerOverlayModifier.swift; sourceTree = ""; }; + 37C8E700294FC97D00EEAB14 /* QueueView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueueView.swift; sourceTree = ""; }; 37CC3F44270CE30600608308 /* PlayerQueueItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerQueueItem.swift; sourceTree = ""; }; 37CC3F4B270CFE1700608308 /* PlayerQueueView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerQueueView.swift; sourceTree = ""; }; 37CC3F4F270D010D00608308 /* VideoBanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoBanner.swift; sourceTree = ""; }; @@ -2138,6 +2142,7 @@ children = ( 3788AC2626F6840700F6BAA9 /* FavoriteItemView.swift */, 37B263192735EAAB00FE0D40 /* FavoriteResourceObserver.swift */, + 37C8E700294FC97D00EEAB14 /* QueueView.swift */, 37A9965D26D6F9B9006E3224 /* HomeView.swift */, 377FF88E291A99580028EB0B /* HistoryView.swift */, ); @@ -3028,6 +3033,7 @@ files = ( 37220564294BEB2800E0D176 /* WatchNextViewModel.swift in Sources */, 37E6D79C2944AE1A00550C3D /* FeedModel.swift in Sources */, + 37C8E701294FC97D00EEAB14 /* QueueView.swift in Sources */, 374710052755291C00CE0F87 /* SearchTextField.swift in Sources */, 37494EA529200B14000DF176 /* DocumentsView.swift in Sources */, 374DE88028BB896C0062BBF2 /* PlayerDragGesture.swift in Sources */, @@ -3458,6 +3464,7 @@ 372D85DE283841B800FF3C7D /* PiPDelegate.swift in Sources */, 37F13B63285E43C000B137E4 /* ControlsOverlay.swift in Sources */, 37B81AFD26D2C9C900675966 /* VideoDetailsPaddingModifier.swift in Sources */, + 37C8E702294FC97D00EEAB14 /* QueueView.swift in Sources */, 37C0697F2725C8D400F7F6CB /* CMTime+DefaultTimescale.swift in Sources */, 37A9965B26D6F8CA006E3224 /* HorizontalCells.swift in Sources */, 37E6D79D2944AE1A00550C3D /* FeedModel.swift in Sources */, @@ -3823,6 +3830,7 @@ 37D4B19926717E1500C925CA /* Video.swift in Sources */, 378E50FD26FE8B9F00F49626 /* Instance.swift in Sources */, 37169AA82729E2CC0011DE61 /* AccountsBridge.swift in Sources */, + 37C8E703294FC97D00EEAB14 /* QueueView.swift in Sources */, 3754B01728B7F84D009717C8 /* Constants.swift in Sources */, 37BC50AE2778BCBA00510953 /* HistoryModel.swift in Sources */, 37D526E02720AC4400ED2F5E /* VideosAPI.swift in Sources */,