more cinematic views
This commit is contained in:
parent
6ad3b3e0f2
commit
6c2d153df4
|
@ -63,5 +63,6 @@ struct PortraitItemsRowView: View {
|
||||||
}
|
}
|
||||||
.edgesIgnoringSafeArea(.horizontal)
|
.edgesIgnoringSafeArea(.horizontal)
|
||||||
}
|
}
|
||||||
|
.focusSection()
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,122 @@
|
||||||
|
//
|
||||||
|
/*
|
||||||
|
* SwiftFin is subject to the terms of the Mozilla Public
|
||||||
|
* License, v2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*
|
||||||
|
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||||
|
*/
|
||||||
|
|
||||||
|
import JellyfinAPI
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct SingleSeasonEpisodesRowView: View {
|
||||||
|
|
||||||
|
@EnvironmentObject var itemRouter: ItemCoordinator.Router
|
||||||
|
@ObservedObject var viewModel: SingleSeasonEpisodesRowViewModel
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
|
||||||
|
Text("Episodes")
|
||||||
|
.font(.title3)
|
||||||
|
.padding(.horizontal, 50)
|
||||||
|
|
||||||
|
ScrollView(.horizontal) {
|
||||||
|
ScrollViewReader { reader in
|
||||||
|
HStack(alignment: .top) {
|
||||||
|
if viewModel.isLoading {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
|
||||||
|
ZStack {
|
||||||
|
Color.secondary.ignoresSafeArea()
|
||||||
|
|
||||||
|
ProgressView()
|
||||||
|
}
|
||||||
|
.mask(Rectangle().frame(width: 500, height: 280))
|
||||||
|
.frame(width: 500, height: 280)
|
||||||
|
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("S-E-")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
Text("--")
|
||||||
|
.font(.footnote)
|
||||||
|
.padding(.bottom, 1)
|
||||||
|
Text("--")
|
||||||
|
.font(.caption)
|
||||||
|
.fontWeight(.light)
|
||||||
|
.lineLimit(4)
|
||||||
|
}
|
||||||
|
.padding(.horizontal)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.frame(width: 500)
|
||||||
|
.focusable()
|
||||||
|
} else if viewModel.episodes.isEmpty {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
|
||||||
|
Color.secondary
|
||||||
|
.mask(Rectangle().frame(width: 500, height: 280))
|
||||||
|
.frame(width: 500, height: 280)
|
||||||
|
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("--")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
Text("No episodes available")
|
||||||
|
.font(.footnote)
|
||||||
|
.padding(.bottom, 1)
|
||||||
|
}
|
||||||
|
.padding(.horizontal)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.frame(width: 500)
|
||||||
|
.focusable()
|
||||||
|
} else {
|
||||||
|
ForEach(viewModel.episodes, id:\.self) { episode in
|
||||||
|
Button {
|
||||||
|
itemRouter.route(to: \.item, episode)
|
||||||
|
} label: {
|
||||||
|
HStack(alignment: .top) {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
|
||||||
|
ImageView(src: episode.getBackdropImage(maxWidth: 445),
|
||||||
|
bh: episode.getBackdropImageBlurHash())
|
||||||
|
.mask(Rectangle().frame(width: 500, height: 280))
|
||||||
|
.frame(width: 500, height: 280)
|
||||||
|
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text(episode.getEpisodeLocator() ?? "")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
Text(episode.name ?? "")
|
||||||
|
.font(.footnote)
|
||||||
|
.padding(.bottom, 1)
|
||||||
|
Text(episode.overview ?? "")
|
||||||
|
.font(.caption)
|
||||||
|
.fontWeight(.light)
|
||||||
|
.lineLimit(4)
|
||||||
|
}
|
||||||
|
.padding(.horizontal)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.frame(width: 500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.buttonStyle(PlainButtonStyle())
|
||||||
|
.id(episode.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 50)
|
||||||
|
.padding(.vertical)
|
||||||
|
}
|
||||||
|
.edgesIgnoringSafeArea(.horizontal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,14 @@ struct CinematicEpisodeItemView: View {
|
||||||
@State var wrappedScrollView: UIScrollView?
|
@State var wrappedScrollView: UIScrollView?
|
||||||
@Default(.showPosterLabels) var showPosterLabels
|
@Default(.showPosterLabels) var showPosterLabels
|
||||||
|
|
||||||
|
func generateSubtitle() -> String? {
|
||||||
|
guard let seriesName = viewModel.item.seriesName, let episodeLocator = viewModel.item.getEpisodeLocator() else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "\(seriesName) - \(episodeLocator)"
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
|
|
||||||
|
@ -28,7 +36,10 @@ struct CinematicEpisodeItemView: View {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
|
|
||||||
CinematicItemViewTopRow(viewModel: viewModel, wrappedScrollView: wrappedScrollView)
|
CinematicItemViewTopRow(viewModel: viewModel,
|
||||||
|
wrappedScrollView: wrappedScrollView,
|
||||||
|
title: viewModel.item.name ?? "",
|
||||||
|
subtitle: generateSubtitle())
|
||||||
.focusSection()
|
.focusSection()
|
||||||
.frame(height: UIScreen.main.bounds.height - 10)
|
.frame(height: UIScreen.main.bounds.height - 10)
|
||||||
|
|
||||||
|
@ -44,6 +55,13 @@ struct CinematicEpisodeItemView: View {
|
||||||
EpisodesRowView(viewModel: EpisodesRowViewModel(episodeItemViewModel: viewModel))
|
EpisodesRowView(viewModel: EpisodesRowViewModel(episodeItemViewModel: viewModel))
|
||||||
.focusSection()
|
.focusSection()
|
||||||
|
|
||||||
|
if let seriesItem = viewModel.series {
|
||||||
|
PortraitItemsRowView(rowTitle: "Series",
|
||||||
|
items: [seriesItem]) { seriesItem in
|
||||||
|
itemRouter.route(to: \.item, seriesItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !viewModel.similarItems.isEmpty {
|
if !viewModel.similarItems.isEmpty {
|
||||||
PortraitItemsRowView(rowTitle: "Recommended",
|
PortraitItemsRowView(rowTitle: "Recommended",
|
||||||
items: viewModel.similarItems,
|
items: viewModel.similarItems,
|
||||||
|
|
|
@ -16,6 +16,8 @@ struct CinematicItemViewTopRow: View {
|
||||||
@Environment(\.isFocused) var envFocused: Bool
|
@Environment(\.isFocused) var envFocused: Bool
|
||||||
@State var focused: Bool = false
|
@State var focused: Bool = false
|
||||||
@State var wrappedScrollView: UIScrollView?
|
@State var wrappedScrollView: UIScrollView?
|
||||||
|
@State var title: String
|
||||||
|
@State var subtitle: String?
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack(alignment: .bottom) {
|
ZStack(alignment: .bottom) {
|
||||||
|
@ -34,7 +36,11 @@ struct CinematicItemViewTopRow: View {
|
||||||
|
|
||||||
// MARK: Play
|
// MARK: Play
|
||||||
Button {
|
Button {
|
||||||
itemRouter.route(to: \.videoPlayer, viewModel.itemVideoPlayerViewModel!)
|
if let itemVideoPlayerViewModel = viewModel.itemVideoPlayerViewModel {
|
||||||
|
itemRouter.route(to: \.videoPlayer, itemVideoPlayerViewModel)
|
||||||
|
} else {
|
||||||
|
LogManager.shared.log.error("Attempted to play item but no playback information available")
|
||||||
|
}
|
||||||
} label: {
|
} label: {
|
||||||
HStack(spacing: 15) {
|
HStack(spacing: 15) {
|
||||||
Image(systemName: "play.fill")
|
Image(systemName: "play.fill")
|
||||||
|
@ -42,37 +48,34 @@ struct CinematicItemViewTopRow: View {
|
||||||
.font(.title3)
|
.font(.title3)
|
||||||
Text(viewModel.playButtonText())
|
Text(viewModel.playButtonText())
|
||||||
.foregroundColor(viewModel.playButtonItem == nil ? Color(UIColor.secondaryLabel) : Color.black)
|
.foregroundColor(viewModel.playButtonItem == nil ? Color(UIColor.secondaryLabel) : Color.black)
|
||||||
// .font(.title3)
|
|
||||||
.fontWeight(.semibold)
|
.fontWeight(.semibold)
|
||||||
}
|
}
|
||||||
.frame(width: 230, height: 100)
|
.frame(width: 230, height: 100)
|
||||||
.background(viewModel.playButtonItem == nil ? Color.secondarySystemFill : Color.white)
|
.background(viewModel.playButtonItem == nil ? Color.secondarySystemFill : Color.white)
|
||||||
.cornerRadius(10)
|
.cornerRadius(10)
|
||||||
|
|
||||||
// ZStack {
|
|
||||||
// Color.white.frame(width: 230, height: 100)
|
|
||||||
//
|
|
||||||
// Text("Play")
|
|
||||||
// .font(.title3)
|
|
||||||
// .foregroundColor(.black)
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
.buttonStyle(CardButtonStyle())
|
.buttonStyle(CardButtonStyle())
|
||||||
.disabled(viewModel.itemVideoPlayerViewModel == nil)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VStack(alignment: .leading, spacing: 5) {
|
VStack(alignment: .leading, spacing: 5) {
|
||||||
Text(viewModel.item.name ?? "")
|
Text(title)
|
||||||
.font(.title2)
|
.font(.title2)
|
||||||
.lineLimit(2)
|
.lineLimit(2)
|
||||||
|
|
||||||
if let seriesName = viewModel.item.seriesName, let episodeLocator = viewModel.item.getEpisodeLocator() {
|
if let subtitle = subtitle {
|
||||||
Text("\(seriesName) - \(episodeLocator)")
|
Text(subtitle)
|
||||||
}
|
}
|
||||||
|
|
||||||
HStack(alignment: .PlayInformationAlignmentGuide, spacing: 20) {
|
HStack(alignment: .PlayInformationAlignmentGuide, spacing: 20) {
|
||||||
|
|
||||||
|
if viewModel.item.itemType == .series {
|
||||||
|
if let airTime = viewModel.item.airTime {
|
||||||
|
Text(airTime)
|
||||||
|
.font(.subheadline)
|
||||||
|
.fontWeight(.medium)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if let runtime = viewModel.item.getItemRuntime() {
|
if let runtime = viewModel.item.getItemRuntime() {
|
||||||
Text(runtime)
|
Text(runtime)
|
||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
|
@ -85,6 +88,7 @@ struct CinematicItemViewTopRow: View {
|
||||||
.fontWeight(.medium)
|
.fontWeight(.medium)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let officialRating = viewModel.item.officialRating {
|
if let officialRating = viewModel.item.officialRating {
|
||||||
Text(officialRating)
|
Text(officialRating)
|
||||||
|
@ -121,17 +125,6 @@ struct CinematicItemViewTopRow: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension HorizontalAlignment {
|
|
||||||
|
|
||||||
private struct TitleSubtitleAlignment: AlignmentID {
|
|
||||||
static func defaultValue(in context: ViewDimensions) -> CGFloat {
|
|
||||||
context[HorizontalAlignment.leading]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static let EpisodeSeriesAlignmentGuide = HorizontalAlignment(TitleSubtitleAlignment.self)
|
|
||||||
}
|
|
||||||
|
|
||||||
extension VerticalAlignment {
|
extension VerticalAlignment {
|
||||||
|
|
||||||
private struct PlayInformationAlignment: AlignmentID {
|
private struct PlayInformationAlignment: AlignmentID {
|
||||||
|
|
|
@ -28,7 +28,10 @@ struct CinematicMovieItemView: View {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
|
|
||||||
CinematicItemViewTopRow(viewModel: viewModel, wrappedScrollView: wrappedScrollView)
|
CinematicItemViewTopRow(viewModel: viewModel,
|
||||||
|
wrappedScrollView: wrappedScrollView,
|
||||||
|
title: viewModel.item.name ?? "",
|
||||||
|
subtitle: nil)
|
||||||
.focusSection()
|
.focusSection()
|
||||||
.frame(height: UIScreen.main.bounds.height - 10)
|
.frame(height: UIScreen.main.bounds.height - 10)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
//
|
||||||
|
/*
|
||||||
|
* SwiftFin is subject to the terms of the Mozilla Public
|
||||||
|
* License, v2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*
|
||||||
|
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Defaults
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct CinematicSeasonItemView: View {
|
||||||
|
|
||||||
|
@EnvironmentObject var itemRouter: ItemCoordinator.Router
|
||||||
|
@ObservedObject var viewModel: SeasonItemViewModel
|
||||||
|
@State var wrappedScrollView: UIScrollView?
|
||||||
|
@Default(.showPosterLabels) var showPosterLabels
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
|
||||||
|
ImageView(src: viewModel.item.getBackdropImage(maxWidth: 1920), bh: viewModel.item.getBackdropImageBlurHash())
|
||||||
|
.ignoresSafeArea()
|
||||||
|
|
||||||
|
ScrollView {
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
|
||||||
|
if let seriesItem = viewModel.seriesItem {
|
||||||
|
CinematicItemViewTopRow(viewModel: viewModel,
|
||||||
|
wrappedScrollView: wrappedScrollView,
|
||||||
|
title: viewModel.item.name ?? "",
|
||||||
|
subtitle: seriesItem.name)
|
||||||
|
.focusSection()
|
||||||
|
.frame(height: UIScreen.main.bounds.height - 10)
|
||||||
|
} else {
|
||||||
|
CinematicItemViewTopRow(viewModel: viewModel,
|
||||||
|
wrappedScrollView: wrappedScrollView,
|
||||||
|
title: viewModel.item.name ?? "")
|
||||||
|
.focusSection()
|
||||||
|
.frame(height: UIScreen.main.bounds.height - 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
ZStack(alignment: .topLeading) {
|
||||||
|
|
||||||
|
Color.black.ignoresSafeArea()
|
||||||
|
.frame(minHeight: UIScreen.main.bounds.height)
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: 20) {
|
||||||
|
|
||||||
|
CinematicItemAboutView(viewModel: viewModel)
|
||||||
|
|
||||||
|
SingleSeasonEpisodesRowView(viewModel: SingleSeasonEpisodesRowViewModel(seasonItemViewModel: viewModel))
|
||||||
|
|
||||||
|
if let seriesItem = viewModel.seriesItem {
|
||||||
|
PortraitItemsRowView(rowTitle: "Series",
|
||||||
|
items: [seriesItem]) { seriesItem in
|
||||||
|
itemRouter.route(to: \.item, seriesItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !viewModel.similarItems.isEmpty {
|
||||||
|
PortraitItemsRowView(rowTitle: "Recommended",
|
||||||
|
items: viewModel.similarItems,
|
||||||
|
showItemTitles: showPosterLabels) { item in
|
||||||
|
itemRouter.route(to: \.item, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.vertical, 50)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.introspectScrollView { scrollView in
|
||||||
|
wrappedScrollView = scrollView
|
||||||
|
}
|
||||||
|
.ignoresSafeArea()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
//
|
||||||
|
/*
|
||||||
|
* SwiftFin is subject to the terms of the Mozilla Public
|
||||||
|
* License, v2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*
|
||||||
|
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Defaults
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct CinematicSeriesItemView: View {
|
||||||
|
|
||||||
|
@EnvironmentObject var itemRouter: ItemCoordinator.Router
|
||||||
|
@ObservedObject var viewModel: SeriesItemViewModel
|
||||||
|
@State var wrappedScrollView: UIScrollView?
|
||||||
|
@Default(.showPosterLabels) var showPosterLabels
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
|
||||||
|
ImageView(src: viewModel.item.getBackdropImage(maxWidth: 1920), bh: viewModel.item.getBackdropImageBlurHash())
|
||||||
|
.ignoresSafeArea()
|
||||||
|
|
||||||
|
ScrollView {
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
|
||||||
|
CinematicItemViewTopRow(viewModel: viewModel,
|
||||||
|
wrappedScrollView: wrappedScrollView,
|
||||||
|
title: viewModel.item.name ?? "",
|
||||||
|
subtitle: nil)
|
||||||
|
.focusSection()
|
||||||
|
.frame(height: UIScreen.main.bounds.height - 10)
|
||||||
|
|
||||||
|
ZStack(alignment: .topLeading) {
|
||||||
|
|
||||||
|
Color.black.ignoresSafeArea()
|
||||||
|
.frame(minHeight: UIScreen.main.bounds.height)
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: 20) {
|
||||||
|
|
||||||
|
CinematicItemAboutView(viewModel: viewModel)
|
||||||
|
|
||||||
|
PortraitItemsRowView(rowTitle: "Seasons",
|
||||||
|
items: viewModel.seasons,
|
||||||
|
showItemTitles: showPosterLabels) { season in
|
||||||
|
itemRouter.route(to: \.item, season)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !viewModel.similarItems.isEmpty {
|
||||||
|
PortraitItemsRowView(rowTitle: "Recommended",
|
||||||
|
items: viewModel.similarItems,
|
||||||
|
showItemTitles: showPosterLabels) { item in
|
||||||
|
itemRouter.route(to: \.item, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.vertical, 50)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.introspectScrollView { scrollView in
|
||||||
|
wrappedScrollView = scrollView
|
||||||
|
}
|
||||||
|
.ignoresSafeArea()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,8 +25,7 @@ struct ItemNavigationView: View {
|
||||||
|
|
||||||
struct ItemView: View {
|
struct ItemView: View {
|
||||||
|
|
||||||
@Default(.tvOSEpisodeItemCinematicView) var tvOSEpisodeItemCinematicView
|
@Default(.tvOSCinematicViews) var tvOSCinematicViews
|
||||||
@Default(.tvOSMovieItemCinematicView) var tvOSMovieItemCinematicView
|
|
||||||
|
|
||||||
private var item: BaseItemDto
|
private var item: BaseItemDto
|
||||||
|
|
||||||
|
@ -36,23 +35,32 @@ struct ItemView: View {
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Group {
|
Group {
|
||||||
if item.type == "Movie" {
|
switch item.itemType {
|
||||||
if tvOSMovieItemCinematicView {
|
case .movie:
|
||||||
|
if tvOSCinematicViews {
|
||||||
CinematicMovieItemView(viewModel: MovieItemViewModel(item: item))
|
CinematicMovieItemView(viewModel: MovieItemViewModel(item: item))
|
||||||
} else {
|
} else {
|
||||||
MovieItemView(viewModel: MovieItemViewModel(item: item))
|
MovieItemView(viewModel: MovieItemViewModel(item: item))
|
||||||
}
|
}
|
||||||
} else if item.type == "Series" {
|
case .episode:
|
||||||
SeriesItemView(viewModel: .init(item: item))
|
if tvOSCinematicViews {
|
||||||
} else if item.type == "Season" {
|
|
||||||
SeasonItemView(viewModel: .init(item: item))
|
|
||||||
} else if item.type == "Episode" {
|
|
||||||
if tvOSEpisodeItemCinematicView {
|
|
||||||
CinematicEpisodeItemView(viewModel: EpisodeItemViewModel(item: item))
|
CinematicEpisodeItemView(viewModel: EpisodeItemViewModel(item: item))
|
||||||
} else {
|
} else {
|
||||||
EpisodeItemView(viewModel: EpisodeItemViewModel(item: item))
|
EpisodeItemView(viewModel: EpisodeItemViewModel(item: item))
|
||||||
}
|
}
|
||||||
|
case .season:
|
||||||
|
if tvOSCinematicViews {
|
||||||
|
CinematicSeasonItemView(viewModel: SeasonItemViewModel(item: item))
|
||||||
} else {
|
} else {
|
||||||
|
SeasonItemView(viewModel: .init(item: item))
|
||||||
|
}
|
||||||
|
case .series:
|
||||||
|
if tvOSCinematicViews {
|
||||||
|
CinematicSeriesItemView(viewModel: SeriesItemViewModel(item: item))
|
||||||
|
} else {
|
||||||
|
SeriesItemView(viewModel: SeriesItemViewModel(item: item))
|
||||||
|
}
|
||||||
|
default:
|
||||||
Text(L10n.notImplementedYetWithType(item.type ?? ""))
|
Text(L10n.notImplementedYetWithType(item.type ?? ""))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,8 +20,7 @@ struct SettingsView: View {
|
||||||
@Default(.videoPlayerJumpBackward) var jumpBackwardLength
|
@Default(.videoPlayerJumpBackward) var jumpBackwardLength
|
||||||
@Default(.downActionShowsMenu) var downActionShowsMenu
|
@Default(.downActionShowsMenu) var downActionShowsMenu
|
||||||
@Default(.confirmClose) var confirmClose
|
@Default(.confirmClose) var confirmClose
|
||||||
@Default(.tvOSEpisodeItemCinematicView) var tvOSEpisodeItemCinematicView
|
@Default(.tvOSCinematicViews) var tvOSCinematicViews
|
||||||
@Default(.tvOSMovieItemCinematicView) var tvOSMovieItemCinematicView
|
|
||||||
@Default(.showPosterLabels) var showPosterLabels
|
@Default(.showPosterLabels) var showPosterLabels
|
||||||
@Default(.resumeOffset) var resumeOffset
|
@Default(.resumeOffset) var resumeOffset
|
||||||
|
|
||||||
|
@ -111,8 +110,7 @@ struct SettingsView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
Toggle("Episode Item Cinematic View", isOn: $tvOSEpisodeItemCinematicView)
|
Toggle("Cinematic Views", isOn: $tvOSCinematicViews)
|
||||||
Toggle("Movie Item Cinematic View", isOn: $tvOSMovieItemCinematicView)
|
|
||||||
Toggle("Show Poster Labels", isOn: $showPosterLabels)
|
Toggle("Show Poster Labels", isOn: $showPosterLabels)
|
||||||
|
|
||||||
} header: {
|
} header: {
|
||||||
|
|
|
@ -307,6 +307,9 @@
|
||||||
E13DD3FA2717E961009D4DAF /* UserListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E13DD3F82717E961009D4DAF /* UserListViewModel.swift */; };
|
E13DD3FA2717E961009D4DAF /* UserListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E13DD3F82717E961009D4DAF /* UserListViewModel.swift */; };
|
||||||
E13DD3FC2717EAE8009D4DAF /* UserListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E13DD3FB2717EAE8009D4DAF /* UserListView.swift */; };
|
E13DD3FC2717EAE8009D4DAF /* UserListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E13DD3FB2717EAE8009D4DAF /* UserListView.swift */; };
|
||||||
E13DD4022717EE79009D4DAF /* UserListCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E13DD4012717EE79009D4DAF /* UserListCoordinator.swift */; };
|
E13DD4022717EE79009D4DAF /* UserListCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E13DD4012717EE79009D4DAF /* UserListCoordinator.swift */; };
|
||||||
|
E13F26AF278754E300DF4761 /* CinematicSeriesItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E13F26AE278754E300DF4761 /* CinematicSeriesItemView.swift */; };
|
||||||
|
E13F26B12787589300DF4761 /* CinematicSeasonItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E13F26B02787589300DF4761 /* CinematicSeasonItemView.swift */; };
|
||||||
|
E13F26B32787597300DF4761 /* SingleSeasonEpisodesRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E13F26B22787597300DF4761 /* SingleSeasonEpisodesRowView.swift */; };
|
||||||
E14F7D0726DB36EF007C3AE6 /* ItemPortraitMainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E14F7D0626DB36EF007C3AE6 /* ItemPortraitMainView.swift */; };
|
E14F7D0726DB36EF007C3AE6 /* ItemPortraitMainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E14F7D0626DB36EF007C3AE6 /* ItemPortraitMainView.swift */; };
|
||||||
E14F7D0926DB36F7007C3AE6 /* ItemLandscapeMainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E14F7D0826DB36F7007C3AE6 /* ItemLandscapeMainView.swift */; };
|
E14F7D0926DB36F7007C3AE6 /* ItemLandscapeMainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E14F7D0826DB36F7007C3AE6 /* ItemLandscapeMainView.swift */; };
|
||||||
E173DA5026D048D600CC4EB7 /* ServerDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E173DA4F26D048D600CC4EB7 /* ServerDetailView.swift */; };
|
E173DA5026D048D600CC4EB7 /* ServerDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E173DA4F26D048D600CC4EB7 /* ServerDetailView.swift */; };
|
||||||
|
@ -650,6 +653,9 @@
|
||||||
E13DD3F82717E961009D4DAF /* UserListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserListViewModel.swift; sourceTree = "<group>"; };
|
E13DD3F82717E961009D4DAF /* UserListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserListViewModel.swift; sourceTree = "<group>"; };
|
||||||
E13DD3FB2717EAE8009D4DAF /* UserListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserListView.swift; sourceTree = "<group>"; };
|
E13DD3FB2717EAE8009D4DAF /* UserListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserListView.swift; sourceTree = "<group>"; };
|
||||||
E13DD4012717EE79009D4DAF /* UserListCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserListCoordinator.swift; sourceTree = "<group>"; };
|
E13DD4012717EE79009D4DAF /* UserListCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserListCoordinator.swift; sourceTree = "<group>"; };
|
||||||
|
E13F26AE278754E300DF4761 /* CinematicSeriesItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CinematicSeriesItemView.swift; sourceTree = "<group>"; };
|
||||||
|
E13F26B02787589300DF4761 /* CinematicSeasonItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CinematicSeasonItemView.swift; sourceTree = "<group>"; };
|
||||||
|
E13F26B22787597300DF4761 /* SingleSeasonEpisodesRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleSeasonEpisodesRowView.swift; sourceTree = "<group>"; };
|
||||||
E14F7D0626DB36EF007C3AE6 /* ItemPortraitMainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemPortraitMainView.swift; sourceTree = "<group>"; };
|
E14F7D0626DB36EF007C3AE6 /* ItemPortraitMainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemPortraitMainView.swift; sourceTree = "<group>"; };
|
||||||
E14F7D0826DB36F7007C3AE6 /* ItemLandscapeMainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemLandscapeMainView.swift; sourceTree = "<group>"; };
|
E14F7D0826DB36F7007C3AE6 /* ItemLandscapeMainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemLandscapeMainView.swift; sourceTree = "<group>"; };
|
||||||
E173DA4F26D048D600CC4EB7 /* ServerDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerDetailView.swift; sourceTree = "<group>"; };
|
E173DA4F26D048D600CC4EB7 /* ServerDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerDetailView.swift; sourceTree = "<group>"; };
|
||||||
|
@ -950,11 +956,15 @@
|
||||||
536D3D77267BB9650004248C /* Components */ = {
|
536D3D77267BB9650004248C /* Components */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
E1E5D5382783A56B00692DFE /* EpisodesRowView.swift */,
|
||||||
|
E13F26B22787597300DF4761 /* SingleSeasonEpisodesRowView.swift */,
|
||||||
|
E1E5D5432783BB5100692DFE /* ItemDetailsView.swift */,
|
||||||
531690F6267ACC00005D8AB9 /* LandscapeItemElement.swift */,
|
531690F6267ACC00005D8AB9 /* LandscapeItemElement.swift */,
|
||||||
E100720626BDABC100CE3E31 /* MediaPlayButtonRowView.swift */,
|
E100720626BDABC100CE3E31 /* MediaPlayButtonRowView.swift */,
|
||||||
53272531268BF09D0035FBF1 /* MediaViewActionButton.swift */,
|
53272531268BF09D0035FBF1 /* MediaViewActionButton.swift */,
|
||||||
53116A18268B947A003024C9 /* PlainLinkButton.swift */,
|
53116A18268B947A003024C9 /* PlainLinkButton.swift */,
|
||||||
536D3D80267BDFC60004248C /* PortraitItemElement.swift */,
|
536D3D80267BDFC60004248C /* PortraitItemElement.swift */,
|
||||||
|
E1E5D5412783B33900692DFE /* PortraitItemsRowView.swift */,
|
||||||
536D3D87267C17350004248C /* PublicUserButton.swift */,
|
536D3D87267C17350004248C /* PublicUserButton.swift */,
|
||||||
E17885A3278105170094FBCF /* SFSymbolButton.swift */,
|
E17885A3278105170094FBCF /* SFSymbolButton.swift */,
|
||||||
);
|
);
|
||||||
|
@ -1368,6 +1378,17 @@
|
||||||
path = Views;
|
path = Views;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
E13F26AD27874ECC00DF4761 /* CompactItemView */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
53272538268C20100035FBF1 /* EpisodeItemView.swift */,
|
||||||
|
53CD2A41268A4B38002ABD4E /* MovieItemView.swift */,
|
||||||
|
53272536268C1DBB0035FBF1 /* SeasonItemView.swift */,
|
||||||
|
53116A16268B919A003024C9 /* SeriesItemView.swift */,
|
||||||
|
);
|
||||||
|
path = CompactItemView;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
E14F7D0A26DB3714007C3AE6 /* ItemView */ = {
|
E14F7D0A26DB3714007C3AE6 /* ItemView */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -1445,14 +1466,8 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
E1E5D53C2783A85F00692DFE /* CinematicItemView */,
|
E1E5D53C2783A85F00692DFE /* CinematicItemView */,
|
||||||
53272538268C20100035FBF1 /* EpisodeItemView.swift */,
|
E13F26AD27874ECC00DF4761 /* CompactItemView */,
|
||||||
E1E5D5382783A56B00692DFE /* EpisodesRowView.swift */,
|
|
||||||
E1E5D5432783BB5100692DFE /* ItemDetailsView.swift */,
|
|
||||||
53CD2A3F268A49C2002ABD4E /* ItemView.swift */,
|
53CD2A3F268A49C2002ABD4E /* ItemView.swift */,
|
||||||
53CD2A41268A4B38002ABD4E /* MovieItemView.swift */,
|
|
||||||
E1E5D5412783B33900692DFE /* PortraitItemsRowView.swift */,
|
|
||||||
53272536268C1DBB0035FBF1 /* SeasonItemView.swift */,
|
|
||||||
53116A16268B919A003024C9 /* SeriesItemView.swift */,
|
|
||||||
);
|
);
|
||||||
path = ItemView;
|
path = ItemView;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1528,6 +1543,8 @@
|
||||||
E1E5D53A2783A80900692DFE /* CinematicItemViewTopRow.swift */,
|
E1E5D53A2783A80900692DFE /* CinematicItemViewTopRow.swift */,
|
||||||
E1E5D53F2783B0C000692DFE /* CinematicItemViewTopRowButton.swift */,
|
E1E5D53F2783B0C000692DFE /* CinematicItemViewTopRowButton.swift */,
|
||||||
E1E5D53D2783B05200692DFE /* CinematicMovieItemView.swift */,
|
E1E5D53D2783B05200692DFE /* CinematicMovieItemView.swift */,
|
||||||
|
E13F26B02787589300DF4761 /* CinematicSeasonItemView.swift */,
|
||||||
|
E13F26AE278754E300DF4761 /* CinematicSeriesItemView.swift */,
|
||||||
);
|
);
|
||||||
path = CinematicItemView;
|
path = CinematicItemView;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -2049,6 +2066,7 @@
|
||||||
E1D4BF852719D25A00A11E64 /* TrackLanguage.swift in Sources */,
|
E1D4BF852719D25A00A11E64 /* TrackLanguage.swift in Sources */,
|
||||||
53272532268BF09D0035FBF1 /* MediaViewActionButton.swift in Sources */,
|
53272532268BF09D0035FBF1 /* MediaViewActionButton.swift in Sources */,
|
||||||
531690F0267ABF72005D8AB9 /* NextUpView.swift in Sources */,
|
531690F0267ABF72005D8AB9 /* NextUpView.swift in Sources */,
|
||||||
|
E13F26B32787597300DF4761 /* SingleSeasonEpisodesRowView.swift in Sources */,
|
||||||
E193D53427193F7F00900D82 /* HomeCoordinator.swift in Sources */,
|
E193D53427193F7F00900D82 /* HomeCoordinator.swift in Sources */,
|
||||||
E193D5502719430400900D82 /* ServerDetailView.swift in Sources */,
|
E193D5502719430400900D82 /* ServerDetailView.swift in Sources */,
|
||||||
E11B1B6D2718CD68006DA3E8 /* JellyfinAPIError.swift in Sources */,
|
E11B1B6D2718CD68006DA3E8 /* JellyfinAPIError.swift in Sources */,
|
||||||
|
@ -2071,6 +2089,7 @@
|
||||||
E1E5D5402783B0C000692DFE /* CinematicItemViewTopRowButton.swift in Sources */,
|
E1E5D5402783B0C000692DFE /* CinematicItemViewTopRowButton.swift in Sources */,
|
||||||
5398514626B64DBB00101B49 /* SearchablePickerView.swift in Sources */,
|
5398514626B64DBB00101B49 /* SearchablePickerView.swift in Sources */,
|
||||||
53ABFDEE26799DCD00886593 /* ImageView.swift in Sources */,
|
53ABFDEE26799DCD00886593 /* ImageView.swift in Sources */,
|
||||||
|
E13F26AF278754E300DF4761 /* CinematicSeriesItemView.swift in Sources */,
|
||||||
62E632E4267D3BA60063E547 /* MovieItemViewModel.swift in Sources */,
|
62E632E4267D3BA60063E547 /* MovieItemViewModel.swift in Sources */,
|
||||||
53649AB2269D019100A2D8B7 /* LogManager.swift in Sources */,
|
53649AB2269D019100A2D8B7 /* LogManager.swift in Sources */,
|
||||||
E1E5D553278419D900692DFE /* ConfirmCloseOverlay.swift in Sources */,
|
E1E5D553278419D900692DFE /* ConfirmCloseOverlay.swift in Sources */,
|
||||||
|
@ -2108,6 +2127,7 @@
|
||||||
C4E5081B2703F82A0045C9AB /* LibraryListView.swift in Sources */,
|
C4E5081B2703F82A0045C9AB /* LibraryListView.swift in Sources */,
|
||||||
E193D53B27193F9200900D82 /* SettingsCoordinator.swift in Sources */,
|
E193D53B27193F9200900D82 /* SettingsCoordinator.swift in Sources */,
|
||||||
E1E5D5392783A56B00692DFE /* EpisodesRowView.swift in Sources */,
|
E1E5D5392783A56B00692DFE /* EpisodesRowView.swift in Sources */,
|
||||||
|
E13F26B12787589300DF4761 /* CinematicSeasonItemView.swift in Sources */,
|
||||||
E1E5D5442783BB5100692DFE /* ItemDetailsView.swift in Sources */,
|
E1E5D5442783BB5100692DFE /* ItemDetailsView.swift in Sources */,
|
||||||
536D3D74267BA8170004248C /* BackgroundManager.swift in Sources */,
|
536D3D74267BA8170004248C /* BackgroundManager.swift in Sources */,
|
||||||
E10D87E327852FD000BD264C /* EpisodesRowViewModel.swift in Sources */,
|
E10D87E327852FD000BD264C /* EpisodesRowViewModel.swift in Sources */,
|
||||||
|
|
|
@ -67,6 +67,5 @@ extension Defaults.Keys {
|
||||||
// tvos specific
|
// tvos specific
|
||||||
static let downActionShowsMenu = Key<Bool>("downActionShowsMenu", default: true, suite: SwiftfinStore.Defaults.generalSuite)
|
static let downActionShowsMenu = Key<Bool>("downActionShowsMenu", default: true, suite: SwiftfinStore.Defaults.generalSuite)
|
||||||
static let confirmClose = Key<Bool>("confirmClose", default: false, suite: SwiftfinStore.Defaults.generalSuite)
|
static let confirmClose = Key<Bool>("confirmClose", default: false, suite: SwiftfinStore.Defaults.generalSuite)
|
||||||
static let tvOSEpisodeItemCinematicView = Key<Bool>("tvOSEpisodeItemCinematicView", default: false, suite: SwiftfinStore.Defaults.generalSuite)
|
static let tvOSCinematicViews = Key<Bool>("tvOSCinematicViews", default: false, suite: SwiftfinStore.Defaults.generalSuite)
|
||||||
static let tvOSMovieItemCinematicView = Key<Bool>("tvOSMovieItemCinematicView", default: false, suite: SwiftfinStore.Defaults.generalSuite)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@ import SwiftUI
|
||||||
|
|
||||||
final class EpisodesRowViewModel: ViewModel {
|
final class EpisodesRowViewModel: ViewModel {
|
||||||
|
|
||||||
|
// TODO: Protocol these viewmodels for generalization instead of Episode
|
||||||
|
|
||||||
@ObservedObject var episodeItemViewModel: EpisodeItemViewModel
|
@ObservedObject var episodeItemViewModel: EpisodeItemViewModel
|
||||||
@Published var seasonsEpisodes: [BaseItemDto: [BaseItemDto]] = [:]
|
@Published var seasonsEpisodes: [BaseItemDto: [BaseItemDto]] = [:]
|
||||||
@Published var selectedSeason: BaseItemDto? {
|
@Published var selectedSeason: BaseItemDto? {
|
||||||
|
@ -63,3 +65,17 @@ final class EpisodesRowViewModel: ViewModel {
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final class SingleSeasonEpisodesRowViewModel: ViewModel {
|
||||||
|
|
||||||
|
// TODO: Protocol these viewmodels for generalization instead of Season
|
||||||
|
|
||||||
|
@ObservedObject var seasonItemViewModel: SeasonItemViewModel
|
||||||
|
@Published var episodes: [BaseItemDto]
|
||||||
|
|
||||||
|
init(seasonItemViewModel: SeasonItemViewModel) {
|
||||||
|
self.seasonItemViewModel = seasonItemViewModel
|
||||||
|
self.episodes = seasonItemViewModel.episodes
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15,7 +15,18 @@ import UIKit
|
||||||
class ItemViewModel: ViewModel {
|
class ItemViewModel: ViewModel {
|
||||||
|
|
||||||
@Published var item: BaseItemDto
|
@Published var item: BaseItemDto
|
||||||
@Published var playButtonItem: BaseItemDto?
|
@Published var playButtonItem: BaseItemDto? {
|
||||||
|
didSet {
|
||||||
|
playButtonItem?.createVideoPlayerViewModel()
|
||||||
|
.sink { completion in
|
||||||
|
self.handleAPIRequestError(completion: completion)
|
||||||
|
} receiveValue: { videoPlayerViewModel in
|
||||||
|
self.itemVideoPlayerViewModel = videoPlayerViewModel
|
||||||
|
self.mediaItems = videoPlayerViewModel.item.createMediaItems()
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
|
}
|
||||||
|
}
|
||||||
@Published var similarItems: [BaseItemDto] = []
|
@Published var similarItems: [BaseItemDto] = []
|
||||||
@Published var isWatched = false
|
@Published var isWatched = false
|
||||||
@Published var isFavorited = false
|
@Published var isFavorited = false
|
||||||
|
|
|
@ -13,13 +13,15 @@ import JellyfinAPI
|
||||||
import Stinsen
|
import Stinsen
|
||||||
|
|
||||||
final class SeasonItemViewModel: ItemViewModel {
|
final class SeasonItemViewModel: ItemViewModel {
|
||||||
|
|
||||||
@RouterObject var itemRouter: ItemCoordinator.Router?
|
@RouterObject var itemRouter: ItemCoordinator.Router?
|
||||||
@Published private(set) var episodes: [BaseItemDto] = []
|
@Published var episodes: [BaseItemDto] = []
|
||||||
|
@Published var seriesItem: BaseItemDto?
|
||||||
|
|
||||||
override init(item: BaseItemDto) {
|
override init(item: BaseItemDto) {
|
||||||
super.init(item: item)
|
super.init(item: item)
|
||||||
self.item = item
|
|
||||||
|
|
||||||
|
getSeriesItem()
|
||||||
requestEpisodes()
|
requestEpisodes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,4 +84,17 @@ final class SeasonItemViewModel: ItemViewModel {
|
||||||
})
|
})
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func getSeriesItem() {
|
||||||
|
guard let seriesID = item.seriesId else { return }
|
||||||
|
UserLibraryAPI.getItem(userId: SessionManager.main.currentLogin.user.id,
|
||||||
|
itemId: seriesID)
|
||||||
|
.trackActivity(loading)
|
||||||
|
.sink { [weak self] completion in
|
||||||
|
self?.handleAPIRequestError(completion: completion)
|
||||||
|
} receiveValue: { [weak self] seriesItem in
|
||||||
|
self?.seriesItem = seriesItem
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue