// // MediaRow.swift // // Copyright 2026 Brendan Szymanski // // 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 . // // SPDX-License-Identifier: GPL-3.0-or-later // import Adwaita import Foundation import LuminateCore import LuminateDI public struct MediaRow: View { public var title: String public var items: [BaseItemDto] public var type: DisplayType public var onSeeAll: (() -> Void)? @Binding public var navigation: NavigationStack @Injected(\.client) private var client public init( title: String, items: [BaseItemDto], type: DisplayType = .mixed, navigation: Binding>, onSeeAll: @escaping () -> Void ) { self.title = title self.items = items self.type = type _navigation = navigation self.onSeeAll = onSeeAll } public var view: Body { VStack(spacing: 16) { HStack { Text(title) .title3() .halign(.start) if let onSeeAll { Button("See All") { onSeeAll() } .halign(.end) .hexpand() } } .halign(.fill) ScrollView { ForEach(items, horizontal: true) { item in HomePosterCell( itemId: type.itemId(for: item), title: type.title(for: item), subtitle: type.subtitle(for: item), width: type.itemWidth(), imageAspectRatio: type.aspectRatio ) { if let childDisplayType = item.childDisplayType { navigation.push(.item(item: item, type: childDisplayType)) } if item.type == .movie { navigation.push(.movieDetail(item: item)) } } .padding(16, .trailing) } } .vscrollbarPolicy(.never) .hscrollbarPolicy(.external) .style("undershoot-start") .style("undershoot-end") } } }