Merge pull request #351 from mshockwave/dev-folder-view

Basic support for folder-type library items
This commit is contained in:
Ethan Pippin 2022-01-28 11:01:18 -08:00 committed by GitHub
commit 6bd07817f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 61 additions and 92 deletions

View File

@ -120,8 +120,12 @@ public extension BaseItemDto {
}
func getSeriesPrimaryImage(maxWidth: Int) -> URL {
guard let seriesId = seriesId else {
return getPrimaryImage(maxWidth: maxWidth)
}
let x = UIScreen.main.nativeScale * CGFloat(maxWidth)
let urlString = ImageAPI.getItemImageWithRequestBuilder(itemId: seriesId ?? "",
let urlString = ImageAPI.getItemImageWithRequestBuilder(itemId: seriesId,
imageType: .primary,
maxWidth: Int(x),
quality: 96,
@ -227,6 +231,7 @@ public extension BaseItemDto {
case series = "Series"
case boxset = "BoxSet"
case collectionFolder = "CollectionFolder"
case folder = "Folder"
case unknown
@ -249,7 +254,7 @@ public extension BaseItemDto {
func portraitHeaderViewURL(maxWidth: Int) -> URL {
switch itemType {
case .movie, .season, .series, .boxset, .collectionFolder:
case .movie, .season, .series, .boxset, .collectionFolder, .folder:
return getPrimaryImage(maxWidth: maxWidth)
case .episode:
return getSeriesPrimaryImage(maxWidth: maxWidth)

View File

@ -324,6 +324,8 @@ internal enum L10n {
internal static var settings: String { return L10n.tr("Localizable", "settings") }
/// Show Cast & Crew
internal static var showCastAndCrew: String { return L10n.tr("Localizable", "showCastAndCrew") }
/// Flatten Library Items
internal static var showFlattenView: String { return L10n.tr("Localizable", "showFlattenView") }
/// Show Missing Episodes
internal static var showMissingEpisodes: String { return L10n.tr("Localizable", "showMissingEpisodes") }
/// Show Missing Seasons

View File

@ -41,6 +41,7 @@ extension Defaults.Keys {
// Customize settings
static let showPosterLabels = Key<Bool>("showPosterLabels", default: true, suite: SwiftfinStore.Defaults.generalSuite)
static let showCastAndCrew = Key<Bool>("showCastAndCrew", default: true, suite: SwiftfinStore.Defaults.generalSuite)
static let showFlattenView = Key<Bool>("showFlattenView", default: true, suite: SwiftfinStore.Defaults.generalSuite)
// Video player / overlay settings
static let overlayType = Key<OverlayType>("overlayType", default: .normal, suite: SwiftfinStore.Defaults.generalSuite)

View File

@ -7,6 +7,7 @@
//
import Combine
import Defaults
import Foundation
import JellyfinAPI
import SwiftUICollection
@ -94,10 +95,20 @@ final class LibraryViewModel: ViewModel {
genreIDs = filters.withGenres.compactMap(\.id)
}
let sortBy = filters.sortBy.map(\.rawValue)
let queryRecursive = Defaults[.showFlattenView] || filters.filters.contains(.isFavorite) ||
self.person != nil ||
self.genre != nil ||
self.studio != nil
let includeItemTypes: [String]
if filters.filters.contains(.isFavorite) {
includeItemTypes = ["Movie", "Series", "Season", "Episode", "BoxSet"]
} else {
includeItemTypes = ["Movie", "Series", "BoxSet"] + (Defaults[.showFlattenView] ? [] : ["Folder"])
}
ItemsAPI.getItemsByUserId(userId: SessionManager.main.currentLogin.user.id, startIndex: currentPage * pageItemSize,
limit: pageItemSize,
recursive: true,
recursive: queryRecursive,
searchTerm: nil,
sortOrder: filters.sortOrder,
parentId: parentID,
@ -110,9 +121,7 @@ final class LibraryViewModel: ViewModel {
.people,
.chapters,
],
includeItemTypes: filters.filters
.contains(.isFavorite) ? ["Movie", "Series", "Season", "Episode", "BoxSet"] :
["Movie", "Series", "BoxSet"],
includeItemTypes: includeItemTypes,
filters: filters.filters,
sortBy: sortBy,
tags: filters.tags,

View File

@ -62,7 +62,7 @@ struct ItemView: View {
} else {
SeriesItemView(viewModel: SeriesItemViewModel(item: item))
}
case .boxset:
case .boxset, .folder:
CinematicCollectionItemView(viewModel: CollectionItemViewModel(item: item))
default:
Text(L10n.notImplementedYetWithType(item.type ?? ""))

View File

@ -21,39 +21,17 @@ struct LibraryListView: View {
@Default(.Experimental.liveTVAlphaEnabled)
var liveTVAlphaEnabled
let supportedCollectionTypes = ["movies", "tvshows", "boxsets", "livetv", "other"]
var body: some View {
ScrollView {
LazyVStack {
if !viewModel.isLoading {
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)
}
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
ForEach(viewModel.libraries.filter { [self] library in
let collectionType = library.collectionType ?? "other"
return self.supportedCollectionTypes.contains(collectionType)
}, id: \.id) { library in
if library.collectionType == "livetv" {
if liveTVAlphaEnabled {
Button {
@ -81,7 +59,8 @@ struct LibraryListView: View {
}
} else {
Button {
self.libraryListRouter.route(to: \.library, (viewModel: LibraryViewModel(), title: library.name ?? ""))
self.libraryListRouter.route(to: \.library,
(viewModel: LibraryViewModel(parentID: library.id), title: library.name ?? ""))
}
label: {
ZStack {

View File

@ -56,17 +56,15 @@ struct LibraryView: View {
} cell: { _, cell in
GeometryReader { _ in
if let item = cell.item {
if item.type != "Folder" {
Button {
libraryRouter.route(to: \.modalItem, item)
} label: {
PortraitItemElement(item: item)
}
.buttonStyle(PlainNavigationLinkButtonStyle())
.onAppear {
if item == viewModel.items.last && viewModel.hasNextPage {
viewModel.requestNextPageAsync()
}
Button {
libraryRouter.route(to: \.modalItem, item)
} label: {
PortraitItemElement(item: item)
}
.buttonStyle(PlainNavigationLinkButtonStyle())
.onAppear {
if item == viewModel.items.last && viewModel.hasNextPage {
viewModel.requestNextPageAsync()
}
}
} else if cell.loadingCell {

View File

@ -15,6 +15,8 @@ struct CustomizeViewsSettings: View {
var showPosterLabels
@Default(.showCastAndCrew)
var showCastAndCrew
@Default(.showFlattenView)
var showFlattenView
var body: some View {
Form {
@ -24,6 +26,7 @@ struct CustomizeViewsSettings: View {
// TODO: Uncomment when cast and crew implemented in item views
// Toggle(L10n.showCastAndCrew, isOn: $showCastAndCrew)
Toggle(L10n.showFlattenView, isOn: $showFlattenView)
} header: {
L10n.customize.text

View File

@ -51,7 +51,7 @@ private struct ItemView: View {
self.viewModel = EpisodeItemViewModel(item: item)
case .series:
self.viewModel = SeriesItemViewModel(item: item)
case .boxset:
case .boxset, .folder:
self.viewModel = CollectionItemViewModel(item: item)
default:
self.viewModel = ItemViewModel(item: item)

View File

@ -16,6 +16,8 @@ struct LibraryListView: View {
@StateObject
var viewModel = LibraryListViewModel()
let supportedCollectionTypes = ["movies", "tvshows", "boxsets", "other"]
var body: some View {
ScrollView {
LazyVStack {
@ -42,21 +44,23 @@ struct LibraryListView: View {
.padding(.bottom, 5)
if !viewModel.isLoading {
if let collectionsLibraryItem = viewModel.libraries.first(where: { $0.collectionType == "boxsets" }) {
ForEach(viewModel.libraries.filter { [self] library in
let collectionType = library.collectionType ?? "other"
return self.supportedCollectionTypes.contains(collectionType)
}, id: \.id) { library in
Button {
libraryListRouter.route(to: \.library,
(viewModel: LibraryViewModel(parentID: collectionsLibraryItem.id),
title: collectionsLibraryItem.name ?? ""))
(viewModel: LibraryViewModel(parentID: library.id),
title: library.name ?? ""))
} label: {
ZStack {
ImageView(src: collectionsLibraryItem.getPrimaryImage(maxWidth: 500),
bh: collectionsLibraryItem.getPrimaryImageBlurHash())
ImageView(src: library.getPrimaryImage(maxWidth: 500), bh: library.getPrimaryImageBlurHash())
.opacity(0.4)
.accessibilityIgnoresInvertColors()
HStack {
Spacer()
VStack {
Text(collectionsLibraryItem.name ?? "")
Text(library.name ?? "")
.foregroundColor(.white)
.font(.title2)
.fontWeight(.semibold)
@ -71,39 +75,6 @@ struct LibraryListView: View {
.shadow(radius: 5)
.padding(.bottom, 5)
}
ForEach(viewModel.libraries, id: \.id) { library in
if library.collectionType ?? "" == "movies" || library.collectionType ?? "" == "tvshows" {
Button {
libraryListRouter.route(to: \.library,
(viewModel: LibraryViewModel(parentID: library.id),
title: library.name ?? ""))
} label: {
ZStack {
ImageView(src: library.getPrimaryImage(maxWidth: 500), bh: library.getPrimaryImageBlurHash())
.opacity(0.4)
.accessibilityIgnoresInvertColors()
HStack {
Spacer()
VStack {
Text(library.name ?? "")
.foregroundColor(.white)
.font(.title2)
.fontWeight(.semibold)
}
Spacer()
}.padding(32)
}.background(Color.black)
.frame(minWidth: 100, maxWidth: .infinity)
.frame(height: 100)
}
.cornerRadius(10)
.shadow(radius: 5)
.padding(.bottom, 5)
} else {
EmptyView()
}
}
} else {
ProgressView()
}

View File

@ -45,10 +45,8 @@ struct LibraryView: View {
VStack {
LazyVGrid(columns: tracks) {
ForEach(viewModel.items, id: \.id) { item in
if item.type != "Folder" {
PortraitItemButton(item: item) { item in
libraryRouter.route(to: \.item, item)
}
PortraitItemButton(item: item) { item in
libraryRouter.route(to: \.item, item)
}
}
}

View File

@ -15,6 +15,8 @@ struct CustomizeViewsSettings: View {
var showPosterLabels
@Default(.showCastAndCrew)
var showCastAndCrew
@Default(.showFlattenView)
var showFlattenView
var body: some View {
Form {
@ -22,6 +24,7 @@ struct CustomizeViewsSettings: View {
Toggle(L10n.showPosterLabels, isOn: $showPosterLabels)
Toggle(L10n.showCastAndCrew, isOn: $showCastAndCrew)
Toggle(L10n.showFlattenView, isOn: $showFlattenView)
} header: {
L10n.customize.text