new SeriesItemView UI (portrait)
This commit is contained in:
parent
88389d1044
commit
3fa97235b1
|
@ -9,28 +9,192 @@ import SwiftUI
|
||||||
|
|
||||||
struct SeriesItemView: View {
|
struct SeriesItemView: View {
|
||||||
@StateObject var viewModel: SeriesItemViewModel
|
@StateObject var viewModel: SeriesItemViewModel
|
||||||
|
@State private var orientation = UIDeviceOrientation.unknown
|
||||||
|
@Environment(\.horizontalSizeClass) var hSizeClass
|
||||||
|
@Environment(\.verticalSizeClass) var vSizeClass
|
||||||
|
|
||||||
@State private var tracks: [GridItem] = Array(repeating: .init(.flexible()), count: Int(UIScreen.main.bounds.size.width) / 125)
|
@State private var tracks: [GridItem] = Array(repeating: .init(.flexible()), count: Int(UIScreen.main.bounds.size.width) / 125)
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
var portraitHeaderView: some View {
|
||||||
|
ImageView(src: viewModel.item
|
||||||
|
.getBackdropImage(maxWidth: UIDevice.current.userInterfaceIdiom == .pad ? 622 : Int(UIScreen.main.bounds.width)),
|
||||||
|
bh: viewModel.item.getBackdropImageBlurHash())
|
||||||
|
.opacity(0.4)
|
||||||
|
.blur(radius: 2.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
var portraitHeaderOverlayView: some View {
|
||||||
|
HStack(alignment: .bottom, spacing: 12) {
|
||||||
|
ImageView(src: viewModel.item.getPrimaryImage(maxWidth: 120), bh: viewModel.item.getPrimaryImageBlurHash())
|
||||||
|
.frame(width: 120, height: 180)
|
||||||
|
.cornerRadius(10)
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text(viewModel.item.name ?? "").font(.headline)
|
||||||
|
.fontWeight(.semibold)
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
|
.offset(y: -4)
|
||||||
|
HStack {
|
||||||
|
Text(String(viewModel.item.productionYear ?? 0)).font(.subheadline)
|
||||||
|
.fontWeight(.medium)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
.lineLimit(1)
|
||||||
|
if viewModel.item.officialRating != nil {
|
||||||
|
Text(viewModel.item.officialRating!).font(.subheadline)
|
||||||
|
.fontWeight(.semibold)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
.lineLimit(1)
|
||||||
|
.padding(EdgeInsets(top: 1, leading: 4, bottom: 1, trailing: 4))
|
||||||
|
.overlay(RoundedRectangle(cornerRadius: 2)
|
||||||
|
.stroke(Color.secondary, lineWidth: 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.offset(y: -32)
|
||||||
|
}.padding(.horizontal, 16)
|
||||||
|
.offset(y: 22)
|
||||||
|
}
|
||||||
|
|
||||||
func recalcTracks() {
|
func recalcTracks() {
|
||||||
tracks = Array(repeating: .init(.flexible()), count: Int(UIScreen.main.bounds.size.width) / 125)
|
tracks = Array(repeating: .init(.flexible()), count: Int(UIScreen.main.bounds.size.width) / 125)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var innerBody: some View {
|
||||||
|
ScrollView {
|
||||||
|
LazyVStack(alignment: .leading, spacing: 0) {
|
||||||
|
if !(viewModel.item.taglines ?? []).isEmpty {
|
||||||
|
Text(viewModel.item.taglines!.first!).font(.body).italic()
|
||||||
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
|
.padding(.bottom, 8)
|
||||||
|
}
|
||||||
|
if !(viewModel.item.genreItems ?? []).isEmpty {
|
||||||
|
ScrollView(.horizontal, showsIndicators: false) {
|
||||||
|
LazyHStack(spacing: 8) {
|
||||||
|
Text("Genres:").font(.callout).fontWeight(.semibold)
|
||||||
|
ForEach(viewModel.item.genreItems!, id: \.id) { genre in
|
||||||
|
NavigationLink(destination: LazyView {
|
||||||
|
LibraryView(viewModel: .init(genre: genre), title: genre.name ?? "")
|
||||||
|
}) {
|
||||||
|
Text(genre.name ?? "").font(.footnote)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.bottom, 8)
|
||||||
|
}
|
||||||
|
Text(viewModel.item.overview ?? "")
|
||||||
|
.font(.footnote)
|
||||||
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
|
.padding(.bottom, 16)
|
||||||
|
Text("Seasons")
|
||||||
|
.font(.callout).fontWeight(.semibold)
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 16)
|
||||||
|
.padding(.top, 24)
|
||||||
|
LazyVGrid(columns: tracks) {
|
||||||
|
ForEach(viewModel.seasons, id: \.id) { season in
|
||||||
|
PortraitItemView(item: season)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.bottom, 16)
|
||||||
|
LazyVStack(alignment: .leading, spacing: 0) {
|
||||||
|
if !(viewModel.item.people ?? []).isEmpty {
|
||||||
|
Text("CAST")
|
||||||
|
.font(.callout).fontWeight(.semibold)
|
||||||
|
.padding(.bottom, 8)
|
||||||
|
ScrollView(.horizontal, showsIndicators: false) {
|
||||||
|
LazyHStack(spacing: 16) {
|
||||||
|
ForEach(viewModel.item.people!, id: \.self) { person in
|
||||||
|
if person.type! == "Actor" {
|
||||||
|
NavigationLink(destination: LazyView {
|
||||||
|
LibraryView(viewModel: .init(person: person), title: person.name ?? "")
|
||||||
|
}) {
|
||||||
|
VStack {
|
||||||
|
ImageView(src: person
|
||||||
|
.getImage(baseURL: ServerEnvironment.current.server.baseURI!, maxWidth: 100),
|
||||||
|
bh: person.getBlurHash())
|
||||||
|
.frame(width: 100, height: 100)
|
||||||
|
.cornerRadius(10)
|
||||||
|
Text(person.name ?? "").font(.footnote).fontWeight(.regular).lineLimit(1)
|
||||||
|
.frame(width: 100).foregroundColor(Color.primary)
|
||||||
|
if person.role != "" {
|
||||||
|
Text(person.role!).font(.caption).fontWeight(.medium).lineLimit(1)
|
||||||
|
.foregroundColor(Color.secondary).frame(width: 100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.bottom, 16)
|
||||||
|
}
|
||||||
|
if !(viewModel.item.studios ?? []).isEmpty {
|
||||||
|
ScrollView(.horizontal, showsIndicators: false) {
|
||||||
|
LazyHStack(spacing: 16) {
|
||||||
|
Text("Studios:").font(.callout).fontWeight(.semibold)
|
||||||
|
ForEach(viewModel.item.studios!, id: \.id) { studio in
|
||||||
|
NavigationLink(destination: LazyView {
|
||||||
|
LibraryView(viewModel: .init(studio: studio), title: studio.name ?? "")
|
||||||
|
}) {
|
||||||
|
Text(studio.name ?? "").font(.footnote)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.bottom, 16)
|
||||||
|
}
|
||||||
|
if !viewModel.similarItems.isEmpty {
|
||||||
|
Text("More Like This")
|
||||||
|
.font(.callout).fontWeight(.semibold)
|
||||||
|
.padding(.bottom, 8)
|
||||||
|
ScrollView(.horizontal, showsIndicators: false) {
|
||||||
|
LazyHStack(spacing: 16) {
|
||||||
|
ForEach(viewModel.similarItems, id: \.self) { similarItem in
|
||||||
|
NavigationLink(destination: LazyView { ItemView(item: similarItem) }) {
|
||||||
|
PortraitItemView(item: similarItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.bottom, 16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
if viewModel.isLoading {
|
if viewModel.isLoading {
|
||||||
ProgressView()
|
ProgressView()
|
||||||
} else {
|
} else {
|
||||||
ScrollView(.vertical) {
|
Group {
|
||||||
Spacer().frame(height: 16)
|
if hSizeClass == .compact && vSizeClass == .regular {
|
||||||
LazyVGrid(columns: tracks) {
|
ParallaxHeaderScrollView(header: portraitHeaderView,
|
||||||
ForEach(viewModel.seasons, id: \.id) { season in
|
staticOverlayView: portraitHeaderOverlayView,
|
||||||
PortraitItemView(item: season)
|
overlayAlignment: .bottomLeading,
|
||||||
|
headerHeight: UIScreen.main.bounds.width * 0.5625) {
|
||||||
|
innerBody
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
GeometryReader { geometry in
|
||||||
|
ZStack {
|
||||||
|
ImageView(src: viewModel.item.getSeriesBackdropImage(maxWidth: 200),
|
||||||
|
bh: viewModel.item.getSeriesBackdropImageBlurHash())
|
||||||
|
.opacity(0.4)
|
||||||
|
.frame(width: geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing,
|
||||||
|
height: geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom)
|
||||||
|
.edgesIgnoringSafeArea(.all)
|
||||||
|
.blur(radius: 4)
|
||||||
|
innerBody
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Spacer().frame(height: 2)
|
|
||||||
}.onRotate { _ in
|
|
||||||
recalcTracks()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.onRotate {
|
||||||
|
orientation = $0
|
||||||
|
recalcTracks()
|
||||||
|
}
|
||||||
.overrideViewPreference(.unspecified)
|
.overrideViewPreference(.unspecified)
|
||||||
.navigationTitle(viewModel.item.name ?? "")
|
.navigationTitle(viewModel.item.name ?? "")
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
|
Loading…
Reference in New Issue