use CollectionView on LibraryView
This commit is contained in:
parent
c3b2fadf91
commit
1c2b1879b4
|
@ -7,57 +7,95 @@
|
|||
*/
|
||||
|
||||
import SwiftUI
|
||||
import SwiftUICollection
|
||||
import JellyfinAPI
|
||||
|
||||
struct LibraryView: View {
|
||||
@StateObject var viewModel: LibraryViewModel
|
||||
var title: String
|
||||
@StateObject var viewModel: LibraryViewModel
|
||||
var title: String
|
||||
|
||||
// MARK: tracks for grid
|
||||
var defaultFilters = LibraryFilters(filters: [], sortOrder: [.ascending], withGenres: [], tags: [], sortBy: [.name])
|
||||
// MARK: tracks for grid
|
||||
var defaultFilters = LibraryFilters(filters: [], sortOrder: [.ascending], withGenres: [], tags: [], sortBy: [.name])
|
||||
|
||||
@State var isShowingSearchView = false
|
||||
@State var isShowingFilterView = false
|
||||
|
||||
@State private var tracks: [GridItem] = Array(repeating: .init(.flexible()), count: Int(UIScreen.main.bounds.size.width) / 250)
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
if viewModel.isLoading == true {
|
||||
ProgressView()
|
||||
} else if !viewModel.items.isEmpty {
|
||||
ScrollView(.vertical) {
|
||||
LazyVGrid(columns: tracks) {
|
||||
ForEach(viewModel.items, id: \.id) { item in
|
||||
if item.type != "Folder" {
|
||||
NavigationLink(destination: LazyView { ItemView(item: item) }) {
|
||||
PortraitItemElement(item: item)
|
||||
}.buttonStyle(PlainNavigationLinkButtonStyle())
|
||||
.onAppear {
|
||||
if item == viewModel.items.last && viewModel.hasNextPage {
|
||||
print("Last item visible, load more items.")
|
||||
viewModel.requestNextPageAsync()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}.padding()
|
||||
}
|
||||
} else {
|
||||
Text("No results.")
|
||||
}
|
||||
}
|
||||
/*
|
||||
.sheet(isPresented: $isShowingFilterView) {
|
||||
LibraryFilterView(filters: $viewModel.filters, enabledFilterType: viewModel.enabledFilterType, parentId: viewModel.parentID ?? "")
|
||||
}
|
||||
.background(
|
||||
NavigationLink(destination: LibrarySearchView(viewModel: .init(parentID: viewModel.parentID)),
|
||||
isActive: $isShowingSearchView) {
|
||||
EmptyView()
|
||||
}
|
||||
@State var isShowingSearchView = false
|
||||
@State var isShowingFilterView = false
|
||||
|
||||
var body: some View {
|
||||
if viewModel.isLoading == true {
|
||||
ProgressView()
|
||||
} else if !viewModel.items.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: { _, item in
|
||||
GeometryReader { _ in
|
||||
if item.type != "Folder" {
|
||||
NavigationLink(destination: LazyView { ItemView(item: item) }) {
|
||||
PortraitItemElement(item: item)
|
||||
}
|
||||
.buttonStyle(PlainNavigationLinkButtonStyle())
|
||||
.onAppear {
|
||||
if item == viewModel.items.last && viewModel.hasNextPage {
|
||||
print("Last item visible, load more items.")
|
||||
viewModel.requestNextPageAsync()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} supplementaryView: { _, indexPath in
|
||||
HStack {
|
||||
Text("Supp View")
|
||||
.font(.title3)
|
||||
Spacer()
|
||||
}.accessibilityIdentifier("\(indexPath.section).\(indexPath.row)")
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.ignoresSafeArea(.all)
|
||||
} else {
|
||||
Text("No results.")
|
||||
}
|
||||
/*
|
||||
.sheet(isPresented: $isShowingFilterView) {
|
||||
LibraryFilterView(filters: $viewModel.filters, enabledFilterType: viewModel.enabledFilterType, parentId: viewModel.parentID ?? "")
|
||||
}
|
||||
.background(
|
||||
NavigationLink(destination: LibrarySearchView(viewModel: .init(parentID: viewModel.parentID)),
|
||||
isActive: $isShowingSearchView) {
|
||||
EmptyView()
|
||||
}
|
||||
)
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
// stream BM^S by nicki!
|
||||
|
|
|
@ -252,6 +252,8 @@
|
|||
62ECA01826FA685A00E8EBB7 /* DeepLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62ECA01726FA685A00E8EBB7 /* DeepLink.swift */; };
|
||||
AE8C3159265D6F90008AA076 /* bitrates.json in Resources */ = {isa = PBXBuildFile; fileRef = AE8C3158265D6F90008AA076 /* bitrates.json */; };
|
||||
C45B29BB26FAC5B600CEF5E0 /* ColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E173DA5126D04AAF00CC4EB7 /* ColorExtension.swift */; };
|
||||
C49FB6592717A06300AAEABB /* SwiftUICollection in Frameworks */ = {isa = PBXBuildFile; productRef = C49FB6582717A06300AAEABB /* SwiftUICollection */; };
|
||||
C4BFD4E527167B63007739E3 /* SwiftUICollection in Frameworks */ = {isa = PBXBuildFile; productRef = C4BFD4E427167B63007739E3 /* SwiftUICollection */; };
|
||||
C4E5081B2703F82A0045C9AB /* LibraryListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E508172703E8190045C9AB /* LibraryListView.swift */; };
|
||||
C4E5081D2703F8370045C9AB /* LibrarySearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E5081C2703F8370045C9AB /* LibrarySearchView.swift */; };
|
||||
E100720726BDABC100CE3E31 /* MediaPlayButtonRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E100720626BDABC100CE3E31 /* MediaPlayButtonRowView.swift */; };
|
||||
|
@ -528,6 +530,7 @@
|
|||
53272535268BF9710035FBF1 /* SwiftUIFocusGuide in Frameworks */,
|
||||
5358708D2669D7A800D05A09 /* KeychainSwift in Frameworks */,
|
||||
536D3D84267BEA550004248C /* ParallaxView in Frameworks */,
|
||||
C49FB6592717A06300AAEABB /* SwiftUICollection in Frameworks */,
|
||||
53ABFDDC267972BF00886593 /* TVServices.framework in Frameworks */,
|
||||
5358709B2669D7A800D05A09 /* NukeUI in Frameworks */,
|
||||
53ABFDED26799D7700886593 /* ActivityIndicator in Frameworks */,
|
||||
|
@ -542,6 +545,7 @@
|
|||
62C29E9C26D0FE4200C1D2E7 /* Stinsen in Frameworks */,
|
||||
62CB3F462685BAF7003D0A6F /* Defaults in Frameworks */,
|
||||
5338F757263B7E2E0014BF09 /* KeychainSwift in Frameworks */,
|
||||
C4BFD4E527167B63007739E3 /* SwiftUICollection in Frameworks */,
|
||||
53EC6E25267EB10F006DD26A /* SwiftyJSON in Frameworks */,
|
||||
53EC6E21267E80B1006DD26A /* Pods_JellyfinPlayer_iOS.framework in Frameworks */,
|
||||
53352571265EA0A0006CCA86 /* Introspect in Frameworks */,
|
||||
|
@ -1151,6 +1155,7 @@
|
|||
53649AAE269CFAF600A2D8B7 /* Puppy */,
|
||||
6261A0DF26A0AB710072EF1C /* CombineExt */,
|
||||
6220D0C826D63F3700B8E046 /* Stinsen */,
|
||||
C49FB6582717A06300AAEABB /* SwiftUICollection */,
|
||||
);
|
||||
productName = "JellyfinPlayer tvOS";
|
||||
productReference = 535870602669D21600D05A09 /* JellyfinPlayer tvOS.app */;
|
||||
|
@ -1186,6 +1191,7 @@
|
|||
53649AAC269CFAEA00A2D8B7 /* Puppy */,
|
||||
6260FFF826A09754003FA968 /* CombineExt */,
|
||||
62C29E9B26D0FE4200C1D2E7 /* Stinsen */,
|
||||
C4BFD4E427167B63007739E3 /* SwiftUICollection */,
|
||||
);
|
||||
productName = JellyfinPlayer;
|
||||
productReference = 5377CBF1263B596A003A4E83 /* JellyfinPlayer iOS.app */;
|
||||
|
@ -1275,6 +1281,7 @@
|
|||
53649AAB269CFAEA00A2D8B7 /* XCRemoteSwiftPackageReference "Puppy" */,
|
||||
6260FFF726A09754003FA968 /* XCRemoteSwiftPackageReference "CombineExt" */,
|
||||
62C29E9A26D0FE4100C1D2E7 /* XCRemoteSwiftPackageReference "stinsen" */,
|
||||
C4BFD4E327167B63007739E3 /* XCRemoteSwiftPackageReference "SwiftUICollection" */,
|
||||
);
|
||||
productRefGroup = 5377CBF2263B596A003A4E83 /* Products */;
|
||||
projectDirPath = "";
|
||||
|
@ -2268,6 +2275,14 @@
|
|||
kind = branch;
|
||||
};
|
||||
};
|
||||
C4BFD4E327167B63007739E3 /* XCRemoteSwiftPackageReference "SwiftUICollection" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/ABJC/SwiftUICollection";
|
||||
requirement = {
|
||||
branch = master;
|
||||
kind = branch;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
|
@ -2406,6 +2421,16 @@
|
|||
package = 62CB3F442685BAF7003D0A6F /* XCRemoteSwiftPackageReference "Defaults" */;
|
||||
productName = Defaults;
|
||||
};
|
||||
C49FB6582717A06300AAEABB /* SwiftUICollection */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = C4BFD4E327167B63007739E3 /* XCRemoteSwiftPackageReference "SwiftUICollection" */;
|
||||
productName = SwiftUICollection;
|
||||
};
|
||||
C4BFD4E427167B63007739E3 /* SwiftUICollection */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = C4BFD4E327167B63007739E3 /* XCRemoteSwiftPackageReference "SwiftUICollection" */;
|
||||
productName = SwiftUICollection;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
|
||||
/* Begin XCVersionGroup section */
|
||||
|
|
|
@ -136,6 +136,15 @@
|
|||
"version": "0.1.3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "SwiftUICollection",
|
||||
"repositoryURL": "https://github.com/ABJC/SwiftUICollection",
|
||||
"state": {
|
||||
"branch": "master",
|
||||
"revision": "e27149382ce8ec21995069c8aab7ca83d61a3120",
|
||||
"version": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "SwiftUIFocusGuide",
|
||||
"repositoryURL": "https://github.com/rmnblm/SwiftUIFocusGuide",
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
import Combine
|
||||
import Foundation
|
||||
import JellyfinAPI
|
||||
import SwiftUICollection
|
||||
|
||||
typealias LibraryRow = CollectionRow<Int, BaseItemDto>
|
||||
|
||||
final class LibraryViewModel: ViewModel {
|
||||
var parentID: String?
|
||||
|
@ -18,6 +21,7 @@ final class LibraryViewModel: ViewModel {
|
|||
var studio: NameGuidPair?
|
||||
|
||||
@Published var items = [BaseItemDto]()
|
||||
@Published var rows = [LibraryRow]()
|
||||
|
||||
@Published var totalPages = 0
|
||||
@Published var currentPage = 0
|
||||
|
@ -26,6 +30,8 @@ final class LibraryViewModel: ViewModel {
|
|||
|
||||
// temp
|
||||
@Published var filters: LibraryFilters
|
||||
|
||||
private let columns: Int
|
||||
|
||||
var enabledFilterType: [FilterType] {
|
||||
if genre == nil {
|
||||
|
@ -35,16 +41,20 @@ final class LibraryViewModel: ViewModel {
|
|||
}
|
||||
}
|
||||
|
||||
init(parentID: String? = nil,
|
||||
person: BaseItemPerson? = nil,
|
||||
genre: NameGuidPair? = nil,
|
||||
studio: NameGuidPair? = nil,
|
||||
filters: LibraryFilters = LibraryFilters(filters: [], sortOrder: [.ascending], withGenres: [], sortBy: [.name])) {
|
||||
init(
|
||||
parentID: String? = nil,
|
||||
person: BaseItemPerson? = nil,
|
||||
genre: NameGuidPair? = nil,
|
||||
studio: NameGuidPair? = nil,
|
||||
filters: LibraryFilters = LibraryFilters(filters: [], sortOrder: [.ascending], withGenres: [], sortBy: [.name]),
|
||||
columns: Int = 7
|
||||
) {
|
||||
self.parentID = parentID
|
||||
self.person = person
|
||||
self.genre = genre
|
||||
self.studio = studio
|
||||
self.filters = filters
|
||||
self.columns = columns
|
||||
super.init()
|
||||
|
||||
$filters
|
||||
|
@ -79,6 +89,7 @@ final class LibraryViewModel: ViewModel {
|
|||
self.hasPreviousPage = self.currentPage > 0
|
||||
self.hasNextPage = self.currentPage < self.totalPages - 1
|
||||
self.items = response.items ?? []
|
||||
self.calculateRows()
|
||||
})
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
@ -108,6 +119,7 @@ final class LibraryViewModel: ViewModel {
|
|||
self.hasPreviousPage = self.currentPage > 0
|
||||
self.hasNextPage = self.currentPage < self.totalPages - 1
|
||||
self.items.append(contentsOf: response.items ?? [])
|
||||
self.calculateRows()
|
||||
})
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
@ -126,4 +138,22 @@ final class LibraryViewModel: ViewModel {
|
|||
currentPage -= 1
|
||||
requestItems(with: filters)
|
||||
}
|
||||
|
||||
private func calculateRows() {
|
||||
let rowCount = items.count / columns
|
||||
rows = [LibraryRow]()
|
||||
for i in (0...rowCount) {
|
||||
let firstItemIndex = i * columns
|
||||
var lastItemIndex = firstItemIndex + columns
|
||||
if lastItemIndex >= items.count {
|
||||
lastItemIndex = items.count - 1
|
||||
}
|
||||
rows.append(
|
||||
LibraryRow(
|
||||
section: i,
|
||||
items: Array(items[firstItemIndex...lastItemIndex])
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue