Files
MobileMusicAssistant/Mobile Music Assistant/ViewsLibraryPodcastsView.swift
T

132 lines
3.8 KiB
Swift

//
// PodcastsView.swift
// Mobile Music Assistant
//
// Created by Sven Hanold on 08.04.26.
//
import SwiftUI
struct PodcastsView: View {
@Environment(MAService.self) private var service
@State private var errorMessage: String?
@State private var showError = false
private var podcasts: [MAPodcast] {
service.libraryManager.podcasts
}
private var isLoading: Bool {
service.libraryManager.isLoadingPodcasts
}
var body: some View {
Group {
if isLoading && podcasts.isEmpty {
ProgressView()
} else if podcasts.isEmpty {
ContentUnavailableView(
"No Podcasts",
systemImage: "mic.fill",
description: Text("Your library doesn't contain any podcasts yet.")
)
} else {
List {
ForEach(podcasts) { podcast in
NavigationLink(value: podcast) {
PodcastRow(podcast: podcast)
}
}
}
.listStyle(.plain)
}
}
.refreshable {
await loadPodcasts()
}
.task {
await loadPodcasts(refresh: !podcasts.isEmpty)
}
.alert("Error", isPresented: $showError) {
Button("OK", role: .cancel) { }
} message: {
if let errorMessage {
Text(errorMessage)
}
}
}
private func loadPodcasts(refresh: Bool = true) async {
do {
try await service.libraryManager.loadPodcasts(refresh: refresh)
} catch {
errorMessage = error.localizedDescription
showError = true
}
}
}
// MARK: - Podcast Row
struct PodcastRow: View {
@Environment(MAService.self) private var service
let podcast: MAPodcast
var body: some View {
HStack(spacing: 12) {
CachedAsyncImage(url: service.imageProxyURL(path: podcast.imageUrl, provider: podcast.imageProvider, size: 128)) { image in
image
.resizable()
.aspectRatio(contentMode: .fill)
} placeholder: {
RoundedRectangle(cornerRadius: 8)
.fill(Color.gray.opacity(0.2))
.overlay {
Image(systemName: "mic.fill")
.font(.title2)
.foregroundStyle(.secondary)
}
}
.frame(width: 64, height: 64)
.clipShape(RoundedRectangle(cornerRadius: 8))
.overlay(alignment: .bottomTrailing) {
if service.libraryManager.isFavorite(uri: podcast.uri) {
Image(systemName: "heart.fill")
.font(.system(size: 10))
.foregroundStyle(.red)
.padding(3)
}
}
VStack(alignment: .leading, spacing: 4) {
Text(podcast.name)
.font(.headline)
.lineLimit(2)
if let publisher = podcast.publisher {
Text(publisher)
.font(.caption)
.foregroundStyle(.secondary)
.lineLimit(1)
}
if let total = podcast.totalEpisodes {
Text("\(total) episodes")
.font(.caption2)
.foregroundStyle(.tertiary)
}
}
Spacer()
}
.padding(.vertical, 4)
}
}
#Preview {
NavigationStack {
PodcastsView()
.environment(MAService())
}
}