diff --git a/Shared/Coordinators/FilterCoordinator.swift b/Shared/Coordinators/FilterCoordinator.swift index 7a02e915..10a13f05 100644 --- a/Shared/Coordinators/FilterCoordinator.swift +++ b/Shared/Coordinators/FilterCoordinator.swift @@ -7,11 +7,10 @@ // import Foundation -import JellyfinAPI import Stinsen import SwiftUI -typealias FilterCoordinatorParams = (libraryItem: BaseItemDto, filters: Binding, enabledFilterType: [FilterType]) +typealias FilterCoordinatorParams = (filters: Binding, enabledFilterType: [FilterType], parentId: String) final class FilterCoordinator: NavigationCoordinatable { @@ -20,19 +19,19 @@ final class FilterCoordinator: NavigationCoordinatable { @Root var start = makeStart - let libraryItem: BaseItemDto @Binding var filters: LibraryFilters var enabledFilterType: [FilterType] + var parentId: String = "" - init(libraryItem: BaseItemDto, filters: Binding, enabledFilterType: [FilterType]) { - self.libraryItem = libraryItem + init(filters: Binding, enabledFilterType: [FilterType], parentId: String) { _filters = filters self.enabledFilterType = enabledFilterType + self.parentId = parentId } @ViewBuilder func makeStart() -> some View { - LibraryFilterView(filters: $filters, enabledFilterType: enabledFilterType, parentId: libraryItem.id!) + LibraryFilterView(filters: $filters, enabledFilterType: enabledFilterType, parentId: parentId) } } diff --git a/Shared/Coordinators/HomeCoordinator.swift b/Shared/Coordinators/HomeCoordinator.swift index da3f6582..3ce9bc3f 100644 --- a/Shared/Coordinators/HomeCoordinator.swift +++ b/Shared/Coordinators/HomeCoordinator.swift @@ -32,8 +32,8 @@ final class HomeCoordinator: NavigationCoordinatable { NavigationViewCoordinator(SettingsCoordinator()) } - func makeLibrary(viewModel: LibraryViewModel) -> LibraryCoordinator { - LibraryCoordinator(viewModel: viewModel) + func makeLibrary(params: LibraryCoordinatorParams) -> LibraryCoordinator { + LibraryCoordinator(viewModel: params.viewModel, title: params.title) } func makeItem(item: BaseItemDto) -> ItemCoordinator { @@ -44,8 +44,8 @@ final class HomeCoordinator: NavigationCoordinatable { NavigationViewCoordinator(ItemCoordinator(item: item)) } - func makeModalLibrary(viewModel: LibraryViewModel) -> NavigationViewCoordinator { - NavigationViewCoordinator(LibraryCoordinator(viewModel: viewModel)) + func makeModalLibrary(params: LibraryCoordinatorParams) -> NavigationViewCoordinator { + NavigationViewCoordinator(LibraryCoordinator(viewModel: params.viewModel, title: params.title)) } @ViewBuilder diff --git a/Shared/Coordinators/ItemCoordinator.swift b/Shared/Coordinators/ItemCoordinator.swift index f8b0cfea..9bb29489 100644 --- a/Shared/Coordinators/ItemCoordinator.swift +++ b/Shared/Coordinators/ItemCoordinator.swift @@ -32,8 +32,8 @@ final class ItemCoordinator: NavigationCoordinatable { self.itemDto = item } - func makeLibrary(viewModel: LibraryViewModel) -> LibraryCoordinator { - LibraryCoordinator(viewModel: viewModel) + func makeLibrary(params: LibraryCoordinatorParams) -> LibraryCoordinator { + LibraryCoordinator(viewModel: params.viewModel, title: params.title) } func makeItem(item: BaseItemDto) -> ItemCoordinator { diff --git a/Shared/Coordinators/LibraryCoordinator.swift b/Shared/Coordinators/LibraryCoordinator.swift index 2176dea2..1678e407 100644 --- a/Shared/Coordinators/LibraryCoordinator.swift +++ b/Shared/Coordinators/LibraryCoordinator.swift @@ -11,6 +11,8 @@ import JellyfinAPI import Stinsen import SwiftUI +typealias LibraryCoordinatorParams = (viewModel: LibraryViewModel, title: String) + final class LibraryCoordinator: NavigationCoordinatable { let stack = NavigationStack(initial: \LibraryCoordinator.start) @@ -27,14 +29,16 @@ final class LibraryCoordinator: NavigationCoordinatable { var modalItem = makeModalItem let viewModel: LibraryViewModel + let title: String - init(viewModel: LibraryViewModel) { + init(viewModel: LibraryViewModel, title: String) { self.viewModel = viewModel + self.title = title } @ViewBuilder func makeStart() -> some View { - LibraryView(viewModel: self.viewModel) + LibraryView(viewModel: self.viewModel, title: title) } func makeSearch(viewModel: LibrarySearchViewModel) -> SearchCoordinator { @@ -42,9 +46,9 @@ final class LibraryCoordinator: NavigationCoordinatable { } func makeFilter(params: FilterCoordinatorParams) -> NavigationViewCoordinator { - NavigationViewCoordinator(FilterCoordinator(libraryItem: viewModel.libraryItem, - filters: params.filters, - enabledFilterType: params.enabledFilterType)) + NavigationViewCoordinator(FilterCoordinator(filters: params.filters, + enabledFilterType: params.enabledFilterType, + parentId: params.parentId)) } func makeItem(item: BaseItemDto) -> ItemCoordinator { diff --git a/Shared/Coordinators/LibraryListCoordinator.swift b/Shared/Coordinators/LibraryListCoordinator.swift index 00989fe6..a413ff83 100644 --- a/Shared/Coordinators/LibraryListCoordinator.swift +++ b/Shared/Coordinators/LibraryListCoordinator.swift @@ -27,8 +27,8 @@ final class LibraryListCoordinator: NavigationCoordinatable { self.viewModel = viewModel } - func makeLibrary(viewModel: LibraryViewModel) -> LibraryCoordinator { - LibraryCoordinator(viewModel: viewModel) + func makeLibrary(params: LibraryCoordinatorParams) -> LibraryCoordinator { + LibraryCoordinator(viewModel: params.viewModel, title: params.title) } func makeSearch(viewModel: LibrarySearchViewModel) -> SearchCoordinator { diff --git a/Shared/Coordinators/TVLibrariesCoordinator.swift b/Shared/Coordinators/TVLibrariesCoordinator.swift index 5335cd31..97c715af 100644 --- a/Shared/Coordinators/TVLibrariesCoordinator.swift +++ b/Shared/Coordinators/TVLibrariesCoordinator.swift @@ -34,6 +34,6 @@ final class TVLibrariesCoordinator: NavigationCoordinatable { } func makeLibrary(library: BaseItemDto) -> LibraryCoordinator { - LibraryCoordinator(viewModel: .init(libraryItem: <#T##BaseItemDto#>)) + LibraryCoordinator(viewModel: .init(libraryItem: <#T##BaseItemDto#>)) } } diff --git a/Shared/Extensions/JellyfinAPIExtensions/BaseItemDtoExtensions.swift b/Shared/Extensions/JellyfinAPIExtensions/BaseItemDtoExtensions.swift index 849dffe8..fdcc7128 100644 --- a/Shared/Extensions/JellyfinAPIExtensions/BaseItemDtoExtensions.swift +++ b/Shared/Extensions/JellyfinAPIExtensions/BaseItemDtoExtensions.swift @@ -206,7 +206,7 @@ public extension BaseItemDto { case episode = "Episode" case series = "Series" case boxset = "BoxSet" - case collectionFolder = "CollectionFolder" + case collectionFolder = "CollectionFolder" case unknown @@ -229,7 +229,7 @@ public extension BaseItemDto { func portraitHeaderViewURL(maxWidth: Int) -> URL { switch itemType { - case .movie, .season, .series, .boxset, .collectionFolder: + case .movie, .season, .series, .boxset, .collectionFolder: return getPrimaryImage(maxWidth: maxWidth) case .episode: return getSeriesPrimaryImage(maxWidth: maxWidth) diff --git a/Shared/ViewModels/LibraryFilterViewModel.swift b/Shared/ViewModels/LibraryFilterViewModel.swift index 2e607508..527cacfa 100644 --- a/Shared/ViewModels/LibraryFilterViewModel.swift +++ b/Shared/ViewModels/LibraryFilterViewModel.swift @@ -67,7 +67,8 @@ final class LibraryFilterViewModel: ViewModel { } func requestQueryFilters() { - FilterAPI.getQueryFilters(userId: SessionManager.main.currentLogin.user.id, parentId: self.parentId) + FilterAPI.getQueryFilters(userId: SessionManager.main.currentLogin.user.id, + parentId: self.parentId) .trackActivity(loading) .sink(receiveCompletion: { [weak self] completion in self?.handleAPIRequestError(completion: completion) diff --git a/Shared/ViewModels/LibraryListViewModel.swift b/Shared/ViewModels/LibraryListViewModel.swift index d4bc01e5..b1c2b85d 100644 --- a/Shared/ViewModels/LibraryListViewModel.swift +++ b/Shared/ViewModels/LibraryListViewModel.swift @@ -13,8 +13,8 @@ final class LibraryListViewModel: ViewModel { @Published var libraries: [BaseItemDto] = [] - @Published - var libraryRandomItems: [BaseItemDto: BaseItemDto] = [:] + @Published + var libraryRandomItems: [BaseItemDto: BaseItemDto] = [:] // temp var withFavorites = LibraryFilters(filters: [.isFavorite], sortOrder: [], withGenres: [], sortBy: []) @@ -31,34 +31,34 @@ final class LibraryListViewModel: ViewModel { .sink(receiveCompletion: { completion in self.handleAPIRequestError(completion: completion) }, receiveValue: { response in - if let libraries = response.items { - self.libraries = libraries - - for library in libraries { - self.getRandomLibraryItem(for: library) - } - } + if let libraries = response.items { + self.libraries = libraries + + for library in libraries { + self.getRandomLibraryItem(for: library) + } + } }) .store(in: &cancellables) } - - // MARK: Library random item - - func getRandomLibraryItem(for library: BaseItemDto) { - guard library.itemType == .collectionFolder else { return } - - ItemsAPI.getItems(userId: SessionManager.main.currentLogin.user.id, - limit: 1, - parentId: library.id) - .sink { completion in - self.handleAPIRequestError(completion: completion) - } receiveValue: { result in - if let item = result.items?.first { - self.libraryRandomItems[library] = item - } else { - self.libraryRandomItems[library] = library - } - } - .store(in: &cancellables) - } + + // MARK: Library random item + + func getRandomLibraryItem(for library: BaseItemDto) { + guard library.itemType == .collectionFolder else { return } + + ItemsAPI.getItems(userId: SessionManager.main.currentLogin.user.id, + limit: 1, + parentId: library.id) + .sink { completion in + self.handleAPIRequestError(completion: completion) + } receiveValue: { result in + if let item = result.items?.first { + self.libraryRandomItems[library] = item + } else { + self.libraryRandomItems[library] = library + } + } + .store(in: &cancellables) + } } diff --git a/Shared/ViewModels/LibraryViewModel.swift b/Shared/ViewModels/LibraryViewModel.swift index 02cbe13b..3f150172 100644 --- a/Shared/ViewModels/LibraryViewModel.swift +++ b/Shared/ViewModels/LibraryViewModel.swift @@ -23,9 +23,9 @@ struct LibraryRowCell: Hashable { final class LibraryViewModel: ViewModel { @Published - var items: [BaseItemDto] = [] + var items: [BaseItemDto] = [] @Published - var rows: [LibraryRow] = [] + var rows: [LibraryRow] = [] @Published var totalPages = 0 @@ -37,13 +37,13 @@ final class LibraryViewModel: ViewModel { // temp @Published var filters: LibraryFilters - - let libraryItem: BaseItemDto - var person: BaseItemPerson? - var genre: NameGuidPair? - var studio: NameGuidPair? + + var parentID: String? + var person: BaseItemPerson? + var genre: NameGuidPair? + var studio: NameGuidPair? private let columns: Int - private let pageItemSize: Int + private let pageItemSize: Int var enabledFilterType: [FilterType] { if genre == nil { @@ -53,40 +53,38 @@ final class LibraryViewModel: ViewModel { } } - init(libraryItem: BaseItemDto, + init(parentID: String? = nil, person: BaseItemPerson? = nil, genre: NameGuidPair? = nil, studio: NameGuidPair? = nil, filters: LibraryFilters = LibraryFilters(filters: [], sortOrder: [.ascending], withGenres: [], sortBy: [.name]), columns: Int = 7) { - self.libraryItem = libraryItem + self.parentID = parentID self.person = person self.genre = genre self.studio = studio self.filters = filters self.columns = columns - - // Size is typical size of portrait items - self.pageItemSize = UIScreen.itemsFillableOnScreen(width: 130, height: 185) - - print("Page item size: \(pageItemSize)") - + + // Size is typical size of portrait items + self.pageItemSize = UIScreen.itemsFillableOnScreen(width: 130, height: 185) + super.init() $filters - .sink(receiveValue: { newFilters in - self.requestItemsAsync(with: newFilters, replaceCurrentItems: true) - }) + .sink(receiveValue: { newFilters in + self.requestItemsAsync(with: newFilters, replaceCurrentItems: true) + }) .store(in: &cancellables) } - func requestItemsAsync(with filters: LibraryFilters, replaceCurrentItems: Bool = false) { - - if replaceCurrentItems { - self.items = [] - } - + func requestItemsAsync(with filters: LibraryFilters, replaceCurrentItems: Bool = false) { + + if replaceCurrentItems { + self.items = [] + } + let personIDs: [String] = [person].compactMap(\.?.id) let studioIDs: [String] = [studio].compactMap(\.?.id) let genreIDs: [String] @@ -102,7 +100,7 @@ final class LibraryViewModel: ViewModel { recursive: true, searchTerm: nil, sortOrder: filters.sortOrder, - parentId: libraryItem.id, + parentId: parentID, fields: [ .primaryImageAspectRatio, .seriesPrimaryImage, @@ -122,14 +120,14 @@ final class LibraryViewModel: ViewModel { studioIds: studioIDs, genreIds: genreIDs, enableImages: true) - .trackActivity(loading) + .trackActivity(loading) .sink(receiveCompletion: { [weak self] completion in self?.handleAPIRequestError(completion: completion) }, receiveValue: { [weak self] response in - + guard let self = self else { return } - let totalPages = ceil(Double(response.totalRecordCount ?? 0) / Double(self.pageItemSize)) - + let totalPages = ceil(Double(response.totalRecordCount ?? 0) / Double(self.pageItemSize)) + self.totalPages = Int(totalPages) self.hasNextPage = self.currentPage < self.totalPages - 1 self.items.append(contentsOf: response.items ?? []) @@ -143,7 +141,7 @@ final class LibraryViewModel: ViewModel { requestItemsAsync(with: filters) } - // tvOS calculations for collection view + // tvOS calculations for collection view private func calculateRows(for itemList: [BaseItemDto]) -> [LibraryRow] { guard !itemList.isEmpty else { return [] } let rowCount = itemList.count / columns @@ -174,12 +172,12 @@ final class LibraryViewModel: ViewModel { } extension UIScreen { - - static func itemsFillableOnScreen(width: CGFloat, height: CGFloat) -> Int { - - let screenSize = UIScreen.main.bounds.height * UIScreen.main.bounds.width - let itemSize = width * height - - return Int(screenSize / itemSize) - } + + static func itemsFillableOnScreen(width: CGFloat, height: CGFloat) -> Int { + + let screenSize = UIScreen.main.bounds.height * UIScreen.main.bounds.width + let itemSize = width * height + + return Int(screenSize / itemSize) + } } diff --git a/Swiftfin/Assets.xcassets/AccentColor.colorset/Contents.json b/Swiftfin/Assets.xcassets/AccentColor.colorset/Contents.json index 10fdb69d..d383c2d1 100644 --- a/Swiftfin/Assets.xcassets/AccentColor.colorset/Contents.json +++ b/Swiftfin/Assets.xcassets/AccentColor.colorset/Contents.json @@ -12,6 +12,18 @@ }, "idiom" : "iphone" }, + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.765", + "green" : "0.361", + "red" : "0.667" + } + }, + "idiom" : "ipad" + }, { "color" : { "color-space" : "srgb", diff --git a/Swiftfin/Components/DetectBottomScrollView.swift b/Swiftfin/Components/DetectBottomScrollView.swift index 5d275f6c..45a2500c 100644 --- a/Swiftfin/Components/DetectBottomScrollView.swift +++ b/Swiftfin/Components/DetectBottomScrollView.swift @@ -11,89 +11,86 @@ import SwiftUI // https://stackoverflow.com/questions/56573373/swiftui-get-size-of-child struct ChildSizeReader: View { - @Binding var size: CGSize - let content: () -> Content - var body: some View { - ZStack { - content() - .background( - GeometryReader { proxy in - Color.clear - .preference(key: SizePreferenceKey.self, value: proxy.size) - } - ) - } - .onPreferenceChange(SizePreferenceKey.self) { preferences in - self.size = preferences - } - } + @Binding + var size: CGSize + let content: () -> Content + var body: some View { + ZStack { + content() + .background(GeometryReader { proxy in + Color.clear + .preference(key: SizePreferenceKey.self, value: proxy.size) + }) + } + .onPreferenceChange(SizePreferenceKey.self) { preferences in + self.size = preferences + } + } } struct SizePreferenceKey: PreferenceKey { - typealias Value = CGSize - static var defaultValue: Value = .zero + typealias Value = CGSize + static var defaultValue: Value = .zero - static func reduce(value _: inout Value, nextValue: () -> Value) { - _ = nextValue() - } + static func reduce(value _: inout Value, nextValue: () -> Value) { + _ = nextValue() + } } struct ViewOffsetKey: PreferenceKey { - typealias Value = CGFloat - static var defaultValue = CGFloat.zero - static func reduce(value: inout Value, nextValue: () -> Value) { - value += nextValue() - } + typealias Value = CGFloat + static var defaultValue = CGFloat.zero + static func reduce(value: inout Value, nextValue: () -> Value) { + value += nextValue() + } } struct DetectBottomScrollView: View { - private let spaceName = "scroll" + private let spaceName = "scroll" - @State private var wholeSize: CGSize = .zero - @State private var scrollViewSize: CGSize = .zero - @State private var previousDidReachBottom = false - let content: () -> Content - let didReachBottom: (Bool) -> Void - - init(content: @escaping () -> Content, - didReachBottom: @escaping (Bool) -> Void) { - self.content = content - self.didReachBottom = didReachBottom - } + @State + private var wholeSize: CGSize = .zero + @State + private var scrollViewSize: CGSize = .zero + @State + private var previousDidReachBottom = false + let content: () -> Content + let didReachBottom: (Bool) -> Void - var body: some View { - ChildSizeReader(size: $wholeSize) { - ScrollView { - ChildSizeReader(size: $scrollViewSize) { - content() - .background( - GeometryReader { proxy in - Color.clear.preference( - key: ViewOffsetKey.self, - value: -1 * proxy.frame(in: .named(spaceName)).origin.y - ) - } - ) - .onPreferenceChange( - ViewOffsetKey.self, - perform: { value in + init(content: @escaping () -> Content, + didReachBottom: @escaping (Bool) -> Void) + { + self.content = content + self.didReachBottom = didReachBottom + } - if value >= scrollViewSize.height - wholeSize.height { - if !previousDidReachBottom { - previousDidReachBottom = true - didReachBottom(true) - } - } else { - if previousDidReachBottom { - previousDidReachBottom = false - didReachBottom(false) - } - } - } - ) - } - } - .coordinateSpace(name: spaceName) - } - } + var body: some View { + ChildSizeReader(size: $wholeSize) { + ScrollView { + ChildSizeReader(size: $scrollViewSize) { + content() + .background(GeometryReader { proxy in + Color.clear.preference(key: ViewOffsetKey.self, + value: -1 * proxy.frame(in: .named(spaceName)).origin.y) + }) + .onPreferenceChange(ViewOffsetKey.self, + perform: { value in + + if value >= scrollViewSize.height - wholeSize.height { + if !previousDidReachBottom { + previousDidReachBottom = true + didReachBottom(true) + } + } else { + if previousDidReachBottom { + previousDidReachBottom = false + didReachBottom(false) + } + } + }) + } + } + .coordinateSpace(name: spaceName) + } + } } diff --git a/Swiftfin/Components/PortraitItemButton.swift b/Swiftfin/Components/PortraitItemButton.swift index c6251064..98a52521 100644 --- a/Swiftfin/Components/PortraitItemButton.swift +++ b/Swiftfin/Components/PortraitItemButton.swift @@ -10,60 +10,60 @@ import JellyfinAPI import SwiftUI struct PortraitItemButton: View { - - let item: ItemType - let maxWidth: CGFloat - let horizontalAlignment: HorizontalAlignment - let textAlignment: TextAlignment - let selectedAction: (ItemType) -> Void - - init(item: ItemType, - maxWidth: CGFloat = 110, - horizontalAlignment: HorizontalAlignment = .leading, - textAlignment: TextAlignment = .leading, - selectedAction: @escaping (ItemType) -> Void) - { - self.item = item - self.maxWidth = maxWidth - self.horizontalAlignment = horizontalAlignment - self.textAlignment = textAlignment - self.selectedAction = selectedAction - } - - var body: some View { - Button { - selectedAction(item) - } label: { - VStack(alignment: horizontalAlignment) { - ImageView(src: item.imageURLContsructor(maxWidth: Int(maxWidth)), - bh: item.blurHash, - failureInitials: item.failureInitials) - .portraitPoster(width: maxWidth) - .shadow(radius: 4, y: 2) - if item.showTitle { - Text(item.title) - .font(.footnote) - .fontWeight(.regular) - .foregroundColor(.primary) - .multilineTextAlignment(textAlignment) - .fixedSize(horizontal: false, vertical: true) - .lineLimit(2) - } + let item: ItemType + let maxWidth: CGFloat + let horizontalAlignment: HorizontalAlignment + let textAlignment: TextAlignment + let selectedAction: (ItemType) -> Void - if let description = item.subtitle { - Text(description) - .font(.caption) - .fontWeight(.medium) - .foregroundColor(.secondary) - .multilineTextAlignment(textAlignment) - .fixedSize(horizontal: false, vertical: true) - .lineLimit(2) - } - } - .frame(width: maxWidth) - } - .frame(alignment: .top) - .padding(.bottom) - } + init(item: ItemType, + maxWidth: CGFloat = 110, + horizontalAlignment: HorizontalAlignment = .leading, + textAlignment: TextAlignment = .leading, + selectedAction: @escaping (ItemType) -> Void) + { + self.item = item + self.maxWidth = maxWidth + self.horizontalAlignment = horizontalAlignment + self.textAlignment = textAlignment + self.selectedAction = selectedAction + } + + var body: some View { + Button { + selectedAction(item) + } label: { + VStack(alignment: horizontalAlignment) { + ImageView(src: item.imageURLContsructor(maxWidth: Int(maxWidth)), + bh: item.blurHash, + failureInitials: item.failureInitials) + .portraitPoster(width: maxWidth) + .shadow(radius: 4, y: 2) + + if item.showTitle { + Text(item.title) + .font(.footnote) + .fontWeight(.regular) + .foregroundColor(.primary) + .multilineTextAlignment(textAlignment) + .fixedSize(horizontal: false, vertical: true) + .lineLimit(2) + } + + if let description = item.subtitle { + Text(description) + .font(.caption) + .fontWeight(.medium) + .foregroundColor(.secondary) + .multilineTextAlignment(textAlignment) + .fixedSize(horizontal: false, vertical: true) + .lineLimit(2) + } + } + .frame(width: maxWidth) + } + .frame(alignment: .top) + .padding(.bottom) + } } diff --git a/Swiftfin/Views/HomeView.swift b/Swiftfin/Views/HomeView.swift index a4fa04cf..e6e8f764 100644 --- a/Swiftfin/Views/HomeView.swift +++ b/Swiftfin/Views/HomeView.swift @@ -90,9 +90,9 @@ struct HomeView: View { Button { homeRouter -// .route(to: \.library, (viewModel: .init(parentID: library.id!, -// filters: viewModel.recentFilterSet), -// title: library.name ?? "")) + .route(to: \.library, (viewModel: .init(parentID: library.id!, + filters: viewModel.recentFilterSet), + title: library.name ?? "")) } label: { HStack { L10n.seeAll.text.font(.subheadline).fontWeight(.bold) diff --git a/Swiftfin/Views/LibraryListView.swift b/Swiftfin/Views/LibraryListView.swift index 8899b088..7c40f19a 100644 --- a/Swiftfin/Views/LibraryListView.swift +++ b/Swiftfin/Views/LibraryListView.swift @@ -12,7 +12,7 @@ import Stinsen import SwiftUI struct LibraryListView: View { - + @EnvironmentObject var libraryListRouter: LibraryListCoordinator.Router @StateObject @@ -23,31 +23,30 @@ struct LibraryListView: View { LazyVStack { Button { libraryListRouter.route(to: \.library, - LibraryViewModel(libraryItem: BaseItemDto(), filters: viewModel.withFavorites)) + (viewModel: LibraryViewModel(filters: viewModel.withFavorites), title: L10n.favorites)) } label: { - HStack { - Spacer() - L10n.yourFavorites.text - .foregroundColor(.black) - .font(.subheadline) - .fontWeight(.semibold) - Spacer() - } - .frame(height: 100) + HStack { + Spacer() + L10n.yourFavorites.text + .foregroundColor(.black) + .font(.subheadline) + .fontWeight(.semibold) + Spacer() + } + .frame(height: 100) .background(Color.white) } .cornerRadius(10) .shadow(radius: 5) - .padding() + .padding() if !viewModel.isLoading { if let collectionsLibraryItem = viewModel.libraries.first(where: { $0.collectionType == "boxsets" }) { Button { libraryListRouter.route(to: \.library, - LibraryViewModel(libraryItem: collectionsLibraryItem)) -// (viewModel: LibraryViewModel(parentID: collectionsLibraryItem.id), -// title: collectionsLibraryItem.name ?? "")) + (viewModel: LibraryViewModel(parentID: collectionsLibraryItem.id), + title: collectionsLibraryItem.name ?? "")) } label: { ZStack { ImageView(src: collectionsLibraryItem.getPrimaryImage(maxWidth: 500), @@ -64,15 +63,15 @@ struct LibraryListView: View { Spacer() } } - .background(Color.black) - .frame(height: 100) + .background(Color.black) + .frame(height: 100) } .cornerRadius(10) .shadow(radius: 5) - .padding() + .padding() } - ForEach(Array(viewModel.libraryRandomItems.keys), id: \.id) { library in + ForEach(Array(viewModel.libraryRandomItems.keys), id: \.id) { library in if library.collectionType ?? "" == "movies" || library.collectionType ?? "" == "tvshows" { Button { libraryListRouter.route(to: \.library, @@ -82,22 +81,22 @@ struct LibraryListView: View { ZStack { // ImageView(src: library.getPrimaryImage(maxWidth: 500), bh: library.getPrimaryImageBlurHash()) // .opacity(0.4) - - ImageView(src: viewModel.libraryRandomItems[library]!.getBackdropImage(maxWidth: 500)) - - VStack { - Text(library.name ?? "") - .foregroundColor(.white) - .font(.title2) - .fontWeight(.semibold) - } + + ImageView(src: viewModel.libraryRandomItems[library]!.getBackdropImage(maxWidth: 500)) + + VStack { + Text(library.name ?? "") + .foregroundColor(.white) + .font(.title2) + .fontWeight(.semibold) + } } - .background(Color.black) - .frame(height: 100) + .background(Color.black) + .frame(height: 100) } .cornerRadius(10) - .shadow(radius: 5) - .padding() + .shadow(radius: 5) + .padding() } else { EmptyView() } diff --git a/Swiftfin/Views/LibrarySearchView.swift b/Swiftfin/Views/LibrarySearchView.swift index d4ae3958..374917a2 100644 --- a/Swiftfin/Views/LibrarySearchView.swift +++ b/Swiftfin/Views/LibrarySearchView.swift @@ -88,7 +88,7 @@ struct LibrarySearchView: View { Button { searchRouter.route(to: \.item, item) } label: { - PortraitItemElement(item: item) + PortraitItemElement(item: item) } } } diff --git a/Swiftfin/Views/LibraryView.swift b/Swiftfin/Views/LibraryView.swift index d015d8d7..adf1d29e 100644 --- a/Swiftfin/Views/LibraryView.swift +++ b/Swiftfin/Views/LibraryView.swift @@ -10,84 +10,87 @@ import Stinsen import SwiftUI struct LibraryView: View { - + @EnvironmentObject var libraryRouter: LibraryCoordinator.Router @StateObject var viewModel: LibraryViewModel + var title: String // MARK: tracks for grid var defaultFilters = LibraryFilters(filters: [], sortOrder: [.ascending], withGenres: [], tags: [], sortBy: [.name]) @State - private var tracks: [GridItem] = Array(repeating: .init(.flexible(), alignment: .top), count: Int(UIScreen.main.bounds.size.width) / 125) + private var tracks: [GridItem] = Array(repeating: .init(.flexible(), alignment: .top), + count: Int(UIScreen.main.bounds.size.width) / 125) func recalcTracks() { tracks = Array(repeating: .init(.flexible(), alignment: .top), count: Int(UIScreen.main.bounds.size.width) / 125) } - - @ViewBuilder - private var loadingView: some View { - ProgressView() - } - - @ViewBuilder - private var noResultsView: some View { - L10n.noResults.text - } - - @ViewBuilder - private var libraryItemsView: some View { - DetectBottomScrollView { - VStack { - LazyVGrid(columns: tracks) { - ForEach(viewModel.items, id: \.id) { item in - if item.type != "Folder" { - PortraitItemButton(item: item) { item in - libraryRouter.route(to: \.item, item) - } - } - } - } - .ignoresSafeArea() - .listRowSeparator(.hidden) - .onRotate { _ in - recalcTracks() - } - - Spacer() - .frame(height: 30) - } - } didReachBottom: { newValue in - if newValue && viewModel.hasNextPage { - viewModel.requestNextPageAsync() - } - } - } + + @ViewBuilder + private var loadingView: some View { + ProgressView() + } + + @ViewBuilder + private var noResultsView: some View { + L10n.noResults.text + } + + @ViewBuilder + private var libraryItemsView: some View { + DetectBottomScrollView { + VStack { + LazyVGrid(columns: tracks) { + ForEach(viewModel.items, id: \.id) { item in + if item.type != "Folder" { + PortraitItemButton(item: item) { item in + libraryRouter.route(to: \.item, item) + } + } + } + } + .ignoresSafeArea() + .listRowSeparator(.hidden) + .onRotate { _ in + recalcTracks() + } + + Spacer() + .frame(height: 30) + } + } didReachBottom: { newValue in + if newValue && viewModel.hasNextPage { + viewModel.requestNextPageAsync() + } + } + } var body: some View { Group { - if viewModel.isLoading && viewModel.items.isEmpty { + if viewModel.isLoading && viewModel.items.isEmpty { ProgressView() } else if !viewModel.items.isEmpty { - libraryItemsView + libraryItemsView } else { noResultsView } } + .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItemGroup(placement: .navigationBarTrailing) { - - Button { -// libraryRouter -// .route(to: \.filter, (filters: $viewModel.filters, enabledFilterType: viewModel.enabledFilterType, -// parentId: viewModel.parentID ?? "")) - } label: { - Image(systemName: "line.horizontal.3.decrease.circle") - } - .foregroundColor(viewModel.filters == defaultFilters ? .accentColor : Color(UIColor.systemOrange)) - + + Button { + libraryRouter + .route(to: \.filter, (filters: $viewModel.filters, enabledFilterType: viewModel.enabledFilterType, + parentId: viewModel.parentID ?? "")) + } label: { + Image(systemName: "line.horizontal.3.decrease.circle") + } + .foregroundColor(viewModel.filters == defaultFilters ? .accentColor : Color(UIColor.systemOrange)) + Button { // libraryRouter.route(to: \.search, .init(parentID: viewModel.parentID)) } label: {