refactor filters system
add LibraryFilterViewModel some func name lowercased
This commit is contained in:
parent
f8a70051ac
commit
39a5b6a2e7
|
@ -131,6 +131,8 @@
|
|||
62E632EA267D3FF50063E547 /* SeasonItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E632E8267D3FF50063E547 /* SeasonItemViewModel.swift */; };
|
||||
62E632EC267D410B0063E547 /* SeriesItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E632EB267D410B0063E547 /* SeriesItemViewModel.swift */; };
|
||||
62E632ED267D410B0063E547 /* SeriesItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E632EB267D410B0063E547 /* SeriesItemViewModel.swift */; };
|
||||
62E632EF267D43320063E547 /* LibraryFilterViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E632EE267D43320063E547 /* LibraryFilterViewModel.swift */; };
|
||||
62E632F0267D43320063E547 /* LibraryFilterViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E632EE267D43320063E547 /* LibraryFilterViewModel.swift */; };
|
||||
62EC3527267665D8000E9F2D /* MobileVLCKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53D5E3DC264B47EE00BADDC8 /* MobileVLCKit.xcframework */; };
|
||||
62EC3528267665D8000E9F2D /* MobileVLCKit.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 53D5E3DC264B47EE00BADDC8 /* MobileVLCKit.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
62EC352C26766675000E9F2D /* ServerEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC352B26766675000E9F2D /* ServerEnvironment.swift */; };
|
||||
|
@ -286,6 +288,7 @@
|
|||
62E632E5267D3F5B0063E547 /* EpisodeItemViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EpisodeItemViewModel.swift; sourceTree = "<group>"; };
|
||||
62E632E8267D3FF50063E547 /* SeasonItemViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeasonItemViewModel.swift; sourceTree = "<group>"; };
|
||||
62E632EB267D410B0063E547 /* SeriesItemViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SeriesItemViewModel.swift; sourceTree = "<group>"; };
|
||||
62E632EE267D43320063E547 /* LibraryFilterViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryFilterViewModel.swift; sourceTree = "<group>"; };
|
||||
62EC352B26766675000E9F2D /* ServerEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerEnvironment.swift; sourceTree = "<group>"; };
|
||||
62EC352E267666A5000E9F2D /* SessionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionManager.swift; sourceTree = "<group>"; };
|
||||
62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceRotationViewModifier.swift; sourceTree = "<group>"; };
|
||||
|
@ -354,6 +357,7 @@
|
|||
62E632E5267D3F5B0063E547 /* EpisodeItemViewModel.swift */,
|
||||
62E632E8267D3FF50063E547 /* SeasonItemViewModel.swift */,
|
||||
62E632EB267D410B0063E547 /* SeriesItemViewModel.swift */,
|
||||
62E632EE267D43320063E547 /* LibraryFilterViewModel.swift */,
|
||||
);
|
||||
path = ViewModels;
|
||||
sourceTree = "<group>";
|
||||
|
@ -730,6 +734,7 @@
|
|||
62E632E1267D30CA0063E547 /* LibraryViewModel.swift in Sources */,
|
||||
535870A82669D8AE00D05A09 /* StringExtensions.swift in Sources */,
|
||||
62E632ED267D410B0063E547 /* SeriesItemViewModel.swift in Sources */,
|
||||
62E632F0267D43320063E547 /* LibraryFilterViewModel.swift in Sources */,
|
||||
53ABFDE6267974EF00886593 /* SettingsViewModel.swift in Sources */,
|
||||
6267B3D826710B9800A7371D /* CollectionExtensions.swift in Sources */,
|
||||
62E632E7267D3F5B0063E547 /* EpisodeItemViewModel.swift in Sources */,
|
||||
|
@ -800,6 +805,7 @@
|
|||
62E632E0267D30CA0063E547 /* LibraryViewModel.swift in Sources */,
|
||||
62EC352F267666A5000E9F2D /* SessionManager.swift in Sources */,
|
||||
62E632E3267D3BA60063E547 /* MovieItemViewModel.swift in Sources */,
|
||||
62E632EF267D43320063E547 /* LibraryFilterViewModel.swift in Sources */,
|
||||
535870AD2669D8DD00D05A09 /* Typings.swift in Sources */,
|
||||
62EC352C26766675000E9F2D /* ServerEnvironment.swift in Sources */,
|
||||
6267B3DA2671138200A7371D /* ImageExtensions.swift in Sources */,
|
||||
|
|
|
@ -5,75 +5,80 @@
|
|||
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||
*/
|
||||
|
||||
import SwiftUI
|
||||
import JellyfinAPI
|
||||
import SwiftUI
|
||||
|
||||
struct LibraryFilterView: View {
|
||||
@Binding var filter: LibraryFilters
|
||||
@Environment(\.presentationMode)
|
||||
var presentationMode
|
||||
@Binding
|
||||
var filters: LibraryFilters
|
||||
|
||||
@StateObject
|
||||
var viewModel: LibraryFilterViewModel
|
||||
|
||||
init(filters: Binding<LibraryFilters>, enabledFilterType: [FilterType]) {
|
||||
_filters = filters
|
||||
_viewModel = StateObject(wrappedValue: .init(filters: filters.wrappedValue, enabledFilterType: enabledFilterType))
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
EmptyView()
|
||||
/*
|
||||
NavigationView {
|
||||
LoadingView(isShowing: $isLoading) {
|
||||
ZStack {
|
||||
Form {
|
||||
Toggle("Only show unplayed items", isOn: $onlyUnplayed)
|
||||
.onChange(of: onlyUnplayed) { value in
|
||||
if value {
|
||||
filter.filterTypes.append(.isUnplayed)
|
||||
} else {
|
||||
filter.filterTypes.removeAll { $0 == .isUnplayed }
|
||||
}
|
||||
}
|
||||
MultiSelector(label: "Genres",
|
||||
options: allGenres,
|
||||
optionToString: { $0.name },
|
||||
selected: $selectedGenres)
|
||||
.onChange(of: selectedGenres) { genres in
|
||||
filter.genres = genres.map(\.id)
|
||||
}
|
||||
MultiSelector(label: "Parental Ratings",
|
||||
options: allRatings,
|
||||
optionToString: { $0.name },
|
||||
selected: $selectedRatings)
|
||||
.onChange(of: selectedRatings) { ratings in
|
||||
filter.officialRatings = ratings.map(\.id)
|
||||
}
|
||||
|
||||
Section(header: Text("Sort settings")) {
|
||||
Picker("Sort by", selection: $sortBySelection) {
|
||||
Text("Name").tag("SortName")
|
||||
Text("Date Added").tag("DateCreated")
|
||||
Text("Date Played").tag("DatePlayed")
|
||||
Text("Date Released").tag("PremiereDate")
|
||||
Text("Runtime").tag("Runtime")
|
||||
}.onChange(of: sortBySelection) { value in
|
||||
guard let sort = SortType(rawValue: value) else { return }
|
||||
filter.sort = sort
|
||||
}
|
||||
Picker("Sort order", selection: $sortOrder) {
|
||||
Text("Ascending").tag("Ascending")
|
||||
Text("Descending").tag("Descending")
|
||||
}.onChange(of: sortOrder) { order in
|
||||
guard let asc = ASC(rawValue: order) else { return }
|
||||
filter.asc = asc
|
||||
}
|
||||
if viewModel.enabledFilterType.contains(.genre) {
|
||||
MultiSelector(label: "Genres",
|
||||
options: viewModel.allGenres,
|
||||
optionToString: { $0.name ?? "" },
|
||||
selected: $viewModel.modifyedFilters.withGenres)
|
||||
}
|
||||
if viewModel.enabledFilterType.contains(.filter) {
|
||||
MultiSelector(label: "Filters",
|
||||
options: viewModel.allItemFilters,
|
||||
optionToString: { $0.localized },
|
||||
selected: $viewModel.modifyedFilters.filters)
|
||||
}
|
||||
if viewModel.enabledFilterType.contains(.tag) {
|
||||
MultiSelector(label: "Tags",
|
||||
options: viewModel.allTags,
|
||||
optionToString: { $0 },
|
||||
selected: $viewModel.modifyedFilters.tags)
|
||||
}
|
||||
if viewModel.enabledFilterType.contains(.sortBy) {
|
||||
MultiSelector(label: "Sort by",
|
||||
options: viewModel.allSortBys,
|
||||
optionToString: { $0.localized },
|
||||
selected: $viewModel.modifyedFilters.sortBy)
|
||||
}
|
||||
if viewModel.enabledFilterType.contains(.sortOrder) {
|
||||
MultiSelector(label: "Sort Order",
|
||||
options: viewModel.allSortOrders,
|
||||
optionToString: { $0.localized },
|
||||
selected: $viewModel.modifyedFilters.sortOrder)
|
||||
}
|
||||
}
|
||||
}.onAppear(perform: onAppear)
|
||||
.navigationBarTitle("Filters", displayMode: .inline)
|
||||
.toolbar {
|
||||
ToolbarItemGroup(placement: .navigationBarLeading) {
|
||||
Button {
|
||||
presentationMode.wrappedValue.dismiss()
|
||||
} label: {
|
||||
HStack {
|
||||
Text("Back").font(.callout)
|
||||
}
|
||||
}
|
||||
if viewModel.isLoading {
|
||||
ProgressView()
|
||||
}
|
||||
}
|
||||
.navigationBarTitle("Filters", displayMode: .inline)
|
||||
.toolbar {
|
||||
ToolbarItemGroup(placement: .navigationBarLeading) {
|
||||
Button {
|
||||
presentationMode.wrappedValue.dismiss()
|
||||
} label: {
|
||||
Image(systemName: "xmark")
|
||||
}
|
||||
}
|
||||
ToolbarItemGroup(placement: .navigationBarTrailing) {
|
||||
Button {
|
||||
self.filters = viewModel.modifyedFilters
|
||||
presentationMode.wrappedValue.dismiss()
|
||||
} label: {
|
||||
Text("Apply")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ struct LibraryView: View {
|
|||
|
||||
@State
|
||||
var isShowingSearchView = false
|
||||
@State
|
||||
var isShowingFilterView = false
|
||||
|
||||
@State
|
||||
private var tracks: [GridItem] = Array(repeating: .init(.flexible()), count: Int(UIScreen.main.bounds.size.width) / 125)
|
||||
|
@ -109,6 +111,11 @@ struct LibraryView: View {
|
|||
Image(systemName: "chevron.right")
|
||||
}
|
||||
}
|
||||
Button(action: {
|
||||
isShowingFilterView = true
|
||||
}) {
|
||||
Image(systemName: "line.horizontal.3.decrease.circle")
|
||||
}
|
||||
Button(action: {
|
||||
isShowingSearchView = true
|
||||
}) {
|
||||
|
@ -116,6 +123,9 @@ struct LibraryView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $isShowingFilterView) {
|
||||
LibraryFilterView(filters: $viewModel.filters, enabledFilterType: viewModel.enabledFilterType)
|
||||
}
|
||||
.background(
|
||||
NavigationLink(destination: LibrarySearchView(viewModel: .init(parentID: viewModel.parentID)),
|
||||
isActive: $isShowingSearchView) {
|
||||
|
|
|
@ -7,44 +7,44 @@
|
|||
|
||||
import SwiftUI
|
||||
|
||||
private struct MultiSelectionView<Selectable: Identifiable & Hashable>: View {
|
||||
private struct MultiSelectionView<Selectable: Hashable>: View {
|
||||
let options: [Selectable]
|
||||
let optionToString: (Selectable) -> String
|
||||
let label: String
|
||||
|
||||
@Binding var selected: Set<Selectable>
|
||||
@Binding var selected: Array<Selectable>
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
ForEach(options) { selectable in
|
||||
ForEach(options, id: \.self) { selectable in
|
||||
Button(action: { toggleSelection(selectable: selectable) }) {
|
||||
HStack {
|
||||
Text(optionToString(selectable)).foregroundColor(Color.primary)
|
||||
Spacer()
|
||||
if selected.contains { $0.id == selectable.id } {
|
||||
if selected.contains { $0 == selectable } {
|
||||
Image(systemName: "checkmark").foregroundColor(.accentColor)
|
||||
}
|
||||
}
|
||||
}.tag(selectable.id)
|
||||
}.tag(selectable)
|
||||
}
|
||||
}.listStyle(GroupedListStyle())
|
||||
}
|
||||
|
||||
private func toggleSelection(selectable: Selectable) {
|
||||
if let existingIndex = selected.firstIndex(where: { $0.id == selectable.id }) {
|
||||
if let existingIndex = selected.firstIndex(where: { $0 == selectable }) {
|
||||
selected.remove(at: existingIndex)
|
||||
} else {
|
||||
selected.insert(selectable)
|
||||
selected.append(selectable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MultiSelector<Selectable: Identifiable & Hashable>: View {
|
||||
struct MultiSelector<Selectable: Hashable>: View {
|
||||
let label: String
|
||||
let options: [Selectable]
|
||||
let optionToString: (Selectable) -> String
|
||||
|
||||
var selected: Binding<Set<Selectable>>
|
||||
var selected: Binding<Array<Selectable>>
|
||||
|
||||
private var formattedSelectedListString: String {
|
||||
ListFormatter.localizedString(byJoining: selected.wrappedValue.map { optionToString($0) })
|
||||
|
|
|
@ -5,15 +5,16 @@
|
|||
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
import Foundation
|
||||
import JellyfinAPI
|
||||
|
||||
struct LibraryFilters: Codable, Hashable {
|
||||
var filters: [ItemFilter] = []
|
||||
var sortOrder: [APISortOrder] = [.descending]
|
||||
var withGenres: [NameGuidPair] = []
|
||||
var sortBy: [String] = ["SortName"]
|
||||
var tags: [String] = []
|
||||
var sortBy: [SortBy] = [.name]
|
||||
}
|
||||
|
||||
public enum SortBy: String, Codable, CaseIterable {
|
||||
|
@ -22,3 +23,54 @@ public enum SortBy: String, Codable, CaseIterable {
|
|||
case name = "SortName"
|
||||
case dateAdded = "DateCreated"
|
||||
}
|
||||
|
||||
extension SortBy {
|
||||
var localized: String {
|
||||
switch self {
|
||||
case .productionYear:
|
||||
return "Production year"
|
||||
case .premiereDate:
|
||||
return "Premiere date"
|
||||
case .name:
|
||||
return "Name"
|
||||
case .dateAdded:
|
||||
return "Date created"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ItemFilter {
|
||||
var localized: String {
|
||||
switch self {
|
||||
case .isFolder:
|
||||
return "Is folder"
|
||||
case .isNotFolder:
|
||||
return "Is not folder"
|
||||
case .isUnplayed:
|
||||
return "Is unplayed"
|
||||
case .isPlayed:
|
||||
return "Is played"
|
||||
case .isFavorite:
|
||||
return "Is favorite"
|
||||
case .isResumable:
|
||||
return "Is resumable"
|
||||
case .likes:
|
||||
return "Likes"
|
||||
case .dislikes:
|
||||
return "Dislikes"
|
||||
case .isFavoriteOrLikes:
|
||||
return "Is favorite or likes"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension APISortOrder {
|
||||
var localized: String {
|
||||
switch self {
|
||||
case .ascending:
|
||||
return "Ascending"
|
||||
case .descending:
|
||||
return "Descending"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ final class ConnectToServerViewModel: ViewModel {
|
|||
if ServerEnvironment.current.server != nil {
|
||||
UserAPI.getPublicUsers()
|
||||
.sink(receiveCompletion: { completion in
|
||||
self.HandleAPIRequestCompletion(completion: completion)
|
||||
self.handleAPIRequestCompletion(completion: completion)
|
||||
}, receiveValue: { response in
|
||||
self.publicUsers = response
|
||||
self.isConnectedServer = true
|
||||
|
@ -74,7 +74,7 @@ final class ConnectToServerViewModel: ViewModel {
|
|||
func login() {
|
||||
SessionManager.current.login(username: username, password: password)
|
||||
.sink(receiveCompletion: { completion in
|
||||
self.HandleAPIRequestCompletion(completion: completion)
|
||||
self.handleAPIRequestCompletion(completion: completion)
|
||||
}, receiveValue: { _ in
|
||||
|
||||
})
|
||||
|
|
|
@ -33,7 +33,7 @@ final class EpisodeItemViewModel: ViewModel {
|
|||
PlaystateAPI.markUnplayedItem(userId: SessionManager.current.user.user_id!, itemId: id)
|
||||
.trackActivity(loading)
|
||||
.sink(receiveCompletion: { [weak self] completion in
|
||||
self?.HandleAPIRequestCompletion(completion: completion)
|
||||
self?.handleAPIRequestCompletion(completion: completion)
|
||||
}, receiveValue: { [weak self] _ in
|
||||
self?.isWatched = false
|
||||
})
|
||||
|
@ -42,7 +42,7 @@ final class EpisodeItemViewModel: ViewModel {
|
|||
PlaystateAPI.markPlayedItem(userId: SessionManager.current.user.user_id!, itemId: id)
|
||||
.trackActivity(loading)
|
||||
.sink(receiveCompletion: { [weak self] completion in
|
||||
self?.HandleAPIRequestCompletion(completion: completion)
|
||||
self?.handleAPIRequestCompletion(completion: completion)
|
||||
}, receiveValue: { [weak self] _ in
|
||||
self?.isWatched = true
|
||||
})
|
||||
|
@ -56,7 +56,7 @@ final class EpisodeItemViewModel: ViewModel {
|
|||
UserLibraryAPI.unmarkFavoriteItem(userId: SessionManager.current.user.user_id!, itemId: id)
|
||||
.trackActivity(loading)
|
||||
.sink(receiveCompletion: { [weak self] completion in
|
||||
self?.HandleAPIRequestCompletion(completion: completion)
|
||||
self?.handleAPIRequestCompletion(completion: completion)
|
||||
}, receiveValue: { [weak self] _ in
|
||||
self?.isFavorited = false
|
||||
})
|
||||
|
@ -65,7 +65,7 @@ final class EpisodeItemViewModel: ViewModel {
|
|||
UserLibraryAPI.markFavoriteItem(userId: SessionManager.current.user.user_id!, itemId: id)
|
||||
.trackActivity(loading)
|
||||
.sink(receiveCompletion: { [weak self] completion in
|
||||
self?.HandleAPIRequestCompletion(completion: completion)
|
||||
self?.handleAPIRequestCompletion(completion: completion)
|
||||
}, receiveValue: { [weak self] _ in
|
||||
self?.isFavorited = true
|
||||
})
|
||||
|
|
|
@ -24,7 +24,7 @@ final class HomeViewModel: ViewModel {
|
|||
var nextUpItems = [BaseItemDto]()
|
||||
|
||||
// temp
|
||||
var recentFilterSet: LibraryFilters = LibraryFilters(filters: [], sortOrder: [.descending], sortBy: ["DateCreated"])
|
||||
var recentFilterSet: LibraryFilters = LibraryFilters(filters: [], sortOrder: [.descending], sortBy: [.dateAdded])
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
|
@ -36,7 +36,7 @@ final class HomeViewModel: ViewModel {
|
|||
UserViewsAPI.getUserViews(userId: SessionManager.current.user.user_id!)
|
||||
.trackActivity(loading)
|
||||
.sink(receiveCompletion: { completion in
|
||||
self.HandleAPIRequestCompletion(completion: completion)
|
||||
self.handleAPIRequestCompletion(completion: completion)
|
||||
}, receiveValue: { response in
|
||||
response.items!.forEach { item in
|
||||
if item.collectionType == "movies" || item.collectionType == "tvshows" {
|
||||
|
@ -47,7 +47,7 @@ final class HomeViewModel: ViewModel {
|
|||
UserAPI.getCurrentUser()
|
||||
.trackActivity(self.loading)
|
||||
.sink(receiveCompletion: { completion in
|
||||
self.HandleAPIRequestCompletion(completion: completion)
|
||||
self.handleAPIRequestCompletion(completion: completion)
|
||||
}, receiveValue: { response in
|
||||
self.libraries.forEach { library in
|
||||
if !(response.configuration?.latestItemsExcludes?.contains(library.id!))! {
|
||||
|
@ -64,7 +64,7 @@ final class HomeViewModel: ViewModel {
|
|||
mediaTypes: ["Video"], imageTypeLimit: 1, enableImageTypes: [.primary, .backdrop, .thumb])
|
||||
.trackActivity(loading)
|
||||
.sink(receiveCompletion: { completion in
|
||||
self.HandleAPIRequestCompletion(completion: completion)
|
||||
self.handleAPIRequestCompletion(completion: completion)
|
||||
}, receiveValue: { response in
|
||||
self.resumeItems = response.items ?? []
|
||||
})
|
||||
|
@ -74,7 +74,7 @@ final class HomeViewModel: ViewModel {
|
|||
fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people])
|
||||
.trackActivity(loading)
|
||||
.sink(receiveCompletion: { completion in
|
||||
self.HandleAPIRequestCompletion(completion: completion)
|
||||
self.handleAPIRequestCompletion(completion: completion)
|
||||
}, receiveValue: { response in
|
||||
self.nextUpItems = response.items ?? []
|
||||
})
|
||||
|
|
|
@ -37,7 +37,7 @@ final class LatestMediaViewModel: ViewModel {
|
|||
enableUserData: true, limit: 12)
|
||||
.trackActivity(loading)
|
||||
.sink(receiveCompletion: { [weak self] completion in
|
||||
self?.HandleAPIRequestCompletion(completion: completion)
|
||||
self?.handleAPIRequestCompletion(completion: completion)
|
||||
}, receiveValue: { [weak self] response in
|
||||
self?.items = response
|
||||
})
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
//
|
||||
/*
|
||||
* 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 2021 Aiden Vigue & Jellyfin Contributors
|
||||
*/
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
import JellyfinAPI
|
||||
|
||||
enum FilterType {
|
||||
case tag
|
||||
case genre
|
||||
case sortOrder
|
||||
case sortBy
|
||||
case filter
|
||||
}
|
||||
|
||||
final class LibraryFilterViewModel: ViewModel {
|
||||
@Published
|
||||
var modifyedFilters = LibraryFilters()
|
||||
|
||||
@Published
|
||||
var allGenres = [NameGuidPair]()
|
||||
@Published
|
||||
var allTags = [String]()
|
||||
@Published
|
||||
var allSortOrders = APISortOrder.allCases
|
||||
@Published
|
||||
var allSortBys = SortBy.allCases
|
||||
@Published
|
||||
var allItemFilters = ItemFilter.allCases
|
||||
@Published
|
||||
var enabledFilterType: [FilterType]
|
||||
|
||||
init(filters: LibraryFilters? = nil,
|
||||
enabledFilterType: [FilterType] = [.tag, .genre, .sortBy, .sortOrder, .filter]) {
|
||||
self.enabledFilterType = enabledFilterType
|
||||
super.init()
|
||||
if let filters = filters {
|
||||
self.modifyedFilters = filters
|
||||
}
|
||||
refresh()
|
||||
}
|
||||
|
||||
func refresh() {
|
||||
FilterAPI.getQueryFilters(userId: SessionManager.current.user.user_id!)
|
||||
.trackActivity(loading)
|
||||
.sink(receiveCompletion: { [weak self] completion in
|
||||
self?.handleAPIRequestCompletion(completion: completion)
|
||||
}, receiveValue: { [weak self] quertFilters in
|
||||
guard let self = self else { return }
|
||||
self.allGenres = quertFilters.genres ?? []
|
||||
self.allTags = quertFilters.tags ?? []
|
||||
})
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
}
|
|
@ -29,7 +29,7 @@ final class LibraryListViewModel: ViewModel {
|
|||
UserViewsAPI.getUserViews(userId: SessionManager.current.user.user_id!)
|
||||
.trackActivity(loading)
|
||||
.sink(receiveCompletion: { completion in
|
||||
self.HandleAPIRequestCompletion(completion: completion)
|
||||
self.handleAPIRequestCompletion(completion: completion)
|
||||
}, receiveValue: { response in
|
||||
self.libraries.append(contentsOf: response.items ?? [])
|
||||
})
|
||||
|
|
|
@ -36,7 +36,7 @@ final class LibrarySearchViewModel: ViewModel {
|
|||
includeItemTypes: ["Movie", "Series"], sortBy: ["SortName"], enableUserData: true, enableImages: true)
|
||||
.trackActivity(loading)
|
||||
.sink(receiveCompletion: { [weak self] completion in
|
||||
self?.HandleAPIRequestCompletion(completion: completion)
|
||||
self?.handleAPIRequestCompletion(completion: completion)
|
||||
}, receiveValue: { [weak self] response in
|
||||
self?.items = response.items ?? []
|
||||
})
|
||||
|
|
|
@ -30,13 +30,22 @@ final class LibraryViewModel: ViewModel {
|
|||
var isCanPreviousPaging = false
|
||||
|
||||
// temp
|
||||
@Published
|
||||
var filters: LibraryFilters
|
||||
|
||||
var enabledFilterType: [FilterType] {
|
||||
if genre == nil {
|
||||
return [.tag, .genre, .sortBy, .sortOrder, .filter]
|
||||
} else {
|
||||
return [.tag, .sortBy, .sortOrder, .filter]
|
||||
}
|
||||
}
|
||||
|
||||
init(parentID: String? = nil,
|
||||
person: BaseItemPerson? = nil,
|
||||
genre: NameGuidPair? = nil,
|
||||
studio: NameGuidPair? = nil,
|
||||
filters: LibraryFilters = LibraryFilters(filters: [], sortOrder: [.ascending], withGenres: [], sortBy: ["SortName"]))
|
||||
filters: LibraryFilters = LibraryFilters(filters: [], sortOrder: [.ascending], withGenres: [], sortBy: [.name]))
|
||||
{
|
||||
self.parentID = parentID
|
||||
self.person = person
|
||||
|
@ -45,18 +54,30 @@ final class LibraryViewModel: ViewModel {
|
|||
self.filters = filters
|
||||
super.init()
|
||||
|
||||
refresh()
|
||||
$filters
|
||||
.sink(receiveValue: refresh(with:))
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func refresh() {
|
||||
|
||||
func refresh(with filters: LibraryFilters) {
|
||||
let personIDs: [String] = [person].compactMap(\.?.id)
|
||||
let studioIDs: [String] = [studio].compactMap(\.?.id)
|
||||
let genreIDs: [String] = [genre].compactMap(\.?.id)
|
||||
|
||||
ItemsAPI.getItemsByUserId(userId: SessionManager.current.user.user_id!, startIndex: currentPage * 100, limit: 100, recursive: true, searchTerm: nil, sortOrder: filters.sortOrder, parentId: parentID, fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people], includeItemTypes: ["Movie", "Series"], filters: filters.filters, sortBy: filters.sortBy, enableUserData: true, personIds: personIDs, studioIds: studioIDs, genreIds: genreIDs, enableImages: true)
|
||||
let genreIDs: [String]
|
||||
if filters.withGenres.isEmpty {
|
||||
genreIDs = [genre].compactMap(\.?.id)
|
||||
} else {
|
||||
genreIDs = filters.withGenres.compactMap(\.id)
|
||||
}
|
||||
let sortBy = filters.sortBy.map(\.rawValue)
|
||||
|
||||
ItemsAPI.getItemsByUserId(userId: SessionManager.current.user.user_id!, startIndex: currentPage * 100, limit: 100, recursive: true,
|
||||
searchTerm: nil, sortOrder: filters.sortOrder, parentId: parentID,
|
||||
fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people],
|
||||
includeItemTypes: ["Movie", "Series"], filters: filters.filters, sortBy: sortBy, tags: filters.tags,
|
||||
enableUserData: true, personIds: personIDs, studioIds: studioIDs, genreIds: genreIDs, enableImages: true)
|
||||
.trackActivity(loading)
|
||||
.sink(receiveCompletion: { [weak self] completion in
|
||||
self?.HandleAPIRequestCompletion(completion: completion)
|
||||
self?.handleAPIRequestCompletion(completion: completion)
|
||||
}, receiveValue: { [weak self] response in
|
||||
guard let self = self else { return }
|
||||
let totalPages = ceil(Double(response.totalRecordCount ?? 0) / 100.0)
|
||||
|
@ -67,14 +88,14 @@ final class LibraryViewModel: ViewModel {
|
|||
})
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
|
||||
func requestNextPage() {
|
||||
currentPage += 1
|
||||
refresh()
|
||||
refresh(with: filters)
|
||||
}
|
||||
|
||||
|
||||
func requestPreviousPage() {
|
||||
currentPage -= 1
|
||||
refresh()
|
||||
refresh(with: filters)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ final class MovieItemViewModel: ViewModel {
|
|||
PlaystateAPI.markUnplayedItem(userId: SessionManager.current.user.user_id!, itemId: id)
|
||||
.trackActivity(loading)
|
||||
.sink(receiveCompletion: { [weak self] completion in
|
||||
self?.HandleAPIRequestCompletion(completion: completion)
|
||||
self?.handleAPIRequestCompletion(completion: completion)
|
||||
}, receiveValue: { [weak self] _ in
|
||||
self?.isWatched = false
|
||||
})
|
||||
|
@ -42,7 +42,7 @@ final class MovieItemViewModel: ViewModel {
|
|||
PlaystateAPI.markPlayedItem(userId: SessionManager.current.user.user_id!, itemId: id)
|
||||
.trackActivity(loading)
|
||||
.sink(receiveCompletion: { [weak self] completion in
|
||||
self?.HandleAPIRequestCompletion(completion: completion)
|
||||
self?.handleAPIRequestCompletion(completion: completion)
|
||||
}, receiveValue: { [weak self] _ in
|
||||
self?.isWatched = true
|
||||
})
|
||||
|
@ -56,7 +56,7 @@ final class MovieItemViewModel: ViewModel {
|
|||
UserLibraryAPI.unmarkFavoriteItem(userId: SessionManager.current.user.user_id!, itemId: id)
|
||||
.trackActivity(loading)
|
||||
.sink(receiveCompletion: { [weak self] completion in
|
||||
self?.HandleAPIRequestCompletion(completion: completion)
|
||||
self?.handleAPIRequestCompletion(completion: completion)
|
||||
}, receiveValue: { [weak self] _ in
|
||||
self?.isFavorited = false
|
||||
})
|
||||
|
@ -65,7 +65,7 @@ final class MovieItemViewModel: ViewModel {
|
|||
UserLibraryAPI.markFavoriteItem(userId: SessionManager.current.user.user_id!, itemId: id)
|
||||
.trackActivity(loading)
|
||||
.sink(receiveCompletion: { [weak self] completion in
|
||||
self?.HandleAPIRequestCompletion(completion: completion)
|
||||
self?.handleAPIRequestCompletion(completion: completion)
|
||||
}, receiveValue: { [weak self] _ in
|
||||
self?.isFavorited = true
|
||||
})
|
||||
|
|
|
@ -31,7 +31,7 @@ final class SeasonItemViewModel: ViewModel {
|
|||
seasonId: item.id ?? "")
|
||||
.trackActivity(loading)
|
||||
.sink(receiveCompletion: { [weak self] completion in
|
||||
self?.HandleAPIRequestCompletion(completion: completion)
|
||||
self?.handleAPIRequestCompletion(completion: completion)
|
||||
}, receiveValue: { [weak self] response in
|
||||
self?.episodes = response.items ?? []
|
||||
})
|
||||
|
|
|
@ -29,7 +29,7 @@ final class SeriesItemViewModel: ViewModel {
|
|||
TvShowsAPI.getSeasons(seriesId: item.id ?? "", fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people])
|
||||
.trackActivity(loading)
|
||||
.sink(receiveCompletion: { [weak self] completion in
|
||||
self?.HandleAPIRequestCompletion(completion: completion)
|
||||
self?.handleAPIRequestCompletion(completion: completion)
|
||||
}, receiveValue: { [weak self] response in
|
||||
self?.seasons = response.items ?? []
|
||||
})
|
||||
|
|
|
@ -32,7 +32,7 @@ class ViewModel: ObservableObject {
|
|||
loading.loading.assign(to: \.isLoading, on: self).store(in: &cancellables)
|
||||
}
|
||||
|
||||
func HandleAPIRequestCompletion(completion: Subscribers.Completion<Error>) {
|
||||
func handleAPIRequestCompletion(completion: Subscribers.Completion<Error>) {
|
||||
switch completion {
|
||||
case .finished:
|
||||
break
|
||||
|
|
Loading…
Reference in New Issue