Fix Folder Libraries (#555)
This commit is contained in:
parent
14f1219500
commit
2a3617bd47
|
@ -48,6 +48,8 @@ final class LibraryCoordinator: NavigationCoordinatable {
|
|||
var item = makeItem
|
||||
@Route(.modal)
|
||||
var filter = makeFilter
|
||||
@Route(.push)
|
||||
var library = makeLibrary
|
||||
#endif
|
||||
|
||||
private let parameters: Parameters
|
||||
|
@ -77,5 +79,9 @@ final class LibraryCoordinator: NavigationCoordinatable {
|
|||
func makeFilter(parameters: FilterCoordinator.Parameters) -> NavigationViewCoordinator<FilterCoordinator> {
|
||||
NavigationViewCoordinator(FilterCoordinator(parameters: parameters))
|
||||
}
|
||||
|
||||
func makeLibrary(parameters: LibraryCoordinator.Parameters) -> LibraryCoordinator {
|
||||
LibraryCoordinator(parameters: parameters)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -86,26 +86,26 @@ final class LibraryViewModel: ViewModel {
|
|||
}
|
||||
}
|
||||
|
||||
var recursive = true
|
||||
let includeItemTypes: [BaseItemKind]
|
||||
|
||||
if filters.filters.contains(ItemFilter.isFavorite.filter) {
|
||||
includeItemTypes = [.movie, .boxSet, .series, .season, .episode]
|
||||
} else if type == .folders {
|
||||
includeItemTypes = [.collectionFolder]
|
||||
recursive = false
|
||||
includeItemTypes = [.movie, .boxSet, .series, .folder, .collectionFolder]
|
||||
} else {
|
||||
includeItemTypes = [.movie, .series, .boxSet]
|
||||
includeItemTypes = [.movie, .boxSet, .series]
|
||||
}
|
||||
|
||||
let excludedIDs: [String]?
|
||||
var excludedIDs: [String]?
|
||||
|
||||
if filters.sortBy.first == SortBy.random.filter {
|
||||
excludedIDs = items.compactMap(\.id)
|
||||
} else {
|
||||
excludedIDs = nil
|
||||
}
|
||||
|
||||
let genreIDs = filters.genres.compactMap(\.id)
|
||||
let sortBy: [String] = filters.sortBy.map(\.filterName)
|
||||
let sortBy: [String] = filters.sortBy.map(\.filterName).appending("IsFolder")
|
||||
let sortOrder = filters.sortOrder.map { SortOrder(rawValue: $0.filterName) ?? .ascending }
|
||||
let itemFilters: [ItemFilter] = filters.filters.compactMap { .init(rawValue: $0.filterName) }
|
||||
let tags: [String] = filters.tags.map(\.filterName)
|
||||
|
@ -115,7 +115,7 @@ final class LibraryViewModel: ViewModel {
|
|||
excludeItemIds: excludedIDs,
|
||||
startIndex: currentPage * pageItemSize,
|
||||
limit: pageItemSize,
|
||||
recursive: true,
|
||||
recursive: recursive,
|
||||
sortOrder: sortOrder,
|
||||
parentId: libraryID,
|
||||
fields: ItemFields.allCases,
|
||||
|
|
|
@ -14,10 +14,10 @@ struct PosterButton<Item: Poster, Content: View, ImageOverlay: View, ContextMenu
|
|||
private var type: PosterType
|
||||
private var itemScale: CGFloat
|
||||
private var horizontalAlignment: HorizontalAlignment
|
||||
private var content: (Item) -> Content
|
||||
private var imageOverlay: (Item) -> ImageOverlay
|
||||
private var contextMenu: (Item) -> ContextMenu
|
||||
private var onSelect: (Item) -> Void
|
||||
private var content: () -> Content
|
||||
private var imageOverlay: () -> ImageOverlay
|
||||
private var contextMenu: () -> ContextMenu
|
||||
private var onSelect: () -> Void
|
||||
private var singleImage: Bool
|
||||
|
||||
private var itemWidth: CGFloat {
|
||||
|
@ -29,10 +29,10 @@ struct PosterButton<Item: Poster, Content: View, ImageOverlay: View, ContextMenu
|
|||
type: PosterType,
|
||||
itemScale: CGFloat,
|
||||
horizontalAlignment: HorizontalAlignment,
|
||||
@ViewBuilder content: @escaping (Item) -> Content,
|
||||
@ViewBuilder imageOverlay: @escaping (Item) -> ImageOverlay,
|
||||
@ViewBuilder contextMenu: @escaping (Item) -> ContextMenu,
|
||||
onSelect: @escaping (Item) -> Void,
|
||||
@ViewBuilder content: @escaping () -> Content,
|
||||
@ViewBuilder imageOverlay: @escaping () -> ImageOverlay,
|
||||
@ViewBuilder contextMenu: @escaping () -> ContextMenu,
|
||||
onSelect: @escaping () -> Void,
|
||||
singleImage: Bool
|
||||
) {
|
||||
self.item = item
|
||||
|
@ -49,7 +49,7 @@ struct PosterButton<Item: Poster, Content: View, ImageOverlay: View, ContextMenu
|
|||
var body: some View {
|
||||
VStack(alignment: horizontalAlignment) {
|
||||
Button {
|
||||
onSelect(item)
|
||||
onSelect()
|
||||
} label: {
|
||||
Group {
|
||||
switch type {
|
||||
|
@ -60,17 +60,17 @@ struct PosterButton<Item: Poster, Content: View, ImageOverlay: View, ContextMenu
|
|||
}
|
||||
}
|
||||
.overlay {
|
||||
imageOverlay(item)
|
||||
imageOverlay()
|
||||
.posterStyle(type: type, width: itemWidth)
|
||||
}
|
||||
}
|
||||
.contextMenu(menuItems: {
|
||||
contextMenu(item)
|
||||
contextMenu()
|
||||
})
|
||||
.posterStyle(type: type, width: itemWidth)
|
||||
.posterShadow()
|
||||
|
||||
content(item)
|
||||
content()
|
||||
}
|
||||
.frame(width: itemWidth)
|
||||
}
|
||||
|
@ -86,10 +86,10 @@ extension PosterButton where Content == PosterButtonDefaultContentView<Item>,
|
|||
type: type,
|
||||
itemScale: 1,
|
||||
horizontalAlignment: .leading,
|
||||
content: { PosterButtonDefaultContentView(item: $0) },
|
||||
imageOverlay: { _ in EmptyView() },
|
||||
contextMenu: { _ in EmptyView() },
|
||||
onSelect: { _ in },
|
||||
content: { PosterButtonDefaultContentView(item: item) },
|
||||
imageOverlay: { EmptyView() },
|
||||
contextMenu: { EmptyView() },
|
||||
onSelect: {},
|
||||
singleImage: singleImage
|
||||
)
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ extension PosterButton {
|
|||
}
|
||||
|
||||
@ViewBuilder
|
||||
func content<C: View>(@ViewBuilder _ content: @escaping (Item) -> C) -> PosterButton<Item, C, ImageOverlay, ContextMenu> {
|
||||
func content<C: View>(@ViewBuilder _ content: @escaping () -> C) -> PosterButton<Item, C, ImageOverlay, ContextMenu> {
|
||||
PosterButton<Item, C, ImageOverlay, ContextMenu>(
|
||||
item: item,
|
||||
type: type,
|
||||
|
@ -124,7 +124,7 @@ extension PosterButton {
|
|||
}
|
||||
|
||||
@ViewBuilder
|
||||
func imageOverlay<O: View>(@ViewBuilder _ imageOverlay: @escaping (Item) -> O) -> PosterButton<Item, Content, O, ContextMenu> {
|
||||
func imageOverlay<O: View>(@ViewBuilder _ imageOverlay: @escaping () -> O) -> PosterButton<Item, Content, O, ContextMenu> {
|
||||
PosterButton<Item, Content, O, ContextMenu>(
|
||||
item: item,
|
||||
type: type,
|
||||
|
@ -139,7 +139,7 @@ extension PosterButton {
|
|||
}
|
||||
|
||||
@ViewBuilder
|
||||
func contextMenu<M: View>(@ViewBuilder _ contextMenu: @escaping (Item) -> M) -> PosterButton<Item, Content, ImageOverlay, M> {
|
||||
func contextMenu<M: View>(@ViewBuilder _ contextMenu: @escaping () -> M) -> PosterButton<Item, Content, ImageOverlay, M> {
|
||||
PosterButton<Item, Content, ImageOverlay, M>(
|
||||
item: item,
|
||||
type: type,
|
||||
|
@ -153,7 +153,7 @@ extension PosterButton {
|
|||
)
|
||||
}
|
||||
|
||||
func onSelect(_ action: @escaping (Item) -> Void) -> Self {
|
||||
func onSelect(_ action: @escaping () -> Void) -> Self {
|
||||
var copy = self
|
||||
copy.onSelect = action
|
||||
return copy
|
||||
|
|
|
@ -65,9 +65,9 @@ struct PosterHStack<Item: Poster, Content: View, ImageOverlay: View, ContextMenu
|
|||
ForEach(items, id: \.hashValue) { item in
|
||||
PosterButton(item: item, type: type)
|
||||
.scaleItem(itemScale)
|
||||
.imageOverlay(imageOverlay)
|
||||
.contextMenu(contextMenu)
|
||||
.onSelect(onSelect)
|
||||
.imageOverlay { imageOverlay(item) }
|
||||
.contextMenu { contextMenu(item) }
|
||||
.onSelect { onSelect(item) }
|
||||
}
|
||||
}
|
||||
.padding(.horizontal)
|
||||
|
|
|
@ -22,7 +22,7 @@ struct EpisodeCard<RowManager: EpisodesRowManager>: View {
|
|||
var body: some View {
|
||||
PosterButton(item: episode, type: .landscape, singleImage: true)
|
||||
.scaleItem(1.2)
|
||||
.imageOverlay { _ in
|
||||
.imageOverlay {
|
||||
if let progress = episode.progress {
|
||||
LandscapePosterProgressBar(
|
||||
title: progress,
|
||||
|
@ -40,7 +40,7 @@ struct EpisodeCard<RowManager: EpisodesRowManager>: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.content { _ in
|
||||
.content {
|
||||
Button {
|
||||
router.route(to: \.item, episode)
|
||||
} label: {
|
||||
|
@ -78,7 +78,7 @@ struct EpisodeCard<RowManager: EpisodesRowManager>: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.onSelect { _ in
|
||||
.onSelect {
|
||||
episode.createVideoPlayerViewModel()
|
||||
.sink { completion in
|
||||
self.viewModel.handleAPIRequestError(completion: completion)
|
||||
|
|
|
@ -45,7 +45,8 @@ struct ItemView: View {
|
|||
case .person:
|
||||
LibraryView(viewModel: .init(parent: item, type: .person))
|
||||
case .collectionFolder:
|
||||
LibraryView(viewModel: .init(parent: item, type: .folders))
|
||||
Text("Here")
|
||||
// LibraryView(viewModel: .init(parent: item, type: .folders))
|
||||
default:
|
||||
Text(L10n.notImplementedYetWithType(item.type ?? "--"))
|
||||
}
|
||||
|
|
|
@ -15,10 +15,11 @@ struct LibraryItemRow: View {
|
|||
private var router: LibraryCoordinator.Router
|
||||
|
||||
let item: BaseItemDto
|
||||
private var onSelect: () -> Void
|
||||
|
||||
var body: some View {
|
||||
Button {
|
||||
router.route(to: \.item, item)
|
||||
onSelect()
|
||||
} label: {
|
||||
HStack(alignment: .bottom) {
|
||||
ImageView(item.portraitPosterImageSource(maxWidth: 60))
|
||||
|
@ -55,3 +56,16 @@ struct LibraryItemRow: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension LibraryItemRow {
|
||||
init(item: BaseItemDto) {
|
||||
self.item = item
|
||||
self.onSelect = {}
|
||||
}
|
||||
|
||||
func onSelect(_ action: @escaping () -> Void) -> Self {
|
||||
var copy = self
|
||||
copy.onSelect = action
|
||||
return copy
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import CollectionView
|
||||
import Defaults
|
||||
import JellyfinAPI
|
||||
import SwiftUI
|
||||
|
||||
struct LibraryView: View {
|
||||
|
@ -40,10 +41,27 @@ struct LibraryView: View {
|
|||
}
|
||||
}
|
||||
|
||||
private func baseItemOnSelect(_ item: BaseItemDto) {
|
||||
if let baseParent = viewModel.parent as? BaseItemDto {
|
||||
if baseParent.collectionType == "folders" {
|
||||
router.route(to: \.library, .init(parent: item, type: .folders, filters: .init()))
|
||||
} else if item.type == .folder {
|
||||
router.route(to: \.library, .init(parent: item, type: .library, filters: .init()))
|
||||
} else {
|
||||
router.route(to: \.item, item)
|
||||
}
|
||||
} else {
|
||||
router.route(to: \.item, item)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var libraryListView: some View {
|
||||
CollectionView(items: viewModel.items) { _, item, _ in
|
||||
LibraryItemRow(item: item)
|
||||
.onSelect {
|
||||
baseItemOnSelect(item)
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.layout { _, layoutEnvironment in
|
||||
|
@ -65,8 +83,8 @@ struct LibraryView: View {
|
|||
CollectionView(items: viewModel.items) { _, item, _ in
|
||||
PosterButton(item: item, type: libraryGridPosterType)
|
||||
.scaleItem(libraryGridPosterType == .landscape && UIDevice.isPhone ? 0.85 : 1)
|
||||
.onSelect { item in
|
||||
router.route(to: \.item, item)
|
||||
.onSelect {
|
||||
baseItemOnSelect(item)
|
||||
}
|
||||
}
|
||||
.layout { _, layoutEnvironment in
|
||||
|
|
|
@ -30,7 +30,7 @@ struct MediaView: View {
|
|||
CollectionView(items: viewModel.libraryItems) { _, item, _ in
|
||||
PosterButton(item: item, type: .landscape)
|
||||
.scaleItem(UIDevice.isPhone ? 0.85 : 1)
|
||||
.onSelect { _ in
|
||||
.onSelect {
|
||||
switch item.library.collectionType {
|
||||
case "favorites":
|
||||
router.route(to: \.library, .init(parent: item.library, type: .library, filters: .favorites))
|
||||
|
@ -42,7 +42,7 @@ struct MediaView: View {
|
|||
router.route(to: \.library, .init(parent: item.library, type: .library, filters: .init()))
|
||||
}
|
||||
}
|
||||
.imageOverlay { _ in
|
||||
.imageOverlay {
|
||||
ZStack {
|
||||
Color.black
|
||||
.opacity(0.5)
|
||||
|
|
Loading…
Reference in New Issue