tvos collection support
This commit is contained in:
parent
dbe95f29db
commit
4e0e6635c2
|
@ -0,0 +1,69 @@
|
|||
//
|
||||
/*
|
||||
* SwiftFin is subject to the terms of the Mozilla Public
|
||||
* License, v2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||
*/
|
||||
|
||||
import Defaults
|
||||
import Introspect
|
||||
import SwiftUI
|
||||
|
||||
struct CinematicCollectionItemView: View {
|
||||
|
||||
@EnvironmentObject var itemRouter: ItemCoordinator.Router
|
||||
@ObservedObject var viewModel: CollectionItemViewModel
|
||||
@State var wrappedScrollView: UIScrollView?
|
||||
@Default(.showPosterLabels) var showPosterLabels
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
|
||||
ImageView(src: viewModel.item.getBackdropImage(maxWidth: 1920),
|
||||
bh: viewModel.item.getBackdropImageBlurHash())
|
||||
.ignoresSafeArea()
|
||||
|
||||
ScrollView {
|
||||
VStack(spacing: 0) {
|
||||
|
||||
CinematicItemViewTopRow(viewModel: viewModel,
|
||||
wrappedScrollView: wrappedScrollView,
|
||||
title: viewModel.item.name ?? "",
|
||||
showDetails: false)
|
||||
.focusSection()
|
||||
.frame(height: UIScreen.main.bounds.height - 10)
|
||||
|
||||
ZStack(alignment: .topLeading) {
|
||||
|
||||
Color.black.ignoresSafeArea()
|
||||
|
||||
VStack(alignment: .leading, spacing: 20) {
|
||||
|
||||
CinematicItemAboutView(viewModel: viewModel)
|
||||
|
||||
PortraitItemsRowView(rowTitle: "Items",
|
||||
items: viewModel.collectionItems) { item in
|
||||
itemRouter.route(to: \.item, item)
|
||||
}
|
||||
|
||||
if !viewModel.similarItems.isEmpty {
|
||||
PortraitItemsRowView(rowTitle: "Recommended",
|
||||
items: viewModel.similarItems,
|
||||
showItemTitles: showPosterLabels) { item in
|
||||
itemRouter.route(to: \.item, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 50)
|
||||
}
|
||||
}
|
||||
}
|
||||
.introspectScrollView { scrollView in
|
||||
wrappedScrollView = scrollView
|
||||
}
|
||||
.ignoresSafeArea()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,6 +18,19 @@ struct CinematicItemViewTopRow: View {
|
|||
@State var wrappedScrollView: UIScrollView?
|
||||
@State var title: String
|
||||
@State var subtitle: String?
|
||||
let showDetails: Bool
|
||||
|
||||
init(viewModel: ItemViewModel,
|
||||
wrappedScrollView: UIScrollView? = nil,
|
||||
title: String,
|
||||
subtitle: String? = nil,
|
||||
showDetails: Bool = true) {
|
||||
self.viewModel = viewModel
|
||||
self.wrappedScrollView = wrappedScrollView
|
||||
self.title = title
|
||||
self.subtitle = subtitle
|
||||
self.showDetails = showDetails
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack(alignment: .bottom) {
|
||||
|
@ -69,35 +82,39 @@ struct CinematicItemViewTopRow: View {
|
|||
|
||||
HStack(alignment: .PlayInformationAlignmentGuide, spacing: 20) {
|
||||
|
||||
if viewModel.item.itemType == .series {
|
||||
if let airTime = viewModel.item.airTime {
|
||||
Text(airTime)
|
||||
.font(.subheadline)
|
||||
.fontWeight(.medium)
|
||||
}
|
||||
} else {
|
||||
if let runtime = viewModel.item.getItemRuntime() {
|
||||
Text(runtime)
|
||||
.font(.subheadline)
|
||||
.fontWeight(.medium)
|
||||
if showDetails {
|
||||
if viewModel.item.itemType == .series {
|
||||
if let airTime = viewModel.item.airTime {
|
||||
Text(airTime)
|
||||
.font(.subheadline)
|
||||
.fontWeight(.medium)
|
||||
}
|
||||
} else {
|
||||
if let runtime = viewModel.item.getItemRuntime() {
|
||||
Text(runtime)
|
||||
.font(.subheadline)
|
||||
.fontWeight(.medium)
|
||||
}
|
||||
|
||||
if let productionYear = viewModel.item.productionYear {
|
||||
Text(String(productionYear))
|
||||
.font(.subheadline)
|
||||
.fontWeight(.medium)
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if let productionYear = viewModel.item.productionYear {
|
||||
Text(String(productionYear))
|
||||
if let officialRating = viewModel.item.officialRating {
|
||||
Text(officialRating)
|
||||
.font(.subheadline)
|
||||
.fontWeight(.medium)
|
||||
.fontWeight(.semibold)
|
||||
.lineLimit(1)
|
||||
.padding(EdgeInsets(top: 1, leading: 4, bottom: 1, trailing: 4))
|
||||
.overlay(RoundedRectangle(cornerRadius: 2)
|
||||
.stroke(Color.secondary, lineWidth: 1))
|
||||
}
|
||||
}
|
||||
|
||||
if let officialRating = viewModel.item.officialRating {
|
||||
Text(officialRating)
|
||||
.font(.subheadline)
|
||||
.fontWeight(.semibold)
|
||||
.lineLimit(1)
|
||||
.padding(EdgeInsets(top: 1, leading: 4, bottom: 1, trailing: 4))
|
||||
.overlay(RoundedRectangle(cornerRadius: 2)
|
||||
.stroke(Color.secondary, lineWidth: 1))
|
||||
} else {
|
||||
Text("")
|
||||
}
|
||||
}
|
||||
.foregroundColor(.secondary)
|
||||
|
|
|
@ -60,6 +60,8 @@ struct ItemView: View {
|
|||
} else {
|
||||
SeriesItemView(viewModel: SeriesItemViewModel(item: item))
|
||||
}
|
||||
case .boxset:
|
||||
CinematicCollectionItemView(viewModel: CollectionItemViewModel(item: item))
|
||||
default:
|
||||
Text(L10n.notImplementedYetWithType(item.type ?? ""))
|
||||
}
|
||||
|
|
|
@ -22,38 +22,38 @@ struct LibraryListView: View {
|
|||
ScrollView {
|
||||
LazyVStack {
|
||||
if !viewModel.isLoading {
|
||||
ForEach(viewModel.libraries, id: \.id) { library in
|
||||
if library.collectionType ?? "" == "movies" || library.collectionType ?? "" == "tvshows" || library.collectionType ?? "" == "music" {
|
||||
EmptyView()
|
||||
} else {
|
||||
if library.collectionType == "livetv" {
|
||||
if liveTVAlphaEnabled {
|
||||
Button() {
|
||||
self.mainCoordinator.root(\.liveTV)
|
||||
|
||||
if let collectionLibraryItem = viewModel.libraries.first(where: { $0.collectionType == "boxsets" }) {
|
||||
Button() {
|
||||
self.libraryListRouter.route(to: \.library,
|
||||
(viewModel: LibraryViewModel(parentID: collectionLibraryItem.id), title: collectionLibraryItem.name ?? ""))
|
||||
}
|
||||
label: {
|
||||
ZStack {
|
||||
HStack {
|
||||
Spacer()
|
||||
VStack {
|
||||
Text(collectionLibraryItem.name ?? "")
|
||||
.foregroundColor(.white)
|
||||
.font(.title2)
|
||||
.fontWeight(.semibold)
|
||||
}
|
||||
label: {
|
||||
ZStack {
|
||||
HStack {
|
||||
Spacer()
|
||||
VStack {
|
||||
Text(library.name ?? "")
|
||||
.foregroundColor(.white)
|
||||
.font(.title2)
|
||||
.fontWeight(.semibold)
|
||||
}
|
||||
Spacer()
|
||||
}.padding(32)
|
||||
}
|
||||
.frame(minWidth: 100, maxWidth: .infinity)
|
||||
.frame(height: 100)
|
||||
}
|
||||
.cornerRadius(10)
|
||||
.shadow(radius: 5)
|
||||
.padding(.bottom, 5)
|
||||
}
|
||||
} else {
|
||||
Spacer()
|
||||
}.padding(32)
|
||||
}
|
||||
.frame(minWidth: 100, maxWidth: .infinity)
|
||||
.frame(height: 100)
|
||||
}
|
||||
.cornerRadius(10)
|
||||
.shadow(radius: 5)
|
||||
.padding(.bottom, 5)
|
||||
}
|
||||
|
||||
ForEach(viewModel.libraries.filter({ $0.collectionType != "boxsets" }), id: \.id) { library in
|
||||
if library.collectionType == "livetv" {
|
||||
if liveTVAlphaEnabled {
|
||||
Button() {
|
||||
self.libraryListRouter.route(to: \.library, (viewModel: LibraryViewModel(), title: library.name ?? ""))
|
||||
self.mainCoordinator.root(\.liveTV)
|
||||
}
|
||||
label: {
|
||||
ZStack {
|
||||
|
@ -75,6 +75,29 @@ struct LibraryListView: View {
|
|||
.shadow(radius: 5)
|
||||
.padding(.bottom, 5)
|
||||
}
|
||||
} else {
|
||||
Button() {
|
||||
self.libraryListRouter.route(to: \.library, (viewModel: LibraryViewModel(), title: library.name ?? ""))
|
||||
}
|
||||
label: {
|
||||
ZStack {
|
||||
HStack {
|
||||
Spacer()
|
||||
VStack {
|
||||
Text(library.name ?? "")
|
||||
.foregroundColor(.white)
|
||||
.font(.title2)
|
||||
.fontWeight(.semibold)
|
||||
}
|
||||
Spacer()
|
||||
}.padding(32)
|
||||
}
|
||||
.frame(minWidth: 100, maxWidth: .infinity)
|
||||
.frame(height: 100)
|
||||
}
|
||||
.cornerRadius(10)
|
||||
.shadow(radius: 5)
|
||||
.padding(.bottom, 5)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -248,6 +248,8 @@
|
|||
E100720726BDABC100CE3E31 /* MediaPlayButtonRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E100720626BDABC100CE3E31 /* MediaPlayButtonRowView.swift */; };
|
||||
E107BB9327880A8F00354E07 /* CollectionItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E107BB9227880A8F00354E07 /* CollectionItemViewModel.swift */; };
|
||||
E107BB9427880A8F00354E07 /* CollectionItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E107BB9227880A8F00354E07 /* CollectionItemViewModel.swift */; };
|
||||
E107BB962788104100354E07 /* CinematicCollectionItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E107BB952788104100354E07 /* CinematicCollectionItemView.swift */; };
|
||||
E107BB972788104100354E07 /* CinematicCollectionItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E107BB952788104100354E07 /* CinematicCollectionItemView.swift */; };
|
||||
E10D87DA2784E4F100BD264C /* ItemViewDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E10D87D92784E4F100BD264C /* ItemViewDetailsView.swift */; };
|
||||
E10D87DC2784EC5200BD264C /* EpisodesRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E10D87DB2784EC5200BD264C /* EpisodesRowView.swift */; };
|
||||
E10D87DE278510E400BD264C /* PosterSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = E10D87DD278510E300BD264C /* PosterSize.swift */; };
|
||||
|
@ -631,6 +633,7 @@
|
|||
C4E52304272CE68800654268 /* LiveTVChannelItemElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveTVChannelItemElement.swift; sourceTree = "<group>"; };
|
||||
E100720626BDABC100CE3E31 /* MediaPlayButtonRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPlayButtonRowView.swift; sourceTree = "<group>"; };
|
||||
E107BB9227880A8F00354E07 /* CollectionItemViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionItemViewModel.swift; sourceTree = "<group>"; };
|
||||
E107BB952788104100354E07 /* CinematicCollectionItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CinematicCollectionItemView.swift; sourceTree = "<group>"; };
|
||||
E10D87D92784E4F100BD264C /* ItemViewDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemViewDetailsView.swift; sourceTree = "<group>"; };
|
||||
E10D87DB2784EC5200BD264C /* EpisodesRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EpisodesRowView.swift; sourceTree = "<group>"; };
|
||||
E10D87DD278510E300BD264C /* PosterSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PosterSize.swift; sourceTree = "<group>"; };
|
||||
|
@ -1550,6 +1553,7 @@
|
|||
E1E5D53C2783A85F00692DFE /* CinematicItemView */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E107BB952788104100354E07 /* CinematicCollectionItemView.swift */,
|
||||
E1E5D5362783A52C00692DFE /* CinematicEpisodeItemView.swift */,
|
||||
E1E5D5452783C28100692DFE /* CinematicItemAboutView.swift */,
|
||||
E1E5D53A2783A80900692DFE /* CinematicItemViewTopRow.swift */,
|
||||
|
@ -2015,6 +2019,7 @@
|
|||
C40CD929271F8DAB000FB198 /* MovieLibrariesView.swift in Sources */,
|
||||
C4E5081D2703F8370045C9AB /* LibrarySearchView.swift in Sources */,
|
||||
53ABFDE9267974EF00886593 /* HomeViewModel.swift in Sources */,
|
||||
E107BB972788104100354E07 /* CinematicCollectionItemView.swift in Sources */,
|
||||
53116A17268B919A003024C9 /* SeriesItemView.swift in Sources */,
|
||||
E13DD3F027178F87009D4DAF /* SwiftfinNotificationCenter.swift in Sources */,
|
||||
531690E7267ABD79005D8AB9 /* HomeView.swift in Sources */,
|
||||
|
@ -2286,6 +2291,7 @@
|
|||
E1AD104D26D96CE3003E4A08 /* BaseItemDtoExtensions.swift in Sources */,
|
||||
E13DD3BF27163DD7009D4DAF /* AppDelegate.swift in Sources */,
|
||||
535870AD2669D8DD00D05A09 /* Typings.swift in Sources */,
|
||||
E107BB962788104100354E07 /* CinematicCollectionItemView.swift in Sources */,
|
||||
C4BE07882728448B003F4AD1 /* LiveTVChannelsCoordinator.swift in Sources */,
|
||||
E1AD105F26D9ADDD003E4A08 /* NameGUIDPairExtensions.swift in Sources */,
|
||||
E13DD3D5271693CD009D4DAF /* SwiftfinStoreDefaults.swift in Sources */,
|
||||
|
|
Loading…
Reference in New Issue