Luminate/Sources/LuminateUI/Components/TVShowView.swift

123 lines
4.2 KiB
Swift

//
// TVShowView.swift
//
// Copyright 2026 Brendan Szymanski <hello@bscubed.dev>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
import Adwaita
import Foundation
import LuminateCore
struct TVShowView: View {
var item: Components.Schemas.BaseItemDto
var client: JellyfinClient
var userId: String
@State private var seasons: [Components.Schemas.BaseItemDto] = []
@State private var selectedSeasonId: String?
@State private var backdropData: Data?
var view: Body {
ScrollView {
VStack {
if let data = backdropData {
Picture()
.data(data)
.frame(minHeight: 300)
.frame(maxHeight: 300)
.hexpand(true)
}
HStack {
PosterCell(item: item, client: client)
.frame(minWidth: 200)
.frame(maxWidth: 200)
VStack {
Text(item.name ?? "")
.title1()
.halign(.start)
if let status = item.status {
Text(status)
.caption()
.halign(.start)
}
if let overview = item.overview {
Text(overview)
.body()
.halign(.start)
.padding(10, .vertical)
}
}
.hexpand(true)
}
.padding()
if !seasons.isEmpty {
VStack {
Text("Season")
.caption()
.halign(.start)
HStack {
ForEach(seasons) { season in
Button(season.name ?? "?") {
selectedSeasonId = season.id
}
.suggested(selectedSeasonId == season.id)
.flat(selectedSeasonId != season.id)
}
}
}
.padding(10, .horizontal)
}
if let seasonId = selectedSeasonId {
EpisodeList(
seriesId: item.id ?? "",
seasonId: seasonId,
client: client,
userId: userId
)
}
}
}
.onAppear {
loadSeasons()
loadBackdrop()
}
}
private func loadBackdrop() {
guard let tag = item.backdropImageTag, let itemId = item.id else { return }
Task {
guard
let url = await client.imageURL(
itemId: itemId, imageType: .backdrop, tag: tag, maxWidth: 1920
)
else { return }
let service = ImageService()
backdropData = try? await service.loadImage(url: url)
}
}
private func loadSeasons() {
Task {
let result = try? await client.getSeasons(seriesId: item.id ?? "", userId: userId)
await MainActor.run {
seasons = result?.items ?? []
selectedSeasonId = seasons.first?.id
}
}
}
}