diff options
author | Shav Kinderlehrer <[email protected]> | 2024-01-09 12:32:06 -0500 |
---|---|---|
committer | Shav Kinderlehrer <[email protected]> | 2024-01-09 12:32:06 -0500 |
commit | 6edc39791a577a500c92f32361cf1e7d2590ec37 (patch) | |
tree | 776de4bae461836f8cba5ff673fc4ccfabd6df55 /Jel | |
parent | 4ec0f962b2a175ae5f1e3e55a720f9534618a4ad (diff) | |
download | jel-6edc39791a577a500c92f32361cf1e7d2590ec37.tar.gz jel-6edc39791a577a500c92f32361cf1e7d2590ec37.zip |
Implement ItemPeopleView
Diffstat (limited to 'Jel')
-rw-r--r-- | Jel/Models/ViewExtensions.swift (renamed from Jel/Models/ViewConditionalMethod.swift) | 18 | ||||
-rw-r--r-- | Jel/Views/Library/Item/ItemGenresView.swift | 8 | ||||
-rw-r--r-- | Jel/Views/Library/Item/ItemHeaderView.swift | 2 | ||||
-rw-r--r-- | Jel/Views/Library/Item/ItemInfoView.swift | 9 | ||||
-rw-r--r-- | Jel/Views/Library/Item/ItemPeopleView.swift | 27 | ||||
-rw-r--r-- | Jel/Views/Library/Item/ItemView.swift | 1 | ||||
-rw-r--r-- | Jel/Views/Library/Item/Person/ItemPeopleView.swift | 37 | ||||
-rw-r--r-- | Jel/Views/Library/Item/Person/ItemPersonIconView.swift | 70 | ||||
-rw-r--r-- | Jel/Views/Library/Item/Types/ItemMovieView.swift | 4 | ||||
-rw-r--r-- | Jel/Views/Library/ItemIconView.swift | 2 | ||||
-rw-r--r-- | Jel/Views/Library/LibraryDetailView.swift | 8 | ||||
-rw-r--r-- | Jel/Views/Utility/TextRatingView.swift | 58 |
12 files changed, 187 insertions, 57 deletions
diff --git a/Jel/Models/ViewConditionalMethod.swift b/Jel/Models/ViewExtensions.swift index 195a8dc..7f54865 100644 --- a/Jel/Models/ViewConditionalMethod.swift +++ b/Jel/Models/ViewExtensions.swift @@ -1,5 +1,5 @@ // -// ViewConditionalMethod.swift +// ViewExtensions.swift // Jel // // Created by zerocool on 12/25/23. @@ -9,10 +9,6 @@ import SwiftUI extension View { /// Applies the given transform if the given condition evaluates to `true`. - /// - Parameters: - /// - condition: The condition to evaluate. - /// - transform: The transform to apply to the source `View`. - /// - Returns: Either the original `View` or the modified `View` if the condition is `true`. @ViewBuilder func `if`<Content: View>(_ condition: @autoclosure () -> Bool, transform: (Self) -> Content) -> some View { if condition() { transform(self) @@ -21,3 +17,15 @@ extension View { } } } + +extension View { + /// Applies an inverse mask to the given view + public func inverseMask<Content: View>(_ mask: Content) -> some View { + let inverseMask = mask + .foregroundStyle(.black) + .background(.white) + .compositingGroup() + .luminanceToAlpha() + return self.mask(inverseMask) + } +} diff --git a/Jel/Views/Library/Item/ItemGenresView.swift b/Jel/Views/Library/Item/ItemGenresView.swift index 5bfccb0..4e8321f 100644 --- a/Jel/Views/Library/Item/ItemGenresView.swift +++ b/Jel/Views/Library/Item/ItemGenresView.swift @@ -20,6 +20,7 @@ struct ItemGenresView: View { VStack(alignment: .leading) { Text("Genres") .font(.title2) + .padding(.horizontal) ScrollView(.horizontal) { HStack { @@ -43,18 +44,15 @@ struct ItemGenresView: View { .clipShape(.capsule) } } + .padding(.horizontal) } + .scrollIndicators(.hidden) } .onAppear { Task { let parameters = Paths.GetItemsParameters( userID: authState.userId ?? "", isRecursive: true, - fields: [.primaryImageAspectRatio, - .genres, - .taglines, - .overview, - .parentID], includeItemTypes: [.movie, .series], genres: item.genres ?? [] ) diff --git a/Jel/Views/Library/Item/ItemHeaderView.swift b/Jel/Views/Library/Item/ItemHeaderView.swift index 44be776..4c2bbe3 100644 --- a/Jel/Views/Library/Item/ItemHeaderView.swift +++ b/Jel/Views/Library/Item/ItemHeaderView.swift @@ -25,7 +25,7 @@ struct ItemHeaderView: View { StickyHeaderView(minHeight: 400) { ItemIconView(item: item, imageType: "Backdrop", contentMode: .fill) .setCornerRadius(0) - .overlay(overlayGradient.opacity(0.8)) + .overlay(overlayGradient) } HStack { diff --git a/Jel/Views/Library/Item/ItemInfoView.swift b/Jel/Views/Library/Item/ItemInfoView.swift index bafbc9f..dda1c39 100644 --- a/Jel/Views/Library/Item/ItemInfoView.swift +++ b/Jel/Views/Library/Item/ItemInfoView.swift @@ -24,14 +24,7 @@ struct ItemInfoView: View { Text(item.getRuntime() ?? "-:--") } if let officialRating = item.officialRating { - Text(officialRating) - .bold() - .padding(2) - .overlay { - RoundedRectangle(cornerSize: CGSize(width: 2, height: 2), style: .continuous) - .stroke(.gray) - } - .foregroundStyle(.gray) + TextRatingView(officialRating, fillStyle: .stroke) } } } diff --git a/Jel/Views/Library/Item/ItemPeopleView.swift b/Jel/Views/Library/Item/ItemPeopleView.swift deleted file mode 100644 index b8b0846..0000000 --- a/Jel/Views/Library/Item/ItemPeopleView.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// ItemPeopleView.swift -// Jel -// -// Created by zerocool on 1/8/24. -// - -import SwiftUI -import JellyfinKit - -struct ItemPeopleView: View { - var item: BaseItemDto - - var body: some View { - ScrollView(.horizontal) { - HStack { - ForEach(item.people ?? []) {person in - Text(person.name ?? "---") - } - } - } - } -} - -//#Preview { -// ItemPeopleView() -//} diff --git a/Jel/Views/Library/Item/ItemView.swift b/Jel/Views/Library/Item/ItemView.swift index de4dc1b..da85f32 100644 --- a/Jel/Views/Library/Item/ItemView.swift +++ b/Jel/Views/Library/Item/ItemView.swift @@ -21,6 +21,7 @@ struct ItemView: View { } } } + .scrollIndicators(.hidden) } } diff --git a/Jel/Views/Library/Item/Person/ItemPeopleView.swift b/Jel/Views/Library/Item/Person/ItemPeopleView.swift new file mode 100644 index 0000000..6e2a974 --- /dev/null +++ b/Jel/Views/Library/Item/Person/ItemPeopleView.swift @@ -0,0 +1,37 @@ +// +// ItemPeopleView.swift +// Jel +// +// Created by zerocool on 1/8/24. +// + +import SwiftUI +import JellyfinKit +import NukeUI + +struct ItemPeopleView: View { + + var item: BaseItemDto + + var body: some View { + VStack(alignment: .leading) { + Text("Cast and Crew") + .font(.title2) + .padding(.leading) + + ScrollView(.horizontal) { + LazyHStack(alignment: .top) { + ForEach(item.people ?? [], id: \.iterId) {person in + ItemPersonIconView(person: person) + } + } + .padding(.horizontal) + } + .scrollIndicators(.hidden) + } + } +} + +//#Preview { +// ItemPeopleView() +//} diff --git a/Jel/Views/Library/Item/Person/ItemPersonIconView.swift b/Jel/Views/Library/Item/Person/ItemPersonIconView.swift new file mode 100644 index 0000000..a6e5161 --- /dev/null +++ b/Jel/Views/Library/Item/Person/ItemPersonIconView.swift @@ -0,0 +1,70 @@ +// +// ItemPersonIconView.swift +// Jel +// +// Created by zerocool on 1/8/24. +// + +import SwiftUI +import JellyfinKit +import NukeUI + +struct ItemPersonIconPlaceholderView: View { + var body: some View { + VStack { + Image(systemName: "person") + .resizable() + .padding() + .scaledToFit() + } + } +} + +struct ItemPersonIconView: View { + @StateObject var authState: AuthStateController = AuthStateController.shared + @EnvironmentObject var jellyfinClient: JellyfinClientController + + var person: BaseItemPerson + + @State var personImageUrl: URL? + @State var loading: Bool = true + + var body: some View { + VStack() { + LazyImage(url: personImageUrl) {state in + if let image = state.image { + image + .resizable() + .clipShape(RoundedRectangle(cornerRadius: 5)) + } else { + ItemPersonIconPlaceholderView() + } + } + .aspectRatio(contentMode: .fit) + .frame(width: 100, height: 170) + + VStack { + Text(person.name ?? "---") + .font(.callout) + Text(person.role ?? "---") + .font(.caption) + .foregroundStyle(.gray) + } + .frame(width: 100) + } + // .redacted(reason: loading ? .placeholder : []) + .onAppear { + Task { + let request = Paths.getItemImage(itemID: person.id ?? "", imageType: "Primary") + + let serverUrl = jellyfinClient.getUrl() + personImageUrl = serverUrl?.appending(path: request.url?.absoluteString ?? "") + // loading = false + } + } + } +} + +//#Preview { +// ItemPersonView() +//} diff --git a/Jel/Views/Library/Item/Types/ItemMovieView.swift b/Jel/Views/Library/Item/Types/ItemMovieView.swift index fcf8c06..5181e73 100644 --- a/Jel/Views/Library/Item/Types/ItemMovieView.swift +++ b/Jel/Views/Library/Item/Types/ItemMovieView.swift @@ -23,7 +23,7 @@ struct ItemMovieView: View { .onChange(of: geo.frame(in: .global).minY) { let minY = geo.frame(in: .global).minY - pageScrolled = minY < 0 + pageScrolled = minY < -100 } } } @@ -32,10 +32,8 @@ struct ItemMovieView: View { .padding() ItemGenresView(item: item) - .padding() ItemPeopleView(item: item) - .padding() } .navigationBarTitleDisplayMode(.inline) .navigationTitle(item.name ?? "Untitled") diff --git a/Jel/Views/Library/ItemIconView.swift b/Jel/Views/Library/ItemIconView.swift index ec3952c..c2006cc 100644 --- a/Jel/Views/Library/ItemIconView.swift +++ b/Jel/Views/Library/ItemIconView.swift @@ -22,7 +22,7 @@ struct ItemIconView: View { @State var imageUrl: URL? @State var contentMode: ContentMode = .fit - @State var placeHolder: AnyView? + var placeHolder: AnyView? var shouldShowCaption: Bool = false var imageCornerRadius: CGFloat = 5 diff --git a/Jel/Views/Library/LibraryDetailView.swift b/Jel/Views/Library/LibraryDetailView.swift index c85651d..5f0d9a7 100644 --- a/Jel/Views/Library/LibraryDetailView.swift +++ b/Jel/Views/Library/LibraryDetailView.swift @@ -83,13 +83,7 @@ struct LibraryDetailView: View { Task { let params = Paths.GetItemsParameters( userID: authState.userId, - parentID: library.id, - fields: [.primaryImageAspectRatio, - .genres, - .taglines, - .overview, - .parentID, - .people] + parentID: library.id ) let request = Paths.getItems(parameters: params) diff --git a/Jel/Views/Utility/TextRatingView.swift b/Jel/Views/Utility/TextRatingView.swift new file mode 100644 index 0000000..760d251 --- /dev/null +++ b/Jel/Views/Utility/TextRatingView.swift @@ -0,0 +1,58 @@ +// +// TextRatingView.swift +// Jel +// +// Created by zerocool on 1/9/24. +// + +import SwiftUI + +enum TextRatingViewStyle { + case stroke + case fill +} + +struct TextRatingView: View { + var text: String + var fillStyle: TextRatingViewStyle + + init(_ text: String, fillStyle: TextRatingViewStyle = .stroke) { + self.text = text + self.fillStyle = fillStyle + } + + var body: some View { + switch (fillStyle) { + case .stroke: + Text(text) + .font(.caption) + .bold() + .padding(EdgeInsets(top: 1, leading: 4, bottom: 1, trailing: 4)) + .overlay { + RoundedRectangle(cornerRadius: 2, style: .continuous) + .stroke(.gray, lineWidth: 1.5) + } + .foregroundStyle(.gray) + case .fill: + Text(text) + .font(.caption) + .bold() + .padding(EdgeInsets(top: 1, leading: 4, bottom: 1, trailing: 4)) + .hidden() + .background { + Color(.gray) + .clipShape(RoundedRectangle(cornerRadius: 2, style: .continuous)) + .inverseMask( + Text(text) + .font(.caption) + .bold() + .padding(EdgeInsets(top: 1, leading: 4, bottom: 1, trailing: 4)) + ) + } + } + } +} + +//#Preview { +// TextRatingView() +//} |