Additional coordinators and routing fixes

This commit is contained in:
jhays 2021-10-20 17:58:45 -05:00
parent ccc09d5718
commit 2b888e9b82
16 changed files with 645 additions and 155 deletions

View File

@ -9,11 +9,14 @@
import SwiftUI import SwiftUI
import JellyfinAPI import JellyfinAPI
import Combine import Combine
import Stinsen
struct ContinueWatchingView: View { struct ContinueWatchingView: View {
var items: [BaseItemDto] var items: [BaseItemDto]
@Namespace private var namespace @Namespace private var namespace
var homeRouter: HomeCoordinator.Router? = RouterStore.shared.retrieve()
var body: some View { var body: some View {
VStack(alignment: .leading) { VStack(alignment: .leading) {
if items.count > 0 { if items.count > 0 {
@ -25,7 +28,9 @@ struct ContinueWatchingView: View {
LazyHStack { LazyHStack {
Spacer().frame(width: 45) Spacer().frame(width: 45)
ForEach(items, id: \.id) { item in ForEach(items, id: \.id) { item in
NavigationLink(destination: LazyView { ItemView(item: item) }) { Button {
self.homeRouter?.route(to: \.modalItem, item)
} label: {
LandscapeItemElement(item: item) LandscapeItemElement(item: item)
} }
.buttonStyle(PlainNavigationLinkButtonStyle()) .buttonStyle(PlainNavigationLinkButtonStyle())

View File

@ -11,6 +11,7 @@ import Foundation
import SwiftUI import SwiftUI
struct HomeView: View { struct HomeView: View {
@EnvironmentObject var homeRouter: HomeCoordinator.Router
@StateObject var viewModel = HomeViewModel() @StateObject var viewModel = HomeViewModel()
@State var showingSettings = false @State var showingSettings = false
@ -33,9 +34,9 @@ struct HomeView: View {
VStack(alignment: .leading) { VStack(alignment: .leading) {
let library = viewModel.libraries.first(where: { $0.id == libraryID }) let library = viewModel.libraries.first(where: { $0.id == libraryID })
NavigationLink(destination: LazyView { Button {
LibraryView(viewModel: .init(parentID: libraryID, filters: viewModel.recentFilterSet), title: library?.name ?? "") self.homeRouter.route(to: \.modalLibrary, (.init(parentID: libraryID, filters: viewModel.recentFilterSet), title: library?.name ?? ""))
}) { } label: {
HStack { HStack {
Text("Latest \(library?.name ?? "")") Text("Latest \(library?.name ?? "")")
.font(.headline) .font(.headline)

View File

@ -16,47 +16,11 @@ struct LibraryListView: View {
var body: some View { var body: some View {
ScrollView { ScrollView {
LazyVStack { LazyVStack {
NavigationLink(destination: LazyView {
LibraryView(viewModel: .init(filters: viewModel.withFavorites), title: "Favorites")
}) {
ZStack {
HStack {
Spacer()
Text("Your Favorites")
.font(.subheadline)
.fontWeight(.semibold)
Spacer()
}
}
.padding(16)
.frame(minWidth: 100, maxWidth: .infinity)
}
.cornerRadius(10)
.shadow(radius: 5)
.padding(.bottom, 5)
NavigationLink(destination: LazyView {
Text("WIP")
}) {
ZStack {
HStack {
Spacer()
Text("All Genres")
.font(.subheadline)
.fontWeight(.semibold)
Spacer()
}
}
.padding(16)
.frame(minWidth: 100, maxWidth: .infinity)
}
.cornerRadius(10)
.shadow(radius: 5)
.padding(.bottom, 15)
if !viewModel.isLoading { if !viewModel.isLoading {
ForEach(viewModel.libraries, id: \.id) { library in ForEach(viewModel.libraries, id: \.id) { library in
if library.collectionType ?? "" == "movies" || library.collectionType ?? "" == "tvshows" { if library.collectionType ?? "" == "movies" || library.collectionType ?? "" == "tvshows" {
EmptyView()
} else {
NavigationLink(destination: LazyView { NavigationLink(destination: LazyView {
LibraryView(viewModel: .init(parentID: library.id), title: library.name ?? "") LibraryView(viewModel: .init(parentID: library.id), title: library.name ?? "")
}) { }) {
@ -80,8 +44,6 @@ struct LibraryListView: View {
.cornerRadius(10) .cornerRadius(10)
.shadow(radius: 5) .shadow(radius: 5)
.padding(.bottom, 5) .padding(.bottom, 5)
} else {
EmptyView()
} }
} }
} else { } else {

View File

@ -11,80 +11,85 @@ import SwiftUICollection
import JellyfinAPI import JellyfinAPI
struct LibraryView: View { struct LibraryView: View {
@StateObject var viewModel: LibraryViewModel @EnvironmentObject var libraryRouter: LibraryCoordinator.Router
var title: String @StateObject var viewModel: LibraryViewModel
var title: String
// MARK: tracks for grid // MARK: tracks for grid
var defaultFilters = LibraryFilters(filters: [], sortOrder: [.ascending], withGenres: [], tags: [], sortBy: [.name]) var defaultFilters = LibraryFilters(filters: [], sortOrder: [.ascending], withGenres: [], tags: [], sortBy: [.name])
@State var isShowingSearchView = false @State var isShowingSearchView = false
@State var isShowingFilterView = false @State var isShowingFilterView = false
var body: some View { var body: some View {
if viewModel.isLoading == true { if viewModel.isLoading == true {
ProgressView() ProgressView()
} else if !viewModel.items.isEmpty { } else if !viewModel.rows.isEmpty {
CollectionView(rows: viewModel.rows) { _, _ in CollectionView(rows: viewModel.rows) { _, _ in
let itemSize = NSCollectionLayoutSize( let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1), widthDimension: .fractionalWidth(1),
heightDimension: .fractionalHeight(1) heightDimension: .fractionalHeight(1)
) )
let item = NSCollectionLayoutItem(layoutSize: itemSize) let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize( let groupSize = NSCollectionLayoutSize(
widthDimension: .absolute(200), widthDimension: .absolute(200),
heightDimension: .absolute(300) heightDimension: .absolute(300)
) )
let group = NSCollectionLayoutGroup.horizontal( let group = NSCollectionLayoutGroup.horizontal(
layoutSize: groupSize, layoutSize: groupSize,
subitems: [item] subitems: [item]
) )
let header = let header =
NSCollectionLayoutBoundarySupplementaryItem( NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: NSCollectionLayoutSize( layoutSize: NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1), widthDimension: .fractionalWidth(1),
heightDimension: .absolute(44) heightDimension: .absolute(44)
), ),
elementKind: UICollectionView.elementKindSectionHeader, elementKind: UICollectionView.elementKindSectionHeader,
alignment: .topLeading alignment: .topLeading
) )
let section = NSCollectionLayoutSection(group: group) let section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(top: 30, leading: 0, bottom: 80, trailing: 80) section.contentInsets = NSDirectionalEdgeInsets(top: 30, leading: 0, bottom: 80, trailing: 80)
section.interGroupSpacing = 48 section.interGroupSpacing = 48
section.orthogonalScrollingBehavior = .continuous section.orthogonalScrollingBehavior = .continuous
section.boundarySupplementaryItems = [header] section.boundarySupplementaryItems = [header]
return section return section
} cell: { _, cell in } cell: { _, cell in
GeometryReader { _ in GeometryReader { _ in
if let item = cell.item { if let item = cell.item {
if item.type != "Folder" { if item.type != "Folder" {
NavigationLink(destination: LazyView { ItemView(item: item) }) { Button {
PortraitItemElement(item: item) libraryRouter.route(to: \.modalItem, item)
} } label: {
.buttonStyle(PlainNavigationLinkButtonStyle()) PortraitItemElement(item: item)
.onAppear { }
if item == viewModel.items.last && viewModel.hasNextPage { .buttonStyle(PlainNavigationLinkButtonStyle())
viewModel.requestNextPageAsync() .onAppear {
if item == viewModel.items.last && viewModel.hasNextPage {
viewModel.requestNextPageAsync()
}
}
}
} else if cell.loadingCell {
ProgressView()
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center)
} }
}
} }
} else if cell.loadingCell { } supplementaryView: { _, indexPath in
ProgressView() HStack {
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center) Spacer()
} }.accessibilityIdentifier("\(indexPath.section).\(indexPath.row)")
} }
} supplementaryView: { _, indexPath in .frame(maxWidth: .infinity, maxHeight: .infinity)
HStack { .ignoresSafeArea(.all)
Spacer()
}.accessibilityIdentifier("\(indexPath.section).\(indexPath.row)")
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.ignoresSafeArea(.all)
} else { } else {
Text("No results.") Button { } label: {
Text("No results.")
}
} }
} }
} }

View File

@ -0,0 +1,82 @@
/*
* JellyfinPlayer/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 SwiftUI
import SwiftUICollection
import JellyfinAPI
struct MovieLibrariesView: View {
@EnvironmentObject var movieLibrariesRouter: MovieLibrariesCoordinator.Router
@StateObject var viewModel: MovieLibrariesViewModel
var title: String
var body: some View {
if viewModel.isLoading == true {
ProgressView()
} else if !viewModel.rows.isEmpty {
CollectionView(rows: viewModel.rows) { _, _ in
let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1),
heightDimension: .fractionalHeight(1)
)
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(
widthDimension: .absolute(200),
heightDimension: .absolute(300)
)
let group = NSCollectionLayoutGroup.horizontal(
layoutSize: groupSize,
subitems: [item]
)
let header =
NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1),
heightDimension: .absolute(44)
),
elementKind: UICollectionView.elementKindSectionHeader,
alignment: .topLeading
)
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(top: 30, leading: 0, bottom: 80, trailing: 80)
section.interGroupSpacing = 48
section.orthogonalScrollingBehavior = .continuous
section.boundarySupplementaryItems = [header]
return section
} cell: { _, cell in
GeometryReader { _ in
if let item = cell.item {
if item.type != "Folder" {
Button {
self.movieLibrariesRouter.route(to: \.library, item)
} label: {
PortraitItemElement(item: item)
}
.buttonStyle(PlainNavigationLinkButtonStyle())
}
} else if cell.loadingCell {
ProgressView()
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center)
}
}
} supplementaryView: { _, indexPath in
HStack {
Spacer()
}.accessibilityIdentifier("\(indexPath.section).\(indexPath.row)")
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.ignoresSafeArea(.all)
} else {
Text("No results.")
}
}
}

View File

@ -9,9 +9,12 @@
import SwiftUI import SwiftUI
import JellyfinAPI import JellyfinAPI
import Combine import Combine
import Stinsen
struct NextUpView: View { struct NextUpView: View {
var items: [BaseItemDto] var items: [BaseItemDto]
var homeRouter: HomeCoordinator.Router? = RouterStore.shared.retrieve()
var body: some View { var body: some View {
VStack(alignment: .leading) { VStack(alignment: .leading) {
@ -24,7 +27,9 @@ struct NextUpView: View {
LazyHStack { LazyHStack {
Spacer().frame(width: 45) Spacer().frame(width: 45)
ForEach(items, id: \.id) { item in ForEach(items, id: \.id) { item in
NavigationLink(destination: LazyView { ItemView(item: item) }) { Button {
self.homeRouter?.route(to: \.modalItem, item)
} label: {
LandscapeItemElement(item: item) LandscapeItemElement(item: item)
}.buttonStyle(PlainNavigationLinkButtonStyle()) }.buttonStyle(PlainNavigationLinkButtonStyle())
} }

View File

@ -0,0 +1,80 @@
/*
* JellyfinPlayer/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 SwiftUI
import SwiftUICollection
import JellyfinAPI
struct TVLibrariesView: View {
@EnvironmentObject var tvLibrariesRouter: TVLibrariesCoordinator.Router
@StateObject var viewModel: TVLibrariesViewModel
var title: String
var body: some View {
if viewModel.isLoading == true {
ProgressView()
} else if !viewModel.rows.isEmpty {
CollectionView(rows: viewModel.rows) { _, _ in
let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1),
heightDimension: .fractionalHeight(1)
)
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(
widthDimension: .absolute(200),
heightDimension: .absolute(300)
)
let group = NSCollectionLayoutGroup.horizontal(
layoutSize: groupSize,
subitems: [item]
)
let header =
NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1),
heightDimension: .absolute(44)
),
elementKind: UICollectionView.elementKindSectionHeader,
alignment: .topLeading
)
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(top: 30, leading: 0, bottom: 80, trailing: 80)
section.interGroupSpacing = 48
section.orthogonalScrollingBehavior = .continuous
section.boundarySupplementaryItems = [header]
return section
} cell: { _, cell in
GeometryReader { _ in
if let item = cell.item {
if item.type != "Folder" {
Button {} label: {
PortraitItemElement(item: item)
}
.buttonStyle(PlainNavigationLinkButtonStyle())
}
} else if cell.loadingCell {
ProgressView()
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center)
}
}
} supplementaryView: { _, indexPath in
HStack {
Spacer()
}.accessibilityIdentifier("\(indexPath.section).\(indexPath.row)")
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.ignoresSafeArea(.all)
} else {
Text("No results.")
}
}
}

View File

@ -219,7 +219,19 @@
62EC353426766B03000E9F2D /* DeviceRotationViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */; }; 62EC353426766B03000E9F2D /* DeviceRotationViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */; };
62ECA01826FA685A00E8EBB7 /* DeepLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62ECA01726FA685A00E8EBB7 /* DeepLink.swift */; }; 62ECA01826FA685A00E8EBB7 /* DeepLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62ECA01726FA685A00E8EBB7 /* DeepLink.swift */; };
AE8C3159265D6F90008AA076 /* bitrates.json in Resources */ = {isa = PBXBuildFile; fileRef = AE8C3158265D6F90008AA076 /* bitrates.json */; }; AE8C3159265D6F90008AA076 /* bitrates.json in Resources */ = {isa = PBXBuildFile; fileRef = AE8C3158265D6F90008AA076 /* bitrates.json */; };
C40CD922271F8CD8000FB198 /* MoviesLibrariesCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40CD921271F8CD8000FB198 /* MoviesLibrariesCoordinator.swift */; };
C40CD923271F8CD8000FB198 /* MoviesLibrariesCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40CD921271F8CD8000FB198 /* MoviesLibrariesCoordinator.swift */; };
C40CD925271F8D1E000FB198 /* MovieLibrariesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40CD924271F8D1E000FB198 /* MovieLibrariesViewModel.swift */; };
C40CD926271F8D1E000FB198 /* MovieLibrariesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40CD924271F8D1E000FB198 /* MovieLibrariesViewModel.swift */; };
C40CD928271F8DAB000FB198 /* MovieLibrariesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40CD927271F8DAB000FB198 /* MovieLibrariesView.swift */; };
C40CD929271F8DAB000FB198 /* MovieLibrariesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40CD927271F8DAB000FB198 /* MovieLibrariesView.swift */; };
C45B29BB26FAC5B600CEF5E0 /* ColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E173DA5126D04AAF00CC4EB7 /* ColorExtension.swift */; }; C45B29BB26FAC5B600CEF5E0 /* ColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E173DA5126D04AAF00CC4EB7 /* ColorExtension.swift */; };
C4BE0763271FC0BB003F4AD1 /* TVLibrariesCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BE0762271FC0BB003F4AD1 /* TVLibrariesCoordinator.swift */; };
C4BE0764271FC0BB003F4AD1 /* TVLibrariesCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BE0762271FC0BB003F4AD1 /* TVLibrariesCoordinator.swift */; };
C4BE0766271FC109003F4AD1 /* TVLibrariesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BE0765271FC109003F4AD1 /* TVLibrariesViewModel.swift */; };
C4BE0767271FC109003F4AD1 /* TVLibrariesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BE0765271FC109003F4AD1 /* TVLibrariesViewModel.swift */; };
C4BE0769271FC164003F4AD1 /* TVLibrariesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BE0768271FC164003F4AD1 /* TVLibrariesView.swift */; };
C4BE076A271FC164003F4AD1 /* TVLibrariesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BE0768271FC164003F4AD1 /* TVLibrariesView.swift */; };
C4E5081B2703F82A0045C9AB /* LibraryListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E508172703E8190045C9AB /* LibraryListView.swift */; }; C4E5081B2703F82A0045C9AB /* LibraryListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E508172703E8190045C9AB /* LibraryListView.swift */; };
C4E5081D2703F8370045C9AB /* LibrarySearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E5081C2703F8370045C9AB /* LibrarySearchView.swift */; }; C4E5081D2703F8370045C9AB /* LibrarySearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E5081C2703F8370045C9AB /* LibrarySearchView.swift */; };
E100720726BDABC100CE3E31 /* MediaPlayButtonRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E100720626BDABC100CE3E31 /* MediaPlayButtonRowView.swift */; }; E100720726BDABC100CE3E31 /* MediaPlayButtonRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E100720626BDABC100CE3E31 /* MediaPlayButtonRowView.swift */; };
@ -522,6 +534,12 @@
62ECA01726FA685A00E8EBB7 /* DeepLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLink.swift; sourceTree = "<group>"; }; 62ECA01726FA685A00E8EBB7 /* DeepLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLink.swift; sourceTree = "<group>"; };
AE8C3158265D6F90008AA076 /* bitrates.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = bitrates.json; sourceTree = "<group>"; }; AE8C3158265D6F90008AA076 /* bitrates.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = bitrates.json; sourceTree = "<group>"; };
BEEC50E7EFD4848C0E320941 /* Pods-JellyfinPlayer iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinPlayer iOS.release.xcconfig"; path = "Target Support Files/Pods-JellyfinPlayer iOS/Pods-JellyfinPlayer iOS.release.xcconfig"; sourceTree = "<group>"; }; BEEC50E7EFD4848C0E320941 /* Pods-JellyfinPlayer iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinPlayer iOS.release.xcconfig"; path = "Target Support Files/Pods-JellyfinPlayer iOS/Pods-JellyfinPlayer iOS.release.xcconfig"; sourceTree = "<group>"; };
C40CD921271F8CD8000FB198 /* MoviesLibrariesCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoviesLibrariesCoordinator.swift; sourceTree = "<group>"; };
C40CD924271F8D1E000FB198 /* MovieLibrariesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieLibrariesViewModel.swift; sourceTree = "<group>"; };
C40CD927271F8DAB000FB198 /* MovieLibrariesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieLibrariesView.swift; sourceTree = "<group>"; };
C4BE0762271FC0BB003F4AD1 /* TVLibrariesCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVLibrariesCoordinator.swift; sourceTree = "<group>"; };
C4BE0765271FC109003F4AD1 /* TVLibrariesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVLibrariesViewModel.swift; sourceTree = "<group>"; };
C4BE0768271FC164003F4AD1 /* TVLibrariesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVLibrariesView.swift; sourceTree = "<group>"; };
C4E508172703E8190045C9AB /* LibraryListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryListView.swift; sourceTree = "<group>"; }; C4E508172703E8190045C9AB /* LibraryListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryListView.swift; sourceTree = "<group>"; };
C4E5081C2703F8370045C9AB /* LibrarySearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibrarySearchView.swift; sourceTree = "<group>"; }; C4E5081C2703F8370045C9AB /* LibrarySearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibrarySearchView.swift; sourceTree = "<group>"; };
D79953919FED0C4DF72BA578 /* Pods-JellyfinPlayer tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinPlayer tvOS.release.xcconfig"; path = "Target Support Files/Pods-JellyfinPlayer tvOS/Pods-JellyfinPlayer tvOS.release.xcconfig"; sourceTree = "<group>"; }; D79953919FED0C4DF72BA578 /* Pods-JellyfinPlayer tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinPlayer tvOS.release.xcconfig"; path = "Target Support Files/Pods-JellyfinPlayer tvOS/Pods-JellyfinPlayer tvOS.release.xcconfig"; sourceTree = "<group>"; };
@ -677,6 +695,8 @@
62E632DB267D2E130063E547 /* LibrarySearchViewModel.swift */, 62E632DB267D2E130063E547 /* LibrarySearchViewModel.swift */,
62E632DF267D30CA0063E547 /* LibraryViewModel.swift */, 62E632DF267D30CA0063E547 /* LibraryViewModel.swift */,
536D3D75267BA9BB0004248C /* MainTabViewModel.swift */, 536D3D75267BA9BB0004248C /* MainTabViewModel.swift */,
C40CD924271F8D1E000FB198 /* MovieLibrariesViewModel.swift */,
C4BE0765271FC109003F4AD1 /* TVLibrariesViewModel.swift */,
62E632E2267D3BA60063E547 /* MovieItemViewModel.swift */, 62E632E2267D3BA60063E547 /* MovieItemViewModel.swift */,
62E632E8267D3FF50063E547 /* SeasonItemViewModel.swift */, 62E632E8267D3FF50063E547 /* SeasonItemViewModel.swift */,
62E632EB267D410B0063E547 /* SeriesItemViewModel.swift */, 62E632EB267D410B0063E547 /* SeriesItemViewModel.swift */,
@ -1050,6 +1070,8 @@
6220D0BF26D61C5000B8E046 /* ItemCoordinator.swift */, 6220D0BF26D61C5000B8E046 /* ItemCoordinator.swift */,
6220D0B326D5ED8000B8E046 /* LibraryCoordinator.swift */, 6220D0B326D5ED8000B8E046 /* LibraryCoordinator.swift */,
62C29EA726D103D500C1D2E7 /* LibraryListCoordinator.swift */, 62C29EA726D103D500C1D2E7 /* LibraryListCoordinator.swift */,
C40CD921271F8CD8000FB198 /* MoviesLibrariesCoordinator.swift */,
C4BE0762271FC0BB003F4AD1 /* TVLibrariesCoordinator.swift */,
6220D0B626D5EE1100B8E046 /* SearchCoordinator.swift */, 6220D0B626D5EE1100B8E046 /* SearchCoordinator.swift */,
E13DD3E827177ED6009D4DAF /* ServerListCoordinator.swift */, E13DD3E827177ED6009D4DAF /* ServerListCoordinator.swift */,
6220D0B026D5EC9900B8E046 /* SettingsCoordinator.swift */, 6220D0B026D5EC9900B8E046 /* SettingsCoordinator.swift */,
@ -1120,6 +1142,8 @@
C4E508172703E8190045C9AB /* LibraryListView.swift */, C4E508172703E8190045C9AB /* LibraryListView.swift */,
C4E5081C2703F8370045C9AB /* LibrarySearchView.swift */, C4E5081C2703F8370045C9AB /* LibrarySearchView.swift */,
53A83C32268A309300DF3D92 /* LibraryView.swift */, 53A83C32268A309300DF3D92 /* LibraryView.swift */,
C40CD927271F8DAB000FB198 /* MovieLibrariesView.swift */,
C4BE0768271FC164003F4AD1 /* TVLibrariesView.swift */,
531690EE267ABF72005D8AB9 /* NextUpView.swift */, 531690EE267ABF72005D8AB9 /* NextUpView.swift */,
E193D54F2719430400900D82 /* ServerDetailView.swift */, E193D54F2719430400900D82 /* ServerDetailView.swift */,
E193D54A271941D300900D82 /* ServerListView.swift */, E193D54A271941D300900D82 /* ServerListView.swift */,
@ -1632,6 +1656,7 @@
E193D4DC27193CCA00900D82 /* PillStackable.swift in Sources */, E193D4DC27193CCA00900D82 /* PillStackable.swift in Sources */,
E193D53327193F7D00900D82 /* FilterCoordinator.swift in Sources */, E193D53327193F7D00900D82 /* FilterCoordinator.swift in Sources */,
6267B3DC2671139500A7371D /* ImageExtensions.swift in Sources */, 6267B3DC2671139500A7371D /* ImageExtensions.swift in Sources */,
C40CD929271F8DAB000FB198 /* MovieLibrariesView.swift in Sources */,
531069592684E7EE00CFFDBA /* SubtitlesView.swift in Sources */, 531069592684E7EE00CFFDBA /* SubtitlesView.swift in Sources */,
C4E5081D2703F8370045C9AB /* LibrarySearchView.swift in Sources */, C4E5081D2703F8370045C9AB /* LibrarySearchView.swift in Sources */,
53ABFDE9267974EF00886593 /* HomeViewModel.swift in Sources */, 53ABFDE9267974EF00886593 /* HomeViewModel.swift in Sources */,
@ -1640,6 +1665,7 @@
531690E7267ABD79005D8AB9 /* HomeView.swift in Sources */, 531690E7267ABD79005D8AB9 /* HomeView.swift in Sources */,
E1D4BF8B2719D3D000A11E64 /* BasicAppSettingsCoordinator.swift in Sources */, E1D4BF8B2719D3D000A11E64 /* BasicAppSettingsCoordinator.swift in Sources */,
E13DD3FA2717E961009D4DAF /* UserListViewModel.swift in Sources */, E13DD3FA2717E961009D4DAF /* UserListViewModel.swift in Sources */,
C40CD926271F8D1E000FB198 /* MovieLibrariesViewModel.swift in Sources */,
62E632DE267D2E170063E547 /* LatestMediaViewModel.swift in Sources */, 62E632DE267D2E170063E547 /* LatestMediaViewModel.swift in Sources */,
E1FCD09726C47118007C8DCF /* ErrorMessage.swift in Sources */, E1FCD09726C47118007C8DCF /* ErrorMessage.swift in Sources */,
E193D53527193F8100900D82 /* ItemCoordinator.swift in Sources */, E193D53527193F8100900D82 /* ItemCoordinator.swift in Sources */,
@ -1672,6 +1698,7 @@
62E632F4267D54030063E547 /* ItemViewModel.swift in Sources */, 62E632F4267D54030063E547 /* ItemViewModel.swift in Sources */,
6267B3D826710B9800A7371D /* CollectionExtensions.swift in Sources */, 6267B3D826710B9800A7371D /* CollectionExtensions.swift in Sources */,
62E632E7267D3F5B0063E547 /* EpisodeItemViewModel.swift in Sources */, 62E632E7267D3F5B0063E547 /* EpisodeItemViewModel.swift in Sources */,
C4BE0767271FC109003F4AD1 /* TVLibrariesViewModel.swift in Sources */,
E193D53727193F8700900D82 /* LibraryListCoordinator.swift in Sources */, E193D53727193F8700900D82 /* LibraryListCoordinator.swift in Sources */,
E100720726BDABC100CE3E31 /* MediaPlayButtonRowView.swift in Sources */, E100720726BDABC100CE3E31 /* MediaPlayButtonRowView.swift in Sources */,
E193D54D2719426600900D82 /* LibraryFilterView.swift in Sources */, E193D54D2719426600900D82 /* LibraryFilterView.swift in Sources */,
@ -1706,6 +1733,7 @@
C45B29BB26FAC5B600CEF5E0 /* ColorExtension.swift in Sources */, C45B29BB26FAC5B600CEF5E0 /* ColorExtension.swift in Sources */,
531069582684E7EE00CFFDBA /* MediaInfoView.swift in Sources */, 531069582684E7EE00CFFDBA /* MediaInfoView.swift in Sources */,
E1D4BF822719D22800A11E64 /* AppAppearance.swift in Sources */, E1D4BF822719D22800A11E64 /* AppAppearance.swift in Sources */,
C40CD923271F8CD8000FB198 /* MoviesLibrariesCoordinator.swift in Sources */,
53272537268C1DBB0035FBF1 /* SeasonItemView.swift in Sources */, 53272537268C1DBB0035FBF1 /* SeasonItemView.swift in Sources */,
09389CC526814E4500AE350E /* DeviceProfileBuilder.swift in Sources */, 09389CC526814E4500AE350E /* DeviceProfileBuilder.swift in Sources */,
E193D53C27193F9500900D82 /* UserListCoordinator.swift in Sources */, E193D53C27193F9500900D82 /* UserListCoordinator.swift in Sources */,
@ -1731,7 +1759,9 @@
5364F456266CA0DC0026ECBA /* BaseItemPersonExtensions.swift in Sources */, 5364F456266CA0DC0026ECBA /* BaseItemPersonExtensions.swift in Sources */,
5364F456266CA0DC0026ECBA /* BaseItemPersonExtensions.swift in Sources */, 5364F456266CA0DC0026ECBA /* BaseItemPersonExtensions.swift in Sources */,
531690FA267AD6EC005D8AB9 /* PlainNavigationLinkButton.swift in Sources */, 531690FA267AD6EC005D8AB9 /* PlainNavigationLinkButton.swift in Sources */,
C4BE0764271FC0BB003F4AD1 /* TVLibrariesCoordinator.swift in Sources */,
E131691826C583BC0074BFEE /* LogConstructor.swift in Sources */, E131691826C583BC0074BFEE /* LogConstructor.swift in Sources */,
C4BE076A271FC164003F4AD1 /* TVLibrariesView.swift in Sources */,
E13DD3C327164941009D4DAF /* SwiftfinStore.swift in Sources */, E13DD3C327164941009D4DAF /* SwiftfinStore.swift in Sources */,
09389CC826819B4600AE350E /* VideoPlayerModel.swift in Sources */, 09389CC826819B4600AE350E /* VideoPlayerModel.swift in Sources */,
E193D553271943D500900D82 /* tvOSMainTabCoordinator.swift in Sources */, E193D553271943D500900D82 /* tvOSMainTabCoordinator.swift in Sources */,
@ -1761,6 +1791,7 @@
E1AD105626D981CE003E4A08 /* PortraitHStackView.swift in Sources */, E1AD105626D981CE003E4A08 /* PortraitHStackView.swift in Sources */,
62C29EA126D102A500C1D2E7 /* iOSMainTabCoordinator.swift in Sources */, 62C29EA126D102A500C1D2E7 /* iOSMainTabCoordinator.swift in Sources */,
535BAE9F2649E569005FA86D /* ItemView.swift in Sources */, 535BAE9F2649E569005FA86D /* ItemView.swift in Sources */,
C40CD925271F8D1E000FB198 /* MovieLibrariesViewModel.swift in Sources */,
6225FCCB2663841E00E067F6 /* ParallaxHeader.swift in Sources */, 6225FCCB2663841E00E067F6 /* ParallaxHeader.swift in Sources */,
6220D0AD26D5EABB00B8E046 /* ViewExtensions.swift in Sources */, 6220D0AD26D5EABB00B8E046 /* ViewExtensions.swift in Sources */,
E13DD3EC27178A54009D4DAF /* UserSignInViewModel.swift in Sources */, E13DD3EC27178A54009D4DAF /* UserSignInViewModel.swift in Sources */,
@ -1781,6 +1812,7 @@
625CB56F2678C23300530A6E /* HomeView.swift in Sources */, 625CB56F2678C23300530A6E /* HomeView.swift in Sources */,
E173DA5226D04AAF00CC4EB7 /* ColorExtension.swift in Sources */, E173DA5226D04AAF00CC4EB7 /* ColorExtension.swift in Sources */,
53892770263C25230035E14B /* NextUpView.swift in Sources */, 53892770263C25230035E14B /* NextUpView.swift in Sources */,
C4BE0766271FC109003F4AD1 /* TVLibrariesViewModel.swift in Sources */,
62ECA01826FA685A00E8EBB7 /* DeepLink.swift in Sources */, 62ECA01826FA685A00E8EBB7 /* DeepLink.swift in Sources */,
535BAEA5264A151C005FA86D /* VideoPlayer.swift in Sources */, 535BAEA5264A151C005FA86D /* VideoPlayer.swift in Sources */,
62E632E6267D3F5B0063E547 /* EpisodeItemViewModel.swift in Sources */, 62E632E6267D3F5B0063E547 /* EpisodeItemViewModel.swift in Sources */,
@ -1789,6 +1821,7 @@
532E68CF267D9F6B007B9F13 /* VideoPlayerCastDeviceSelector.swift in Sources */, 532E68CF267D9F6B007B9F13 /* VideoPlayerCastDeviceSelector.swift in Sources */,
E14F7D0926DB36F7007C3AE6 /* ItemLandscapeMainView.swift in Sources */, E14F7D0926DB36F7007C3AE6 /* ItemLandscapeMainView.swift in Sources */,
532175402671EE4F005491E6 /* LibraryFilterView.swift in Sources */, 532175402671EE4F005491E6 /* LibraryFilterView.swift in Sources */,
C4BE0763271FC0BB003F4AD1 /* TVLibrariesCoordinator.swift in Sources */,
53DF641E263D9C0600A7CD1A /* LibraryView.swift in Sources */, 53DF641E263D9C0600A7CD1A /* LibraryView.swift in Sources */,
E188460026DECB9E00B0C5B7 /* ItemLandscapeTopBarView.swift in Sources */, E188460026DECB9E00B0C5B7 /* ItemLandscapeTopBarView.swift in Sources */,
091B5A8B2683142E00D78B61 /* UDPBroadCastConnection.swift in Sources */, 091B5A8B2683142E00D78B61 /* UDPBroadCastConnection.swift in Sources */,
@ -1828,12 +1861,14 @@
62E632E3267D3BA60063E547 /* MovieItemViewModel.swift in Sources */, 62E632E3267D3BA60063E547 /* MovieItemViewModel.swift in Sources */,
091B5A8A2683142E00D78B61 /* ServerDiscovery.swift in Sources */, 091B5A8A2683142E00D78B61 /* ServerDiscovery.swift in Sources */,
62E632EF267D43320063E547 /* LibraryFilterViewModel.swift in Sources */, 62E632EF267D43320063E547 /* LibraryFilterViewModel.swift in Sources */,
C40CD922271F8CD8000FB198 /* MoviesLibrariesCoordinator.swift in Sources */,
E13DD3C827164B1E009D4DAF /* UIDeviceExtensions.swift in Sources */, E13DD3C827164B1E009D4DAF /* UIDeviceExtensions.swift in Sources */,
E1AD104D26D96CE3003E4A08 /* BaseItemDtoExtensions.swift in Sources */, E1AD104D26D96CE3003E4A08 /* BaseItemDtoExtensions.swift in Sources */,
E13DD3BF27163DD7009D4DAF /* AppDelegate.swift in Sources */, E13DD3BF27163DD7009D4DAF /* AppDelegate.swift in Sources */,
535870AD2669D8DD00D05A09 /* Typings.swift in Sources */, 535870AD2669D8DD00D05A09 /* Typings.swift in Sources */,
E1AD105F26D9ADDD003E4A08 /* NameGUIDPairExtensions.swift in Sources */, E1AD105F26D9ADDD003E4A08 /* NameGUIDPairExtensions.swift in Sources */,
E13DD3D5271693CD009D4DAF /* SwiftfinStoreDefaults.swift in Sources */, E13DD3D5271693CD009D4DAF /* SwiftfinStoreDefaults.swift in Sources */,
C4BE0769271FC164003F4AD1 /* TVLibrariesView.swift in Sources */,
E1267D3E271A1F46003C492E /* PreferenceUIHostingController.swift in Sources */, E1267D3E271A1F46003C492E /* PreferenceUIHostingController.swift in Sources */,
6220D0BA26D6092100B8E046 /* FilterCoordinator.swift in Sources */, 6220D0BA26D6092100B8E046 /* FilterCoordinator.swift in Sources */,
6267B3DA2671138200A7371D /* ImageExtensions.swift in Sources */, 6267B3DA2671138200A7371D /* ImageExtensions.swift in Sources */,
@ -1848,6 +1883,7 @@
6220D0B726D5EE1100B8E046 /* SearchCoordinator.swift in Sources */, 6220D0B726D5EE1100B8E046 /* SearchCoordinator.swift in Sources */,
E13DD3EF27178F87009D4DAF /* SwiftfinNotificationCenter.swift in Sources */, E13DD3EF27178F87009D4DAF /* SwiftfinNotificationCenter.swift in Sources */,
5377CBF5263B596A003A4E83 /* JellyfinPlayerApp.swift in Sources */, 5377CBF5263B596A003A4E83 /* JellyfinPlayerApp.swift in Sources */,
C40CD928271F8DAB000FB198 /* MovieLibrariesView.swift in Sources */,
E13DD4022717EE79009D4DAF /* UserListCoordinator.swift in Sources */, E13DD4022717EE79009D4DAF /* UserListCoordinator.swift in Sources */,
E1FCD09626C47118007C8DCF /* ErrorMessage.swift in Sources */, E1FCD09626C47118007C8DCF /* ErrorMessage.swift in Sources */,
53EE24E6265060780068F029 /* LibrarySearchView.swift in Sources */, 53EE24E6265060780068F029 /* LibrarySearchView.swift in Sources */,

View File

@ -20,6 +20,8 @@ final class HomeCoordinator: NavigationCoordinatable {
@Route(.modal) var settings = makeSettings @Route(.modal) var settings = makeSettings
@Route(.push) var library = makeLibrary @Route(.push) var library = makeLibrary
@Route(.push) var item = makeItem @Route(.push) var item = makeItem
@Route(.modal) var modalItem = makeModalItem
@Route(.modal) var modalLibrary = makeModalLibrary
func makeSettings() -> NavigationViewCoordinator<SettingsCoordinator> { func makeSettings() -> NavigationViewCoordinator<SettingsCoordinator> {
NavigationViewCoordinator(SettingsCoordinator()) NavigationViewCoordinator(SettingsCoordinator())
@ -32,6 +34,14 @@ final class HomeCoordinator: NavigationCoordinatable {
func makeItem(item: BaseItemDto) -> ItemCoordinator { func makeItem(item: BaseItemDto) -> ItemCoordinator {
ItemCoordinator(item: item) ItemCoordinator(item: item)
} }
func makeModalItem(item: BaseItemDto) -> NavigationViewCoordinator<ItemCoordinator> {
return NavigationViewCoordinator(ItemCoordinator(item: item))
}
func makeModalLibrary(params: LibraryCoordinatorParams) -> NavigationViewCoordinator<LibraryCoordinator> {
return NavigationViewCoordinator(LibraryCoordinator(viewModel: params.viewModel, title: params.title))
}
@ViewBuilder func makeStart() -> some View { @ViewBuilder func makeStart() -> some View {
HomeView() HomeView()

View File

@ -22,6 +22,7 @@ final class LibraryCoordinator: NavigationCoordinatable {
@Route(.push) var search = makeSearch @Route(.push) var search = makeSearch
@Route(.modal) var filter = makeFilter @Route(.modal) var filter = makeFilter
@Route(.push) var item = makeItem @Route(.push) var item = makeItem
@Route(.modal) var modalItem = makeModalItem
let viewModel: LibraryViewModel let viewModel: LibraryViewModel
let title: String let title: String
@ -48,4 +49,8 @@ final class LibraryCoordinator: NavigationCoordinatable {
func makeItem(item: BaseItemDto) -> ItemCoordinator { func makeItem(item: BaseItemDto) -> ItemCoordinator {
ItemCoordinator(item: item) ItemCoordinator(item: item)
} }
func makeModalItem(item: BaseItemDto) -> NavigationViewCoordinator<ItemCoordinator> {
return NavigationViewCoordinator(ItemCoordinator(item: item))
}
} }

View File

@ -14,12 +14,16 @@ import Stinsen
final class MainTabCoordinator: TabCoordinatable { final class MainTabCoordinator: TabCoordinatable {
var child = TabChild(startingItems: [ var child = TabChild(startingItems: [
\MainTabCoordinator.home, \MainTabCoordinator.home,
\MainTabCoordinator.allMedia, \MainTabCoordinator.tv,
\MainTabCoordinator.movies,
\MainTabCoordinator.other,
\MainTabCoordinator.settings \MainTabCoordinator.settings
]) ])
@Route(tabItem: makeHomeTab) var home = makeHome @Route(tabItem: makeHomeTab) var home = makeHome
@Route(tabItem: makeAllMediaTab) var allMedia = makeAllMedia @Route(tabItem: makeTvTab) var tv = makeTv
@Route(tabItem: makeMoviesTab) var movies = makeMovies
@Route(tabItem: makeOtherTab) var other = makeOther
@Route(tabItem: makeSettingsTab) var settings = makeSettings @Route(tabItem: makeSettingsTab) var settings = makeSettings
func makeHome() -> NavigationViewCoordinator<HomeCoordinator> { func makeHome() -> NavigationViewCoordinator<HomeCoordinator> {
@ -32,15 +36,37 @@ final class MainTabCoordinator: TabCoordinatable {
Text("Home") Text("Home")
} }
} }
func makeTv() -> NavigationViewCoordinator<TVLibrariesCoordinator> {
return NavigationViewCoordinator(TVLibrariesCoordinator(viewModel: TVLibrariesViewModel(), title: "TV Shows"))
}
func makeAllMedia() -> NavigationViewCoordinator<LibraryListCoordinator> { @ViewBuilder func makeTvTab(isActive: Bool) -> some View {
HStack {
Image(systemName: "tv")
Text("TV Shows")
}
}
func makeMovies() -> NavigationViewCoordinator<MovieLibrariesCoordinator> {
return NavigationViewCoordinator(MovieLibrariesCoordinator(viewModel: MovieLibrariesViewModel(), title: "Movies"))
}
@ViewBuilder func makeMoviesTab(isActive: Bool) -> some View {
HStack {
Image(systemName: "film")
Text("Movies")
}
}
func makeOther() -> NavigationViewCoordinator<LibraryListCoordinator> {
return NavigationViewCoordinator(LibraryListCoordinator()) return NavigationViewCoordinator(LibraryListCoordinator())
} }
@ViewBuilder func makeAllMediaTab(isActive: Bool) -> some View { @ViewBuilder func makeOtherTab(isActive: Bool) -> some View {
HStack { HStack {
Image(systemName: "folder") Image(systemName: "folder")
Text("All Media") Text("Other")
} }
} }

View File

@ -0,0 +1,37 @@
//
/*
* 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 Foundation
import JellyfinAPI
import Stinsen
import SwiftUI
final class MovieLibrariesCoordinator: NavigationCoordinatable {
let stack = NavigationStack(initial: \MovieLibrariesCoordinator.start)
@Root var start = makeStart
@Route(.push) var library = makeLibrary
let viewModel: MovieLibrariesViewModel
let title: String
init(viewModel: MovieLibrariesViewModel, title: String) {
self.viewModel = viewModel
self.title = title
}
@ViewBuilder func makeStart() -> some View {
MovieLibrariesView(viewModel: self.viewModel, title: title)
}
func makeLibrary(library: BaseItemDto) -> LibraryCoordinator {
LibraryCoordinator(viewModel: LibraryViewModel(parentID: library.id), title: library.title)
}
}

View File

@ -0,0 +1,37 @@
//
/*
* 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 Foundation
import JellyfinAPI
import Stinsen
import SwiftUI
final class TVLibrariesCoordinator: NavigationCoordinatable {
let stack = NavigationStack(initial: \TVLibrariesCoordinator.start)
@Root var start = makeStart
@Route(.push) var library = makeLibrary
let viewModel: TVLibrariesViewModel
let title: String
init(viewModel: TVLibrariesViewModel, title: String) {
self.viewModel = viewModel
self.title = title
}
@ViewBuilder func makeStart() -> some View {
TVLibrariesView(viewModel: self.viewModel, title: title)
}
func makeLibrary(library: BaseItemDto) -> LibraryCoordinator {
LibraryCoordinator(viewModel: LibraryViewModel(parentID: library.id), title: library.title)
}
}

View File

@ -15,9 +15,9 @@ import SwiftUICollection
typealias LibraryRow = CollectionRow<Int, LibraryRowCell> typealias LibraryRow = CollectionRow<Int, LibraryRowCell>
struct LibraryRowCell: Hashable { struct LibraryRowCell: Hashable {
let id = UUID() let id = UUID()
let item: BaseItemDto? let item: BaseItemDto?
var loadingCell: Bool = false var loadingCell: Bool = false
} }
final class LibraryViewModel: ViewModel { final class LibraryViewModel: ViewModel {
@ -38,6 +38,7 @@ final class LibraryViewModel: ViewModel {
@Published var filters: LibraryFilters @Published var filters: LibraryFilters
private let columns: Int private let columns: Int
private var libraries = [BaseItemDto]()
var enabledFilterType: [FilterType] { var enabledFilterType: [FilterType] {
if genre == nil { if genre == nil {
@ -48,12 +49,12 @@ final class LibraryViewModel: ViewModel {
} }
init( init(
parentID: String? = nil, parentID: String? = nil,
person: BaseItemPerson? = nil, person: BaseItemPerson? = nil,
genre: NameGuidPair? = nil, genre: NameGuidPair? = nil,
studio: NameGuidPair? = nil, studio: NameGuidPair? = nil,
filters: LibraryFilters = LibraryFilters(filters: [], sortOrder: [.ascending], withGenres: [], sortBy: [.name]), filters: LibraryFilters = LibraryFilters(filters: [], sortOrder: [.ascending], withGenres: [], sortBy: [.name]),
columns: Int = 7 columns: Int = 7
) { ) {
self.parentID = parentID self.parentID = parentID
self.person = person self.person = person
@ -63,9 +64,11 @@ final class LibraryViewModel: ViewModel {
self.columns = columns self.columns = columns
super.init() super.init()
$filters $filters
.sink(receiveValue: requestItems(with:)) .sink(receiveValue: requestItems(with:))
.store(in: &cancellables) .store(in: &cancellables)
} }
func requestItems(with filters: LibraryFilters) { func requestItems(with filters: LibraryFilters) {
@ -95,7 +98,7 @@ final class LibraryViewModel: ViewModel {
self.hasPreviousPage = self.currentPage > 0 self.hasPreviousPage = self.currentPage > 0
self.hasNextPage = self.currentPage < self.totalPages - 1 self.hasNextPage = self.currentPage < self.totalPages - 1
self.items = response.items ?? [] self.items = response.items ?? []
self.rows = self.calculateRows() self.rows = self.calculateRows(for: self.items)
}) })
.store(in: &cancellables) .store(in: &cancellables)
} }
@ -125,7 +128,7 @@ final class LibraryViewModel: ViewModel {
self.hasPreviousPage = self.currentPage > 0 self.hasPreviousPage = self.currentPage > 0
self.hasNextPage = self.currentPage < self.totalPages - 1 self.hasNextPage = self.currentPage < self.totalPages - 1
self.items.append(contentsOf: response.items ?? []) self.items.append(contentsOf: response.items ?? [])
self.rows = self.calculateRows() self.rows = self.calculateRows(for: self.items)
}) })
.store(in: &cancellables) .store(in: &cancellables)
} }
@ -145,37 +148,35 @@ final class LibraryViewModel: ViewModel {
requestItems(with: filters) requestItems(with: filters)
} }
private func calculateRows() -> [LibraryRow] { private func calculateRows(for itemList: [BaseItemDto]) -> [LibraryRow] {
guard items.count > 0 else { return [] } guard itemList.count > 0 else { return [] }
let rowCount = items.count / columns let rowCount = itemList.count / columns
var calculatedRows = [LibraryRow]() var calculatedRows = [LibraryRow]()
for i in (0...rowCount) { for i in (0...rowCount) {
let firstItemIndex = i * columns
let firstItemIndex = i * columns var lastItemIndex = firstItemIndex + columns
var lastItemIndex = firstItemIndex + columns if lastItemIndex > itemList.count {
if lastItemIndex > items.count { lastItemIndex = itemList.count
lastItemIndex = items.count }
}
var rowCells = [LibraryRowCell]()
var rowCells = [LibraryRowCell]() for item in itemList[firstItemIndex..<lastItemIndex] {
for item in items[firstItemIndex..<lastItemIndex] { let newCell = LibraryRowCell(item: item)
let newCell = LibraryRowCell(item: item) rowCells.append(newCell)
rowCells.append(newCell) }
} if i == rowCount && hasNextPage {
if i == rowCount && hasNextPage { var loadingCell = LibraryRowCell(item: nil)
var loadingCell = LibraryRowCell(item: nil) loadingCell.loadingCell = true
loadingCell.loadingCell = true rowCells.append(loadingCell)
rowCells.append(loadingCell) }
}
calculatedRows.append(
calculatedRows.append( LibraryRow(
LibraryRow( section: i,
section: i, items: rowCells
items: rowCells )
) )
) }
return calculatedRows
} }
return calculatedRows
}
} }

View File

@ -0,0 +1,99 @@
//
/*
* 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 Combine
import Foundation
import JellyfinAPI
import Stinsen
import SwiftUICollection
final class MovieLibrariesViewModel: ViewModel {
@Published var rows = [LibraryRow]()
@Published var totalPages = 0
@Published var currentPage = 0
@Published var hasNextPage = false
@Published var hasPreviousPage = false
private var libraries = [BaseItemDto]()
private let columns: Int
@RouterObject
var router: MovieLibrariesCoordinator.Router?
init(
columns: Int = 7
) {
self.columns = columns
super.init()
requestLibraries()
}
func requestLibraries() {
UserViewsAPI.getUserViews(
userId: SessionManager.main.currentLogin.user.id)
.trackActivity(loading)
.sink(receiveCompletion: { completion in
self.handleAPIRequestError(completion: completion)
}, receiveValue: { response in
if let responseItems = response.items {
self.libraries = []
for library in responseItems {
if library.collectionType == "movies" {
self.libraries.append(library)
}
}
if self.libraries.count == 1, let library = self.libraries.first {
// show library
self.router?.route(to: \.library, library)
} else {
// display list of libraries
self.rows = self.calculateRows()
}
}
})
.store(in: &cancellables)
}
private func calculateRows() -> [LibraryRow] {
guard libraries.count > 0 else { return [] }
let rowCount = libraries.count / columns
var calculatedRows = [LibraryRow]()
for i in (0...rowCount) {
let firstItemIndex = i * columns
var lastItemIndex = firstItemIndex + columns
if lastItemIndex > libraries.count {
lastItemIndex = libraries.count
}
var rowCells = [LibraryRowCell]()
for item in libraries[firstItemIndex..<lastItemIndex] {
print("item: \(item.title) index: \(i)")
let newCell = LibraryRowCell(item: item)
rowCells.append(newCell)
}
if i == rowCount && hasNextPage {
var loadingCell = LibraryRowCell(item: nil)
loadingCell.loadingCell = true
rowCells.append(loadingCell)
}
calculatedRows.append(
LibraryRow(
section: i,
items: rowCells
)
)
}
print("caluculated \(calculatedRows.count) rows")
return calculatedRows
}
}

View File

@ -0,0 +1,99 @@
//
/*
* 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 Combine
import Foundation
import JellyfinAPI
import Stinsen
import SwiftUICollection
final class TVLibrariesViewModel: ViewModel {
@Published var rows = [LibraryRow]()
@Published var totalPages = 0
@Published var currentPage = 0
@Published var hasNextPage = false
@Published var hasPreviousPage = false
private var libraries = [BaseItemDto]()
private let columns: Int
@RouterObject
var router: TVLibrariesCoordinator.Router?
init(
columns: Int = 7
) {
self.columns = columns
super.init()
requestLibraries()
}
func requestLibraries() {
UserViewsAPI.getUserViews(
userId: SessionManager.main.currentLogin.user.id)
.trackActivity(loading)
.sink(receiveCompletion: { completion in
self.handleAPIRequestError(completion: completion)
}, receiveValue: { response in
if let responseItems = response.items {
self.libraries = []
for library in responseItems {
if library.collectionType == "tvshows" {
self.libraries.append(library)
}
}
if self.libraries.count == 1, let library = self.libraries.first {
// show library
self.router?.route(to: \.library, library)
} else {
// display list of libraries
self.rows = self.calculateRows()
}
}
})
.store(in: &cancellables)
}
private func calculateRows() -> [LibraryRow] {
guard libraries.count > 0 else { return [] }
let rowCount = libraries.count / columns
var calculatedRows = [LibraryRow]()
for i in (0...rowCount) {
let firstItemIndex = i * columns
var lastItemIndex = firstItemIndex + columns
if lastItemIndex > libraries.count {
lastItemIndex = libraries.count
}
var rowCells = [LibraryRowCell]()
for item in libraries[firstItemIndex..<lastItemIndex] {
print("item: \(item.title) index: \(i)")
let newCell = LibraryRowCell(item: item)
rowCells.append(newCell)
}
if i == rowCount && hasNextPage {
var loadingCell = LibraryRowCell(item: nil)
loadingCell.loadingCell = true
rowCells.append(loadingCell)
}
calculatedRows.append(
LibraryRow(
section: i,
items: rowCells
)
)
}
print("caluculated \(calculatedRows.count) rows")
return calculatedRows
}
}