Files
MobileMusicAssistant/Mobile Music Assistant/ViewsLibraryRadiosView.swift
T

142 lines
4.0 KiB
Swift

//
// RadiosView.swift
// Mobile Music Assistant
//
// Created by Sven Hanold on 26.03.26.
//
import SwiftUI
struct RadiosView: View {
@Environment(MAService.self) private var service
@State private var radios: [MAMediaItem] = []
@State private var isLoading = true
@State private var errorMessage: String?
@State private var showError = false
@State private var selectedRadio: MAMediaItem?
private var players: [MAPlayer] {
Array(service.playerManager.players.values)
.filter { $0.available }
.sorted { $0.name < $1.name }
}
var body: some View {
List(radios) { radio in
Button {
handleRadioTap(radio)
} label: {
RadioRow(radio: radio)
}
.buttonStyle(.plain)
.listRowSeparator(.visible)
}
.listStyle(.plain)
.overlay {
if isLoading {
ProgressView()
} else if radios.isEmpty && errorMessage == nil {
ContentUnavailableView(
"No Radio Stations",
systemImage: "antenna.radiowaves.left.and.right",
description: Text("No radio stations found in your library.")
)
}
}
.task {
await loadRadios()
}
.refreshable {
await loadRadios()
}
.alert("Error", isPresented: $showError) {
Button("OK", role: .cancel) { }
} message: {
if let errorMessage { Text(errorMessage) }
}
.sheet(item: $selectedRadio) { radio in
EnhancedPlayerPickerView(
players: players,
showNowPlayingOnSelect: true,
onSelect: { player in
Task { await playRadio(radio, on: player) }
}
)
}
}
private func handleRadioTap(_ radio: MAMediaItem) {
if players.count == 1 {
Task { await playRadio(radio, on: players.first!) }
} else {
selectedRadio = radio
}
}
private func loadRadios() async {
isLoading = true
errorMessage = nil
do {
radios = try await service.getRadios()
isLoading = false
} catch {
errorMessage = error.localizedDescription
showError = true
isLoading = false
}
}
private func playRadio(_ radio: MAMediaItem, on player: MAPlayer) async {
do {
try await service.playerManager.playMedia(playerId: player.playerId, uri: radio.uri)
} catch {
errorMessage = error.localizedDescription
showError = true
}
}
}
// MARK: - Radio Row
struct RadioRow: View {
@Environment(MAService.self) private var service
let radio: MAMediaItem
var body: some View {
HStack(spacing: 12) {
CachedAsyncImage(url: service.imageProxyURL(path: radio.imageUrl, provider: radio.imageProvider, size: 128)) { image in
image.resizable().aspectRatio(contentMode: .fill)
} placeholder: {
RoundedRectangle(cornerRadius: 8)
.fill(Color.gray.opacity(0.2))
.overlay {
Image(systemName: "antenna.radiowaves.left.and.right")
.foregroundStyle(.secondary)
}
}
.frame(width: 50, height: 50)
.clipShape(RoundedRectangle(cornerRadius: 8))
Text(radio.name)
.font(.body)
.lineLimit(2)
Spacer()
Image(systemName: "play.circle")
.font(.title2)
.foregroundStyle(.secondary)
}
.padding(.vertical, 4)
}
}
#Preview {
NavigationStack {
RadiosView()
.environment(MAService())
}
}