96 lines
3.1 KiB
Swift
96 lines
3.1 KiB
Swift
//
|
|
// Swiftfin is subject to the terms of the Mozilla Public
|
|
// License, v2.0. If a copy of the MPL was not distributed with this
|
|
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
|
//
|
|
// Copyright (c) 2025 Jellyfin & Jellyfin Contributors
|
|
//
|
|
|
|
import JellyfinAPI
|
|
import SwiftUI
|
|
|
|
struct EPGChannelRow: View {
|
|
|
|
let channelProgram: ChannelProgram
|
|
let timeWindowStart: Date
|
|
let timeWindowEnd: Date
|
|
let pixelsPerMinute: CGFloat
|
|
|
|
private let channelColumnWidth: CGFloat = 200
|
|
|
|
var body: some View {
|
|
HStack(spacing: 0) {
|
|
// Channel info column (fixed width on left)
|
|
channelInfoView
|
|
.frame(width: channelColumnWidth)
|
|
|
|
// Programs timeline
|
|
programsTimeline
|
|
}
|
|
.frame(height: 120)
|
|
}
|
|
|
|
private var channelInfoView: some View {
|
|
VStack(spacing: 8) {
|
|
ZStack {
|
|
Color.clear
|
|
|
|
ImageView(channelProgram.portraitImageSources(maxWidth: 80))
|
|
.image {
|
|
$0.aspectRatio(contentMode: .fit)
|
|
}
|
|
.failure {
|
|
SystemImageContentView(systemName: channelProgram.systemImage, ratio: 0.66)
|
|
.background(color: .clear)
|
|
}
|
|
.placeholder { _ in
|
|
EmptyView()
|
|
}
|
|
}
|
|
.frame(width: 80, height: 80)
|
|
.aspectRatio(1.0, contentMode: .fit)
|
|
|
|
Text(channelProgram.displayTitle)
|
|
.font(.caption)
|
|
.lineLimit(2)
|
|
.multilineTextAlignment(.center)
|
|
}
|
|
.padding(.horizontal, 8)
|
|
}
|
|
|
|
private var programsTimeline: some View {
|
|
ScrollViewReader { proxy in
|
|
ScrollView(.horizontal, showsIndicators: false) {
|
|
HStack(spacing: 4) {
|
|
ForEach(channelProgram.programs, id: \.id) { program in
|
|
if let startDate = program.startDate,
|
|
let endDate = program.endDate
|
|
{
|
|
|
|
let duration = endDate.timeIntervalSince(startDate) / 60 // minutes
|
|
let cellWidth = CGFloat(duration) * pixelsPerMinute
|
|
|
|
let isCurrentlyAiring = (startDate ... endDate).contains(Date.now)
|
|
|
|
EPGProgramCell(
|
|
program: program,
|
|
channel: channelProgram.channel,
|
|
cellWidth: max(cellWidth, 150), // Minimum width for readability
|
|
isCurrentlyAiring: isCurrentlyAiring
|
|
)
|
|
.id(program.id)
|
|
}
|
|
}
|
|
}
|
|
.padding(.horizontal, 8)
|
|
}
|
|
.onAppear {
|
|
// Scroll to currently airing program
|
|
if let currentProgram = channelProgram.currentProgram {
|
|
proxy.scrollTo(currentProgram.id, anchor: .leading)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|