add LibrarySearchViewModel
This commit is contained in:
parent
88fcbf5aec
commit
88ed1c4a3e
|
@ -118,6 +118,9 @@
|
||||||
628B953A2670CE250091AF3B /* KeychainSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 628B95392670CE250091AF3B /* KeychainSwift */; };
|
628B953A2670CE250091AF3B /* KeychainSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 628B95392670CE250091AF3B /* KeychainSwift */; };
|
||||||
628B953C2670D2430091AF3B /* StringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 621338922660107500A81A2A /* StringExtensions.swift */; };
|
628B953C2670D2430091AF3B /* StringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 621338922660107500A81A2A /* StringExtensions.swift */; };
|
||||||
62E632DA267D2BC40063E547 /* LatestMediaViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E632D9267D2BC40063E547 /* LatestMediaViewModel.swift */; };
|
62E632DA267D2BC40063E547 /* LatestMediaViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E632D9267D2BC40063E547 /* LatestMediaViewModel.swift */; };
|
||||||
|
62E632DC267D2E130063E547 /* LibrarySearchviewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E632DB267D2E130063E547 /* LibrarySearchviewModel.swift */; };
|
||||||
|
62E632DD267D2E130063E547 /* LibrarySearchviewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E632DB267D2E130063E547 /* LibrarySearchviewModel.swift */; };
|
||||||
|
62E632DE267D2E170063E547 /* LatestMediaViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E632D9267D2BC40063E547 /* LatestMediaViewModel.swift */; };
|
||||||
62EC3527267665D8000E9F2D /* MobileVLCKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53D5E3DC264B47EE00BADDC8 /* MobileVLCKit.xcframework */; };
|
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, ); }; };
|
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 */; };
|
62EC352C26766675000E9F2D /* ServerEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC352B26766675000E9F2D /* ServerEnvironment.swift */; };
|
||||||
|
@ -267,6 +270,7 @@
|
||||||
628B95362670CB800091AF3B /* JellyfinWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JellyfinWidget.swift; sourceTree = "<group>"; };
|
628B95362670CB800091AF3B /* JellyfinWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JellyfinWidget.swift; sourceTree = "<group>"; };
|
||||||
628B953B2670D1FC0091AF3B /* WidgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WidgetExtension.entitlements; sourceTree = "<group>"; };
|
628B953B2670D1FC0091AF3B /* WidgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WidgetExtension.entitlements; sourceTree = "<group>"; };
|
||||||
62E632D9267D2BC40063E547 /* LatestMediaViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LatestMediaViewModel.swift; sourceTree = "<group>"; };
|
62E632D9267D2BC40063E547 /* LatestMediaViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LatestMediaViewModel.swift; sourceTree = "<group>"; };
|
||||||
|
62E632DB267D2E130063E547 /* LibrarySearchviewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibrarySearchviewModel.swift; sourceTree = "<group>"; };
|
||||||
62EC352B26766675000E9F2D /* ServerEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerEnvironment.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>"; };
|
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>"; };
|
62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceRotationViewModifier.swift; sourceTree = "<group>"; };
|
||||||
|
@ -329,6 +333,7 @@
|
||||||
625CB57B2678CE1000530A6E /* ViewModel.swift */,
|
625CB57B2678CE1000530A6E /* ViewModel.swift */,
|
||||||
536D3D75267BA9BB0004248C /* MainTabViewModel.swift */,
|
536D3D75267BA9BB0004248C /* MainTabViewModel.swift */,
|
||||||
62E632D9267D2BC40063E547 /* LatestMediaViewModel.swift */,
|
62E632D9267D2BC40063E547 /* LatestMediaViewModel.swift */,
|
||||||
|
62E632DB267D2E130063E547 /* LibrarySearchviewModel.swift */,
|
||||||
);
|
);
|
||||||
path = ViewModels;
|
path = ViewModels;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -695,6 +700,7 @@
|
||||||
531690E7267ABD79005D8AB9 /* HomeView.swift in Sources */,
|
531690E7267ABD79005D8AB9 /* HomeView.swift in Sources */,
|
||||||
53ABFDDE267974E300886593 /* SplashView.swift in Sources */,
|
53ABFDDE267974E300886593 /* SplashView.swift in Sources */,
|
||||||
53ABFDE8267974EF00886593 /* SplashViewModel.swift in Sources */,
|
53ABFDE8267974EF00886593 /* SplashViewModel.swift in Sources */,
|
||||||
|
62E632DE267D2E170063E547 /* LatestMediaViewModel.swift in Sources */,
|
||||||
536D3D88267C17350004248C /* PublicUserButton.swift in Sources */,
|
536D3D88267C17350004248C /* PublicUserButton.swift in Sources */,
|
||||||
536D3D7F267BDF100004248C /* LatestMediaView.swift in Sources */,
|
536D3D7F267BDF100004248C /* LatestMediaView.swift in Sources */,
|
||||||
531690ED267ABF46005D8AB9 /* ContinueWatchingView.swift in Sources */,
|
531690ED267ABF46005D8AB9 /* ContinueWatchingView.swift in Sources */,
|
||||||
|
@ -706,6 +712,7 @@
|
||||||
535870A52669D8AE00D05A09 /* ParallaxHeader.swift in Sources */,
|
535870A52669D8AE00D05A09 /* ParallaxHeader.swift in Sources */,
|
||||||
531690F0267ABF72005D8AB9 /* NextUpView.swift in Sources */,
|
531690F0267ABF72005D8AB9 /* NextUpView.swift in Sources */,
|
||||||
535870A72669D8AE00D05A09 /* MultiSelectorView.swift in Sources */,
|
535870A72669D8AE00D05A09 /* MultiSelectorView.swift in Sources */,
|
||||||
|
62E632DD267D2E130063E547 /* LibrarySearchviewModel.swift in Sources */,
|
||||||
536D3D81267BDFC60004248C /* PortraitItemElement.swift in Sources */,
|
536D3D81267BDFC60004248C /* PortraitItemElement.swift in Sources */,
|
||||||
531690E5267ABD5C005D8AB9 /* MainTabView.swift in Sources */,
|
531690E5267ABD5C005D8AB9 /* MainTabView.swift in Sources */,
|
||||||
53ABFDE7267974EF00886593 /* ConnectToServerViewModel.swift in Sources */,
|
53ABFDE7267974EF00886593 /* ConnectToServerViewModel.swift in Sources */,
|
||||||
|
@ -734,6 +741,7 @@
|
||||||
621338932660107500A81A2A /* StringExtensions.swift in Sources */,
|
621338932660107500A81A2A /* StringExtensions.swift in Sources */,
|
||||||
53FF7F2A263CF3F500585C35 /* LatestMediaView.swift in Sources */,
|
53FF7F2A263CF3F500585C35 /* LatestMediaView.swift in Sources */,
|
||||||
625CB5732678C32A00530A6E /* HomeViewModel.swift in Sources */,
|
625CB5732678C32A00530A6E /* HomeViewModel.swift in Sources */,
|
||||||
|
62E632DC267D2E130063E547 /* LibrarySearchviewModel.swift in Sources */,
|
||||||
5377CBFE263B596B003A4E83 /* PersistenceController.swift in Sources */,
|
5377CBFE263B596B003A4E83 /* PersistenceController.swift in Sources */,
|
||||||
5389276E263C25100035E14B /* ContinueWatchingView.swift in Sources */,
|
5389276E263C25100035E14B /* ContinueWatchingView.swift in Sources */,
|
||||||
53AD124E26702B8A0094A276 /* SeasonItemView.swift in Sources */,
|
53AD124E26702B8A0094A276 /* SeasonItemView.swift in Sources */,
|
||||||
|
|
|
@ -39,7 +39,7 @@ struct LibraryListView: View {
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItemGroup(placement: .navigationBarTrailing) {
|
ToolbarItemGroup(placement: .navigationBarTrailing) {
|
||||||
NavigationLink(destination: LazyView {
|
NavigationLink(destination: LazyView {
|
||||||
LibrarySearchView(usingParentID: "")
|
LibrarySearchView(viewModel: .init(parentID: nil))
|
||||||
}) {
|
}) {
|
||||||
Image(systemName: "magnifyingglass")
|
Image(systemName: "magnifyingglass")
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,67 +5,33 @@
|
||||||
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
import JellyfinAPI
|
|
||||||
import Combine
|
import Combine
|
||||||
|
import JellyfinAPI
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
struct LibrarySearchView: View {
|
struct LibrarySearchView: View {
|
||||||
|
|
||||||
@StateObject
|
@StateObject
|
||||||
var tempViewModel = ViewModel()
|
var viewModel: LibrarySearchViewModel
|
||||||
@State private var items: [BaseItemDto] = []
|
|
||||||
@State private var searchQuery: String = ""
|
|
||||||
@State private var isLoading: Bool = false
|
|
||||||
private var usingParentID: String = ""
|
|
||||||
@State private var lastSearchTime: Double = CACurrentMediaTime()
|
|
||||||
|
|
||||||
init(usingParentID: String) {
|
|
||||||
self.usingParentID = usingParentID
|
|
||||||
}
|
|
||||||
|
|
||||||
func onAppear() {
|
|
||||||
recalcTracks()
|
|
||||||
requestSearch(query: "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func requestSearch(query: String) {
|
|
||||||
isLoading = true
|
|
||||||
DispatchQueue.global(qos: .userInitiated).async {
|
|
||||||
ItemsAPI.getItemsByUserId(userId: SessionManager.current.user.user_id!, limit: 60, recursive: true, searchTerm: query, sortOrder: [.ascending], parentId: (usingParentID != "" ? usingParentID : nil), fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people], includeItemTypes: ["Movie", "Series"], sortBy: ["SortName"], enableUserData: true, enableImages: true)
|
|
||||||
.sink(receiveCompletion: { completion in
|
|
||||||
print(completion)
|
|
||||||
}, receiveValue: { response in
|
|
||||||
items = response.items ?? []
|
|
||||||
isLoading = false
|
|
||||||
})
|
|
||||||
.store(in: &tempViewModel.cancellables)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: tracks for grid
|
// MARK: tracks for grid
|
||||||
@State private var tracks: [GridItem] = []
|
|
||||||
|
@State
|
||||||
|
private var tracks: [GridItem] = Array(repeating: .init(.flexible()), count: Int(UIScreen.main.bounds.size.width) / 125)
|
||||||
|
|
||||||
func recalcTracks() {
|
func recalcTracks() {
|
||||||
let trkCnt = Int(floor(UIScreen.main.bounds.size.width / 125))
|
tracks = Array(repeating: .init(.flexible()), count: Int(UIScreen.main.bounds.size.width) / 125)
|
||||||
tracks = []
|
|
||||||
for _ in 0 ..< trkCnt {
|
|
||||||
tracks.append(GridItem(.flexible()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
VStack {
|
||||||
Spacer().frame(height: 6)
|
Spacer().frame(height: 6)
|
||||||
SearchBar(text: $searchQuery)
|
SearchBar(text: $viewModel.searchQuery)
|
||||||
if isLoading == true {
|
ZStack {
|
||||||
Spacer()
|
ScrollView(.vertical) {
|
||||||
ProgressView()
|
if !viewModel.items.isEmpty {
|
||||||
Spacer()
|
|
||||||
} else {
|
|
||||||
if !items.isEmpty {
|
|
||||||
ScrollView(.vertical) {
|
|
||||||
Spacer().frame(height: 16)
|
Spacer().frame(height: 16)
|
||||||
LazyVGrid(columns: tracks) {
|
LazyVGrid(columns: tracks) {
|
||||||
ForEach(items, id: \.id) { item in
|
ForEach(viewModel.items, id: \.id) { item in
|
||||||
NavigationLink(destination: ItemView(item: item)) {
|
NavigationLink(destination: ItemView(item: item)) {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
ImageView(src: item.getPrimaryImage(maxWidth: 100), bh: item.getPrimaryImageBlurHash())
|
ImageView(src: item.getPrimaryImage(maxWidth: 100), bh: item.getPrimaryImageBlurHash())
|
||||||
|
@ -87,26 +53,20 @@ struct LibrarySearchView: View {
|
||||||
}.frame(width: 100)
|
}.frame(width: 100)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Spacer().frame(height: 16)
|
||||||
}
|
}
|
||||||
Spacer().frame(height: 16)
|
} else {
|
||||||
.onRotate { _ in
|
Text("No results :(")
|
||||||
recalcTracks()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
Text("No results :(")
|
if viewModel.isLoading {
|
||||||
|
ProgressView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onAppear(perform: onAppear)
|
|
||||||
.navigationBarTitle("Search", displayMode: .inline)
|
.navigationBarTitle("Search", displayMode: .inline)
|
||||||
.onChange(of: searchQuery) { query in
|
.onRotate { _ in
|
||||||
if CACurrentMediaTime() - lastSearchTime > 0.5 {
|
recalcTracks()
|
||||||
lastSearchTime = CACurrentMediaTime()
|
|
||||||
requestSearch(query: query)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// stream NM5 by nicki!
|
|
||||||
|
|
|
@ -184,7 +184,7 @@ struct LibraryView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if usingParentID != "" {
|
if usingParentID != "" {
|
||||||
NavigationLink(destination: LibrarySearchView(usingParentID: usingParentID)) {
|
NavigationLink(destination: LibrarySearchView(viewModel: .init(parentID: usingParentID))) {
|
||||||
Image(systemName: "magnifyingglass")
|
Image(systemName: "magnifyingglass")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
//
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
final class LibrarySearchViewModel: ViewModel {
|
||||||
|
@Published
|
||||||
|
var items = [BaseItemDto]()
|
||||||
|
|
||||||
|
@Published
|
||||||
|
var searchQuery = ""
|
||||||
|
var parentID: String?
|
||||||
|
|
||||||
|
init(parentID: String?) {
|
||||||
|
self.parentID = parentID
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
$searchQuery
|
||||||
|
.debounce(for: 0.25, scheduler: DispatchQueue.main)
|
||||||
|
.sink(receiveValue: search(with:))
|
||||||
|
.store(in: &cancellables)
|
||||||
|
}
|
||||||
|
|
||||||
|
func search(with query: String) {
|
||||||
|
ItemsAPI.getItemsByUserId(userId: SessionManager.current.user.user_id!, limit: 60, recursive: true, searchTerm: query,
|
||||||
|
sortOrder: [.ascending], parentId: parentID,
|
||||||
|
fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people],
|
||||||
|
includeItemTypes: ["Movie", "Series"], sortBy: ["SortName"], enableUserData: true, enableImages: true)
|
||||||
|
.trackActivity(loading)
|
||||||
|
.sink(receiveCompletion: { [weak self] completion in
|
||||||
|
self?.HandleAPIRequestCompletion(completion: completion)
|
||||||
|
}, receiveValue: { [weak self] response in
|
||||||
|
self?.items = response.items ?? []
|
||||||
|
})
|
||||||
|
.store(in: &cancellables)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue