diff --git a/Jel.xcodeproj/project.pbxproj b/Jel.xcodeproj/project.pbxproj index 9a1e3cd..d427a34 100644 --- a/Jel.xcodeproj/project.pbxproj +++ b/Jel.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 3D13F95C2B375A9E00E91913 /* NukeUI in Frameworks */ = {isa = PBXBuildFile; productRef = 3D13F95B2B375A9E00E91913 /* NukeUI */; }; 3D13F95F2B375DB800E91913 /* ItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D13F95E2B375DB800E91913 /* ItemView.swift */; }; 3D13F9612B37637500E91913 /* ItemMovieView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D13F9602B37637500E91913 /* ItemMovieView.swift */; }; + 3D13F9652B37EC7A00E91913 /* ItemHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D13F9642B37EC7A00E91913 /* ItemHeaderView.swift */; }; 3D16FC3C2B2CDFB500E6D8B3 /* DashboardLibraryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D16FC3B2B2CDFB500E6D8B3 /* DashboardLibraryView.swift */; }; 3D41D1F52B2C962500E58234 /* AppearancePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D41D1F42B2C962500E58234 /* AppearancePicker.swift */; }; 3D41D1FA2B2CAE0000E58234 /* LibraryIconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D41D1F92B2CAE0000E58234 /* LibraryIconView.swift */; }; @@ -73,6 +74,7 @@ 3D1015E32B28000E00F5C29A /* AuthStateController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthStateController.swift; sourceTree = ""; }; 3D13F95E2B375DB800E91913 /* ItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemView.swift; sourceTree = ""; }; 3D13F9602B37637500E91913 /* ItemMovieView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemMovieView.swift; sourceTree = ""; }; + 3D13F9642B37EC7A00E91913 /* ItemHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemHeaderView.swift; sourceTree = ""; }; 3D16FC3B2B2CDFB500E6D8B3 /* DashboardLibraryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardLibraryView.swift; sourceTree = ""; }; 3D41D1F42B2C962500E58234 /* AppearancePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearancePicker.swift; sourceTree = ""; }; 3D41D1F92B2CAE0000E58234 /* LibraryIconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryIconView.swift; sourceTree = ""; }; @@ -165,6 +167,7 @@ children = ( 3D13F95E2B375DB800E91913 /* ItemView.swift */, 3D13F9602B37637500E91913 /* ItemMovieView.swift */, + 3D13F9642B37EC7A00E91913 /* ItemHeaderView.swift */, ); path = Item; sourceTree = ""; @@ -413,6 +416,7 @@ 3D9063CD2B279A310063DD2A /* ContentView.swift in Sources */, 3DF1ED3E2B282836000AD8EA /* JellyfinClientController.swift in Sources */, 3D1015D92B27F57400F5C29A /* AddServerView.swift in Sources */, + 3D13F9652B37EC7A00E91913 /* ItemHeaderView.swift in Sources */, 3D9063CB2B279A310063DD2A /* JelApp.swift in Sources */, 3D91FDCD2B2907E800919017 /* JellyfinDateFormatter.swift in Sources */, 3D91FDC92B28C62800919017 /* SignInView.swift in Sources */, diff --git a/Jel/Views/Library/Item/ItemHeaderView.swift b/Jel/Views/Library/Item/ItemHeaderView.swift new file mode 100644 index 0000000..1ff25c7 --- /dev/null +++ b/Jel/Views/Library/Item/ItemHeaderView.swift @@ -0,0 +1,49 @@ +// +// ItemHeaderView.swift +// Jel +// +// Created by zerocool on 12/23/23. +// + +import SwiftUI +import JellyfinKit + +struct ItemHeaderView: View { + @State var item: BaseItemDto + + let overlayGradient = LinearGradient(gradient: Gradient(stops: [ + .init(color: .clear, location: 0), + .init(color: .black, location: 0.3), + .init(color: .black, location: 0.7), + .init(color: .clear, location: 1) + ]), startPoint: .bottom, endPoint: .top) + + var body: some View { + ZStack(alignment: .bottom) { + LibraryIconView(library: item, imageType: "Backdrop", contentMode: .fill) + .hideCaption() + .setCornerRadius(0) + .mask(overlayGradient) + .padding(.top, 50) + .background { + LibraryIconView(library: item, imageType: "Backdrop", contentMode: .fill) + .hideCaption() + .setCornerRadius(0) + .blur(radius: 50) + } + + HStack { + LibraryIconView(library: item, imageType: "Logo", width: 150) + .hideCaption() + .setCornerRadius(0) + .shadow(radius: 10) + Spacer() + } + .padding(.leading) + } + } +} + +#Preview { + ItemHeaderView(item: BaseItemDto()) +} diff --git a/Jel/Views/Library/Item/ItemMovieView.swift b/Jel/Views/Library/Item/ItemMovieView.swift index 6a29654..9c95923 100644 --- a/Jel/Views/Library/Item/ItemMovieView.swift +++ b/Jel/Views/Library/Item/ItemMovieView.swift @@ -14,18 +14,23 @@ struct ItemMovieView: View { @State var item: BaseItemDto @State var loading: Bool = true + var body: some View { - VStack { - Text(item.name ?? "Unknown") - .font(.title) + ScrollView { + ItemHeaderView(item: item) Text(item.taglines?[0] ?? "Unknown") .font(.headline) + .padding(.top, 20) + Text(item.overview ?? "Unknown") + .padding() } + .redacted(reason: loading ? .placeholder : []) + .ignoresSafeArea(edges: .top) + .toolbarRole(.editor) .navigationTitle(item.name ?? "Unknown") .navigationBarTitleDisplayMode(.inline) - .redacted(reason: loading ? .placeholder : []) .onAppear { Task { do { diff --git a/Jel/Views/Library/LibraryIconView.swift b/Jel/Views/Library/LibraryIconView.swift index 71c6e14..7651e56 100644 --- a/Jel/Views/Library/LibraryIconView.swift +++ b/Jel/Views/Library/LibraryIconView.swift @@ -20,6 +20,10 @@ struct LibraryIconView: View { @State var blurHashImage: UIImage = UIImage() @State var imageUrl: URL? + @State var contentMode: ContentMode = .fit + + var shouldShowCaption: Bool = true + var imageCornerRadius: CGFloat = 5 var body: some View { VStack { LazyImage(url: imageUrl) {state in @@ -33,22 +37,35 @@ struct LibraryIconView: View { .resizable() } } - .aspectRatio(contentMode: .fit) + .aspectRatio(contentMode: contentMode) .frame(width: width, height: height) - .clipShape(RoundedRectangle(cornerRadius: 5)) + .clipShape(RoundedRectangle(cornerRadius: imageCornerRadius)) + .onAppear { + let blurhash = library.imageBlurHashes?.primary?[library.imageTags?[imageType] ?? ""] ?? "" + blurHashImage = UIImage(blurHash: blurhash, size: CGSize(width: 16, height: 16)) ?? UIImage() + + let imageId = library.id ?? "" + let request = Paths.getItemImage(itemID: imageId, imageType: imageType) + imageUrl = jellyfinClient.getUrl()?.appending(path: request.url?.absoluteString ?? "") + } - Text(library.name ?? "Unknown") - .font(.subheadline) - .onAppear { - let blurhash = library.imageBlurHashes?.primary?[library.imageTags?[imageType] ?? ""] ?? "" - blurHashImage = UIImage(blurHash: blurhash, size: CGSize(width: 16, height: 16)) ?? UIImage() - - let imageId = library.id ?? "" - let request = Paths.getItemImage(itemID: imageId, imageType: imageType) - imageUrl = jellyfinClient.getUrl()?.appending(path: request.url?.absoluteString ?? "") - } + if shouldShowCaption { + Text(library.name ?? "Unknown") + .font(.subheadline) + } } } + func hideCaption(_ isHidden: Bool = true) -> Self { + var copy = self + copy.shouldShowCaption = !isHidden + return copy + } + + func setCornerRadius(_ cornerRadius: CGFloat = 5) -> Self { + var copy = self + copy.imageCornerRadius = cornerRadius + return copy + } } //#Preview {