Shuffle play (#816)
This commit is contained in:
parent
a2f9da506c
commit
eb99dfe30b
|
@ -28,7 +28,7 @@ internal enum L10n {
|
||||||
internal static let allGenres = L10n.tr("Localizable", "allGenres", fallback: "All Genres")
|
internal static let allGenres = L10n.tr("Localizable", "allGenres", fallback: "All Genres")
|
||||||
/// All Media
|
/// All Media
|
||||||
internal static let allMedia = L10n.tr("Localizable", "allMedia", fallback: "All Media")
|
internal static let allMedia = L10n.tr("Localizable", "allMedia", fallback: "All Media")
|
||||||
/// Appearance
|
/// Represents the Appearance setting label
|
||||||
internal static let appearance = L10n.tr("Localizable", "appearance", fallback: "Appearance")
|
internal static let appearance = L10n.tr("Localizable", "appearance", fallback: "Appearance")
|
||||||
/// App Icon
|
/// App Icon
|
||||||
internal static let appIcon = L10n.tr("Localizable", "appIcon", fallback: "App Icon")
|
internal static let appIcon = L10n.tr("Localizable", "appIcon", fallback: "App Icon")
|
||||||
|
@ -106,7 +106,7 @@ internal enum L10n {
|
||||||
internal static let currentPosition = L10n.tr("Localizable", "currentPosition", fallback: "Current Position")
|
internal static let currentPosition = L10n.tr("Localizable", "currentPosition", fallback: "Current Position")
|
||||||
/// Customize
|
/// Customize
|
||||||
internal static let customize = L10n.tr("Localizable", "customize", fallback: "Customize")
|
internal static let customize = L10n.tr("Localizable", "customize", fallback: "Customize")
|
||||||
/// Dark
|
/// Represents the dark theme setting
|
||||||
internal static let dark = L10n.tr("Localizable", "dark", fallback: "Dark")
|
internal static let dark = L10n.tr("Localizable", "dark", fallback: "Dark")
|
||||||
/// Default Scheme
|
/// Default Scheme
|
||||||
internal static let defaultScheme = L10n.tr("Localizable", "defaultScheme", fallback: "Default Scheme")
|
internal static let defaultScheme = L10n.tr("Localizable", "defaultScheme", fallback: "Default Scheme")
|
||||||
|
@ -156,6 +156,8 @@ internal enum L10n {
|
||||||
internal static let genres = L10n.tr("Localizable", "genres", fallback: "Genres")
|
internal static let genres = L10n.tr("Localizable", "genres", fallback: "Genres")
|
||||||
/// Green
|
/// Green
|
||||||
internal static let green = L10n.tr("Localizable", "green", fallback: "Green")
|
internal static let green = L10n.tr("Localizable", "green", fallback: "Green")
|
||||||
|
/// Grid
|
||||||
|
internal static let grid = L10n.tr("Localizable", "grid", fallback: "Grid")
|
||||||
/// Haptic Feedback
|
/// Haptic Feedback
|
||||||
internal static let hapticFeedback = L10n.tr("Localizable", "hapticFeedback", fallback: "Haptic Feedback")
|
internal static let hapticFeedback = L10n.tr("Localizable", "hapticFeedback", fallback: "Haptic Feedback")
|
||||||
/// Home
|
/// Home
|
||||||
|
@ -194,8 +196,10 @@ internal enum L10n {
|
||||||
}
|
}
|
||||||
/// Library
|
/// Library
|
||||||
internal static let library = L10n.tr("Localizable", "library", fallback: "Library")
|
internal static let library = L10n.tr("Localizable", "library", fallback: "Library")
|
||||||
/// Light
|
/// Represents the light theme setting
|
||||||
internal static let light = L10n.tr("Localizable", "light", fallback: "Light")
|
internal static let light = L10n.tr("Localizable", "light", fallback: "Light")
|
||||||
|
/// List
|
||||||
|
internal static let list = L10n.tr("Localizable", "list", fallback: "List")
|
||||||
/// Live TV
|
/// Live TV
|
||||||
internal static let liveTV = L10n.tr("Localizable", "liveTV", fallback: "Live TV")
|
internal static let liveTV = L10n.tr("Localizable", "liveTV", fallback: "Live TV")
|
||||||
/// Loading
|
/// Loading
|
||||||
|
@ -340,6 +344,8 @@ internal enum L10n {
|
||||||
internal static let quickConnectStep3 = L10n.tr("Localizable", "quickConnectStep3", fallback: "3. Enter the following code:")
|
internal static let quickConnectStep3 = L10n.tr("Localizable", "quickConnectStep3", fallback: "3. Enter the following code:")
|
||||||
/// Authorizing Quick Connect successful. Please continue on your other device.
|
/// Authorizing Quick Connect successful. Please continue on your other device.
|
||||||
internal static let quickConnectSuccessMessage = L10n.tr("Localizable", "quickConnectSuccessMessage", fallback: "Authorizing Quick Connect successful. Please continue on your other device.")
|
internal static let quickConnectSuccessMessage = L10n.tr("Localizable", "quickConnectSuccessMessage", fallback: "Authorizing Quick Connect successful. Please continue on your other device.")
|
||||||
|
/// Random
|
||||||
|
internal static let random = L10n.tr("Localizable", "random", fallback: "Random")
|
||||||
/// Random Image
|
/// Random Image
|
||||||
internal static let randomImage = L10n.tr("Localizable", "randomImage", fallback: "Random Image")
|
internal static let randomImage = L10n.tr("Localizable", "randomImage", fallback: "Random Image")
|
||||||
/// Rated
|
/// Rated
|
||||||
|
@ -480,7 +486,7 @@ internal enum L10n {
|
||||||
internal static let suggestions = L10n.tr("Localizable", "suggestions", fallback: "Suggestions")
|
internal static let suggestions = L10n.tr("Localizable", "suggestions", fallback: "Suggestions")
|
||||||
/// Switch User
|
/// Switch User
|
||||||
internal static let switchUser = L10n.tr("Localizable", "switchUser", fallback: "Switch User")
|
internal static let switchUser = L10n.tr("Localizable", "switchUser", fallback: "Switch User")
|
||||||
/// System
|
/// Represents the system theme setting
|
||||||
internal static let system = L10n.tr("Localizable", "system", fallback: "System")
|
internal static let system = L10n.tr("Localizable", "system", fallback: "System")
|
||||||
/// System Control Gestures Enabled
|
/// System Control Gestures Enabled
|
||||||
internal static let systemControlGesturesEnabled = L10n.tr("Localizable", "systemControlGesturesEnabled", fallback: "System Control Gestures Enabled")
|
internal static let systemControlGesturesEnabled = L10n.tr("Localizable", "systemControlGesturesEnabled", fallback: "System Control Gestures Enabled")
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
import Combine
|
import Combine
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import Get
|
||||||
import JellyfinAPI
|
import JellyfinAPI
|
||||||
|
|
||||||
final class ItemTypeLibraryViewModel: PagingLibraryViewModel {
|
final class ItemTypeLibraryViewModel: PagingLibraryViewModel {
|
||||||
|
@ -35,25 +36,8 @@ final class ItemTypeLibraryViewModel: PagingLibraryViewModel {
|
||||||
hasNextPage = true
|
hasNextPage = true
|
||||||
}
|
}
|
||||||
|
|
||||||
let genreIDs = filters.genres.compactMap(\.id)
|
|
||||||
let sortBy: [String] = filters.sortBy.map(\.filterName).appending("IsFolder")
|
|
||||||
let sortOrder = filters.sortOrder.map { SortOrder(rawValue: $0.filterName) ?? .ascending }
|
|
||||||
let itemFilters: [ItemFilter] = filters.filters.compactMap { .init(rawValue: $0.filterName) }
|
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
let parameters = Paths.GetItemsParameters(
|
var parameters = self._getDefaultParams()
|
||||||
userID: userSession.user.id,
|
|
||||||
startIndex: currentPage * pageItemSize,
|
|
||||||
limit: pageItemSize,
|
|
||||||
isRecursive: true,
|
|
||||||
sortOrder: sortOrder,
|
|
||||||
fields: ItemFields.allCases,
|
|
||||||
includeItemTypes: itemTypes,
|
|
||||||
filters: itemFilters,
|
|
||||||
sortBy: sortBy,
|
|
||||||
enableUserData: true,
|
|
||||||
genreIDs: genreIDs
|
|
||||||
)
|
|
||||||
let request = Paths.getItems(parameters: parameters)
|
let request = Paths.getItems(parameters: parameters)
|
||||||
let response = try await userSession.client.send(request)
|
let response = try await userSession.client.send(request)
|
||||||
|
|
||||||
|
@ -68,6 +52,30 @@ final class ItemTypeLibraryViewModel: PagingLibraryViewModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func _getDefaultParams() -> Paths.GetItemsParameters? {
|
||||||
|
let filters = filterViewModel.currentFilters
|
||||||
|
let genreIDs = filters.genres.compactMap(\.id)
|
||||||
|
let sortBy: [String] = filters.sortBy.map(\.filterName).appending("IsFolder")
|
||||||
|
let sortOrder = filters.sortOrder.map { SortOrder(rawValue: $0.filterName) ?? .ascending }
|
||||||
|
let itemFilters: [ItemFilter] = filters.filters.compactMap { .init(rawValue: $0.filterName) }
|
||||||
|
|
||||||
|
let parameters = Paths.GetItemsParameters(
|
||||||
|
userID: userSession.user.id,
|
||||||
|
startIndex: currentPage * pageItemSize,
|
||||||
|
limit: pageItemSize,
|
||||||
|
isRecursive: true,
|
||||||
|
sortOrder: sortOrder,
|
||||||
|
fields: ItemFields.allCases,
|
||||||
|
includeItemTypes: itemTypes,
|
||||||
|
filters: itemFilters,
|
||||||
|
sortBy: sortBy,
|
||||||
|
enableUserData: true,
|
||||||
|
genreIDs: genreIDs
|
||||||
|
)
|
||||||
|
|
||||||
|
return parameters
|
||||||
|
}
|
||||||
|
|
||||||
override func _requestNextPage() {
|
override func _requestNextPage() {
|
||||||
requestItems(with: filterViewModel.currentFilters)
|
requestItems(with: filterViewModel.currentFilters)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
import Combine
|
import Combine
|
||||||
import Defaults
|
import Defaults
|
||||||
import Factory
|
import Factory
|
||||||
|
import Get
|
||||||
import JellyfinAPI
|
import JellyfinAPI
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import UIKit
|
import UIKit
|
||||||
|
@ -65,67 +66,21 @@ final class LibraryViewModel: PagingLibraryViewModel {
|
||||||
self.hasNextPage = true
|
self.hasNextPage = true
|
||||||
}
|
}
|
||||||
|
|
||||||
var libraryID: String?
|
var parameters = _getDefaultParams()
|
||||||
var personIDs: [String]?
|
parameters?.limit = pageItemSize
|
||||||
var studioIDs: [String]?
|
parameters?.startIndex = currentPage * pageItemSize
|
||||||
|
parameters?.sortOrder = filters.sortOrder.map { SortOrder(rawValue: $0.filterName) ?? .ascending }
|
||||||
if let parent = parent {
|
parameters?.sortBy = filters.sortBy.map(\.filterName).appending("IsFolder")
|
||||||
switch type {
|
|
||||||
case .library, .folders:
|
|
||||||
libraryID = parent.id
|
|
||||||
case .person:
|
|
||||||
personIDs = [parent].compactMap(\.id)
|
|
||||||
case .studio:
|
|
||||||
studioIDs = [parent].compactMap(\.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var recursive = true
|
|
||||||
let includeItemTypes: [BaseItemKind]
|
|
||||||
|
|
||||||
if filters.filters.contains(ItemFilter.isFavorite.filter) {
|
|
||||||
includeItemTypes = [.movie, .boxSet, .series, .season, .episode]
|
|
||||||
} else if type == .folders {
|
|
||||||
recursive = false
|
|
||||||
includeItemTypes = [.movie, .boxSet, .series, .folder, .collectionFolder]
|
|
||||||
} else {
|
|
||||||
includeItemTypes = [.movie, .boxSet, .series]
|
|
||||||
}
|
|
||||||
|
|
||||||
var excludedIDs: [String]?
|
|
||||||
|
|
||||||
if filters.sortBy.first == SortBy.random.filter {
|
if filters.sortBy.first == SortBy.random.filter {
|
||||||
excludedIDs = items.compactMap(\.id)
|
parameters?.excludeItemIDs = items.compactMap(\.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
let genreIDs = filters.genres.compactMap(\.id)
|
|
||||||
let sortBy: [String] = filters.sortBy.map(\.filterName).appending("IsFolder")
|
|
||||||
let sortOrder = filters.sortOrder.map { SortOrder(rawValue: $0.filterName) ?? .ascending }
|
|
||||||
let itemFilters: [ItemFilter] = filters.filters.compactMap { .init(rawValue: $0.filterName) }
|
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
self.isLoading = true
|
self.isLoading = true
|
||||||
}
|
}
|
||||||
|
|
||||||
let parameters = Paths.GetItemsParameters(
|
|
||||||
userID: userSession.user.id,
|
|
||||||
excludeItemIDs: excludedIDs,
|
|
||||||
startIndex: currentPage * pageItemSize,
|
|
||||||
limit: pageItemSize,
|
|
||||||
isRecursive: recursive,
|
|
||||||
sortOrder: sortOrder,
|
|
||||||
parentID: libraryID,
|
|
||||||
fields: ItemFields.allCases,
|
|
||||||
includeItemTypes: includeItemTypes,
|
|
||||||
filters: itemFilters,
|
|
||||||
sortBy: sortBy,
|
|
||||||
enableUserData: true,
|
|
||||||
personIDs: personIDs,
|
|
||||||
studioIDs: studioIDs,
|
|
||||||
genreIDs: genreIDs,
|
|
||||||
enableImages: true
|
|
||||||
)
|
|
||||||
let request = Paths.getItems(parameters: parameters)
|
let request = Paths.getItems(parameters: parameters)
|
||||||
let response = try await userSession.client.send(request)
|
let response = try await userSession.client.send(request)
|
||||||
|
|
||||||
|
@ -144,4 +99,53 @@ final class LibraryViewModel: PagingLibraryViewModel {
|
||||||
override func _requestNextPage() {
|
override func _requestNextPage() {
|
||||||
requestItems(with: filterViewModel.currentFilters)
|
requestItems(with: filterViewModel.currentFilters)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func _getDefaultParams() -> Paths.GetItemsParameters? {
|
||||||
|
|
||||||
|
let filters = filterViewModel.currentFilters
|
||||||
|
var libraryID: String?
|
||||||
|
var personIDs: [String]?
|
||||||
|
var studioIDs: [String]?
|
||||||
|
let includeItemTypes: [BaseItemKind]
|
||||||
|
var recursive = true
|
||||||
|
|
||||||
|
if let parent = parent {
|
||||||
|
switch type {
|
||||||
|
case .library, .folders:
|
||||||
|
libraryID = parent.id
|
||||||
|
case .person:
|
||||||
|
personIDs = [parent].compactMap(\.id)
|
||||||
|
case .studio:
|
||||||
|
studioIDs = [parent].compactMap(\.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if filters.filters.contains(ItemFilter.isFavorite.filter) {
|
||||||
|
includeItemTypes = [.movie, .boxSet, .series, .season, .episode]
|
||||||
|
} else if type == .folders {
|
||||||
|
recursive = false
|
||||||
|
includeItemTypes = [.movie, .boxSet, .series, .folder, .collectionFolder]
|
||||||
|
} else {
|
||||||
|
includeItemTypes = [.movie, .boxSet, .series]
|
||||||
|
}
|
||||||
|
|
||||||
|
let genreIDs = filters.genres.compactMap(\.id)
|
||||||
|
let itemFilters: [ItemFilter] = filters.filters.compactMap { .init(rawValue: $0.filterName) }
|
||||||
|
|
||||||
|
let parameters = Paths.GetItemsParameters(
|
||||||
|
userID: userSession.user.id,
|
||||||
|
isRecursive: recursive,
|
||||||
|
parentID: libraryID,
|
||||||
|
fields: ItemFields.allCases,
|
||||||
|
includeItemTypes: includeItemTypes,
|
||||||
|
filters: itemFilters,
|
||||||
|
enableUserData: true,
|
||||||
|
personIDs: personIDs,
|
||||||
|
studioIDs: studioIDs,
|
||||||
|
genreIDs: genreIDs,
|
||||||
|
enableImages: true
|
||||||
|
)
|
||||||
|
|
||||||
|
return parameters
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
import Defaults
|
import Defaults
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import Get
|
||||||
import JellyfinAPI
|
import JellyfinAPI
|
||||||
import OrderedCollections
|
import OrderedCollections
|
||||||
import UIKit
|
import UIKit
|
||||||
|
@ -28,6 +29,30 @@ class PagingLibraryViewModel: ViewModel {
|
||||||
return UIScreen.main.maxChildren(width: libraryGridPosterType.width, height: height)
|
return UIScreen.main.maxChildren(width: libraryGridPosterType.width, height: height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func getRandomItemFromLibrary() async throws -> BaseItemDtoQueryResult {
|
||||||
|
|
||||||
|
var parameters = _getDefaultParams()
|
||||||
|
parameters?.limit = 1
|
||||||
|
parameters?.sortBy = [SortBy.random.rawValue]
|
||||||
|
|
||||||
|
await MainActor.run {
|
||||||
|
self.isLoading = true
|
||||||
|
}
|
||||||
|
|
||||||
|
let request = Paths.getItems(parameters: parameters)
|
||||||
|
let response = try await userSession.client.send(request)
|
||||||
|
|
||||||
|
await MainActor.run {
|
||||||
|
self.isLoading = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.value
|
||||||
|
}
|
||||||
|
|
||||||
|
func _getDefaultParams() -> Paths.GetItemsParameters? {
|
||||||
|
Paths.GetItemsParameters()
|
||||||
|
}
|
||||||
|
|
||||||
func refresh() {
|
func refresh() {
|
||||||
currentPage = 0
|
currentPage = 0
|
||||||
hasNextPage = true
|
hasNextPage = true
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
4E8B34EA2AB91B6E0018F305 /* FilterDrawerSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E8B34E92AB91B6E0018F305 /* FilterDrawerSelection.swift */; };
|
4E8B34EA2AB91B6E0018F305 /* FilterDrawerSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E8B34E92AB91B6E0018F305 /* FilterDrawerSelection.swift */; };
|
||||||
4E8B34EB2AB91B6E0018F305 /* FilterDrawerSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E8B34E92AB91B6E0018F305 /* FilterDrawerSelection.swift */; };
|
4E8B34EB2AB91B6E0018F305 /* FilterDrawerSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E8B34E92AB91B6E0018F305 /* FilterDrawerSelection.swift */; };
|
||||||
4EAA35BB2AB9699B00D840DD /* FilterDrawerButtonSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EAA35BA2AB9699B00D840DD /* FilterDrawerButtonSelectorView.swift */; };
|
4EAA35BB2AB9699B00D840DD /* FilterDrawerButtonSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EAA35BA2AB9699B00D840DD /* FilterDrawerButtonSelectorView.swift */; };
|
||||||
|
4F1282B12A7F3E8F005BCA29 /* RandomItemButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F1282B02A7F3E8F005BCA29 /* RandomItemButton.swift */; };
|
||||||
531690E7267ABD79005D8AB9 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531690E6267ABD79005D8AB9 /* HomeView.swift */; };
|
531690E7267ABD79005D8AB9 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531690E6267ABD79005D8AB9 /* HomeView.swift */; };
|
||||||
53192D5D265AA78A008A4215 /* DeviceProfileBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53192D5C265AA78A008A4215 /* DeviceProfileBuilder.swift */; };
|
53192D5D265AA78A008A4215 /* DeviceProfileBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53192D5C265AA78A008A4215 /* DeviceProfileBuilder.swift */; };
|
||||||
531AC8BF26750DE20091C7EB /* ImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531AC8BE26750DE20091C7EB /* ImageView.swift */; };
|
531AC8BF26750DE20091C7EB /* ImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531AC8BE26750DE20091C7EB /* ImageView.swift */; };
|
||||||
|
@ -777,6 +778,7 @@
|
||||||
4E5E48E42AB59806003F1B48 /* CustomizeViewsSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomizeViewsSettings.swift; sourceTree = "<group>"; };
|
4E5E48E42AB59806003F1B48 /* CustomizeViewsSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomizeViewsSettings.swift; sourceTree = "<group>"; };
|
||||||
4E8B34E92AB91B6E0018F305 /* FilterDrawerSelection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FilterDrawerSelection.swift; sourceTree = "<group>"; };
|
4E8B34E92AB91B6E0018F305 /* FilterDrawerSelection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FilterDrawerSelection.swift; sourceTree = "<group>"; };
|
||||||
4EAA35BA2AB9699B00D840DD /* FilterDrawerButtonSelectorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FilterDrawerButtonSelectorView.swift; sourceTree = "<group>"; };
|
4EAA35BA2AB9699B00D840DD /* FilterDrawerButtonSelectorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FilterDrawerButtonSelectorView.swift; sourceTree = "<group>"; };
|
||||||
|
4F1282B02A7F3E8F005BCA29 /* RandomItemButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RandomItemButton.swift; sourceTree = "<group>"; };
|
||||||
531690E6267ABD79005D8AB9 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
|
531690E6267ABD79005D8AB9 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
|
||||||
531690F9267AD6EC005D8AB9 /* PlainNavigationLinkButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlainNavigationLinkButton.swift; sourceTree = "<group>"; };
|
531690F9267AD6EC005D8AB9 /* PlainNavigationLinkButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlainNavigationLinkButton.swift; sourceTree = "<group>"; };
|
||||||
53192D5C265AA78A008A4215 /* DeviceProfileBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceProfileBuilder.swift; sourceTree = "<group>"; };
|
53192D5C265AA78A008A4215 /* DeviceProfileBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceProfileBuilder.swift; sourceTree = "<group>"; };
|
||||||
|
@ -1817,6 +1819,7 @@
|
||||||
E1581E26291EF59800D6C640 /* SplitContentView.swift */,
|
E1581E26291EF59800D6C640 /* SplitContentView.swift */,
|
||||||
E157562F29355B7900976E1F /* UpdateView.swift */,
|
E157562F29355B7900976E1F /* UpdateView.swift */,
|
||||||
E192607F28D28AAD002314B4 /* UserProfileButton.swift */,
|
E192607F28D28AAD002314B4 /* UserProfileButton.swift */,
|
||||||
|
4F1282B02A7F3E8F005BCA29 /* RandomItemButton.swift */,
|
||||||
);
|
);
|
||||||
path = Components;
|
path = Components;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -3374,6 +3377,7 @@
|
||||||
535BAE9F2649E569005FA86D /* ItemView.swift in Sources */,
|
535BAE9F2649E569005FA86D /* ItemView.swift in Sources */,
|
||||||
E17AC9712954F636003D2BC2 /* DownloadListCoordinator.swift in Sources */,
|
E17AC9712954F636003D2BC2 /* DownloadListCoordinator.swift in Sources */,
|
||||||
E10EAA4F277BBCC4000269ED /* CGSize.swift in Sources */,
|
E10EAA4F277BBCC4000269ED /* CGSize.swift in Sources */,
|
||||||
|
4F1282B12A7F3E8F005BCA29 /* RandomItemButton.swift in Sources */,
|
||||||
E18E01EB288747230022598C /* MovieItemContentView.swift in Sources */,
|
E18E01EB288747230022598C /* MovieItemContentView.swift in Sources */,
|
||||||
E17FB55B28C1266400311DFE /* GenresHStack.swift in Sources */,
|
E17FB55B28C1266400311DFE /* GenresHStack.swift in Sources */,
|
||||||
E18E01FA288747580022598C /* AboutAppView.swift in Sources */,
|
E18E01FA288747580022598C /* AboutAppView.swift in Sources */,
|
||||||
|
|
|
@ -25,9 +25,9 @@ struct LibraryViewTypeToggle: View {
|
||||||
} label: {
|
} label: {
|
||||||
switch libraryViewType {
|
switch libraryViewType {
|
||||||
case .grid:
|
case .grid:
|
||||||
Image(systemName: "list.dash")
|
Label(L10n.list, systemImage: "list.dash")
|
||||||
case .list:
|
case .list:
|
||||||
Image(systemName: "square.grid.2x2")
|
Label(L10n.grid, systemImage: "square.grid.2x2")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
//
|
||||||
|
// 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 Defaults
|
||||||
|
import JellyfinAPI
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct RandomItemButton: View {
|
||||||
|
|
||||||
|
@ObservedObject
|
||||||
|
private var viewModel: PagingLibraryViewModel
|
||||||
|
private var onSelect: (BaseItemDtoQueryResult) -> Void
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Button {
|
||||||
|
Task {
|
||||||
|
let response = try await viewModel.getRandomItemFromLibrary()
|
||||||
|
onSelect(response)
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Label(L10n.random, systemImage: "dice.fill")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension RandomItemButton {
|
||||||
|
init(viewModel: PagingLibraryViewModel) {
|
||||||
|
self.init(
|
||||||
|
viewModel: viewModel,
|
||||||
|
onSelect: { _ in }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func onSelect(_ action: @escaping (BaseItemDtoQueryResult) -> Void) -> Self {
|
||||||
|
copy(modifying: \.onSelect, with: action)
|
||||||
|
}
|
||||||
|
}
|
|
@ -58,8 +58,17 @@ struct BasicLibraryView: View {
|
||||||
if viewModel.isLoading && !viewModel.items.isEmpty {
|
if viewModel.isLoading && !viewModel.items.isEmpty {
|
||||||
ProgressView()
|
ProgressView()
|
||||||
}
|
}
|
||||||
|
Menu {
|
||||||
LibraryViewTypeToggle(libraryViewType: $libraryViewType)
|
LibraryViewTypeToggle(libraryViewType: $libraryViewType)
|
||||||
|
RandomItemButton(viewModel: viewModel)
|
||||||
|
.onSelect { response in
|
||||||
|
if let item = response.items?.first {
|
||||||
|
router.route(to: \.item, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "ellipsis.circle")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,12 +84,20 @@ struct LibraryView: View {
|
||||||
}
|
}
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItemGroup(placement: .navigationBarTrailing) {
|
ToolbarItemGroup(placement: .navigationBarTrailing) {
|
||||||
|
|
||||||
if viewModel.isLoading && !viewModel.items.isEmpty {
|
if viewModel.isLoading && !viewModel.items.isEmpty {
|
||||||
ProgressView()
|
ProgressView()
|
||||||
}
|
}
|
||||||
|
Menu {
|
||||||
LibraryViewTypeToggle(libraryViewType: $libraryViewType)
|
LibraryViewTypeToggle(libraryViewType: $libraryViewType)
|
||||||
|
RandomItemButton(viewModel: viewModel)
|
||||||
|
.onSelect { response in
|
||||||
|
if let item = response.items?.first {
|
||||||
|
router.route(to: \.item, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "ellipsis.circle")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue