Some work
This commit is contained in:
parent
f9114ae6be
commit
b349258086
|
|
@ -993,8 +993,8 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
535BAE9E2649E569005FA86D /* ItemView.swift */,
|
535BAE9E2649E569005FA86D /* ItemView.swift */,
|
||||||
E14F7D0626DB36EF007C3AE6 /* ItemPortraitBodyView.swift */,
|
|
||||||
E14F7D0826DB36F7007C3AE6 /* ItemLandscapeBodyView.swift */,
|
E14F7D0826DB36F7007C3AE6 /* ItemLandscapeBodyView.swift */,
|
||||||
|
E14F7D0626DB36EF007C3AE6 /* ItemPortraitBodyView.swift */,
|
||||||
E1AD106126D9B7CD003E4A08 /* ItemPortraitHeaderOverlayView.swift */,
|
E1AD106126D9B7CD003E4A08 /* ItemPortraitHeaderOverlayView.swift */,
|
||||||
);
|
);
|
||||||
path = ItemView;
|
path = ItemView;
|
||||||
|
|
|
||||||
|
|
@ -12,16 +12,16 @@ import JellyfinAPI
|
||||||
|
|
||||||
struct ItemPortraitBodyView<PortraitHeaderView: View, PortraitStaticOverlayView: View>: View {
|
struct ItemPortraitBodyView<PortraitHeaderView: View, PortraitStaticOverlayView: View>: View {
|
||||||
|
|
||||||
@Binding var videoIsLoading: Bool
|
@Binding private var videoIsLoading: Bool
|
||||||
@EnvironmentObject var viewModel: MovieItemViewModel
|
@EnvironmentObject private var viewModel: DetailItemViewModel
|
||||||
@EnvironmentObject var videoPlayerItem: VideoPlayerItem
|
@EnvironmentObject private var videoPlayerItem: VideoPlayerItem
|
||||||
|
|
||||||
private let item: BaseItemDto
|
private let portraitHeaderView: (DetailItemViewModel) -> PortraitHeaderView
|
||||||
private let portraitHeaderView: (BaseItemDto) -> PortraitHeaderView
|
private let portraitStaticOverlayView: (DetailItemViewModel) -> PortraitStaticOverlayView
|
||||||
private let portraitStaticOverlayView: (BaseItemDto) -> PortraitStaticOverlayView
|
|
||||||
|
|
||||||
init(item: BaseItemDto, videoIsLoading: Binding<Bool>, portraitHeaderView: @escaping (BaseItemDto) -> PortraitHeaderView, portraitStaticOverlayView: @escaping (BaseItemDto) -> PortraitStaticOverlayView) {
|
init(videoIsLoading: Binding<Bool>,
|
||||||
self.item = item
|
portraitHeaderView: @escaping (DetailItemViewModel) -> PortraitHeaderView,
|
||||||
|
portraitStaticOverlayView: @escaping (DetailItemViewModel) -> PortraitStaticOverlayView) {
|
||||||
self._videoIsLoading = videoIsLoading
|
self._videoIsLoading = videoIsLoading
|
||||||
self.portraitHeaderView = portraitHeaderView
|
self.portraitHeaderView = portraitHeaderView
|
||||||
self.portraitStaticOverlayView = portraitStaticOverlayView
|
self.portraitStaticOverlayView = portraitStaticOverlayView
|
||||||
|
|
@ -43,8 +43,8 @@ struct ItemPortraitBodyView<PortraitHeaderView: View, PortraitStaticOverlayView:
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Body
|
// MARK: Body
|
||||||
ParallaxHeaderScrollView(header: portraitHeaderView(item),
|
ParallaxHeaderScrollView(header: portraitHeaderView(viewModel),
|
||||||
staticOverlayView: portraitStaticOverlayView(item),
|
staticOverlayView: portraitStaticOverlayView(viewModel),
|
||||||
overlayAlignment: .bottomLeading,
|
overlayAlignment: .bottomLeading,
|
||||||
headerHeight: UIDevice.current.userInterfaceIdiom == .pad ? 350 : UIScreen.main.bounds.width * 0.5625) {
|
headerHeight: UIDevice.current.userInterfaceIdiom == .pad ? 350 : UIScreen.main.bounds.width * 0.5625) {
|
||||||
VStack {
|
VStack {
|
||||||
|
|
@ -53,7 +53,7 @@ struct ItemPortraitBodyView<PortraitHeaderView: View, PortraitStaticOverlayView:
|
||||||
.padding(.bottom, UIDevice.current.userInterfaceIdiom == .pad ? 54 : 24)
|
.padding(.bottom, UIDevice.current.userInterfaceIdiom == .pad ? 54 : 24)
|
||||||
|
|
||||||
// MARK: Overview
|
// MARK: Overview
|
||||||
Text(item.overview ?? "")
|
Text(viewModel.item.overview ?? "")
|
||||||
.font(.footnote)
|
.font(.footnote)
|
||||||
.padding(.top, 3)
|
.padding(.top, 3)
|
||||||
.fixedSize(horizontal: false, vertical: true)
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
|
|
@ -63,12 +63,12 @@ struct ItemPortraitBodyView<PortraitHeaderView: View, PortraitStaticOverlayView:
|
||||||
|
|
||||||
// MARK: Genres
|
// MARK: Genres
|
||||||
PillHStackView(title: "Genres",
|
PillHStackView(title: "Genres",
|
||||||
items: item.genreItems ?? []) { genre in
|
items: viewModel.item.genreItems ?? []) { genre in
|
||||||
LibraryView(viewModel: .init(genre: genre), title: genre.title)
|
LibraryView(viewModel: .init(genre: genre), title: genre.title)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Studios
|
// MARK: Studios
|
||||||
if let studios = item.studios {
|
if let studios = viewModel.item.studios {
|
||||||
PillHStackView(title: "Studios",
|
PillHStackView(title: "Studios",
|
||||||
items: studios) { studio in
|
items: studios) { studio in
|
||||||
LibraryView(viewModel: .init(studio: studio), title: studio.name ?? "")
|
LibraryView(viewModel: .init(studio: studio), title: studio.name ?? "")
|
||||||
|
|
@ -76,7 +76,7 @@ struct ItemPortraitBodyView<PortraitHeaderView: View, PortraitStaticOverlayView:
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Cast
|
// MARK: Cast
|
||||||
PortraitImageHStackView(items: item.people?.filter({ $0.type == "Actor" }) ?? [],
|
PortraitImageHStackView(items: viewModel.item.people?.filter({ $0.type == "Actor" }) ?? [],
|
||||||
maxWidth: 150) {
|
maxWidth: 150) {
|
||||||
Text("Cast")
|
Text("Cast")
|
||||||
.font(.callout)
|
.font(.callout)
|
||||||
|
|
|
||||||
|
|
@ -15,26 +15,24 @@ struct PortraitHeaderOverlayView: View {
|
||||||
@EnvironmentObject private var viewModel: DetailItemViewModel
|
@EnvironmentObject private var viewModel: DetailItemViewModel
|
||||||
@EnvironmentObject private var videoPlayerItem: VideoPlayerItem
|
@EnvironmentObject private var videoPlayerItem: VideoPlayerItem
|
||||||
|
|
||||||
let item: BaseItemDto
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
HStack(alignment: .bottom, spacing: 12) {
|
HStack(alignment: .bottom, spacing: 12) {
|
||||||
ImageView(src: item.portraitHeaderViewURL(maxWidth: 130))
|
ImageView(src: viewModel.item.portraitHeaderViewURL(maxWidth: 130))
|
||||||
.frame(width: 130, height: 195)
|
.frame(width: 130, height: 195)
|
||||||
.cornerRadius(10)
|
.cornerRadius(10)
|
||||||
|
|
||||||
VStack(alignment: .leading, spacing: 1) {
|
VStack(alignment: .leading, spacing: 1) {
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Text(item.name ?? "")
|
Text(viewModel.item.name ?? "")
|
||||||
.font(.headline)
|
.font(.headline)
|
||||||
.fontWeight(.semibold)
|
.fontWeight(.semibold)
|
||||||
.foregroundColor(.primary)
|
.foregroundColor(.primary)
|
||||||
.fixedSize(horizontal: false, vertical: true)
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
.offset(y: 5)
|
.offset(y: 5)
|
||||||
|
|
||||||
Text(item.getItemRuntime())
|
Text(viewModel.item.getItemRuntime())
|
||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
.fontWeight(.medium)
|
.fontWeight(.medium)
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
|
|
@ -42,7 +40,7 @@ struct PortraitHeaderOverlayView: View {
|
||||||
.padding(.top, 10)
|
.padding(.top, 10)
|
||||||
|
|
||||||
HStack {
|
HStack {
|
||||||
if let productionYear = item.productionYear {
|
if let productionYear = viewModel.item.productionYear {
|
||||||
Text(String(productionYear))
|
Text(String(productionYear))
|
||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
.fontWeight(.medium)
|
.fontWeight(.medium)
|
||||||
|
|
@ -50,7 +48,7 @@ struct PortraitHeaderOverlayView: View {
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let officialRating = item.officialRating {
|
if let officialRating = viewModel.item.officialRating {
|
||||||
Text(officialRating)
|
Text(officialRating)
|
||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
.fontWeight(.semibold)
|
.fontWeight(.semibold)
|
||||||
|
|
@ -65,18 +63,19 @@ struct PortraitHeaderOverlayView: View {
|
||||||
.padding(.bottom, UIDevice.current.userInterfaceIdiom == .pad ? 98 : 30)
|
.padding(.bottom, UIDevice.current.userInterfaceIdiom == .pad ? 98 : 30)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if viewModel.item.itemType != .series {
|
||||||
HStack {
|
HStack {
|
||||||
|
|
||||||
// MARK: Play
|
// MARK: Play
|
||||||
Button {
|
Button {
|
||||||
self.videoPlayerItem.itemToPlay = item
|
self.videoPlayerItem.itemToPlay = viewModel.item
|
||||||
self.videoPlayerItem.shouldShowPlayer = true
|
self.videoPlayerItem.shouldShowPlayer = true
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
Image(systemName: "play.fill")
|
Image(systemName: "play.fill")
|
||||||
.foregroundColor(Color.white)
|
.foregroundColor(Color.white)
|
||||||
.font(.system(size: 20))
|
.font(.system(size: 20))
|
||||||
Text(item.getItemProgressString() == "" ? "Play" : item.getItemProgressString())
|
Text(viewModel.item.getItemProgressString() == "" ? "Play" : viewModel.item.getItemProgressString())
|
||||||
.foregroundColor(Color.white)
|
.foregroundColor(Color.white)
|
||||||
.font(.callout)
|
.font(.callout)
|
||||||
.fontWeight(.semibold)
|
.fontWeight(.semibold)
|
||||||
|
|
@ -121,6 +120,7 @@ struct PortraitHeaderOverlayView: View {
|
||||||
.disabled(viewModel.isLoading)
|
.disabled(viewModel.isLoading)
|
||||||
}.padding(.top, 8)
|
}.padding(.top, 8)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.padding(.horizontal, 16)
|
.padding(.horizontal, 16)
|
||||||
.padding(.bottom, UIDevice.current.userInterfaceIdiom == .pad ? -189 : -64)
|
.padding(.bottom, UIDevice.current.userInterfaceIdiom == .pad ? -189 : -64)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,26 +23,27 @@ struct ItemView: View {
|
||||||
@Environment(\.horizontalSizeClass) var hSizeClass
|
@Environment(\.horizontalSizeClass) var hSizeClass
|
||||||
@Environment(\.verticalSizeClass) var vSizeClass
|
@Environment(\.verticalSizeClass) var vSizeClass
|
||||||
|
|
||||||
private let item: BaseItemDto
|
private let viewModel: DetailItemViewModel
|
||||||
|
|
||||||
init(item: BaseItemDto) {
|
init(item: BaseItemDto) {
|
||||||
self.item = item
|
self.viewModel = DetailItemViewModel(item: item)
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
if hSizeClass == .compact && vSizeClass == .regular {
|
if hSizeClass == .compact && vSizeClass == .regular {
|
||||||
ItemPortraitBodyView(item: item,
|
ItemPortraitBodyView(videoIsLoading: $videoIsLoading,
|
||||||
videoIsLoading: $videoIsLoading,
|
portraitHeaderView: { viewModel in
|
||||||
portraitHeaderView: { item in
|
ImageView(src: viewModel.item.getBackdropImage(maxWidth: UIDevice.current.userInterfaceIdiom == .pad ? 622 : Int(UIScreen.main.bounds.width)),
|
||||||
ImageView(src: item.getBackdropImage(maxWidth: UIDevice.current.userInterfaceIdiom == .pad ? 622 : Int(UIScreen.main.bounds.width)),
|
bh: viewModel.item.getBackdropImageBlurHash())
|
||||||
bh: item.getBackdropImageBlurHash())
|
|
||||||
.opacity(0.4)
|
.opacity(0.4)
|
||||||
.blur(radius: 2.0)
|
.blur(radius: 2.0)
|
||||||
},
|
},
|
||||||
portraitStaticOverlayView: { item in
|
portraitStaticOverlayView: { viewModel in
|
||||||
PortraitHeaderOverlayView(item: item)
|
PortraitHeaderOverlayView()
|
||||||
.environmentObject(DetailItemViewModel(item: item))
|
.environmentObject(viewModel)
|
||||||
}).environmentObject(videoPlayerItem)
|
})
|
||||||
|
.environmentObject(videoPlayerItem)
|
||||||
|
.environmentObject(viewModel)
|
||||||
} else {
|
} else {
|
||||||
Text("Hello there")
|
Text("Hello there")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import Foundation
|
||||||
import Foundation
|
import Foundation
|
||||||
import JellyfinAPI
|
import JellyfinAPI
|
||||||
|
|
||||||
class DetailItemViewModel: ViewModel {
|
class ItemViewModel: ViewModel {
|
||||||
|
|
||||||
@Published var item: BaseItemDto
|
@Published var item: BaseItemDto
|
||||||
@Published var similarItems: [BaseItemDto] = []
|
@Published var similarItems: [BaseItemDto] = []
|
||||||
|
|
@ -83,3 +83,7 @@ class DetailItemViewModel: ViewModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DetailItemViewModel: ItemViewModel {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue