Files
MobileMusicAssistant/Mobile Music Assistant/ViewsLibraryPlaylistsView.swift
T
2026-03-27 09:21:41 +01:00

139 lines
4.0 KiB
Swift

//
// PlaylistsView.swift
// Mobile Music Assistant
//
// Created by Sven Hanold on 26.03.26.
//
import SwiftUI
struct PlaylistsView: View {
@Environment(MAService.self) private var service
@State private var errorMessage: String?
@State private var showError = false
private var playlists: [MAPlaylist] {
service.libraryManager.playlists
}
private var isLoading: Bool {
service.libraryManager.isLoadingPlaylists
}
var body: some View {
Group {
if isLoading && playlists.isEmpty {
ProgressView()
} else if playlists.isEmpty {
ContentUnavailableView(
"No Playlists",
systemImage: "music.note.list",
description: Text("Your library doesn't contain any playlists yet")
)
} else {
List {
ForEach(playlists) { playlist in
NavigationLink(value: playlist) {
PlaylistRow(playlist: playlist)
}
}
}
.listStyle(.plain)
}
}
.navigationDestination(for: MAPlaylist.self) { playlist in
PlaylistDetailView(playlist: playlist)
}
.refreshable {
await loadPlaylists(refresh: true)
}
.task {
if playlists.isEmpty {
await loadPlaylists(refresh: false)
}
}
.alert("Error", isPresented: $showError) {
Button("OK", role: .cancel) { }
} message: {
if let errorMessage {
Text(errorMessage)
}
}
}
private func loadPlaylists(refresh: Bool) async {
do {
try await service.libraryManager.loadPlaylists(refresh: refresh)
} catch {
errorMessage = error.localizedDescription
showError = true
}
}
}
// MARK: - Playlist Row
struct PlaylistRow: View {
@Environment(MAService.self) private var service
let playlist: MAPlaylist
var body: some View {
HStack(spacing: 12) {
// Playlist Cover
if let imageUrl = playlist.imageUrl {
let coverURL = service.imageProxyURL(path: imageUrl, size: 128)
CachedAsyncImage(url: coverURL) { image in
image
.resizable()
.aspectRatio(contentMode: .fill)
} placeholder: {
Rectangle()
.fill(Color.gray.opacity(0.2))
}
.frame(width: 64, height: 64)
.clipShape(RoundedRectangle(cornerRadius: 8))
} else {
RoundedRectangle(cornerRadius: 8)
.fill(Color.gray.opacity(0.2))
.frame(width: 64, height: 64)
.overlay {
Image(systemName: "music.note.list")
.font(.title2)
.foregroundStyle(.secondary)
}
}
// Playlist Info
VStack(alignment: .leading, spacing: 4) {
Text(playlist.name)
.font(.headline)
.lineLimit(1)
if let owner = playlist.owner {
Text("By \(owner)")
.font(.caption)
.foregroundStyle(.secondary)
.lineLimit(1)
}
if playlist.isEditable {
Label("Editable", systemImage: "pencil")
.font(.caption2)
.foregroundStyle(.blue)
}
}
Spacer()
}
.padding(.vertical, 4)
}
}
#Preview {
NavigationStack {
PlaylistsView()
.environment(MAService())
}
}