Merge pull request #182 from jhays/jhays/swiftUICollection

use CollectionView on tvOS LibraryView
This commit is contained in:
aiden 3 2021-10-15 16:53:52 -04:00 committed by GitHub
commit f1127aca35
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 165 additions and 50 deletions

View File

@ -7,57 +7,86 @@
*/
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: { _, cell in
GeometryReader { _ in
if let item = cell.item {
if item.type != "Folder" {
NavigationLink(destination: LazyView { ItemView(item: item) }) {
PortraitItemElement(item: item)
}
.buttonStyle(PlainNavigationLinkButtonStyle())
.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)
}
}
} supplementaryView: { _, indexPath in
HStack {
Spacer()
}.accessibilityIdentifier("\(indexPath.section).\(indexPath.row)")
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.ignoresSafeArea(.all)
} else {
Text("No results.")
}
}
}
// stream BM^S by nicki!

View File

@ -253,6 +253,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 */; };
@ -529,6 +531,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 */,
@ -543,6 +546,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 */,
@ -1152,6 +1156,7 @@
53649AAE269CFAF600A2D8B7 /* Puppy */,
6261A0DF26A0AB710072EF1C /* CombineExt */,
6220D0C826D63F3700B8E046 /* Stinsen */,
C49FB6582717A06300AAEABB /* SwiftUICollection */,
);
productName = "JellyfinPlayer tvOS";
productReference = 535870602669D21600D05A09 /* JellyfinPlayer tvOS.app */;
@ -1187,6 +1192,7 @@
53649AAC269CFAEA00A2D8B7 /* Puppy */,
6260FFF826A09754003FA968 /* CombineExt */,
62C29E9B26D0FE4200C1D2E7 /* Stinsen */,
C4BFD4E427167B63007739E3 /* SwiftUICollection */,
);
productName = JellyfinPlayer;
productReference = 5377CBF1263B596A003A4E83 /* JellyfinPlayer iOS.app */;
@ -1276,6 +1282,7 @@
53649AAB269CFAEA00A2D8B7 /* XCRemoteSwiftPackageReference "Puppy" */,
6260FFF726A09754003FA968 /* XCRemoteSwiftPackageReference "CombineExt" */,
62C29E9A26D0FE4100C1D2E7 /* XCRemoteSwiftPackageReference "stinsen" */,
C4BFD4E327167B63007739E3 /* XCRemoteSwiftPackageReference "SwiftUICollection" */,
);
productRefGroup = 5377CBF2263B596A003A4E83 /* Products */;
projectDirPath = "";
@ -2270,6 +2277,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 */
@ -2408,6 +2423,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 */

View File

@ -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",

View File

@ -10,6 +10,15 @@
import Combine
import Foundation
import JellyfinAPI
import SwiftUICollection
typealias LibraryRow = CollectionRow<Int, LibraryRowCell>
struct LibraryRowCell: Hashable {
let id = UUID()
let item: BaseItemDto?
var loadingCell: Bool = false
}
final class LibraryViewModel: ViewModel {
var parentID: String?
@ -18,6 +27,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 +36,8 @@ final class LibraryViewModel: ViewModel {
// temp
@Published var filters: LibraryFilters
private let columns: Int
var enabledFilterType: [FilterType] {
if genre == nil {
@ -35,16 +47,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 +95,7 @@ final class LibraryViewModel: ViewModel {
self.hasPreviousPage = self.currentPage > 0
self.hasNextPage = self.currentPage < self.totalPages - 1
self.items = response.items ?? []
self.rows = self.calculateRows()
})
.store(in: &cancellables)
}
@ -108,6 +125,7 @@ final class LibraryViewModel: ViewModel {
self.hasPreviousPage = self.currentPage > 0
self.hasNextPage = self.currentPage < self.totalPages - 1
self.items.append(contentsOf: response.items ?? [])
self.rows = self.calculateRows()
})
.store(in: &cancellables)
}
@ -126,4 +144,38 @@ final class LibraryViewModel: ViewModel {
currentPage -= 1
requestItems(with: filters)
}
private func calculateRows() -> [LibraryRow] {
guard items.count > 0 else { return [] }
let rowCount = items.count / columns
var calculatedRows = [LibraryRow]()
for i in (0...rowCount) {
let firstItemIndex = i * columns
var lastItemIndex = firstItemIndex + columns
if lastItemIndex > items.count {
lastItemIndex = items.count
}
var rowCells = [LibraryRowCell]()
for item in items[firstItemIndex..<lastItemIndex] {
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
)
)
}
return calculatedRows
}
}