jellyflood/Swiftfin tvOS/Views/ItemView/Components/PlayButton/PlayButton.swift
Joe Kribs 216375905c
[iOS & tvOS] Trailers (#1456)
* ItemViewModel Trailers

* iOS done.

* Sections >>> Divider

* tvOS kind of.

* Button/Menu cleanup

* Huge ActionButton overhaul

* Error Handling, ActionButton/Menu standardization, and ActionButtonLayout cleanup part 1.

* cleanup

* cleanup

* Combine ActionButton logic. Complete ActionButton rework and animation/style rework. Should this be 3 files??

* Dumb sizing error. Get size from WIDTH not HEIGHT! Height is always 100 and Width is larger.

* Pressed buttons are but focused buttons but slight less. Pressed buttons are still bigger than default, unfocused buttons. TIL.

* Cleanup / Structure

* Remove Test.

* New Setting. Version on PlayButton Row. Complete TrailerMenu revamp. Make ActionButtonLayout a single row.

* Spacing & remove test logic

* VERY WIP

* Fix the compact-ness

* Linting.

* Remove Testing logic.

* Pre-Cleanup - WIP

* Finalized. Moved ScrollingText to tvOS Only.

* MediaURL? = nil but it's already nil by default.

* Error on the View not the button. This was NOT showing for the button since it lived on the Menu. This resolves this.

* wip

* Update VersionMenu.swift

* Remove scrollingText from this PR.

* Remove labels & iOS Action Button cleanup / no foregroundStyle on de-selected.

* ActionButtonScaling

* .card all buttons in ActionButton

* Slow and less bounce-i-fy the menu animations. Also, slight padding

* Wait, don't add this padding this isn't needed.

* localize

---------

Co-authored-by: Ethan Pippin <ethanpippin2343@gmail.com>
2025-04-05 01:13:20 -04:00

118 lines
3.9 KiB
Swift

//
// 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 (c) 2025 Jellyfin & Jellyfin Contributors
//
import Factory
import JellyfinAPI
import SwiftUI
extension ItemView {
struct PlayButton: View {
@Injected(\.logService)
private var logger
@EnvironmentObject
private var router: ItemCoordinator.Router
@ObservedObject
var viewModel: ItemViewModel
@FocusState
private var isFocused: Bool
// MARK: - Media Sources
private var mediaSources: [MediaSourceInfo] {
viewModel.playButtonItem?.mediaSources ?? []
}
// MARK: - Multiple Media Sources
private var multipleVersions: Bool {
mediaSources.count > 1
}
// MARK: - Title
private var title: String {
if let seriesViewModel = viewModel as? SeriesItemViewModel {
return seriesViewModel.playButtonItem?.seasonEpisodeLabel ?? L10n.play
} else {
return viewModel.playButtonItem?.playButtonLabel ?? L10n.play
}
}
// MARK: - Body
var body: some View {
HStack(spacing: 20) {
playButton
if multipleVersions {
VersionMenu(viewModel: viewModel, mediaSources: mediaSources)
.frame(width: 100, height: 100)
}
}
}
// MARK: - Play Button
private var playButton: some View {
Button {
if let playButtonItem = viewModel.playButtonItem, let selectedMediaSource = viewModel.selectedMediaSource {
router.route(to: \.videoPlayer, OnlineVideoPlayerManager(item: playButtonItem, mediaSource: selectedMediaSource))
} else {
logger.error("No media source available")
}
} label: {
HStack(spacing: 15) {
Image(systemName: "play.fill")
.foregroundColor(viewModel.playButtonItem == nil ? Color(UIColor.secondaryLabel) : Color.black)
.font(.title3)
Text(title)
.foregroundStyle(viewModel.playButtonItem == nil ? Color(UIColor.secondaryLabel) : Color.black)
.fontWeight(.semibold)
}
.padding(20)
.frame(width: multipleVersions ? 320 : 440, height: 100, alignment: .center)
.background {
if isFocused {
viewModel.playButtonItem == nil ? Color.secondarySystemFill : Color.white
} else {
Color.white
.opacity(0.5)
}
}
.cornerRadius(10)
}
.focused($isFocused)
.buttonStyle(.card)
// .contextMenu {
// if viewModel.playButtonItem != nil, viewModel.item.userData?.playbackPositionTicks ?? 0 > 0 {
// Button {
// if let selectedVideoPlayerViewModel = viewModel.selectedVideoPlayerViewModel {
// selectedVideoPlayerViewModel.injectCustomValues(startFromBeginning: true)
// router.route(to: \.videoPlayer, selectedVideoPlayerViewModel)
// } else {
// logger.error("Attempted to play item but no playback information available")
// }
// } label: {
// Label(L10n.playFromBeginning, systemImage: "gobackward")
// }
//
// Button(role: .cancel) {} label: {
// L10n.cancel.text
// }
// }
// }
}
}
}