Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
LePips committed Mar 7, 2024
1 parent 80cf376 commit 7dfe7af
Show file tree
Hide file tree
Showing 15 changed files with 166 additions and 203 deletions.
3 changes: 3 additions & 0 deletions Shared/Components/ImageView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import NukeUI
import SwiftUI
import UIKit

// TODO: move to separate file
struct ImageSource: Hashable {

let url: URL?
Expand All @@ -26,6 +27,8 @@ struct ImageSource: Hashable {

private let imagePipeline = ImagePipeline(configuration: .withDataCache)

// TODO: Binding inits?
// - instead of removing first source on failure, just safe index into sources
struct ImageView: View {

@State
Expand Down
1 change: 1 addition & 0 deletions Shared/Components/InitialFailureView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import SwiftUI

// TODO: remove and replace with icon of item type instead
struct InitialFailureView: View {

let initials: String
Expand Down
4 changes: 4 additions & 0 deletions Shared/Components/TruncatedText.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import Defaults
import SwiftUI

// TODO: allow `isTruncated` to be communicated externally via modifier
// TODO: `seeMoreBehavior` for iOS
// - `entireView`: entire view becomes a button
// - `seeMore`: can only select see more

struct TruncatedText: View {

Expand Down Expand Up @@ -109,6 +112,7 @@ extension TruncatedText {
)
}

// TODO: rename `onSeeMore`
func seeMoreAction(_ action: @escaping () -> Void) -> Self {
copy(modifying: \.seeMoreAction, with: action)
}
Expand Down
File renamed without changes.
2 changes: 2 additions & 0 deletions Shared/Extensions/ViewExtensions/ViewExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ extension View {
AnyView(self)
}

// TODO: rename `invertedMask`?
func inverseMask(alignment: Alignment = .center, _ content: @escaping () -> some View) -> some View {
mask(alignment: alignment) {
content()
Expand Down Expand Up @@ -217,6 +218,7 @@ extension View {
modifier(AttributeViewModifier(style: style))
}

// TODO: rename `blurredFullScreenCover`
func blurFullScreenCover(
isPresented: Binding<Bool>,
onDismiss: (() -> Void)? = nil,
Expand Down
3 changes: 1 addition & 2 deletions Shared/ViewModels/HomeViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,12 @@ final class HomeViewModel: ViewModel, Stateful {
let resumeItems = try await getResumeItems()
let libraries = try await getLibraries()

// should probably in a task group, but fast enough
for library in libraries {
await library.send(.refresh)
}

await MainActor.run {
self.resumeItems.append(contentsOf: resumeItems)
self.resumeItems.elements = resumeItems
self.libraries = libraries
}
}
Expand Down
21 changes: 14 additions & 7 deletions Shared/ViewModels/LibraryViewModel/PagingLibraryViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class PagingLibraryViewModel<Element: Poster>: LibraryViewModel<Element>, Eventf
enum State: Equatable {
case error(LibraryError)
case gettingNextPage
case items
case content
case refreshing
}

Expand Down Expand Up @@ -69,9 +69,10 @@ class PagingLibraryViewModel<Element: Poster>: LibraryViewModel<Element>, Eventf
@Published
final var state: State = .refreshing

private var currentPage = 0
private(set) final var currentPage = 0
private(set) final var hasNextPage = true

private var eventSubject: PassthroughSubject<Event, Never> = .init()
private var hasNextPage = true
private var isStatic: Bool

// tasks
Expand All @@ -96,7 +97,7 @@ class PagingLibraryViewModel<Element: Poster>: LibraryViewModel<Element>, Eventf
func respond(to action: Action) -> State {

if action == .refresh, isStatic {
return .items
return .content
}

switch action {
Expand Down Expand Up @@ -133,7 +134,7 @@ class PagingLibraryViewModel<Element: Poster>: LibraryViewModel<Element>, Eventf
guard !Task.isCancelled else { return }

await MainActor.run {
self?.state = .items
self?.state = .content
}
} catch {
guard !Task.isCancelled else { return }
Expand All @@ -148,7 +149,7 @@ class PagingLibraryViewModel<Element: Poster>: LibraryViewModel<Element>, Eventf
return .refreshing
case .getNextPage:

guard hasNextPage else { return .items }
guard hasNextPage else { return .content }

pagingTask = Task { [weak self] in
do {
Expand All @@ -157,7 +158,7 @@ class PagingLibraryViewModel<Element: Poster>: LibraryViewModel<Element>, Eventf
guard !Task.isCancelled else { return }

await MainActor.run {
self?.state = .items
self?.state = .content
}
} catch {
guard !Task.isCancelled else { return }
Expand Down Expand Up @@ -216,9 +217,15 @@ class PagingLibraryViewModel<Element: Poster>: LibraryViewModel<Element>, Eventf

hasNextPage = !(pageItems.count < DefaultPageSize)

// Sometimes, a subclass may return a page even if it's contextually
// "out of pages". Check explicitly if items were duplicated.
let preItemCount = items.count

await MainActor.run {
items.append(contentsOf: pageItems)
}

print("increased item size by: \(items.count - preItemCount)")
}

/// Gets the items at the given page. If the number of items
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Combine
import Foundation
import JellyfinAPI

// TODO: verify this properly returns pages of items in correct date-added order
final class RecentlyAddedLibraryViewModel: PagingLibraryViewModel<BaseItemDto> {

init() {
Expand Down Expand Up @@ -37,6 +38,11 @@ final class RecentlyAddedLibraryViewModel: PagingLibraryViewModel<BaseItemDto> {
parameters.sortOrder = [.descending]
parameters.startIndex = page

// Ncessary to get an actual "next page" with this endpoint.
// Could be a performance issue for lots of items, but there's
// nothing we can do about it.
parameters.excludeItemIDs = items.compactMap(\.id)

return parameters
}
}
119 changes: 0 additions & 119 deletions Shared/ViewModels/MediaItemViewModel.swift

This file was deleted.

63 changes: 55 additions & 8 deletions Shared/ViewModels/MediaViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,26 @@ import OrderedCollections
// TODO: excluded userviews
final class MediaViewModel: ViewModel, Stateful {

enum MediaType: Displayable, Hashable {
case downloads
case favorites
case liveTV
case userView(BaseItemDto)

var displayTitle: String {
switch self {
case .downloads:
return L10n.downloads
case .favorites:
return L10n.favorites
case .liveTV:
return L10n.liveTV
case let .userView(item):
return item.displayTitle
}
}
}

// TODO: remove once collection types become an enum
static let supportedCollectionTypes: [String] = ["boxsets", "folders", "movies", "tvshows", "livetv"]

Expand All @@ -38,7 +58,7 @@ final class MediaViewModel: ViewModel, Stateful {
}

@Published
var mediaItems: OrderedSet<MediaItemViewModel> = []
var mediaItems: OrderedSet<MediaType> = []

@Published
var state: State = .initial
Expand Down Expand Up @@ -75,14 +95,12 @@ final class MediaViewModel: ViewModel, Stateful {
mediaItems.removeAll()
}

let userViews = try await getUserViews()
.map { MediaItemViewModel(type: .userView($0)) }

let allMediaItems = userViews
.prepending(.init(type: .favorites), if: Defaults[.Customization.Library.showFavorites])
let media = try await getUserViews()
.map(MediaType.userView)
.prepending(.favorites, if: Defaults[.Customization.Library.showFavorites])

await MainActor.run {
mediaItems.append(contentsOf: allMediaItems)
mediaItems.elements = media
}
}

Expand Down Expand Up @@ -114,6 +132,35 @@ final class MediaViewModel: ViewModel, Stateful {
let currentUserPath = Paths.getCurrentUser
let response = try await userSession.client.send(currentUserPath)

return response.value.configuration?.latestItemsExcludes ?? []
return response.value.configuration?.myMediaExcludes ?? []
}

func randomItemImageSources(for mediaType: MediaType) async throws -> [ImageSource] {

var parentID: String?

if case let MediaType.userView(item) = mediaType {
parentID = item.id
}

var filters: [ItemTrait]?

if mediaType == .favorites {
filters = [.isFavorite]
}

var parameters = Paths.GetItemsByUserIDParameters()
parameters.limit = 3
parameters.isRecursive = true
parameters.parentID = parentID
parameters.includeItemTypes = [.movie, .series, .boxSet]
parameters.filters = filters
parameters.sortBy = [ItemSortBy.random.rawValue]

let request = Paths.getItemsByUserID(userID: userSession.user.id, parameters: parameters)
let response = try await userSession.client.send(request)

return (response.value.items ?? [])
.map { $0.imageSource(.backdrop, maxWidth: 500) }
}
}
Loading

0 comments on commit 7dfe7af

Please sign in to comment.