| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116 |
- import SwiftData
- import SwiftUI
- /// Root view — Playlists as the main screen, Library and Settings accessible from toolbar.
- struct ContentView: View {
- @Environment(PlayerViewModel.self) private var playerVM
- @Environment(PlaylistViewModel.self) private var playlistVM
- @EnvironmentObject private var libraryManager: LibraryManager
- @EnvironmentObject private var theme: AppTheme
- @EnvironmentObject private var syncManager: SyncManager
- @Environment(\.modelContext) private var modelContext
- @State private var showLibrary = false
- @State private var showSettings = false
- @State private var showCloudBrowser = false
- @Query(sort: \Playlist.dateModified, order: .reverse)
- private var playlists: [Playlist]
- var body: some View {
- VStack(spacing: 0) {
- // Main content: Playlists
- PlaylistListView()
- // Mini player at bottom
- if playerVM.currentTrack != nil || playerVM.isCloudPlayback {
- MiniPlayerView()
- }
- }
- .accessibilityIdentifier("ContentView")
- .overlay(alignment: .bottom) {
- // Undo queue replacement toast
- if playerVM.showUndoToast {
- HStack(spacing: 8) {
- Text(playerVM.undoMessage)
- .font(.subheadline)
- .foregroundStyle(theme.primaryText)
- Button("Undo") {
- playerVM.undoQueueReplacement()
- }
- .font(.subheadline.bold())
- .foregroundStyle(theme.accent)
- }
- .padding(.horizontal, 16)
- .padding(.vertical, 10)
- .background(theme.cardBackground.opacity(0.95))
- .clipShape(Capsule())
- .shadow(radius: 8)
- .padding(.bottom, (playerVM.currentTrack != nil || playerVM.isCloudPlayback) ? 90 : 60)
- .transition(.move(edge: .bottom).combined(with: .opacity))
- .animation(.easeInOut(duration: 0.3), value: playerVM.showUndoToast)
- }
- }
- .overlay(alignment: .bottom) {
- // Status toast
- if let status = playlistVM.statusMessage {
- HStack(spacing: 8) {
- Image(systemName: "checkmark.circle.fill")
- .foregroundStyle(theme.accent)
- Text(status)
- .font(.subheadline)
- .foregroundStyle(theme.primaryText)
- }
- .padding(.horizontal, 16)
- .padding(.vertical, 10)
- .background(theme.cardBackground.opacity(0.95))
- .clipShape(Capsule())
- .shadow(radius: 8)
- .padding(.bottom, (playerVM.currentTrack != nil || playerVM.isCloudPlayback) ? 90 : 60)
- .transition(.move(edge: .bottom).combined(with: .opacity))
- .animation(.easeInOut(duration: 0.3), value: playlistVM.statusMessage)
- }
- }
- .onAppear {
- libraryManager.setModelContext(modelContext)
- libraryManager.fixBadPathsIfNeeded()
- playlistVM.restoreTargetPlaylist(from: playlists)
- playerVM.modelContext = modelContext
- }
- .onChange(of: playlists.count) { _, newCount in
- guard newCount > 0 else { return }
- Task {
- try? await Task.sleep(for: .seconds(2))
- syncManager.exportPlaylists(playlists)
- }
- }
- .fullScreenCover(isPresented: Binding(
- get: { playerVM.showNowPlaying },
- set: { playerVM.showNowPlaying = $0 }
- )) {
- NowPlayingView()
- .environmentObject(theme)
- .environmentObject(libraryManager)
- }
- .sheet(isPresented: $showLibrary) {
- NavigationStack {
- LibraryView()
- }
- .environmentObject(theme)
- }
- .sheet(isPresented: $showSettings) {
- SettingsView()
- .environmentObject(theme)
- .environmentObject(syncManager)
- }
- .sheet(isPresented: $showCloudBrowser) {
- CloudBrowserView()
- .environmentObject(theme)
- }
- .sheet(isPresented: Bindable(playerVM).showQueue) {
- QueueView()
- .environment(playerVM)
- .environmentObject(theme)
- }
- }
- }
|