This commit is contained in:
Ethan Pippin 2022-01-12 13:21:31 -07:00
parent 5e9d3a1c9c
commit 2d16b2886b
11 changed files with 143 additions and 145 deletions

View File

@ -185,19 +185,19 @@ final class HomeViewModel: ViewModel {
.store(in: &cancellables) .store(in: &cancellables)
} }
func removeItemFromResume(_ item: BaseItemDto) { func removeItemFromResume(_ item: BaseItemDto) {
guard let itemID = item.id, resumeItems.contains(where: { $0.id == itemID }) else { return } guard let itemID = item.id, resumeItems.contains(where: { $0.id == itemID }) else { return }
PlaystateAPI.markUnplayedItem(userId: SessionManager.main.currentLogin.user.id, PlaystateAPI.markUnplayedItem(userId: SessionManager.main.currentLogin.user.id,
itemId: item.id!) itemId: item.id!)
.sink(receiveCompletion: { [weak self] completion in .sink(receiveCompletion: { [weak self] completion in
self?.handleAPIRequestError(completion: completion) self?.handleAPIRequestError(completion: completion)
}, receiveValue: { _ in }, receiveValue: { _ in
self.refreshResumeItems() self.refreshResumeItems()
self.refreshNextUpItems() self.refreshNextUpItems()
}) })
.store(in: &cancellables) .store(in: &cancellables)
} }
// MARK: Next Up Items // MARK: Next Up Items

View File

@ -24,7 +24,7 @@ final class CollectionItemViewModel: ItemViewModel {
private func getCollectionItems() { private func getCollectionItems() {
ItemsAPI.getItems(userId: SessionManager.main.currentLogin.user.id, ItemsAPI.getItems(userId: SessionManager.main.currentLogin.user.id,
parentId: item.id, parentId: item.id,
fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people]) fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people])
.trackActivity(loading) .trackActivity(loading)
.sink { [weak self] completion in .sink { [weak self] completion in
self?.handleAPIRequestError(completion: completion) self?.handleAPIRequestError(completion: completion)

View File

@ -239,16 +239,16 @@ final class VideoPlayerViewModel: ViewModel {
extension VideoPlayerViewModel { extension VideoPlayerViewModel {
// Injects custom values that override certain settings // Injects custom values that override certain settings
func injectCustomValues(startFromBeginning: Bool = false) { func injectCustomValues(startFromBeginning: Bool = false) {
if startFromBeginning { if startFromBeginning {
item.userData?.playbackPositionTicks = 0 item.userData?.playbackPositionTicks = 0
item.userData?.playedPercentage = 0 item.userData?.playedPercentage = 0
sliderPercentage = 0 sliderPercentage = 0
sliderPercentageChanged(newValue: 0) sliderPercentageChanged(newValue: 0)
} }
} }
} }
// MARK: Adjacent Items // MARK: Adjacent Items

View File

@ -17,47 +17,47 @@ struct EpisodeRowCard: View {
let episode: BaseItemDto let episode: BaseItemDto
var body: some View { var body: some View {
VStack { VStack {
Button { Button {
itemRouter.route(to: \.item, episode) itemRouter.route(to: \.item, episode)
} label: { } label: {
ImageView(src: episode.getBackdropImage(maxWidth: 550), ImageView(src: episode.getBackdropImage(maxWidth: 550),
bh: episode.getBackdropImageBlurHash()) bh: episode.getBackdropImageBlurHash())
.mask(Rectangle().frame(width: 550, height: 308)) .mask(Rectangle().frame(width: 550, height: 308))
.frame(width: 550, height: 308) .frame(width: 550, height: 308)
} }
.buttonStyle(CardButtonStyle()) .buttonStyle(CardButtonStyle())
VStack(alignment: .leading) { VStack(alignment: .leading) {
VStack(alignment: .leading) { VStack(alignment: .leading) {
Text(episode.getEpisodeLocator() ?? "") Text(episode.getEpisodeLocator() ?? "")
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundColor(.secondary)
Text(episode.name ?? "") Text(episode.name ?? "")
.font(.footnote) .font(.footnote)
.padding(.bottom, 1) .padding(.bottom, 1)
if episode.unaired { if episode.unaired {
Text(episode.airDateLabel ?? L10n.noOverviewAvailable) Text(episode.airDateLabel ?? L10n.noOverviewAvailable)
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundColor(.secondary)
.fontWeight(.light) .fontWeight(.light)
.lineLimit(3) .lineLimit(3)
} else { } else {
Text(episode.overview ?? "") Text(episode.overview ?? "")
.font(.caption) .font(.caption)
.fontWeight(.light) .fontWeight(.light)
.lineLimit(4) .lineLimit(4)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
} }
} }
Spacer() Spacer()
} }
.padding() .padding()
.frame(width: 550) .frame(width: 550)
} }
.focusSection() .focusSection()
} }
} }

View File

@ -13,8 +13,8 @@ struct CinematicResumeCardView: View {
@EnvironmentObject @EnvironmentObject
var homeRouter: HomeCoordinator.Router var homeRouter: HomeCoordinator.Router
@ObservedObject @ObservedObject
var viewModel: HomeViewModel var viewModel: HomeViewModel
let item: BaseItemDto let item: BaseItemDto
var body: some View { var body: some View {
@ -55,15 +55,15 @@ struct CinematicResumeCardView: View {
} }
.frame(width: 350, height: 210) .frame(width: 350, height: 210)
} }
.buttonStyle(CardButtonStyle()) .buttonStyle(CardButtonStyle())
.padding(.top) .padding(.top)
.contextMenu { .contextMenu {
Button(role: .destructive) { Button(role: .destructive) {
viewModel.removeItemFromResume(item) viewModel.removeItemFromResume(item)
} label: { } label: {
L10n.removeFromResume.text L10n.removeFromResume.text
} }
} }
} }
.padding(.vertical) .padding(.vertical)
} }

View File

@ -33,8 +33,8 @@ struct HomeCinematicView: View {
@FocusState @FocusState
var selectedItem: BaseItemDto? var selectedItem: BaseItemDto?
@ObservedObject @ObservedObject
var viewModel: HomeViewModel var viewModel: HomeViewModel
@State @State
private var updatedSelectedItem: BaseItemDto? private var updatedSelectedItem: BaseItemDto?
@State @State
@ -43,8 +43,8 @@ struct HomeCinematicView: View {
private let items: [HomeCinematicViewItem] private let items: [HomeCinematicViewItem]
private let backgroundViewModel = DynamicCinematicBackgroundViewModel() private let backgroundViewModel = DynamicCinematicBackgroundViewModel()
init(viewModel: HomeViewModel, items: [HomeCinematicViewItem], forcedItemSubtitle: String? = nil) { init(viewModel: HomeViewModel, items: [HomeCinematicViewItem], forcedItemSubtitle: String? = nil) {
self.viewModel = viewModel self.viewModel = viewModel
self.items = items self.items = items
self.forcedItemSubtitle = forcedItemSubtitle self.forcedItemSubtitle = forcedItemSubtitle
} }
@ -102,7 +102,7 @@ struct HomeCinematicView: View {
CinematicNextUpCardView(item: item.item, showOverlay: true) CinematicNextUpCardView(item: item.item, showOverlay: true)
.focused($selectedItem, equals: item.item) .focused($selectedItem, equals: item.item)
case .resume: case .resume:
CinematicResumeCardView(viewModel: viewModel, item: item.item) CinematicResumeCardView(viewModel: viewModel, item: item.item)
.focused($selectedItem, equals: item.item) .focused($selectedItem, equals: item.item)
case .plain: case .plain:
CinematicNextUpCardView(item: item.item, showOverlay: false) CinematicNextUpCardView(item: item.item, showOverlay: false)

View File

@ -32,8 +32,8 @@ struct HomeView: View {
LazyVStack(alignment: .leading) { LazyVStack(alignment: .leading) {
if viewModel.resumeItems.isEmpty { if viewModel.resumeItems.isEmpty {
HomeCinematicView(viewModel: viewModel, HomeCinematicView(viewModel: viewModel,
items: viewModel.latestAddedItems.map { .init(item: $0, type: .plain) }, items: viewModel.latestAddedItems.map { .init(item: $0, type: .plain) },
forcedItemSubtitle: L10n.recentlyAdded) forcedItemSubtitle: L10n.recentlyAdded)
if !viewModel.nextUpItems.isEmpty { if !viewModel.nextUpItems.isEmpty {
@ -42,7 +42,7 @@ struct HomeView: View {
} }
} else { } else {
HomeCinematicView(viewModel: viewModel, HomeCinematicView(viewModel: viewModel,
items: viewModel.resumeItems.map { .init(item: $0, type: .resume) }) items: viewModel.resumeItems.map { .init(item: $0, type: .resume) })
if !viewModel.nextUpItems.isEmpty { if !viewModel.nextUpItems.isEmpty {
NextUpView(items: viewModel.nextUpItems) NextUpView(items: viewModel.nextUpItems)

View File

@ -78,26 +78,24 @@ struct CinematicItemViewTopRow: View {
.cornerRadius(10) .cornerRadius(10)
} }
.buttonStyle(CardButtonStyle()) .buttonStyle(CardButtonStyle())
.contextMenu { .contextMenu {
if viewModel.playButtonItem != nil, viewModel.item.userData?.playbackPositionTicks ?? 0 > 0 { if viewModel.playButtonItem != nil, viewModel.item.userData?.playbackPositionTicks ?? 0 > 0 {
Button { Button {
if let itemVideoPlayerViewModel = viewModel.itemVideoPlayerViewModel { if let itemVideoPlayerViewModel = viewModel.itemVideoPlayerViewModel {
itemVideoPlayerViewModel.injectCustomValues(startFromBeginning: true) itemVideoPlayerViewModel.injectCustomValues(startFromBeginning: true)
itemRouter.route(to: \.videoPlayer, itemVideoPlayerViewModel) itemRouter.route(to: \.videoPlayer, itemVideoPlayerViewModel)
} else { } else {
LogManager.shared.log.error("Attempted to play item but no playback information available") LogManager.shared.log.error("Attempted to play item but no playback information available")
} }
} label: { } label: {
Label(L10n.playFromBeginning, systemImage: "gobackward") Label(L10n.playFromBeginning, systemImage: "gobackward")
} }
Button(role: .cancel) { Button(role: .cancel) {} label: {
L10n.cancel.text
} label: { }
L10n.cancel.text }
} }
}
}
} }
} }

View File

@ -81,13 +81,13 @@ struct ContinueWatchingView: View {
} }
} }
} }
.contextMenu { .contextMenu {
Button(role: .destructive) { Button(role: .destructive) {
viewModel.removeItemFromResume(item) viewModel.removeItemFromResume(item)
} label: { } label: {
L10n.removeFromResume.text L10n.removeFromResume.text
} }
} }
} }
} }
.padding(.horizontal) .padding(.horizontal)

View File

@ -50,20 +50,20 @@ struct ItemLandscapeMainView: View {
.cornerRadius(10) .cornerRadius(10)
} }
.disabled(viewModel.playButtonItem == nil || viewModel.itemVideoPlayerViewModel == nil) .disabled(viewModel.playButtonItem == nil || viewModel.itemVideoPlayerViewModel == nil)
.contextMenu { .contextMenu {
if viewModel.playButtonItem != nil, viewModel.item.userData?.playbackPositionTicks ?? 0 > 0 { if viewModel.playButtonItem != nil, viewModel.item.userData?.playbackPositionTicks ?? 0 > 0 {
Button { Button {
if let itemVideoPlayerViewModel = viewModel.itemVideoPlayerViewModel { if let itemVideoPlayerViewModel = viewModel.itemVideoPlayerViewModel {
itemVideoPlayerViewModel.injectCustomValues(startFromBeginning: true) itemVideoPlayerViewModel.injectCustomValues(startFromBeginning: true)
itemRouter.route(to: \.videoPlayer, itemVideoPlayerViewModel) itemRouter.route(to: \.videoPlayer, itemVideoPlayerViewModel)
} else { } else {
LogManager.shared.log.error("Attempted to play item but no playback information available") LogManager.shared.log.error("Attempted to play item but no playback information available")
} }
} label: { } label: {
Label(L10n.playFromBeginning, systemImage: "gobackward") Label(L10n.playFromBeginning, systemImage: "gobackward")
} }
} }
} }
Spacer() Spacer()
} }

View File

@ -90,11 +90,11 @@ struct PortraitHeaderOverlayView: View {
// MARK: Play // MARK: Play
Button { Button {
if let itemVideoPlayerViewModel = viewModel.itemVideoPlayerViewModel { if let itemVideoPlayerViewModel = viewModel.itemVideoPlayerViewModel {
itemRouter.route(to: \.videoPlayer, itemVideoPlayerViewModel) itemRouter.route(to: \.videoPlayer, itemVideoPlayerViewModel)
} else { } else {
LogManager.shared.log.error("Attempted to play item but no playback information available") LogManager.shared.log.error("Attempted to play item but no playback information available")
} }
} label: { } label: {
HStack { HStack {
Image(systemName: "play.fill") Image(systemName: "play.fill")
@ -109,21 +109,21 @@ struct PortraitHeaderOverlayView: View {
.background(viewModel.playButtonItem == nil ? Color(UIColor.secondarySystemFill) : Color.jellyfinPurple) .background(viewModel.playButtonItem == nil ? Color(UIColor.secondarySystemFill) : Color.jellyfinPurple)
.cornerRadius(10) .cornerRadius(10)
} }
.disabled(viewModel.playButtonItem == nil) .disabled(viewModel.playButtonItem == nil)
.contextMenu { .contextMenu {
if viewModel.playButtonItem != nil, viewModel.item.userData?.playbackPositionTicks ?? 0 > 0 { if viewModel.playButtonItem != nil, viewModel.item.userData?.playbackPositionTicks ?? 0 > 0 {
Button { Button {
if let itemVideoPlayerViewModel = viewModel.itemVideoPlayerViewModel { if let itemVideoPlayerViewModel = viewModel.itemVideoPlayerViewModel {
itemVideoPlayerViewModel.injectCustomValues(startFromBeginning: true) itemVideoPlayerViewModel.injectCustomValues(startFromBeginning: true)
itemRouter.route(to: \.videoPlayer, itemVideoPlayerViewModel) itemRouter.route(to: \.videoPlayer, itemVideoPlayerViewModel)
} else { } else {
LogManager.shared.log.error("Attempted to play item but no playback information available") LogManager.shared.log.error("Attempted to play item but no playback information available")
} }
} label: { } label: {
Label(L10n.playFromBeginning, systemImage: "gobackward") Label(L10n.playFromBeginning, systemImage: "gobackward")
} }
} }
} }
Spacer() Spacer()