diff --git a/Shared/ViewModels/LatestInLibraryViewModel.swift b/Shared/ViewModels/LatestInLibraryViewModel.swift new file mode 100644 index 000000000..f41cb9707 --- /dev/null +++ b/Shared/ViewModels/LatestInLibraryViewModel.swift @@ -0,0 +1,72 @@ +// +// 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) 2023 Jellyfin & Jellyfin Contributors +// + +import Combine +import Foundation +import JellyfinAPI + +final class LatestInLibraryViewModel: PagingLibraryViewModel { + + let parent: LibraryParent + + init(parent: LibraryParent) { + self.parent = parent + + super.init() + + _requestNextPage() + } + + override func _requestNextPage() { + Task { + + await MainActor.run { + self.isLoading = true + } + + let parameters = Paths.GetLatestMediaParameters( + parentID: self.parent.id, + fields: ItemFields.minimumCases, + enableUserData: true, + limit: self.pageItemSize * 3 + ) + let request = Paths.getLatestMedia(userID: userSession.user.id, parameters: parameters) + let response = try await userSession.client.send(request) + + let items = response.value + if items.isEmpty { + hasNextPage = false + return + } + + await MainActor.run { + self.isLoading = false + self.items.append(contentsOf: items) + } + } + } + + override public func getRandomItemFromLibrary() async throws -> BaseItemDtoQueryResult { + BaseItemDtoQueryResult(items: items.elements) + } + + func markPlayed(item: BaseItemDto) { + Task { + + let request = Paths.markPlayedItem( + userID: userSession.user.id, + itemID: item.id! + ) + let _ = try await userSession.client.send(request) + + await MainActor.run { + refresh() + } + } + } +} diff --git a/Swiftfin tvOS/Views/HomeView/Components/LatestInLibraryView.swift b/Swiftfin tvOS/Views/HomeView/Components/LatestInLibraryView.swift index 534880bb1..076fa3f92 100644 --- a/Swiftfin tvOS/Views/HomeView/Components/LatestInLibraryView.swift +++ b/Swiftfin tvOS/Views/HomeView/Components/LatestInLibraryView.swift @@ -17,18 +17,24 @@ extension HomeView { private var router: HomeCoordinator.Router @StateObject - var viewModel: LibraryViewModel + var viewModel: LatestInLibraryViewModel var body: some View { PosterHStack( - title: L10n.latestWithString(viewModel.parent?.displayTitle ?? .emptyDash), + title: L10n.latestWithString(viewModel.parent.displayTitle), type: .portrait, items: viewModel.items.prefix(20).asArray ) .trailing { SeeAllPosterButton(type: .portrait) .onSelect { - router.route(to: \.library, viewModel.libraryCoordinatorParameters) + router.route( + to: \.basicLibrary, + .init( + title: L10n.latestWithString(viewModel.parent.displayTitle), + viewModel: viewModel + ) + ) } } .onSelect { item in diff --git a/Swiftfin tvOS/Views/HomeView/HomeContentView.swift b/Swiftfin tvOS/Views/HomeView/HomeContentView.swift index 87b8dc176..ee90252f7 100644 --- a/Swiftfin tvOS/Views/HomeView/HomeContentView.swift +++ b/Swiftfin tvOS/Views/HomeView/HomeContentView.swift @@ -47,7 +47,7 @@ extension HomeView { } ForEach(viewModel.libraries, id: \.self) { library in - LatestInLibraryView(viewModel: .init(parent: library, type: .library, filters: .recent)) + LatestInLibraryView(viewModel: .init(parent: library)) } } } diff --git a/Swiftfin.xcodeproj/project.pbxproj b/Swiftfin.xcodeproj/project.pbxproj index 445034cf7..c57088f3e 100644 --- a/Swiftfin.xcodeproj/project.pbxproj +++ b/Swiftfin.xcodeproj/project.pbxproj @@ -151,6 +151,8 @@ 62ECA01826FA685A00E8EBB7 /* DeepLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62ECA01726FA685A00E8EBB7 /* DeepLink.swift */; }; 6334175B287DDFB9000603CE /* QuickConnectSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6334175A287DDFB9000603CE /* QuickConnectSettingsView.swift */; }; 6334175D287DE0D0000603CE /* QuickConnectSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6334175C287DE0D0000603CE /* QuickConnectSettingsViewModel.swift */; }; + 8BF1BD842AE93DFB00C3B271 /* LatestInLibraryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BF1BD832AE93DFB00C3B271 /* LatestInLibraryViewModel.swift */; }; + 8BF1BD852AE93DFB00C3B271 /* LatestInLibraryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BF1BD832AE93DFB00C3B271 /* LatestInLibraryViewModel.swift */; }; AE8C3159265D6F90008AA076 /* bitrates.json in Resources */ = {isa = PBXBuildFile; fileRef = AE8C3158265D6F90008AA076 /* bitrates.json */; }; BD0BA22B2AD6503B00306A8D /* OnlineVideoPlayerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD0BA22A2AD6503B00306A8D /* OnlineVideoPlayerManager.swift */; }; BD0BA22C2AD6503B00306A8D /* OnlineVideoPlayerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD0BA22A2AD6503B00306A8D /* OnlineVideoPlayerManager.swift */; }; @@ -908,6 +910,7 @@ 6334175A287DDFB9000603CE /* QuickConnectSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickConnectSettingsView.swift; sourceTree = ""; }; 6334175C287DE0D0000603CE /* QuickConnectSettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickConnectSettingsViewModel.swift; sourceTree = ""; }; 637FCAF3287B5B2600C0A353 /* UDPBroadcast.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = UDPBroadcast.xcframework; path = Carthage/Build/UDPBroadcast.xcframework; sourceTree = ""; }; + 8BF1BD832AE93DFB00C3B271 /* LatestInLibraryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LatestInLibraryViewModel.swift; sourceTree = ""; }; AE8C3158265D6F90008AA076 /* bitrates.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = bitrates.json; sourceTree = ""; }; BD0BA22A2AD6503B00306A8D /* OnlineVideoPlayerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnlineVideoPlayerManager.swift; sourceTree = ""; }; BD0BA22D2AD6508C00306A8D /* DownloadVideoPlayerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadVideoPlayerManager.swift; sourceTree = ""; }; @@ -1450,6 +1453,7 @@ E13DD3EB27178A54009D4DAF /* UserSignInViewModel.swift */, E14A08CA28E6831D004FC984 /* VideoPlayerViewModel.swift */, 625CB57B2678CE1000530A6E /* ViewModel.swift */, + 8BF1BD832AE93DFB00C3B271 /* LatestInLibraryViewModel.swift */, ); path = ViewModels; sourceTree = ""; @@ -3290,6 +3294,7 @@ E1575E90293E7B1E001665B1 /* EdgeInsets.swift in Sources */, E1E6C43D29AECC310064123F /* BarActionButtons.swift in Sources */, E1E6C44529AECCF20064123F /* PlayNextItemActionButton.swift in Sources */, + 8BF1BD852AE93DFB00C3B271 /* LatestInLibraryViewModel.swift in Sources */, 6264E88D273850380081A12A /* Strings.swift in Sources */, E1C926102887565C002A7A66 /* PlayButton.swift in Sources */, E1575E67293E77B5001665B1 /* OverlayType.swift in Sources */, @@ -3619,6 +3624,7 @@ E1BDF2F329524C3B00CC0294 /* ChaptersActionButton.swift in Sources */, E173DA5026D048D600CC4EB7 /* ServerDetailView.swift in Sources */, E1CFE28028FA606800B7D34C /* ChapterTrack.swift in Sources */, + 8BF1BD842AE93DFB00C3B271 /* LatestInLibraryViewModel.swift in Sources */, E1401CA22938122C00E8B599 /* AppIcons.swift in Sources */, E1BDF2FB2952502300CC0294 /* SubtitleActionButton.swift in Sources */, E17FB55728C1256400311DFE /* CastAndCrewHStack.swift in Sources */, diff --git a/Swiftfin/Views/HomeView/Components/LatestInLibraryView.swift b/Swiftfin/Views/HomeView/Components/LatestInLibraryView.swift index c963ccedd..369258fd4 100644 --- a/Swiftfin/Views/HomeView/Components/LatestInLibraryView.swift +++ b/Swiftfin/Views/HomeView/Components/LatestInLibraryView.swift @@ -21,7 +21,7 @@ extension HomeView { private var router: HomeCoordinator.Router @ObservedObject - var viewModel: LibraryViewModel + var viewModel: LatestInLibraryViewModel private var items: [BaseItemDto] { viewModel.items.prefix(20).asArray @@ -29,14 +29,20 @@ extension HomeView { var body: some View { PosterHStack( - title: L10n.latestWithString(viewModel.parent?.displayTitle ?? .emptyDash), + title: L10n.latestWithString(viewModel.parent.displayTitle), type: latestInLibraryPosterType, items: items ) .trailing { SeeAllButton() .onSelect { - router.route(to: \.library, viewModel.libraryCoordinatorParameters) + router.route( + to: \.basicLibrary, + .init( + title: L10n.latestWithString(viewModel.parent.displayTitle), + viewModel: viewModel + ) + ) } } .onSelect { item in diff --git a/Swiftfin/Views/HomeView/HomeContentView.swift b/Swiftfin/Views/HomeView/HomeContentView.swift index 1b52c8e26..c9d967908 100644 --- a/Swiftfin/Views/HomeView/HomeContentView.swift +++ b/Swiftfin/Views/HomeView/HomeContentView.swift @@ -44,7 +44,7 @@ extension HomeView { } ForEach(viewModel.libraries, id: \.self) { library in - LatestInLibraryView(viewModel: .init(parent: library, type: .library, filters: .recent)) + LatestInLibraryView(viewModel: .init(parent: library)) } } .padding(.bottom, 50)