diff --git a/Jel.xcodeproj/project.pbxproj b/Jel.xcodeproj/project.pbxproj index 1954e5a..3a42034 100644 --- a/Jel.xcodeproj/project.pbxproj +++ b/Jel.xcodeproj/project.pbxproj @@ -18,10 +18,13 @@ 3D13F96F2B38A32500E91913 /* StickyHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D13F96E2B38A32500E91913 /* StickyHeaderView.swift */; }; 3D16FC3C2B2CDFB500E6D8B3 /* DashboardLibraryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D16FC3B2B2CDFB500E6D8B3 /* DashboardLibraryView.swift */; }; 3D21F4292B69A8B0007207D2 /* ExpandableText in Frameworks */ = {isa = PBXBuildFile; productRef = 3D21F4282B69A8B0007207D2 /* ExpandableText */; }; + 3D2552462B7A847700192879 /* ItemSeriesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D2552452B7A847700192879 /* ItemSeriesView.swift */; }; + 3D2552492B7A8B3100192879 /* ItemSeriesSeriesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D2552482B7A8B3100192879 /* ItemSeriesSeriesView.swift */; }; 3D3816C92B4B5648006414D7 /* ItemGenresView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D3816C82B4B5648006414D7 /* ItemGenresView.swift */; }; 3D41D1FA2B2CAE0000E58234 /* ItemIconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D41D1F92B2CAE0000E58234 /* ItemIconView.swift */; }; 3D4C15722B3CAA670035373E /* DashboardSectionTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D4C15712B3CAA670035373E /* DashboardSectionTitleView.swift */; }; 3D58F07E2B4DB19300DB2936 /* TextRatingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D58F07D2B4DB19300DB2936 /* TextRatingView.swift */; }; + 3D6A7DB92B7C5D3900A5E31B /* ItemSeasonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D6A7DB82B7C5D3900A5E31B /* ItemSeasonView.swift */; }; 3D7709392B29139700199889 /* Pulse in Frameworks */ = {isa = PBXBuildFile; productRef = 3D7709382B29139700199889 /* Pulse */; }; 3D77093B2B29139700199889 /* PulseUI in Frameworks */ = {isa = PBXBuildFile; productRef = 3D77093A2B29139700199889 /* PulseUI */; }; 3D8AB2A52B36440D005BD7D0 /* BlurHashDecode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D8AB2A42B36440D005BD7D0 /* BlurHashDecode.swift */; }; @@ -92,10 +95,13 @@ 3D13F9682B389FA300E91913 /* ViewOffsetKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewOffsetKey.swift; sourceTree = ""; }; 3D13F96E2B38A32500E91913 /* StickyHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickyHeaderView.swift; sourceTree = ""; }; 3D16FC3B2B2CDFB500E6D8B3 /* DashboardLibraryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardLibraryView.swift; sourceTree = ""; }; + 3D2552452B7A847700192879 /* ItemSeriesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemSeriesView.swift; sourceTree = ""; }; + 3D2552482B7A8B3100192879 /* ItemSeriesSeriesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemSeriesSeriesView.swift; sourceTree = ""; }; 3D3816C82B4B5648006414D7 /* ItemGenresView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemGenresView.swift; sourceTree = ""; }; 3D41D1F92B2CAE0000E58234 /* ItemIconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemIconView.swift; sourceTree = ""; }; 3D4C15712B3CAA670035373E /* DashboardSectionTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardSectionTitleView.swift; sourceTree = ""; }; 3D58F07D2B4DB19300DB2936 /* TextRatingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRatingView.swift; sourceTree = ""; }; + 3D6A7DB82B7C5D3900A5E31B /* ItemSeasonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemSeasonView.swift; sourceTree = ""; }; 3D8AB2A42B36440D005BD7D0 /* BlurHashDecode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurHashDecode.swift; sourceTree = ""; }; 3D8AB2A72B366353005BD7D0 /* LibraryDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryDetailView.swift; sourceTree = ""; }; 3D9063C72B279A310063DD2A /* Jel.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Jel.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -199,10 +205,11 @@ 3D13F95D2B375DAC00E91913 /* Item */ = { isa = PBXGroup; children = ( - 3D41D1F92B2CAE0000E58234 /* ItemIconView.swift */, + 3D13F95E2B375DB800E91913 /* ItemView.swift */, 3DAFA8ED2B3B707100D71AD1 /* Types */, 3DBAC9E82B4C891C005F8764 /* Person */, - 3D13F95E2B375DB800E91913 /* ItemView.swift */, + 3D2552472B7A8A9400192879 /* Series */, + 3D41D1F92B2CAE0000E58234 /* ItemIconView.swift */, 3D13F9602B37637500E91913 /* ItemMediaView.swift */, 3DAFA8E72B38AFED00D71AD1 /* ItemInfoView.swift */, 3D13F9642B37EC7A00E91913 /* ItemHeaderView.swift */, @@ -220,6 +227,14 @@ path = Utility; sourceTree = ""; }; + 3D2552472B7A8A9400192879 /* Series */ = { + isa = PBXGroup; + children = ( + 3D2552482B7A8B3100192879 /* ItemSeriesSeriesView.swift */, + ); + path = Series; + sourceTree = ""; + }; 3D8AB2A62B366309005BD7D0 /* Library */ = { isa = PBXGroup; children = ( @@ -304,6 +319,8 @@ children = ( 3DAFA8EE2B3B707B00D71AD1 /* ItemMovieView.swift */, 3DFE7AF62B5260FF005461FE /* ItemPersonView.swift */, + 3D2552452B7A847700192879 /* ItemSeriesView.swift */, + 3D6A7DB82B7C5D3900A5E31B /* ItemSeasonView.swift */, ); path = Types; sourceTree = ""; @@ -494,11 +511,13 @@ 3D13F9692B389FA300E91913 /* ViewOffsetKey.swift in Sources */, 3D91FDCD2B2907E800919017 /* JellyfinDateFormatter.swift in Sources */, 3D58F07E2B4DB19300DB2936 /* TextRatingView.swift in Sources */, + 3D6A7DB92B7C5D3900A5E31B /* ItemSeasonView.swift in Sources */, 3D91FDC92B28C62800919017 /* SignInView.swift in Sources */, 3DAFA8EF2B3B707B00D71AD1 /* ItemMovieView.swift in Sources */, 3D8AB2A82B366353005BD7D0 /* LibraryDetailView.swift in Sources */, 3DDD67932B293BC40026781E /* DashboardView.swift in Sources */, 3D13F9612B37637500E91913 /* ItemMediaView.swift in Sources */, + 3D2552492B7A8B3100192879 /* ItemSeriesSeriesView.swift in Sources */, 3DFE7AF72B5260FF005461FE /* ItemPersonView.swift in Sources */, 3D41D1FA2B2CAE0000E58234 /* ItemIconView.swift in Sources */, 3D8AB2A52B36440D005BD7D0 /* BlurHashDecode.swift in Sources */, @@ -509,6 +528,7 @@ 3D4C15722B3CAA670035373E /* DashboardSectionTitleView.swift in Sources */, 3D16FC3C2B2CDFB500E6D8B3 /* DashboardLibraryView.swift in Sources */, 3D1015E42B28000E00F5C29A /* AuthStateController.swift in Sources */, + 3D2552462B7A847700192879 /* ItemSeriesView.swift in Sources */, 3D13F95F2B375DB800E91913 /* ItemView.swift in Sources */, 3DDD67962B29E28B0026781E /* SettingsView.swift in Sources */, 3DBAC9E22B4C31BE005F8764 /* ItemPeopleView.swift in Sources */, diff --git a/Jel/Views/Item/ItemIconView.swift b/Jel/Views/Item/ItemIconView.swift index c4e958e..906ed01 100644 --- a/Jel/Views/Item/ItemIconView.swift +++ b/Jel/Views/Item/ItemIconView.swift @@ -18,11 +18,11 @@ struct ItemIconView: View { var width: CGFloat? var height: CGFloat? - @State var blurHashImage: UIImage = UIImage() + @State var blurHashImage: UIImage? = UIImage() @State var imageUrl: URL? @State var contentMode: ContentMode = .fit - var placeHolder: AnyView? = AnyView(Color(uiColor: UIColor.secondarySystemBackground)) + var placeHolder: AnyView? var shouldShowCaption: Bool = false var imageCornerRadius: CGFloat = 5 @@ -37,9 +37,13 @@ struct ItemIconView: View { if let content = placeHolder { content } else { - Image(uiImage: blurHashImage) - .resizable() - .aspectRatio(contentMode: .fill) + if let blurHash = blurHashImage { + Image(uiImage: blurHash) + .resizable() + .aspectRatio(contentMode: .fill) + } else { + Color(uiColor: UIColor.secondarySystemBackground) + } } } } @@ -47,7 +51,7 @@ struct ItemIconView: View { .clipShape(RoundedRectangle(cornerRadius: imageCornerRadius)) .onAppear { let blurhash = getBlurHash(imageType: imageType) - blurHashImage = UIImage(blurHash: blurhash, size: CGSize(width: 32, height: 32)) ?? UIImage() + blurHashImage = UIImage(blurHash: blurhash, size: CGSize(width: 32, height: 32)) let imageId = item.id ?? "" let request = Paths.getItemImage(itemID: imageId, imageType: imageType) diff --git a/Jel/Views/Item/ItemMediaView.swift b/Jel/Views/Item/ItemMediaView.swift index 75fa2e1..efcfb55 100644 --- a/Jel/Views/Item/ItemMediaView.swift +++ b/Jel/Views/Item/ItemMediaView.swift @@ -10,9 +10,6 @@ import JellyfinKit import ExpandableText struct ItemMediaView: View { - @EnvironmentObject var jellyfinClient: JellyfinClientController - @StateObject var authState: AuthStateController = AuthStateController.shared - var item: BaseItemDto diff --git a/Jel/Views/Item/ItemView.swift b/Jel/Views/Item/ItemView.swift index 7611450..f8eba43 100644 --- a/Jel/Views/Item/ItemView.swift +++ b/Jel/Views/Item/ItemView.swift @@ -16,6 +16,10 @@ struct ItemView: View { switch item.type { case .movie: ItemMovieView(item: item) + case .series: + ItemSeriesView(item: item) + case .season: + ItemSeasonView(item: item) case .person: ItemPersonView(item: item) default: diff --git a/Jel/Views/Item/Series/ItemSeriesSeriesView.swift b/Jel/Views/Item/Series/ItemSeriesSeriesView.swift new file mode 100644 index 0000000..1e54185 --- /dev/null +++ b/Jel/Views/Item/Series/ItemSeriesSeriesView.swift @@ -0,0 +1,58 @@ +// +// ItemSeriesSeriesView.swift +// Jel +// +// Created by zerocool on 2/12/24. +// + +import SwiftUI +import JellyfinKit + +struct ItemSeriesSeriesView: View { + var item: BaseItemDto + + @EnvironmentObject var jellyfinClient: JellyfinClientController + @StateObject var authState: AuthStateController = AuthStateController.shared + + @State var seriesItems: [BaseItemDto] = [] + + var body: some View { + VStack(alignment: .leading) { + Text("Series") + .font(.title2) + .padding(.horizontal) + + ScrollView(.horizontal) { + LazyHStack { + ForEach(seriesItems) {series in + NavigationLink { + ItemView(item: series) + } label: { + ItemIconView(item: series, height: 170) + .setAspectRatio(series.primaryImageAspectRatio ?? 0.6) + .showCaption() + } + } + }.padding(.horizontal) + } + } + .onAppear{ + Task { + let parameters = Paths.GetItemsParameters( + userID: authState.userId ?? "", + parentID: item.id ?? "" + ) + let req = Paths.getItems(parameters: parameters) + + do { + let res = try await jellyfinClient.send(req) + seriesItems = res.value.items ?? [] + } catch {} + } + } + } +} + +//#Preview { +// ItemSeriesSeriesView() +//} diff --git a/Jel/Views/Item/Types/ItemPersonView.swift b/Jel/Views/Item/Types/ItemPersonView.swift index 0d93281..d9b991d 100644 --- a/Jel/Views/Item/Types/ItemPersonView.swift +++ b/Jel/Views/Item/Types/ItemPersonView.swift @@ -80,7 +80,6 @@ struct ItemPersonView: View { do { let res = try await jellyfinClient.send(request) items = res.value.items ?? [] - print(items![0]) } catch {} } } diff --git a/Jel/Views/Item/Types/ItemSeasonView.swift b/Jel/Views/Item/Types/ItemSeasonView.swift new file mode 100644 index 0000000..9d02dfe --- /dev/null +++ b/Jel/Views/Item/Types/ItemSeasonView.swift @@ -0,0 +1,23 @@ +// +// ItemSeasonView.swift +// Jel +// +// Created by zerocool on 2/13/24. +// + +import SwiftUI +import JellyfinKit + +struct ItemSeasonView: View { + var item: BaseItemDto + + var body: some View { + VStack { + ItemMediaView(item: item) + } + } +} + +//#Preview { +// ItemSeasonView() +//} diff --git a/Jel/Views/Item/Types/ItemSeriesView.swift b/Jel/Views/Item/Types/ItemSeriesView.swift new file mode 100644 index 0000000..f0559b9 --- /dev/null +++ b/Jel/Views/Item/Types/ItemSeriesView.swift @@ -0,0 +1,58 @@ +// +// ItemSeriesView.swift +// Jel +// +// Created by zerocool on 2/12/24. +// + +import SwiftUI +import JellyfinKit + +struct ItemSeriesView: View { + var item: BaseItemDto + + @State var pageScrolled: Bool = false + + var body: some View { + VStack { + ItemHeaderView(item: item) + .foregroundStyle(.white) + .background { + GeometryReader {geo in + EmptyView() + .onChange(of: geo.frame(in: .global).minY) { + let minY = geo.frame(in: .global).minY + + pageScrolled = minY < -150 + } + } + } + + ItemMediaView(item: item) + .padding() + + ItemGenresView(item: item) + .foregroundStyle(Color.primary) + + ItemSeriesSeriesView(item: item) + .foregroundStyle(Color.primary) + + ItemPeopleView(item: item) + .foregroundStyle(Color.primary) + } + .navigationBarTitleDisplayMode(.inline) + .navigationTitle(item.name ?? "Untitled") + .toolbarRole(.editor) + .toolbar { + ToolbarItem(placement: .principal) { + Text(pageScrolled ? item.name ?? "Untitled" : "") + .bold() + } + } + .toolbarBackground(pageScrolled ? .visible : .hidden) + } +} + +//#Preview { +// ItemShowView() +//}