Add channels thumbnails to cells and list

This commit is contained in:
Arkadiusz Fal
2022-12-14 12:56:47 +01:00
parent 3b31f21c81
commit 037d3a0481
7 changed files with 155 additions and 120 deletions

View 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
}

View File

@@ -11,4 +11,20 @@ struct Constants {
0.6
#endif
}
static var channelThumbnailSize: Double {
#if os(tvOS)
50
#else
30
#endif
}
static var channelDetailsStackSpacing: Double {
#if os(tvOS)
12
#else
6
#endif
}
}

View File

@@ -44,7 +44,7 @@ extension Defaults.Keys {
#if os(iOS)
static let lockPortraitWhenBrowsing = Key<Bool>("lockPortraitWhenBrowsing", default: UIDevice.current.userInterfaceIdiom == .phone)
#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 roundedThumbnails = Key<Bool>("roundedThumbnails", default: true)
static let thumbnailsQuality = Key<ThumbnailsQuality>("thumbnailsQuality", default: .highest)

View File

@@ -114,11 +114,11 @@ struct PlayerControls: View {
Text(player.currentVideo?.displayAuthor ?? "")
.fontWeight(.semibold)
.shadow(radius: 10)
.foregroundColor(.secondary)
.foregroundColor(.init(white: 0.8))
.font(.system(size: playerControlsLayout.authorLineFontSize))
.lineLimit(1)
}
.foregroundColor(.white)
.frame(maxWidth: .infinity, alignment: .leading)
.offset(y: -40)
}

View File

@@ -74,21 +74,22 @@ struct VideoBanner: View {
HStack {
HStack {
if !inChannelView,
let video,
let url = video.channel.thumbnailURLOrCached
{
ThumbnailView(url: url)
.frame(width: 30, height: 30)
.clipShape(Circle())
}
VStack(alignment: .leading) {
Group {
if let video {
if !inChannelView, !video.isLocal || video.localStreamIsRemoteURL {
channelControl
.font(.subheadline)
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)
}
}
} else {
#if os(iOS)
if DocumentsModel.shared.isDocument(video) {
@@ -220,7 +221,7 @@ struct VideoBanner: View {
private var thumbnailWidth: Double {
#if os(tvOS)
250
356
#else
120
#endif
@@ -228,7 +229,7 @@ struct VideoBanner: View {
private var thumbnailHeight: Double {
#if os(tvOS)
140
200
#else
72
#endif
@@ -308,50 +309,7 @@ struct VideoBanner: View {
(progressViewValue / progressViewTotal) * 100 > Double(Defaults[.watchedThreshold])
}
@ViewBuilder private var channelControl: 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 {
@ViewBuilder private var channelLabel: some View {
if let video, !video.displayAuthor.isEmpty {
Text(video.displayAuthor)
.fontWeight(.semibold)

View File

@@ -41,7 +41,7 @@ struct VideoCell: View {
Button(action: playAction) {
content
#if os(tvOS)
.frame(width: 580, height: 470)
.frame(width: 580, height: channelOnThumbnail ? 470 : 500)
#endif
}
.opacity(contentOpacity)
@@ -166,17 +166,22 @@ struct VideoCell: View {
videoDetail(video.displayTitle, lineLimit: 5)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
HStack(spacing: 12) {
HStack(spacing: Constants.channelDetailsStackSpacing) {
if !inChannelView,
let video,
let url = video.channel.thumbnailURLOrCached
{
ThumbnailView(url: url)
.frame(width: 30, height: 30)
.clipShape(Circle())
ChannelLinkView(channel: video.channel) {
ThumbnailView(url: url)
.frame(width: Constants.channelThumbnailSize, height: Constants.channelThumbnailSize)
.clipShape(Circle())
}
}
if !channelOnThumbnail, !inChannelView {
channelControl(badge: false)
ChannelLinkView(channel: video.channel) {
channelLabel(badge: false)
}
}
}
@@ -259,14 +264,22 @@ struct VideoCell: View {
#if os(tvOS)
.frame(minHeight: 60, alignment: .top)
#elseif os(macOS)
.frame(minHeight: 32, alignment: .top)
.frame(minHeight: 35, alignment: .top)
#else
.frame(minHeight: 40, alignment: .top)
.frame(minHeight: 43, alignment: .top)
#endif
if !channelOnThumbnail, !inChannelView {
channelControl(badge: false)
.padding(.top, 4)
.padding(.bottom, 6)
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(badge: false)
}
}
}
}
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
@@ -274,20 +287,23 @@ struct VideoCell: View {
#if os(tvOS)
.frame(minHeight: channelOnThumbnail ? 80 : 120, alignment: .top)
#elseif os(macOS)
.frame(minHeight: 35, alignment: .top)
.frame(minHeight: channelOnThumbnail ? 52 : 75, alignment: .top)
#else
.frame(minHeight: 50, alignment: .top)
.frame(minHeight: channelOnThumbnail ? 50 : 70, alignment: .top)
#endif
.padding(.bottom, 4)
HStack(spacing: 8) {
if !inChannelView,
if channelOnThumbnail,
!inChannelView,
let video,
let url = video.channel.thumbnailURLOrCached
{
ThumbnailView(url: url)
.frame(width: 30, height: 30)
.clipShape(Circle())
ChannelLinkView(channel: video.channel) {
ThumbnailView(url: url)
.frame(width: Constants.channelThumbnailSize, height: Constants.channelThumbnailSize)
.clipShape(Circle())
}
}
if let date = video.publishedDate {
@@ -314,7 +330,7 @@ struct VideoCell: View {
}
.lineLimit(1)
.foregroundColor(.secondary)
.frame(maxWidth: .infinity, minHeight: 30, alignment: .topLeading)
.frame(maxWidth: .infinity, minHeight: 35, alignment: .topLeading)
#if os(tvOS)
.padding(.bottom, 10)
#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 {
if badge {
DetailBadge(text: video.author, style: .prominent)
@@ -415,7 +390,9 @@ struct VideoCell: View {
Spacer()
if channelOnThumbnail, !inChannelView {
channelControl()
ChannelLinkView(channel: video.channel) {
channelLabel()
}
}
}
#if os(tvOS)