Files
yattee/Yattee/Views/Components/ChannelRowView.swift
2026-02-08 18:33:56 +01:00

106 lines
3.0 KiB
Swift

//
// ChannelRowView.swift
// Yattee
//
// A row view for displaying channel information in search results.
//
import SwiftUI
import NukeUI
struct ChannelRowView: View {
let channel: Channel
var style: VideoRowStyle = .regular
var authHeader: String?
// Style-based dimensions - use thumbnail height to keep avatar square and match row height
private var avatarSize: CGFloat {
style.thumbnailHeight
}
private var nameLines: Int {
switch style {
case .large: return 2
case .regular: return 1
case .compact: return 1
}
}
private var nameFont: Font {
#if os(tvOS)
style == .compact ? .caption : .subheadline
#else
.subheadline
#endif
}
private var subscriberFont: Font {
#if os(tvOS)
style == .compact ? .caption2 : .caption
#else
style == .compact ? .caption2 : .caption
#endif
}
private var showChevron: Bool {
style != .compact
}
var body: some View {
HStack(spacing: 12) {
// Avatar - centered in container with same width as video thumbnail
LazyImage(request: AvatarURLBuilder.imageRequest(url: channel.thumbnailURL, authHeader: authHeader)) { state in
if let image = state.image {
image
.resizable()
.aspectRatio(contentMode: .fill)
} else {
avatarPlaceholder
}
}
.frame(width: avatarSize, height: avatarSize)
.clipShape(Circle())
.frame(width: style.thumbnailWidth)
// Info
VStack(alignment: .leading, spacing: 4) {
Text(channel.name)
.font(nameFont)
.fontWeight(.medium)
.lineLimit(nameLines)
if let subscribers = channel.formattedSubscriberCount {
HStack(spacing: 4) {
Text(String(localized: "channel.subscriberCount \(subscribers)"))
.font(subscriberFont)
.foregroundStyle(.secondary)
if channel.isVerified {
Image(systemName: "checkmark.seal.fill")
.font(.caption2)
.foregroundStyle(.secondary)
}
}
}
}
Spacer()
if showChevron {
Image(systemName: "chevron.right")
.foregroundStyle(.tertiary)
}
}
}
private var avatarPlaceholder: some View {
Circle()
.fill(.quaternary)
.overlay {
Text(String(channel.name.prefix(1)))
.font(.title3)
.foregroundStyle(.secondary)
}
}
}