Luminate/Sources/LuminateLibrary/MovieDetailView.swift

153 lines
5.4 KiB
Swift

import Adwaita
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(initialValue: item.UserData?.IsFavorite ?? false)
_isPlayed = .init(initialValue: item.UserData?.Played ?? false)
}
var view: Body {
ScrollView {
VStack {
if let data = backdropData {
Picture()
.data(data)
.frame(minHeight: 300, maxHeight: 300)
.hexpand(true)
}
HStack(alignment: .top) {
PosterCell(item: item, client: client)
.frame(minWidth: 200, 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: rating)
}
}
.halign(.start)
HStack {
Button("Play", icon: .default(icon: .mediaPlaybackStart)) {
}
.style("suggested-action")
Button(icon: .default(icon: .bookmark)) {
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 {
ForEach(people) { person in
PersonCell(person: person)
}
}
}
.padding()
}
if !similarItems.isEmpty {
VStack {
Text("Similar")
.style("title-3")
.halign(.start)
ScrollView(.horizontal) {
HStack {
ForEach(similarItems) { sim in
PosterCell(item: sim, client: client)
}
}
}
}
.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() }
}
}
}