Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[iOS & tvOS] ItemLibraryViewModel - Cleanup #1411

Merged
merged 8 commits into from
Feb 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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