import SwiftUI import FlixyDesign import MediaKit /// Der Aufmacher oben auf der Startseite. struct HeroSection: View { let item: MediaItem @Environment(FlixySession.self) private var session @Environment(PlaybackCoordinator.self) private var playback @Environment(QuickPlay.self) private var quickPlay @Environment(\.flixyAccent) private var accent @Environment(\.horizontalSizeClass) private var sizeClass var body: some View { HeroBanner( title: item.rowTitle, metadata: metadata, summary: item.summary, artworkURLs: session.backend.map { HeroArtwork.backdropURLs(for: item, backend: $0, compact: sizeClass == .compact) } ?? [], logoURL: session.backend.flatMap { HeroArtwork.logoURL(for: item, backend: $0) }, // Auf dem iPhone hochkant: 16:9 ließ dem Text so wenig Platz, dass // Schriftzug und Beschreibung übereinanderlagen. aspectRatio: HeroArtwork.aspectRatio(compact: sizeClass == .compact), // Auf einem großen Bildschirm nimmt der Aufmacher die Höhe, die // das Karussell ihm gibt — sonst wäre er so hoch, wie 16:9 es // vorschreibt, und darüber und darunter bliebe der halbe // Bildschirm leer. fillsAvailableSpace: sizeClass != .compact ) { Button { // **Über QuickPlay, nicht unmittelbar.** Steht im Aufmacher // eine Serie — und Jellyfins „Zuletzt hinzugefügt" liefert // neue Folgen als ihre Serie —, schickte der direkte Weg // deren eigene Kennung an den Player, und der scheiterte mit // „keine abspielbare Datei". Eine Serie hat keine Fassungen. quickPlay.start(item, backend: session.backend, coordinator: playback) } label: { Label(playLabel, systemImage: "play.fill") .font(.flixyCardTitle) .padding(.horizontal, FlixySpacing.loose) .padding(.vertical, FlixySpacing.snug) } .buttonStyle(.borderedProminent) .tint(accent) NavigationLink(value: MediaRoute(ratingKey: item.id, title: item.title)) { Label("hero.details", systemImage: "info.circle") .font(.flixyCardTitle) .padding(.horizontal, FlixySpacing.loose) .padding(.vertical, FlixySpacing.snug) } .buttonStyle(.bordered) .tint(Color.flixyTextSecondary) } } /// „Weiterschauen" statt „Abspielen", wenn schon etwas gesehen wurde — /// sonst wäre nicht erkennbar, dass der Film an der alten Stelle weitergeht. private var playLabel: LocalizedStringKey { item.watchProgress == nil ? "hero.play" : "hero.resume" } private var metadata: String? { var pieces: [String] = [] if item.kind == .episode, let season = item.parentIndex, let episode = item.index { pieces.append(String(localized: "episode.short \(season) \(episode)")) } if let year = item.year { pieces.append(String(year)) } if let duration = item.duration { pieces.append(Duration.milliseconds(duration).runtimeLabel) } if let rating = item.contentRating { pieces.append(rating) } return pieces.isEmpty ? nil : pieces.joined(separator: " · ") } }