mirror of
				https://github.com/yattee/yattee.git
				synced 2025-11-04 14:42:05 +00:00 
			
		
		
		
	Add channels thumbnails to cells and list
This commit is contained in:
		
							
								
								
									
										76
									
								
								Shared/Channels/ChannelLinkView.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								Shared/Channels/ChannelLinkView.swift
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,76 @@
 | 
				
			|||||||
 | 
					import Foundation
 | 
				
			||||||
 | 
					import SwiftUI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ChannelLinkView<ChannelLabel: View>: View {
 | 
				
			||||||
 | 
					    let channel: Channel
 | 
				
			||||||
 | 
					    let channelLabel: ChannelLabel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Environment(\.inChannelView) private var inChannelView
 | 
				
			||||||
 | 
					    @Environment(\.navigationStyle) private var navigationStyle
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    init(
 | 
				
			||||||
 | 
					        channel: Channel,
 | 
				
			||||||
 | 
					        @ViewBuilder channelLabel: () -> ChannelLabel
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        self.channel = channel
 | 
				
			||||||
 | 
					        self.channelLabel = channelLabel()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var body: some View {
 | 
				
			||||||
 | 
					        channelControl
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @ViewBuilder private var channelControl: some View {
 | 
				
			||||||
 | 
					        if !channel.name.isEmpty {
 | 
				
			||||||
 | 
					            #if os(tvOS)
 | 
				
			||||||
 | 
					                channelLabel
 | 
				
			||||||
 | 
					            #else
 | 
				
			||||||
 | 
					                if navigationStyle == .tab {
 | 
				
			||||||
 | 
					                    channelNavigationLink
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    channelButton
 | 
				
			||||||
 | 
					                    #if os(macOS)
 | 
				
			||||||
 | 
					                    .onHover(perform: onHover(_:))
 | 
				
			||||||
 | 
					                    #endif
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            #endif
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @ViewBuilder private var channelNavigationLink: some View {
 | 
				
			||||||
 | 
					        NavigationLink(destination: ChannelVideosView(channel: channel)) {
 | 
				
			||||||
 | 
					            channelLabel
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @ViewBuilder private var channelButton: some View {
 | 
				
			||||||
 | 
					        Button {
 | 
				
			||||||
 | 
					            guard !inChannelView else {
 | 
				
			||||||
 | 
					                return
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            NavigationModel.shared.openChannel(
 | 
				
			||||||
 | 
					                channel,
 | 
				
			||||||
 | 
					                navigationStyle: navigationStyle
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        } label: {
 | 
				
			||||||
 | 
					            channelLabel
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #if os(tvOS)
 | 
				
			||||||
 | 
					        .buttonStyle(.card)
 | 
				
			||||||
 | 
					        #else
 | 
				
			||||||
 | 
					        .buttonStyle(.plain)
 | 
				
			||||||
 | 
					        #endif
 | 
				
			||||||
 | 
					        .help("\(channel.name) Channel")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #if os(macOS)
 | 
				
			||||||
 | 
					        private func onHover(_ inside: Bool) {
 | 
				
			||||||
 | 
					            if inside {
 | 
				
			||||||
 | 
					                NSCursor.pointingHand.push()
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                NSCursor.pop()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    #endif
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -11,4 +11,20 @@ struct Constants {
 | 
				
			|||||||
            0.6
 | 
					            0.6
 | 
				
			||||||
        #endif
 | 
					        #endif
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static var channelThumbnailSize: Double {
 | 
				
			||||||
 | 
					        #if os(tvOS)
 | 
				
			||||||
 | 
					            50
 | 
				
			||||||
 | 
					        #else
 | 
				
			||||||
 | 
					            30
 | 
				
			||||||
 | 
					        #endif
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static var channelDetailsStackSpacing: Double {
 | 
				
			||||||
 | 
					        #if os(tvOS)
 | 
				
			||||||
 | 
					            12
 | 
				
			||||||
 | 
					        #else
 | 
				
			||||||
 | 
					            6
 | 
				
			||||||
 | 
					        #endif
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,7 +44,7 @@ extension Defaults.Keys {
 | 
				
			|||||||
    #if os(iOS)
 | 
					    #if os(iOS)
 | 
				
			||||||
        static let lockPortraitWhenBrowsing = Key<Bool>("lockPortraitWhenBrowsing", default: UIDevice.current.userInterfaceIdiom == .phone)
 | 
					        static let lockPortraitWhenBrowsing = Key<Bool>("lockPortraitWhenBrowsing", default: UIDevice.current.userInterfaceIdiom == .phone)
 | 
				
			||||||
    #endif
 | 
					    #endif
 | 
				
			||||||
    static let channelOnThumbnail = Key<Bool>("channelOnThumbnail", default: true)
 | 
					    static let channelOnThumbnail = Key<Bool>("channelOnThumbnail", default: false)
 | 
				
			||||||
    static let timeOnThumbnail = Key<Bool>("timeOnThumbnail", default: true)
 | 
					    static let timeOnThumbnail = Key<Bool>("timeOnThumbnail", default: true)
 | 
				
			||||||
    static let roundedThumbnails = Key<Bool>("roundedThumbnails", default: true)
 | 
					    static let roundedThumbnails = Key<Bool>("roundedThumbnails", default: true)
 | 
				
			||||||
    static let thumbnailsQuality = Key<ThumbnailsQuality>("thumbnailsQuality", default: .highest)
 | 
					    static let thumbnailsQuality = Key<ThumbnailsQuality>("thumbnailsQuality", default: .highest)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -114,11 +114,11 @@ struct PlayerControls: View {
 | 
				
			|||||||
                                        Text(player.currentVideo?.displayAuthor ?? "")
 | 
					                                        Text(player.currentVideo?.displayAuthor ?? "")
 | 
				
			||||||
                                            .fontWeight(.semibold)
 | 
					                                            .fontWeight(.semibold)
 | 
				
			||||||
                                            .shadow(radius: 10)
 | 
					                                            .shadow(radius: 10)
 | 
				
			||||||
                                            .foregroundColor(.secondary)
 | 
					                                            .foregroundColor(.init(white: 0.8))
 | 
				
			||||||
                                            .font(.system(size: playerControlsLayout.authorLineFontSize))
 | 
					                                            .font(.system(size: playerControlsLayout.authorLineFontSize))
 | 
				
			||||||
                                            .lineLimit(1)
 | 
					                                            .lineLimit(1)
 | 
				
			||||||
                                    }
 | 
					                                    }
 | 
				
			||||||
 | 
					                                    .foregroundColor(.white)
 | 
				
			||||||
                                    .frame(maxWidth: .infinity, alignment: .leading)
 | 
					                                    .frame(maxWidth: .infinity, alignment: .leading)
 | 
				
			||||||
                                    .offset(y: -40)
 | 
					                                    .offset(y: -40)
 | 
				
			||||||
                                }
 | 
					                                }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -74,21 +74,22 @@ struct VideoBanner: View {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                HStack {
 | 
					                HStack {
 | 
				
			||||||
                    HStack {
 | 
					                    HStack {
 | 
				
			||||||
                        if !inChannelView,
 | 
					 | 
				
			||||||
                           let video,
 | 
					 | 
				
			||||||
                           let url = video.channel.thumbnailURLOrCached
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            ThumbnailView(url: url)
 | 
					 | 
				
			||||||
                                .frame(width: 30, height: 30)
 | 
					 | 
				
			||||||
                                .clipShape(Circle())
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        VStack(alignment: .leading) {
 | 
					                        VStack(alignment: .leading) {
 | 
				
			||||||
                            Group {
 | 
					                            Group {
 | 
				
			||||||
                                if let video {
 | 
					                                if let video {
 | 
				
			||||||
                                    if !inChannelView, !video.isLocal || video.localStreamIsRemoteURL {
 | 
					                                    if !inChannelView, !video.isLocal || video.localStreamIsRemoteURL {
 | 
				
			||||||
                                        channelControl
 | 
					                                        ChannelLinkView(channel: video.channel) {
 | 
				
			||||||
 | 
					                                            HStack(spacing: Constants.channelDetailsStackSpacing) {
 | 
				
			||||||
 | 
					                                                if let url = video.channel.thumbnailURLOrCached {
 | 
				
			||||||
 | 
					                                                    ThumbnailView(url: url)
 | 
				
			||||||
 | 
					                                                        .frame(width: Constants.channelThumbnailSize, height: Constants.channelThumbnailSize)
 | 
				
			||||||
 | 
					                                                        .clipShape(Circle())
 | 
				
			||||||
 | 
					                                                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                                channelLabel
 | 
				
			||||||
                                                    .font(.subheadline)
 | 
					                                                    .font(.subheadline)
 | 
				
			||||||
 | 
					                                            }
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
                                    } else {
 | 
					                                    } else {
 | 
				
			||||||
                                        #if os(iOS)
 | 
					                                        #if os(iOS)
 | 
				
			||||||
                                            if DocumentsModel.shared.isDocument(video) {
 | 
					                                            if DocumentsModel.shared.isDocument(video) {
 | 
				
			||||||
@@ -220,7 +221,7 @@ struct VideoBanner: View {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private var thumbnailWidth: Double {
 | 
					    private var thumbnailWidth: Double {
 | 
				
			||||||
        #if os(tvOS)
 | 
					        #if os(tvOS)
 | 
				
			||||||
            250
 | 
					            356
 | 
				
			||||||
        #else
 | 
					        #else
 | 
				
			||||||
            120
 | 
					            120
 | 
				
			||||||
        #endif
 | 
					        #endif
 | 
				
			||||||
@@ -228,7 +229,7 @@ struct VideoBanner: View {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private var thumbnailHeight: Double {
 | 
					    private var thumbnailHeight: Double {
 | 
				
			||||||
        #if os(tvOS)
 | 
					        #if os(tvOS)
 | 
				
			||||||
            140
 | 
					            200
 | 
				
			||||||
        #else
 | 
					        #else
 | 
				
			||||||
            72
 | 
					            72
 | 
				
			||||||
        #endif
 | 
					        #endif
 | 
				
			||||||
@@ -308,50 +309,7 @@ struct VideoBanner: View {
 | 
				
			|||||||
        (progressViewValue / progressViewTotal) * 100 > Double(Defaults[.watchedThreshold])
 | 
					        (progressViewValue / progressViewTotal) * 100 > Double(Defaults[.watchedThreshold])
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @ViewBuilder private var channelControl: some View {
 | 
					    @ViewBuilder private var channelLabel: some View {
 | 
				
			||||||
        if let video, !video.displayAuthor.isEmpty {
 | 
					 | 
				
			||||||
            #if os(tvOS)
 | 
					 | 
				
			||||||
                displayAuthor
 | 
					 | 
				
			||||||
            #else
 | 
					 | 
				
			||||||
                if navigationStyle == .tab, inNavigationView {
 | 
					 | 
				
			||||||
                    channelNavigationLink
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    channelButton
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            #endif
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @ViewBuilder private var channelNavigationLink: some View {
 | 
					 | 
				
			||||||
        if let channel = video?.channel {
 | 
					 | 
				
			||||||
            NavigationLink(destination: ChannelVideosView(channel: channel)) {
 | 
					 | 
				
			||||||
                displayAuthor
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @ViewBuilder private var channelButton: some View {
 | 
					 | 
				
			||||||
        if let video {
 | 
					 | 
				
			||||||
            Button {
 | 
					 | 
				
			||||||
                guard !inChannelView else { return }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                NavigationModel.shared.openChannel(
 | 
					 | 
				
			||||||
                    video.channel,
 | 
					 | 
				
			||||||
                    navigationStyle: navigationStyle
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            } label: {
 | 
					 | 
				
			||||||
                displayAuthor
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            #if os(tvOS)
 | 
					 | 
				
			||||||
            .buttonStyle(.card)
 | 
					 | 
				
			||||||
            #else
 | 
					 | 
				
			||||||
            .buttonStyle(.plain)
 | 
					 | 
				
			||||||
            #endif
 | 
					 | 
				
			||||||
            .help("\(video.channel.name) Channel")
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @ViewBuilder private var displayAuthor: some View {
 | 
					 | 
				
			||||||
        if let video, !video.displayAuthor.isEmpty {
 | 
					        if let video, !video.displayAuthor.isEmpty {
 | 
				
			||||||
            Text(video.displayAuthor)
 | 
					            Text(video.displayAuthor)
 | 
				
			||||||
                .fontWeight(.semibold)
 | 
					                .fontWeight(.semibold)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,7 +41,7 @@ struct VideoCell: View {
 | 
				
			|||||||
        Button(action: playAction) {
 | 
					        Button(action: playAction) {
 | 
				
			||||||
            content
 | 
					            content
 | 
				
			||||||
            #if os(tvOS)
 | 
					            #if os(tvOS)
 | 
				
			||||||
            .frame(width: 580, height: 470)
 | 
					            .frame(width: 580, height: channelOnThumbnail ? 470 : 500)
 | 
				
			||||||
            #endif
 | 
					            #endif
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        .opacity(contentOpacity)
 | 
					        .opacity(contentOpacity)
 | 
				
			||||||
@@ -166,17 +166,22 @@ struct VideoCell: View {
 | 
				
			|||||||
                    videoDetail(video.displayTitle, lineLimit: 5)
 | 
					                    videoDetail(video.displayTitle, lineLimit: 5)
 | 
				
			||||||
                        .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
 | 
					                        .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    HStack(spacing: 12) {
 | 
					                    HStack(spacing: Constants.channelDetailsStackSpacing) {
 | 
				
			||||||
                        if !inChannelView,
 | 
					                        if !inChannelView,
 | 
				
			||||||
                           let video,
 | 
					                           let video,
 | 
				
			||||||
                           let url = video.channel.thumbnailURLOrCached
 | 
					                           let url = video.channel.thumbnailURLOrCached
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
 | 
					                            ChannelLinkView(channel: video.channel) {
 | 
				
			||||||
                                ThumbnailView(url: url)
 | 
					                                ThumbnailView(url: url)
 | 
				
			||||||
                                .frame(width: 30, height: 30)
 | 
					                                    .frame(width: Constants.channelThumbnailSize, height: Constants.channelThumbnailSize)
 | 
				
			||||||
                                    .clipShape(Circle())
 | 
					                                    .clipShape(Circle())
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        if !channelOnThumbnail, !inChannelView {
 | 
					                        if !channelOnThumbnail, !inChannelView {
 | 
				
			||||||
                            channelControl(badge: false)
 | 
					                            ChannelLinkView(channel: video.channel) {
 | 
				
			||||||
 | 
					                                channelLabel(badge: false)
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -259,14 +264,22 @@ struct VideoCell: View {
 | 
				
			|||||||
                        #if os(tvOS)
 | 
					                        #if os(tvOS)
 | 
				
			||||||
                            .frame(minHeight: 60, alignment: .top)
 | 
					                            .frame(minHeight: 60, alignment: .top)
 | 
				
			||||||
                        #elseif os(macOS)
 | 
					                        #elseif os(macOS)
 | 
				
			||||||
                            .frame(minHeight: 32, alignment: .top)
 | 
					                            .frame(minHeight: 35, alignment: .top)
 | 
				
			||||||
                        #else
 | 
					                        #else
 | 
				
			||||||
                            .frame(minHeight: 40, alignment: .top)
 | 
					                            .frame(minHeight: 43, alignment: .top)
 | 
				
			||||||
                        #endif
 | 
					                        #endif
 | 
				
			||||||
                        if !channelOnThumbnail, !inChannelView {
 | 
					                        if !channelOnThumbnail, !inChannelView {
 | 
				
			||||||
                            channelControl(badge: false)
 | 
					                            ChannelLinkView(channel: video.channel) {
 | 
				
			||||||
                                .padding(.top, 4)
 | 
					                                HStack(spacing: Constants.channelDetailsStackSpacing) {
 | 
				
			||||||
                                .padding(.bottom, 6)
 | 
					                                    if let url = video.channel.thumbnailURLOrCached {
 | 
				
			||||||
 | 
					                                        ThumbnailView(url: url)
 | 
				
			||||||
 | 
					                                            .frame(width: Constants.channelThumbnailSize, height: Constants.channelThumbnailSize)
 | 
				
			||||||
 | 
					                                            .clipShape(Circle())
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                    channelLabel(badge: false)
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
 | 
					                    .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
 | 
				
			||||||
@@ -274,21 +287,24 @@ struct VideoCell: View {
 | 
				
			|||||||
                #if os(tvOS)
 | 
					                #if os(tvOS)
 | 
				
			||||||
                .frame(minHeight: channelOnThumbnail ? 80 : 120, alignment: .top)
 | 
					                .frame(minHeight: channelOnThumbnail ? 80 : 120, alignment: .top)
 | 
				
			||||||
                #elseif os(macOS)
 | 
					                #elseif os(macOS)
 | 
				
			||||||
                .frame(minHeight: 35, alignment: .top)
 | 
					                .frame(minHeight: channelOnThumbnail ? 52 : 75, alignment: .top)
 | 
				
			||||||
                #else
 | 
					                #else
 | 
				
			||||||
                .frame(minHeight: 50, alignment: .top)
 | 
					                .frame(minHeight: channelOnThumbnail ? 50 : 70, alignment: .top)
 | 
				
			||||||
                #endif
 | 
					                #endif
 | 
				
			||||||
                .padding(.bottom, 4)
 | 
					                .padding(.bottom, 4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                HStack(spacing: 8) {
 | 
					                HStack(spacing: 8) {
 | 
				
			||||||
                    if !inChannelView,
 | 
					                    if channelOnThumbnail,
 | 
				
			||||||
 | 
					                       !inChannelView,
 | 
				
			||||||
                       let video,
 | 
					                       let video,
 | 
				
			||||||
                       let url = video.channel.thumbnailURLOrCached
 | 
					                       let url = video.channel.thumbnailURLOrCached
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
 | 
					                        ChannelLinkView(channel: video.channel) {
 | 
				
			||||||
                            ThumbnailView(url: url)
 | 
					                            ThumbnailView(url: url)
 | 
				
			||||||
                            .frame(width: 30, height: 30)
 | 
					                                .frame(width: Constants.channelThumbnailSize, height: Constants.channelThumbnailSize)
 | 
				
			||||||
                                .clipShape(Circle())
 | 
					                                .clipShape(Circle())
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if let date = video.publishedDate {
 | 
					                    if let date = video.publishedDate {
 | 
				
			||||||
                        HStack(spacing: 2) {
 | 
					                        HStack(spacing: 2) {
 | 
				
			||||||
@@ -314,7 +330,7 @@ struct VideoCell: View {
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
                .lineLimit(1)
 | 
					                .lineLimit(1)
 | 
				
			||||||
                .foregroundColor(.secondary)
 | 
					                .foregroundColor(.secondary)
 | 
				
			||||||
                .frame(maxWidth: .infinity, minHeight: 30, alignment: .topLeading)
 | 
					                .frame(maxWidth: .infinity, minHeight: 35, alignment: .topLeading)
 | 
				
			||||||
                #if os(tvOS)
 | 
					                #if os(tvOS)
 | 
				
			||||||
                    .padding(.bottom, 10)
 | 
					                    .padding(.bottom, 10)
 | 
				
			||||||
                #endif
 | 
					                #endif
 | 
				
			||||||
@@ -327,47 +343,6 @@ struct VideoCell: View {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @ViewBuilder private func channelControl(badge: Bool = true) -> some View {
 | 
					 | 
				
			||||||
        if !video.channel.name.isEmpty {
 | 
					 | 
				
			||||||
            #if os(tvOS)
 | 
					 | 
				
			||||||
                channelButton(badge: badge)
 | 
					 | 
				
			||||||
            #else
 | 
					 | 
				
			||||||
                if navigationStyle == .tab {
 | 
					 | 
				
			||||||
                    channelNavigationLink(badge: badge)
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    channelButton(badge: badge)
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            #endif
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @ViewBuilder private func channelNavigationLink(badge: Bool = true) -> some View {
 | 
					 | 
				
			||||||
        NavigationLink(destination: ChannelVideosView(channel: video.channel)) {
 | 
					 | 
				
			||||||
            channelLabel(badge: badge)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @ViewBuilder private func channelButton(badge: Bool = true) -> some View {
 | 
					 | 
				
			||||||
        Button {
 | 
					 | 
				
			||||||
            guard !inChannelView else {
 | 
					 | 
				
			||||||
                return
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            NavigationModel.shared.openChannel(
 | 
					 | 
				
			||||||
                video.channel,
 | 
					 | 
				
			||||||
                navigationStyle: navigationStyle
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        } label: {
 | 
					 | 
				
			||||||
            channelLabel(badge: badge)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        #if os(tvOS)
 | 
					 | 
				
			||||||
        .buttonStyle(.card)
 | 
					 | 
				
			||||||
        #else
 | 
					 | 
				
			||||||
        .buttonStyle(.plain)
 | 
					 | 
				
			||||||
        #endif
 | 
					 | 
				
			||||||
        .help("\(video.channel.name) Channel")
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @ViewBuilder private func channelLabel(badge: Bool = true) -> some View {
 | 
					    @ViewBuilder private func channelLabel(badge: Bool = true) -> some View {
 | 
				
			||||||
        if badge {
 | 
					        if badge {
 | 
				
			||||||
            DetailBadge(text: video.author, style: .prominent)
 | 
					            DetailBadge(text: video.author, style: .prominent)
 | 
				
			||||||
@@ -415,7 +390,9 @@ struct VideoCell: View {
 | 
				
			|||||||
                    Spacer()
 | 
					                    Spacer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if channelOnThumbnail, !inChannelView {
 | 
					                    if channelOnThumbnail, !inChannelView {
 | 
				
			||||||
                        channelControl()
 | 
					                        ChannelLinkView(channel: video.channel) {
 | 
				
			||||||
 | 
					                            channelLabel()
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                #if os(tvOS)
 | 
					                #if os(tvOS)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -171,6 +171,9 @@
 | 
				
			|||||||
		37169AA62729E2CC0011DE61 /* AccountsBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37169AA52729E2CC0011DE61 /* AccountsBridge.swift */; };
 | 
							37169AA62729E2CC0011DE61 /* AccountsBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37169AA52729E2CC0011DE61 /* AccountsBridge.swift */; };
 | 
				
			||||||
		37169AA72729E2CC0011DE61 /* AccountsBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37169AA52729E2CC0011DE61 /* AccountsBridge.swift */; };
 | 
							37169AA72729E2CC0011DE61 /* AccountsBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37169AA52729E2CC0011DE61 /* AccountsBridge.swift */; };
 | 
				
			||||||
		37169AA82729E2CC0011DE61 /* AccountsBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37169AA52729E2CC0011DE61 /* AccountsBridge.swift */; };
 | 
							37169AA82729E2CC0011DE61 /* AccountsBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37169AA52729E2CC0011DE61 /* AccountsBridge.swift */; };
 | 
				
			||||||
 | 
							3717407D2949D40800FDDBC7 /* ChannelLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3717407C2949D40800FDDBC7 /* ChannelLinkView.swift */; };
 | 
				
			||||||
 | 
							3717407E2949D40800FDDBC7 /* ChannelLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3717407C2949D40800FDDBC7 /* ChannelLinkView.swift */; };
 | 
				
			||||||
 | 
							3717407F2949D40800FDDBC7 /* ChannelLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3717407C2949D40800FDDBC7 /* ChannelLinkView.swift */; };
 | 
				
			||||||
		3718B9A02921A9620003DB2E /* VideoDetailsOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E80F3B287B107F00561799 /* VideoDetailsOverlay.swift */; };
 | 
							3718B9A02921A9620003DB2E /* VideoDetailsOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E80F3B287B107F00561799 /* VideoDetailsOverlay.swift */; };
 | 
				
			||||||
		3718B9A12921A9640003DB2E /* VideoDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B81AFE26D2CA3700675966 /* VideoDetails.swift */; };
 | 
							3718B9A12921A9640003DB2E /* VideoDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B81AFE26D2CA3700675966 /* VideoDetails.swift */; };
 | 
				
			||||||
		3718B9A22921A9670003DB2E /* VideoActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374924EF29216C630017D862 /* VideoActions.swift */; };
 | 
							3718B9A22921A9670003DB2E /* VideoActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374924EF29216C630017D862 /* VideoActions.swift */; };
 | 
				
			||||||
@@ -1144,6 +1147,7 @@
 | 
				
			|||||||
		37152EE926EFEB95004FB96D /* LazyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LazyView.swift; sourceTree = "<group>"; };
 | 
							37152EE926EFEB95004FB96D /* LazyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LazyView.swift; sourceTree = "<group>"; };
 | 
				
			||||||
		37169AA12729D98A0011DE61 /* InstancesBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstancesBridge.swift; sourceTree = "<group>"; };
 | 
							37169AA12729D98A0011DE61 /* InstancesBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstancesBridge.swift; sourceTree = "<group>"; };
 | 
				
			||||||
		37169AA52729E2CC0011DE61 /* AccountsBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsBridge.swift; sourceTree = "<group>"; };
 | 
							37169AA52729E2CC0011DE61 /* AccountsBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsBridge.swift; sourceTree = "<group>"; };
 | 
				
			||||||
 | 
							3717407C2949D40800FDDBC7 /* ChannelLinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelLinkView.swift; sourceTree = "<group>"; };
 | 
				
			||||||
		37192D5628B179D60012EEDD /* ChaptersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChaptersView.swift; sourceTree = "<group>"; };
 | 
							37192D5628B179D60012EEDD /* ChaptersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChaptersView.swift; sourceTree = "<group>"; };
 | 
				
			||||||
		371B7E5B27596B8400D21217 /* Comment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Comment.swift; sourceTree = "<group>"; };
 | 
							371B7E5B27596B8400D21217 /* Comment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Comment.swift; sourceTree = "<group>"; };
 | 
				
			||||||
		371B7E602759706A00D21217 /* CommentsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentsView.swift; sourceTree = "<group>"; };
 | 
							371B7E602759706A00D21217 /* CommentsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentsView.swift; sourceTree = "<group>"; };
 | 
				
			||||||
@@ -2143,6 +2147,7 @@
 | 
				
			|||||||
			children = (
 | 
								children = (
 | 
				
			||||||
				3776924D294630110055EC18 /* ChannelAvatarView.swift */,
 | 
									3776924D294630110055EC18 /* ChannelAvatarView.swift */,
 | 
				
			||||||
				3743B86727216D3600261544 /* ChannelCell.swift */,
 | 
									3743B86727216D3600261544 /* ChannelCell.swift */,
 | 
				
			||||||
 | 
									3717407C2949D40800FDDBC7 /* ChannelLinkView.swift */,
 | 
				
			||||||
				37BDFF1A29487C5A000C6404 /* ChannelListItem.swift */,
 | 
									37BDFF1A29487C5A000C6404 /* ChannelListItem.swift */,
 | 
				
			||||||
				37C3A24827235FAA0087A57A /* ChannelPlaylistCell.swift */,
 | 
									37C3A24827235FAA0087A57A /* ChannelPlaylistCell.swift */,
 | 
				
			||||||
				37BDFF1E29488117000C6404 /* ChannelPlaylistListItem.swift */,
 | 
									37BDFF1E29488117000C6404 /* ChannelPlaylistListItem.swift */,
 | 
				
			||||||
@@ -3079,6 +3084,7 @@
 | 
				
			|||||||
				3722AEBC274DA396005EA4D6 /* Badge+Backport.swift in Sources */,
 | 
									3722AEBC274DA396005EA4D6 /* Badge+Backport.swift in Sources */,
 | 
				
			||||||
				3748186626A7627F0084E870 /* Video+Fixtures.swift in Sources */,
 | 
									3748186626A7627F0084E870 /* Video+Fixtures.swift in Sources */,
 | 
				
			||||||
				37599F34272B44000087F250 /* FavoritesModel.swift in Sources */,
 | 
									37599F34272B44000087F250 /* FavoritesModel.swift in Sources */,
 | 
				
			||||||
 | 
									3717407D2949D40800FDDBC7 /* ChannelLinkView.swift in Sources */,
 | 
				
			||||||
				37030FF727B0347C00ECDDAA /* MPVPlayerView.swift in Sources */,
 | 
									37030FF727B0347C00ECDDAA /* MPVPlayerView.swift in Sources */,
 | 
				
			||||||
				37BA794726DC2E56002A0235 /* AppSidebarSubscriptions.swift in Sources */,
 | 
									37BA794726DC2E56002A0235 /* AppSidebarSubscriptions.swift in Sources */,
 | 
				
			||||||
				377FC7DC267A081800A6BBAF /* PopularView.swift in Sources */,
 | 
									377FC7DC267A081800A6BBAF /* PopularView.swift in Sources */,
 | 
				
			||||||
@@ -3484,6 +3490,7 @@
 | 
				
			|||||||
				378E9C39294552A700B2D696 /* ThumbnailView.swift in Sources */,
 | 
									378E9C39294552A700B2D696 /* ThumbnailView.swift in Sources */,
 | 
				
			||||||
				370F4FAB27CC164D001B35DC /* PlayerControlsModel.swift in Sources */,
 | 
									370F4FAB27CC164D001B35DC /* PlayerControlsModel.swift in Sources */,
 | 
				
			||||||
				37E8B0ED27B326C00024006F /* TimelineView.swift in Sources */,
 | 
									37E8B0ED27B326C00024006F /* TimelineView.swift in Sources */,
 | 
				
			||||||
 | 
									3717407E2949D40800FDDBC7 /* ChannelLinkView.swift in Sources */,
 | 
				
			||||||
				37FB28422721B22200A57617 /* ContentItem.swift in Sources */,
 | 
									37FB28422721B22200A57617 /* ContentItem.swift in Sources */,
 | 
				
			||||||
				376CD21726FBE18D001E1AC1 /* Instance+Fixtures.swift in Sources */,
 | 
									376CD21726FBE18D001E1AC1 /* Instance+Fixtures.swift in Sources */,
 | 
				
			||||||
				37B17DA1268A1F89006AEE9B /* VideoContextMenuView.swift in Sources */,
 | 
									37B17DA1268A1F89006AEE9B /* VideoContextMenuView.swift in Sources */,
 | 
				
			||||||
@@ -3694,6 +3701,7 @@
 | 
				
			|||||||
				374C0541272472C0009BDDBE /* PlayerSponsorBlock.swift in Sources */,
 | 
									374C0541272472C0009BDDBE /* PlayerSponsorBlock.swift in Sources */,
 | 
				
			||||||
				37130A61277657300033018A /* PersistenceController.swift in Sources */,
 | 
									37130A61277657300033018A /* PersistenceController.swift in Sources */,
 | 
				
			||||||
				37E70929271CDDAE00D34DDE /* OpenSettingsButton.swift in Sources */,
 | 
									37E70929271CDDAE00D34DDE /* OpenSettingsButton.swift in Sources */,
 | 
				
			||||||
 | 
									3717407F2949D40800FDDBC7 /* ChannelLinkView.swift in Sources */,
 | 
				
			||||||
				370F4FAA27CC163B001B35DC /* PlayerBackend.swift in Sources */,
 | 
									370F4FAA27CC163B001B35DC /* PlayerBackend.swift in Sources */,
 | 
				
			||||||
				376A33E62720CB35000C1D6B /* Account.swift in Sources */,
 | 
									376A33E62720CB35000C1D6B /* Account.swift in Sources */,
 | 
				
			||||||
				3763C98B290C7A50004D3B5F /* OpenVideosView.swift in Sources */,
 | 
									3763C98B290C7A50004D3B5F /* OpenVideosView.swift in Sources */,
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user