131 lines
4.3 KiB
Swift
131 lines
4.3 KiB
Swift
//
|
|
// LibraryView.swift
|
|
// Mobile Music Assistant
|
|
//
|
|
// Created by Sven Hanold on 26.03.26.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
enum LibraryTab: String, CaseIterable {
|
|
case artists = "Artists"
|
|
case albums = "Albums"
|
|
case playlists = "Playlists"
|
|
case radio = "Radio"
|
|
}
|
|
|
|
struct LibraryView: View {
|
|
@Environment(MAService.self) private var service
|
|
@State private var selectedTab: LibraryTab = .artists
|
|
@State private var showSearch = false
|
|
@State private var refreshError: String?
|
|
@State private var showError = false
|
|
|
|
private var isRefreshing: Bool {
|
|
switch selectedTab {
|
|
case .artists: return service.libraryManager.isLoadingArtists
|
|
case .albums: return service.libraryManager.isLoadingAlbums
|
|
case .playlists: return service.libraryManager.isLoadingPlaylists
|
|
case .radio: return false
|
|
}
|
|
}
|
|
|
|
private var lastRefresh: Date? {
|
|
switch selectedTab {
|
|
case .artists: return service.libraryManager.lastArtistsRefresh
|
|
case .albums: return service.libraryManager.lastAlbumsRefresh
|
|
case .playlists: return service.libraryManager.lastPlaylistsRefresh
|
|
case .radio: return nil
|
|
}
|
|
}
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
Group {
|
|
switch selectedTab {
|
|
case .artists: ArtistsView()
|
|
case .albums: AlbumsView()
|
|
case .playlists: PlaylistsView()
|
|
case .radio: RadiosView()
|
|
}
|
|
}
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.toolbar {
|
|
ToolbarItem(placement: .navigationBarLeading) {
|
|
Button {
|
|
Task { await refresh() }
|
|
} label: {
|
|
if isRefreshing {
|
|
ProgressView()
|
|
} else {
|
|
Image(systemName: "arrow.clockwise")
|
|
}
|
|
}
|
|
.disabled(isRefreshing || selectedTab == .radio)
|
|
.help(lastRefreshLabel)
|
|
}
|
|
|
|
ToolbarItem(placement: .principal) {
|
|
Picker("Library", selection: $selectedTab) {
|
|
ForEach(LibraryTab.allCases, id: \.self) { tab in
|
|
Text(tab.rawValue).tag(tab)
|
|
}
|
|
}
|
|
.pickerStyle(.segmented)
|
|
.frame(maxWidth: 360)
|
|
}
|
|
|
|
ToolbarItem(placement: .primaryAction) {
|
|
Button {
|
|
showSearch = true
|
|
} label: {
|
|
Label("Search", systemImage: "magnifyingglass")
|
|
}
|
|
}
|
|
}
|
|
.withMANavigation()
|
|
.alert("Refresh Failed", isPresented: $showError) {
|
|
Button("OK", role: .cancel) { }
|
|
} message: {
|
|
if let refreshError { Text(refreshError) }
|
|
}
|
|
}
|
|
// Search presented as isolated sheet with its own NavigationStack.
|
|
// This prevents the main LibraryView stack from being affected by
|
|
// search-internal navigation (artist/album/playlist detail).
|
|
.sheet(isPresented: $showSearch) {
|
|
NavigationStack {
|
|
SearchView()
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Helpers
|
|
|
|
private var lastRefreshLabel: String {
|
|
guard let date = lastRefresh else { return "Never refreshed" }
|
|
let formatter = RelativeDateTimeFormatter()
|
|
formatter.unitsStyle = .full
|
|
return "Last refreshed \(formatter.localizedString(for: date, relativeTo: .now))"
|
|
}
|
|
|
|
private func refresh() async {
|
|
do {
|
|
switch selectedTab {
|
|
case .artists: try await service.libraryManager.loadArtists(refresh: true)
|
|
case .albums: try await service.libraryManager.loadAlbums(refresh: true)
|
|
case .playlists: try await service.libraryManager.loadPlaylists(refresh: true)
|
|
case .radio: break
|
|
}
|
|
} catch {
|
|
refreshError = error.localizedDescription
|
|
showError = true
|
|
}
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
LibraryView()
|
|
.environment(MAService())
|
|
}
|