Version 1.5: Groups
This commit is contained in:
@@ -9,12 +9,13 @@ import SwiftUI
|
||||
import UIKit
|
||||
|
||||
enum FavoritesTab: CaseIterable {
|
||||
case artists, albums, radios, podcasts
|
||||
case artists, albums, songs, radios, podcasts
|
||||
|
||||
var title: LocalizedStringKey {
|
||||
switch self {
|
||||
case .artists: return "Artists"
|
||||
case .albums: return "Albums"
|
||||
case .songs: return "Songs"
|
||||
case .radios: return "Radios"
|
||||
case .podcasts: return "Podcasts"
|
||||
}
|
||||
@@ -38,6 +39,7 @@ struct FavoritesView: View {
|
||||
switch selectedTab {
|
||||
case .artists: FavoriteArtistsSection()
|
||||
case .albums: FavoriteAlbumsSection()
|
||||
case .songs: FavoriteSongsSection()
|
||||
case .radios: FavoriteRadiosSection()
|
||||
case .podcasts: FavoritePodcastsSection()
|
||||
}
|
||||
@@ -237,6 +239,126 @@ private struct FavoriteAlbumsSection: View {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Favorite Songs
|
||||
|
||||
private struct FavoriteSongsSection: View {
|
||||
@Environment(MAService.self) private var service
|
||||
|
||||
@State private var tracks: [MAMediaItem] = []
|
||||
@State private var isLoading = true
|
||||
@State private var errorMessage: String?
|
||||
@State private var showError = false
|
||||
@State private var selectedTrackIndex: Int?
|
||||
@State private var showPlayerPicker = false
|
||||
|
||||
private var players: [MAPlayer] {
|
||||
Array(service.playerManager.players.values)
|
||||
.filter { $0.available }
|
||||
.sorted { $0.name < $1.name }
|
||||
}
|
||||
|
||||
private var nowPlayingURIs: Set<String> {
|
||||
Set(service.playerManager.playerQueues.values.compactMap {
|
||||
$0.currentItem?.mediaItem?.uri
|
||||
})
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
if isLoading {
|
||||
ProgressView()
|
||||
} else if tracks.isEmpty {
|
||||
ContentUnavailableView(
|
||||
"No Favorite Songs",
|
||||
systemImage: "heart.slash",
|
||||
description: Text("Tap the heart icon on any song to add it here.")
|
||||
)
|
||||
} else {
|
||||
List {
|
||||
ForEach(Array(tracks.enumerated()), id: \.element.id) { index, track in
|
||||
Button {
|
||||
handleTrackTap(index: index)
|
||||
} label: {
|
||||
TrackRow(
|
||||
track: track,
|
||||
trackNumber: index + 1,
|
||||
isPlaying: nowPlayingURIs.contains(track.uri)
|
||||
)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.listRowSeparator(.visible)
|
||||
}
|
||||
}
|
||||
.listStyle(.plain)
|
||||
}
|
||||
}
|
||||
.task {
|
||||
await loadTracks()
|
||||
}
|
||||
.refreshable {
|
||||
await loadTracks()
|
||||
}
|
||||
.alert("Error", isPresented: $showError) {
|
||||
Button("OK", role: .cancel) { }
|
||||
} message: {
|
||||
if let errorMessage { Text(errorMessage) }
|
||||
}
|
||||
.sheet(isPresented: $showPlayerPicker) {
|
||||
EnhancedPlayerPickerView(
|
||||
players: players,
|
||||
showNowPlayingOnSelect: true,
|
||||
onSelect: { player in
|
||||
if let index = selectedTrackIndex {
|
||||
Task { await playFrom(index: index, on: player) }
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private func handleTrackTap(index: Int) {
|
||||
if players.count == 1 {
|
||||
Task { await playFrom(index: index, on: players.first!) }
|
||||
} else {
|
||||
selectedTrackIndex = index
|
||||
showPlayerPicker = true
|
||||
}
|
||||
}
|
||||
|
||||
private func loadTracks() async {
|
||||
isLoading = true
|
||||
errorMessage = nil
|
||||
do {
|
||||
// Fetch all favorited tracks from the server directly
|
||||
var allTracks: [MAMediaItem] = []
|
||||
var offset = 0
|
||||
let pageSize = 50
|
||||
var hasMore = true
|
||||
while hasMore {
|
||||
let page = try await service.getTracks(favorite: true, limit: pageSize, offset: offset)
|
||||
allTracks.append(contentsOf: page)
|
||||
offset += page.count
|
||||
hasMore = page.count >= pageSize
|
||||
}
|
||||
tracks = allTracks.sorted { $0.name.lowercased() < $1.name.lowercased() }
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
showError = true
|
||||
}
|
||||
isLoading = false
|
||||
}
|
||||
|
||||
private func playFrom(index: Int, on player: MAPlayer) async {
|
||||
let uris = tracks[index...].map { $0.uri }
|
||||
do {
|
||||
try await service.playerManager.playMedia(playerId: player.playerId, uris: uris)
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
showError = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Favorite Radios
|
||||
|
||||
private struct FavoriteRadiosSection: View {
|
||||
|
||||
Reference in New Issue
Block a user