Luminate/Sources/LuminateLibrary/MovieDetailView.swift

157 lines
5.5 KiB
Swift

import Adwaita
import Foundation
import LuminateCore
struct MovieDetailView: View {
var item: Components.Schemas.BaseItemDto
var client: JellyfinClient
var userId: String
@State private var isFavorite: Bool
@State private var isPlayed: Bool
@State private var similarItems: [Components.Schemas.BaseItemDto] = []
@State private var backdropData: Data?
init(item: Components.Schemas.BaseItemDto, client: JellyfinClient, userId: String) {
self.item = item
self.client = client
self.userId = userId
_isFavorite = .init(wrappedValue: item.userData?.value1.isFavorite ?? false)
_isPlayed = .init(wrappedValue: item.userData?.value1.played ?? false)
}
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 ?? "")
.style("title-1")
.halign(.start)
HStack {
if let year = item.productionYear {
Text("\(year)")
}
Text(item.runtimeString)
if let rating = item.communityRating {
RatingBadge(rating: Double(rating))
}
}
.halign(.start)
HStack {
Button("Play", icon: .default(icon: .mediaPlaybackStart)) {
}
.style("suggested-action")
Button(icon: .default(icon: .bookmarkNew)) {
toggleFavorite()
}
Button(isPlayed ? "Mark Unplayed" : "Mark Played") {
togglePlayed()
}
.style("flat")
}
.padding(10, .vertical)
if let overview = item.overview {
Text(overview)
.style("body")
.halign(.start)
}
}
.hexpand(true)
}
.padding()
if let people = item.people, !people.isEmpty {
VStack {
Text("Cast")
.style("title-3")
.halign(.start)
FlowBox(people) { person in
PersonCell(person: person)
}
}
.padding()
}
if !similarItems.isEmpty {
VStack {
Text("Similar")
.style("title-3")
.halign(.start)
ScrollView {
HStack {
ForEach(similarItems) { sim in
PosterCell(item: sim, client: client)
}
}
}
.hscrollbarPolicy(.automatic)
}
.padding()
}
}
}
.onAppear {
loadBackdrop()
loadSimilar()
}
}
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 loadSimilar() {
Task {
let result = try? await client.getItems(
userId: userId,
includeItemTypes: [.movie],
fields: [.overview, .genres, .mediaSources],
sortBy: [.sortName],
limit: 10,
recursive: true
)
await MainActor.run { similarItems = result?.items ?? [] }
}
}
private func toggleFavorite() {
Task {
if isFavorite {
try? await client.unmarkFavoriteItem(itemId: item.id ?? "", userId: userId)
} else {
try? await client.markFavoriteItem(itemId: item.id ?? "", userId: userId)
}
await MainActor.run { isFavorite.toggle() }
}
}
private func togglePlayed() {
Task {
if isPlayed {
try? await client.markUnplayedItem(itemId: item.id ?? "", userId: userId)
} else {
try? await client.markPlayedItem(itemId: item.id ?? "", userId: userId)
}
await MainActor.run { isPlayed.toggle() }
}
}
}