import Siesta import SwiftUI struct ChannelVideosView: View { var channel: Channel? @State private var presentingShareSheet = false @State private var shareURL: URL? @State private var subscriptionToggleButtonDisabled = false @StateObject private var store = Store() @Environment(\.colorScheme) private var colorScheme @Environment(\.navigationStyle) private var navigationStyle #if os(iOS) @Environment(\.horizontalSizeClass) private var horizontalSizeClass @EnvironmentObject private var player #endif @EnvironmentObject private var accounts @EnvironmentObject private var navigation @EnvironmentObject private var recents @EnvironmentObject private var subscriptions @Namespace private var focusNamespace var presentedChannel: Channel? { channel ?? recents.presentedChannel } var videos: [ContentItem] { ContentItem.array(of: store.item?.videos ?? []) } var body: some View { if navigationStyle == .tab { NavigationView { BrowserPlayerControls { content } } } else { BrowserPlayerControls { content } } } var content: some View { let content = VStack { #if os(tvOS) HStack { Text(navigationTitle) .font(.title2) .frame(alignment: .leading) Spacer() if let channel = presentedChannel { FavoriteButton(item: FavoriteItem(section: .channel(channel.id, channel.name))) .labelStyle(.iconOnly) } if let subscribers = store.item?.subscriptionsString { Text("**\(subscribers)** subscribers") .foregroundColor(.secondary) } subscriptionToggleButton } .frame(maxWidth: .infinity) #endif VerticalCells(items: videos) .environment(\.inChannelView, true) #if os(tvOS) .prefersDefaultFocus(in: focusNamespace) #endif } #if !os(tvOS) .toolbar { ToolbarItem(placement: .cancellationAction) { if navigationStyle == .tab { Button { withAnimation(Constants.overlayAnimation) { navigation.presentingChannel = false } } label: { Label("Close", systemImage: "xmark") } } } ToolbarItem { HStack { HStack(spacing: 3) { Text("\(store.item?.subscriptionsString ?? "")") .fontWeight(.bold) let subscribers = Text(" subscribers") .allowsTightening(true) .foregroundColor(.secondary) .opacity(store.item?.subscriptionsString != nil ? 1 : 0) #if os(iOS) if navigationStyle == .sidebar { subscribers } #else subscribers #endif } ShareButton(contentItem: contentItem) subscriptionToggleButton if let channel = presentedChannel { FavoriteButton(item: FavoriteItem(section: .channel(channel.id, channel.name))) } } } } #endif .onAppear { if navigationStyle == .tab { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { resource?.loadIfNeeded() } } else { resource?.loadIfNeeded() } } #if !os(tvOS) .navigationTitle(navigationTitle) #endif return Group { if #available(macOS 12.0, *) { content #if os(tvOS) .background(Color.background(scheme: colorScheme)) #endif #if !os(iOS) .focusScope(focusNamespace) #endif } else { content } } } private var resource: Resource? { guard let channel = presentedChannel else { return nil } let resource = accounts.api.channel(channel.id) resource.addObserver(store) return resource } @ViewBuilder private var subscriptionToggleButton: some View { if let channel = presentedChannel { Group { if accounts.app.supportsSubscriptions && accounts.signedIn { if subscriptions.isSubscribing(channel.id) { Button { subscriptionToggleButtonDisabled = true subscriptions.unsubscribe(channel.id) { subscriptionToggleButtonDisabled = false } } label: { Label("Unsubscribe", systemImage: "star.circle") #if os(iOS) .labelStyle(.automatic) #else .labelStyle(.titleOnly) #endif } } else { Button { subscriptionToggleButtonDisabled = true subscriptions.subscribe(channel.id) { subscriptionToggleButtonDisabled = false navigation.sidebarSectionChanged.toggle() } } label: { Label("Subscribe", systemImage: "circle") #if os(iOS) .labelStyle(.automatic) #else .labelStyle(.titleOnly) #endif } } } } .disabled(subscriptionToggleButtonDisabled) } } private var contentItem: ContentItem { ContentItem(channel: presentedChannel) } private var navigationTitle: String { presentedChannel?.name ?? store.item?.name ?? "No channel" } } struct ChannelVideosView_Previews: PreviewProvider { static var previews: some View { ChannelVideosView(channel: Video.fixture.channel) .injectFixtureEnvironmentObjects() } }