import Defaults import SDWebImageSwiftUI import SwiftUI struct ChannelsView: View { @ObservedObject private var feed = FeedModel.shared @ObservedObject private var subscriptions = SubscribedChannelsModel.shared @ObservedObject private var accounts = AccountsModel.shared @ObservedObject private var feedCount = UnwatchedFeedCountModel.shared private var navigation = NavigationModel.shared @Default(.showCacheStatus) private var showCacheStatus @Default(.showUnwatchedFeedBadges) private var showUnwatchedFeedBadges @Default(.keepChannelsWithUnwatchedFeedOnTop) private var keepChannelsWithUnwatchedFeedOnTop @State private var channelLinkActive = false @State private var channelForLink: Channel? var body: some View { List { Section(header: header) { ForEach(channels) { channel in let label = HStack { if let url = channel.thumbnailURLOrCached { ThumbnailView(url: url) .frame(width: 35, height: 35) .clipShape(RoundedRectangle(cornerRadius: 35)) } else { Image(systemName: RecentsModel.symbolSystemImage(channel.name)) .imageScale(.large) .foregroundColor(.accentColor) .frame(width: 35, height: 35) } Text(channel.name) .lineLimit(1) } .backport .badge(showUnwatchedFeedBadges ? feedCount.unwatchedByChannelText(channel) : nil) Group { #if os(tvOS) Button { navigation.openChannel(channel, navigationStyle: .tab) } label: { label } #else Button { channelForLink = channel channelLinkActive = channelForLink != nil } label: { label .contentShape(Rectangle()) .foregroundColor(.primary) } .buttonStyle(.plain) #endif } .contextMenu { if subscriptions.isSubscribing(channel.id) { toggleWatchedButton(channel) } Button { subscriptions.unsubscribe(channel.id) } label: { Label("Unsubscribe", systemImage: "xmark.circle") } } } #if os(tvOS) .padding(.horizontal, 50) #endif Color.clear.padding(.bottom, 50) .listRowBackground(Color.clear) .backport .listRowSeparator(false) } } #if !os(tvOS) .background( NavigationLink(destination: ChannelVideosView(channel: channelForLink ?? Video.fixture.channel), isActive: $channelLinkActive, label: EmptyView.init) ) #endif .onAppear { subscriptions.load() } .onChange(of: accounts.current) { _ in subscriptions.load(force: true) } #if os(iOS) .refreshControl { refreshControl in subscriptions.load(force: true) { refreshControl.endRefreshing() } } .backport .refreshable { await subscriptions.load(force: true) } #endif #if !os(tvOS) .background( Button("Refresh") { subscriptions.load(force: true) } .keyboardShortcut("r") .opacity(0) ) #endif #if !os(macOS) .onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in subscriptions.load() } #endif #if os(tvOS) .padding(.horizontal, 30) #endif } var channels: [Channel] { keepChannelsWithUnwatchedFeedOnTop ? subscriptions.allByUnwatchedCount : subscriptions.all } var header: some View { HStack { #if os(tvOS) SubscriptionsPageButton() #endif if showCacheStatus { Spacer() CacheStatusHeader( refreshTime: subscriptions.formattedCacheTime, isLoading: subscriptions.isLoading ) } #if os(tvOS) if !showCacheStatus { Spacer() } Button { subscriptions.load(force: true) } label: { Label("Refresh", systemImage: "arrow.clockwise") .labelStyle(.iconOnly) .imageScale(.small) .font(.caption) } #endif } #if os(tvOS) .padding(.bottom, 15) .padding(.top, 15) #endif } @ViewBuilder func toggleWatchedButton(_ channel: Channel) -> some View { if feed.canMarkChannelAsWatched(channel.id) { markChannelAsWatchedButton(channel) } else { markChannelAsUnwatchedButton(channel) } } func markChannelAsWatchedButton(_ channel: Channel) -> some View { Button { feed.markChannelAsWatched(channel.id) } label: { Label("Mark channel feed as watched", systemImage: "checkmark.circle.fill") } .disabled(!feed.canMarkAllFeedAsWatched) } func markChannelAsUnwatchedButton(_ channel: Channel) -> some View { Button { feed.markChannelAsUnwatched(channel.id) } label: { Label("Mark channel feed as unwatched", systemImage: "checkmark.circle") } } } struct ChannelsView_Previews: PreviewProvider { static var previews: some View { NavigationView { ChannelsView() } } }