Skip to content

Commit

Permalink
[iOS & tvOS] ItemLibraryViewModel - Cleanup (#1411)
Browse files Browse the repository at this point in the history
* Move ItemType to Filter

* Init but normally...

* filter on people?

* Default to easiest / least change solution.

* Reset `.collectionFolder`, `.folder`, and `.BaseItemPerson` in `PagingLibraryView` to have the default filters. This was originally in place. This Commit just ensures that iOS and tvOS have the same implementation.

* wip

* Update ItemLibraryViewModel.swift

* Update ItemLibraryViewModel.swift

---------

Co-authored-by: Ethan Pippin <ethanpippin2343@gmail.com>
  • Loading branch information
JPKribs and LePips authored Feb 2, 2025
1 parent 21cf786 commit 3ee2abe
Show file tree
Hide file tree
Showing 13 changed files with 126 additions and 225 deletions.
10 changes: 4 additions & 6 deletions Shared/Coordinators/MainCoordinator/tvOSMainTabCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,8 @@ final class MainTabCoordinator: TabCoordinatable {
}

func makeTVShows() -> NavigationViewCoordinator<LibraryCoordinator<BaseItemDto>> {
let viewModel = ItemTypeLibraryViewModel(
itemTypes: [.series],
filters: .default
let viewModel = ItemLibraryViewModel(
filters: .init(itemTypes: [.series])
)
return NavigationViewCoordinator(LibraryCoordinator(viewModel: viewModel))
}
Expand All @@ -65,9 +64,8 @@ final class MainTabCoordinator: TabCoordinatable {
}

func makeMovies() -> NavigationViewCoordinator<LibraryCoordinator<BaseItemDto>> {
let viewModel = ItemTypeLibraryViewModel(
itemTypes: [.movie],
filters: .default
let viewModel = ItemLibraryViewModel(
filters: .init(itemTypes: [.movie])
)
return NavigationViewCoordinator(LibraryCoordinator(viewModel: viewModel))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ extension BaseItemDto: Displayable {
}

extension BaseItemDto: LibraryParent {

var libraryType: BaseItemKind? {
type
}
Expand Down
31 changes: 31 additions & 0 deletions Shared/Extensions/JellyfinAPI/BaseItemKind.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// 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 (c) 2025 Jellyfin & Jellyfin Contributors
//

import JellyfinAPI

extension BaseItemKind: SupportedCaseIterable {

/// The base supported cases for media navigation.
/// This differs from media viewing, which may include
/// `.episode`.
///
/// These is the *base* supported cases and other objects
/// like `LibararyParent` may have additional supported
/// cases for querying a library.
static var supportedCases: [BaseItemKind] {
[.movie, .series, .boxSet]
}
}

extension BaseItemKind: ItemFilter {

// TODO: localize
var displayTitle: String {
rawValue
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ extension BaseItemPerson: Displayable {
}

extension BaseItemPerson: LibraryParent {

var libraryType: BaseItemKind? {
.person
}
Expand Down
6 changes: 5 additions & 1 deletion Shared/Objects/ItemFilter/ItemFilterCollection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import JellyfinAPI
struct ItemFilterCollection: Codable, Defaults.Serializable, Hashable {

var genres: [ItemGenre] = []
var itemTypes: [BaseItemKind] = []
var letter: [ItemLetter] = []
var sortBy: [ItemSortBy] = [ItemSortBy.name]
var sortOrder: [ItemSortOrder] = [ItemSortOrder.ascending]
Expand All @@ -32,7 +33,10 @@ struct ItemFilterCollection: Codable, Defaults.Serializable, Hashable {
sortOrder: [ItemSortOrder.descending]
)

/// A collection that has all statically available values
/// A collection that has all statically available values.
///
/// These may be altered when used to better represent all
/// available values within the current context.
static let all: ItemFilterCollection = .init(
letter: ItemLetter.allCases,
sortBy: ItemSortBy.allCases,
Expand Down
57 changes: 49 additions & 8 deletions Shared/Objects/LibraryParent/LibraryParent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,58 @@
// Copyright (c) 2025 Jellyfin & Jellyfin Contributors
//

import Foundation
import JellyfinAPI

protocol LibraryParent: Displayable, Hashable, Identifiable<String?> {

// Only called `libraryType` because `BaseItemPerson` has
// a different `type` property. However, people should have
// different views so this can be renamed when they do, or
// this protocol to be removed entirely and replace just with
// a concrete `BaseItemDto`
//
// edit: studios also implement `LibraryParent` - reconsider above comment
/// The type of the library, reusing `BaseItemKind` for some
/// ease of provided variety like `folder` and `userView`.
var libraryType: BaseItemKind? { get }

/// The `BaseItemKind` types that this library parent
/// support. Mainly used for `.folder` support.
///
/// When using filters, this is used to determine the initial
/// set of supported types and then
var supportedItemTypes: [BaseItemKind] { get }

/// Modifies the parameters for the items request per this library parent.
func setParentParameters(_ parameters: Paths.GetItemsByUserIDParameters) -> Paths.GetItemsByUserIDParameters
}

extension LibraryParent {

var supportedItemTypes: [BaseItemKind] {
switch libraryType {
case .folder:
BaseItemKind.supportedCases
.appending([.folder, .collectionFolder])
default:
BaseItemKind.supportedCases
}
}

func setParentParameters(_ parameters: Paths.GetItemsByUserIDParameters) -> Paths.GetItemsByUserIDParameters {

guard let id else { return parameters }

var parameters = parameters
parameters.isRecursive = true
parameters.includeItemTypes = supportedItemTypes

switch libraryType {
case .collectionFolder, .userView:
parameters.parentID = id
case .folder:
parameters.parentID = id
parameters.isRecursive = nil
case .person:
parameters.personIDs = [id]
case .studio:
parameters.studioIDs = [id]
default: ()
}

return parameters
}
}
18 changes: 5 additions & 13 deletions Shared/ViewModels/FilterViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,18 @@ final class FilterViewModel: ViewModel {
var allFilters: ItemFilterCollection = .all

private let parent: (any LibraryParent)?
private let itemTypes: [BaseItemKind]?

init(
parent: (any LibraryParent)? = nil,
currentFilters: ItemFilterCollection = .default
) {
self.parent = parent
self.itemTypes = nil
self.currentFilters = currentFilters
super.init()
}

init(
itemTypes: [BaseItemKind],
currentFilters: ItemFilterCollection = .default
) {
self.parent = nil
self.itemTypes = itemTypes
self.currentFilters = currentFilters
super.init()
if let parent {
self.allFilters.itemTypes = parent.supportedItemTypes
}
}

/// Sets the query filters from the parent
Expand All @@ -53,10 +45,10 @@ final class FilterViewModel: ViewModel {
}

private func getQueryFilters() async -> (genres: [ItemGenre], tags: [ItemTag], years: [ItemYear]) {

let parameters = Paths.GetQueryFiltersLegacyParameters(
userID: userSession.user.id,
parentID: parent?.id as? String,
includeItemTypes: itemTypes
parentID: parent?.id
)

let request = Paths.getQueryFiltersLegacy(parameters: parameters)
Expand Down
51 changes: 19 additions & 32 deletions Shared/ViewModels/LibraryViewModel/ItemLibraryViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,42 +48,23 @@ final class ItemLibraryViewModel: PagingLibraryViewModel<BaseItemDto> {

// MARK: item parameters

func itemParameters(for page: Int?) -> Paths.GetItemsByUserIDParameters {

var libraryID: String?
var personIDs: [String]?
var studioIDs: [String]?
var includeItemTypes: [BaseItemKind] = [.movie, .series, .boxSet]
var isRecursive: Bool? = true

// TODO: this logic should be moved to a `LibraryParent` function
// that transforms a `GetItemsByUserIDParameters` struct, instead
// of having to do this case-by-case.

if let libraryType = parent?.libraryType, let id = parent?.id {
switch libraryType {
case .collectionFolder, .userView:
libraryID = id
case .folder:
libraryID = id
isRecursive = nil
includeItemTypes = [.movie, .series, .boxSet, .folder, .collectionFolder]
case .person:
personIDs = [id]
case .studio:
studioIDs = [id]
default: ()
}
}
private func itemParameters(for page: Int?) -> Paths.GetItemsByUserIDParameters {

var parameters = Paths.GetItemsByUserIDParameters()

parameters.enableUserData = true
parameters.fields = .MinimumFields
parameters.includeItemTypes = includeItemTypes
parameters.isRecursive = isRecursive
parameters.parentID = libraryID
parameters.personIDs = personIDs
parameters.studioIDs = studioIDs

// Default values, expected to be overridden
// by parent or filters
parameters.includeItemTypes = BaseItemKind.supportedCases
parameters.sortOrder = [.ascending]
parameters.sortBy = [ItemSortBy.name.rawValue]

// Parent
if let parent {
parameters = parent.setParentParameters(parameters)
}

// Page size
if let page {
Expand All @@ -101,6 +82,12 @@ final class ItemLibraryViewModel: PagingLibraryViewModel<BaseItemDto> {
parameters.tags = filters.tags.map(\.value)
parameters.years = filters.years.compactMap { Int($0.value) }

// Only set filtering on item types if selected, where
// supported values should have been set by the parent.
if filters.itemTypes.isNotEmpty {
parameters.includeItemTypes = filters.itemTypes
}

if filters.letter.first?.value == "#" {
parameters.nameLessThan = "A"
} else {
Expand Down
105 changes: 0 additions & 105 deletions Shared/ViewModels/LibraryViewModel/ItemTypeLibraryViewModel.swift

This file was deleted.

Loading

0 comments on commit 3ee2abe

Please sign in to comment.