// // 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) 2024 Jellyfin & Jellyfin Contributors // import Defaults import Factory import JellyfinAPI import SwiftUI extension DownloadTaskView { struct ContentView: View { @Default(.accentColor) private var accentColor @Injected(\.downloadManager) private var downloadManager @EnvironmentObject private var mainCoordinator: MainCoordinator.Router @EnvironmentObject private var router: DownloadTaskCoordinator.Router @ObservedObject var downloadTask: DownloadTask @State private var isPresentingVideoPlayerTypeError: Bool = false var body: some View { VStack(alignment: .leading, spacing: 10) { VStack(alignment: .center) { ImageView(downloadTask.item.landscapeImageSources(maxWidth: 600)) .frame(maxHeight: 300) .aspectRatio(1.77, contentMode: .fill) .cornerRadius(10) .padding(.horizontal) .posterShadow() ShelfView(downloadTask: downloadTask) // TODO: Break into subview switch downloadTask.state { case .ready, .cancelled: PrimaryButton(title: "Download") .onSelect { downloadManager.download(task: downloadTask) } .frame(maxWidth: 300) .frame(height: 50) case let .downloading(progress): HStack { // CircularProgressView(progress: progress) // .buttonStyle(.plain) // .frame(width: 30, height: 30) Text("\(Int(progress * 100))%") .foregroundColor(.secondary) Spacer() Button { downloadManager.cancel(task: downloadTask) } label: { Image(systemName: "stop.circle") .foregroundColor(.red) } } .padding(.horizontal) case let .error(error): VStack { PrimaryButton(title: L10n.retry) .onSelect { downloadManager.download(task: downloadTask) } .frame(maxWidth: 300) .frame(height: 50) Text("Error: \(error.localizedDescription)") .padding(.horizontal) } case .complete: PrimaryButton(title: L10n.play) .onSelect { if Defaults[.VideoPlayer.videoPlayerType] == .swiftfin { router.dismissCoordinator { mainCoordinator.route(to: \.videoPlayer, DownloadVideoPlayerManager(downloadTask: downloadTask)) } } else { isPresentingVideoPlayerTypeError = true } } .frame(maxWidth: 300) .frame(height: 50) } } // Text("Media Info") // .font(.title2) // .fontWeight(.semibold) // .padding(.horizontal) } .alert( L10n.error, isPresented: $isPresentingVideoPlayerTypeError ) { Button { isPresentingVideoPlayerTypeError = false } label: { Text(L10n.dismiss) } } message: { Text("Downloaded items are only playable through the Swiftfin video player.") } } } } extension DownloadTaskView.ContentView { struct ShelfView: View { @ObservedObject var downloadTask: DownloadTask var body: some View { VStack(alignment: .center, spacing: 10) { if let seriesName = downloadTask.item.seriesName { Text(seriesName) .font(.headline) .fontWeight(.semibold) .multilineTextAlignment(.center) .lineLimit(2) .padding(.horizontal) .foregroundColor(.secondary) } Text(downloadTask.item.displayTitle) .font(.title2) .fontWeight(.bold) .multilineTextAlignment(.center) .lineLimit(2) .padding(.horizontal) DotHStack { if downloadTask.item.type == .episode { if let episodeLocation = downloadTask.item.episodeLocator { Text(episodeLocation) } } else { if let firstGenre = downloadTask.item.genres?.first { Text(firstGenre) } } if let productionYear = downloadTask.item.premiereDateYear { Text(productionYear) } if let runtime = downloadTask.item.runTimeLabel { Text(runtime) } } .font(.caption) .foregroundColor(.secondary) .padding(.horizontal) } } } }