diff --git a/Model/NavigationModel.swift b/Model/NavigationModel.swift index f33bdd53..29ef2add 100644 --- a/Model/NavigationModel.swift +++ b/Model/NavigationModel.swift @@ -78,6 +78,8 @@ final class NavigationModel: ObservableObject { return } + navigation.presentingChannel = false + let recent = RecentItem(from: channel) #if os(macOS) Windows.main.open() diff --git a/Model/Player/PlayerModel.swift b/Model/Player/PlayerModel.swift index a8f433d1..89fa9753 100644 --- a/Model/Player/PlayerModel.swift +++ b/Model/Player/PlayerModel.swift @@ -549,4 +549,8 @@ final class PlayerModel: ObservableObject { } #endif } + + func setNeedsDrawing(_ needsDrawing: Bool) { + backends.forEach { $0.setNeedsDrawing(needsDrawing) } + } } diff --git a/Model/RecentsModel.swift b/Model/RecentsModel.swift index 2bf48b73..b54915fa 100644 --- a/Model/RecentsModel.swift +++ b/Model/RecentsModel.swift @@ -4,6 +4,7 @@ import Foundation final class RecentsModel: ObservableObject { @Default(.recentlyOpened) var items @Default(.saveRecents) var saveRecents + func clear() { items = [] } diff --git a/Shared/Navigation/AppTabNavigation.swift b/Shared/Navigation/AppTabNavigation.swift index f14a7649..957f196d 100644 --- a/Shared/Navigation/AppTabNavigation.swift +++ b/Shared/Navigation/AppTabNavigation.swift @@ -44,22 +44,8 @@ struct AppTabNavigation: View { } .id(accounts.current?.id ?? "") .environment(\.navigationStyle, .tab) - .background( - EmptyView().sheet(isPresented: $navigation.presentingChannel) { - if let channel = recents.presentedChannel { - NavigationView { - ChannelVideosView(channel: channel) - .environment(\.managedObjectContext, persistenceController.container.viewContext) - .environment(\.inChannelView, true) - .environmentObject(accounts) - .environmentObject(navigation) - .environmentObject(player) - .environmentObject(subscriptions) - .environmentObject(thumbnailsModel) - } - } - } - ) + .overlay(channelView) + .background( EmptyView().sheet(isPresented: $navigation.presentingPlaylist) { if let playlist = recents.presentedPlaylist { @@ -188,4 +174,16 @@ struct AppTabNavigation: View { } #endif } + + private var channelView: some View { + ChannelVideosView() + .environment(\.managedObjectContext, persistenceController.container.viewContext) + .environment(\.inChannelView, true) + .environment(\.navigationStyle, .tab) + .environmentObject(accounts) + .environmentObject(navigation) + .environmentObject(player) + .environmentObject(subscriptions) + .environmentObject(thumbnailsModel) + } } diff --git a/Shared/Navigation/ContentView.swift b/Shared/Navigation/ContentView.swift index 035b6fce..30616063 100644 --- a/Shared/Navigation/ContentView.swift +++ b/Shared/Navigation/ContentView.swift @@ -26,6 +26,8 @@ struct ContentView: View { @Environment(\.horizontalSizeClass) private var horizontalSizeClass #endif + let persistenceController = PersistenceController.shared + var body: some View { Group { #if os(iOS) diff --git a/Shared/Player/AppleAVPlayerViewController.swift b/Shared/Player/AppleAVPlayerViewController.swift index 890fa388..880af902 100644 --- a/Shared/Player/AppleAVPlayerViewController.swift +++ b/Shared/Player/AppleAVPlayerViewController.swift @@ -179,6 +179,7 @@ extension AppleAVPlayerViewController: AVPlayerViewControllerDelegate { ) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { self.playerModel.show() + self.playerModel.setNeedsDrawing(true) #if os(tvOS) if self.playerModel.playingInPictureInPicture { diff --git a/Shared/Videos/VideoCell.swift b/Shared/Videos/VideoCell.swift index 7f710e09..8b842d21 100644 --- a/Shared/Videos/VideoCell.swift +++ b/Shared/Videos/VideoCell.swift @@ -7,6 +7,7 @@ struct VideoCell: View { private var video: Video @Environment(\.navigationStyle) private var navigationStyle + @Environment(\.inChannelView) private var inChannelView #if os(iOS) @Environment(\.verticalSizeClass) private var verticalSizeClass @@ -294,6 +295,10 @@ struct VideoCell: View { private func channelButton(badge: Bool = true) -> some View { Button { + guard !inChannelView else { + return + } + NavigationModel.openChannel( video.channel, player: player, diff --git a/Shared/Views/ChannelVideosView.swift b/Shared/Views/ChannelVideosView.swift index 87dcbe0e..d666323b 100644 --- a/Shared/Views/ChannelVideosView.swift +++ b/Shared/Views/ChannelVideosView.swift @@ -2,15 +2,23 @@ import Siesta import SwiftUI struct ChannelVideosView: View { - let channel: Channel + #if os(iOS) + static let hiddenOffset = max(UIScreen.main.bounds.height, UIScreen.main.bounds.width) + 100 + #endif + var channel: Channel? @State private var presentingShareSheet = false @State private var shareURL: URL? @State private var subscriptionToggleButtonDisabled = false + #if os(iOS) + @State private var viewVerticalOffset = Self.hiddenOffset + #endif + @StateObject private var store = Store() @Environment(\.colorScheme) private var colorScheme + @Environment(\.navigationStyle) private var navigationStyle #if os(iOS) @Environment(\.horizontalSizeClass) private var horizontalSizeClass @@ -19,17 +27,42 @@ struct ChannelVideosView: View { @EnvironmentObject private var accounts @EnvironmentObject private var navigation + @EnvironmentObject private var recents @EnvironmentObject private var subscriptions @Namespace private var focusNamespace + var presentedChannel: Channel? { + recents.presentedChannel ?? channel + } + var videos: [ContentItem] { ContentItem.array(of: store.item?.videos ?? []) } var body: some View { - BrowserPlayerControls { - content + if navigationStyle == .tab { + NavigationView { + BrowserPlayerControls { + content + } + } + #if os(iOS) + .onChange(of: navigation.presentingChannel) { newValue in + if newValue { + resource?.load() + viewVerticalOffset = 0 + } else { + viewVerticalOffset = Self.hiddenOffset + } + } + .offset(y: viewVerticalOffset) + .animation(.easeIn(duration: 0.2), value: viewVerticalOffset) + #endif + } else { + BrowserPlayerControls { + content + } } } @@ -43,8 +76,10 @@ struct ChannelVideosView: View { Spacer() - FavoriteButton(item: FavoriteItem(section: .channel(channel.id, channel.name))) - .labelStyle(.iconOnly) + if let channel = presentedChannel { + FavoriteButton(item: FavoriteItem(section: .channel(channel.id, channel.name))) + .labelStyle(.iconOnly) + } if let subscribers = store.item?.subscriptionsString { Text("**\(subscribers)** subscribers") @@ -66,11 +101,11 @@ struct ChannelVideosView: View { #if !os(tvOS) .toolbar { ToolbarItem(placement: .navigation) { - ShareButton( - contentItem: contentItem, - presentingShareSheet: $presentingShareSheet, - shareURL: $shareURL - ) + if navigationStyle == .tab { + Button("Done") { + navigation.presentingChannel = false + } + } } ToolbarItem { @@ -79,14 +114,22 @@ struct ChannelVideosView: View { Text("\(store.item?.subscriptionsString ?? "loading")") .fontWeight(.bold) Text(" subscribers") + .allowsTightening(true) + .foregroundColor(.secondary) + .opacity(store.item?.subscriptionsString != nil ? 1 : 0) } - .allowsTightening(true) - .foregroundColor(.secondary) - .opacity(store.item?.subscriptionsString != nil ? 1 : 0) + + ShareButton( + contentItem: contentItem, + presentingShareSheet: $presentingShareSheet, + shareURL: $shareURL + ) subscriptionToggleButton - FavoriteButton(item: FavoriteItem(section: .channel(channel.id, channel.name))) + if let channel = presentedChannel { + FavoriteButton(item: FavoriteItem(section: .channel(channel.id, channel.name))) + } } } } @@ -99,12 +142,11 @@ struct ChannelVideosView: View { } #endif .onAppear { - if store.item.isNil { - resource.addObserver(store) - resource.load() - } + resource?.loadIfNeeded() } + #if !os(tvOS) .navigationTitle(navigationTitle) + #endif return Group { if #available(macOS 12.0, *) { @@ -121,44 +163,50 @@ struct ChannelVideosView: View { } } - private var resource: Resource { + private var resource: Resource? { + guard let channel = presentedChannel else { + return nil + } + let resource = accounts.api.channel(channel.id) resource.addObserver(store) return resource } - private var subscriptionToggleButton: some View { - Group { - if accounts.app.supportsSubscriptions && accounts.signedIn { - if subscriptions.isSubscribing(channel.id) { - Button("Unsubscribe") { - subscriptionToggleButtonDisabled = true + @ViewBuilder private var subscriptionToggleButton: some View { + if let channel = presentedChannel { + Group { + if accounts.app.supportsSubscriptions && accounts.signedIn { + if subscriptions.isSubscribing(channel.id) { + Button("Unsubscribe") { + subscriptionToggleButtonDisabled = true - subscriptions.unsubscribe(channel.id) { - subscriptionToggleButtonDisabled = false + subscriptions.unsubscribe(channel.id) { + subscriptionToggleButtonDisabled = false + } } - } - } else { - Button("Subscribe") { - subscriptionToggleButtonDisabled = true + } else { + Button("Subscribe") { + subscriptionToggleButtonDisabled = true - subscriptions.subscribe(channel.id) { - subscriptionToggleButtonDisabled = false - navigation.sidebarSectionChanged.toggle() + subscriptions.subscribe(channel.id) { + subscriptionToggleButtonDisabled = false + navigation.sidebarSectionChanged.toggle() + } } } } } + .disabled(subscriptionToggleButtonDisabled) } - .disabled(subscriptionToggleButtonDisabled) } private var contentItem: ContentItem { - ContentItem(channel: channel) + ContentItem(channel: presentedChannel) } private var navigationTitle: String { - store.item?.name ?? channel.name + presentedChannel?.name ?? store.item?.name ?? "No channel" } }