From 8f4e330c928f7c66a9976502f93887cac1662375 Mon Sep 17 00:00:00 2001 From: Laurent Date: Sun, 24 May 2026 09:42:16 +0200 Subject: [PATCH] feat: wire PlaylistViewModel into app and content view --- Music/ContentView.swift | 22 ++++++++++++----- Music/MusicApp.swift | 53 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 64 insertions(+), 11 deletions(-) diff --git a/Music/ContentView.swift b/Music/ContentView.swift index dfd6886..fd21793 100644 --- a/Music/ContentView.swift +++ b/Music/ContentView.swift @@ -6,12 +6,18 @@ struct ContentView: View { var player: PlayerViewModel var scanner: ScannerService var audio: AudioService + var playlist: PlaylistViewModel var body: some View { VStack(spacing: 0) { SearchBarView( - trackCount: library.trackCount, - onSearch: { library.search($0) } + trackCount: playlist.selectedPlaylist != nil ? playlist.playlistTracks.count : library.trackCount, + onSearch: { text in + library.search(text) + if playlist.selectedPlaylist != nil { + playlist.search(text) + } + } ) if scanner.isScanning { @@ -26,15 +32,19 @@ struct ContentView: View { } TrackTableView( - tracks: library.tracks, + tracks: playlist.selectedPlaylist != nil ? playlist.playlistTracks : library.tracks, playingTrackId: player.currentTrack?.id, onSort: { column in - library.sort(by: column) + if playlist.selectedPlaylist == nil { + library.sort(by: column) + } }, onDoubleClick: { track in - player.setQueue(library.tracks) + let trackList = playlist.selectedPlaylist != nil ? playlist.playlistTracks : library.tracks + player.setQueue(trackList) player.play(track) - } + }, + onPlayPause: { audio.togglePlayPause() } ) PlayerControlsView( diff --git a/Music/MusicApp.swift b/Music/MusicApp.swift index 81f35b6..13fc8df 100644 --- a/Music/MusicApp.swift +++ b/Music/MusicApp.swift @@ -7,6 +7,7 @@ struct MusicApp: App { @State private var playerVM: PlayerViewModel? @State private var scannerService: ScannerService? @State private var audioService = AudioService() + @State private var playlistVM: PlaylistViewModel? @State private var initError: String? var body: some Scene { @@ -15,12 +16,14 @@ struct MusicApp: App { if let db = dbService, let library = libraryVM, let player = playerVM, - let scanner = scannerService { + let scanner = scannerService, + let playlist = playlistVM { ContentView( library: library, player: player, scanner: scanner, - audio: audioService + audio: audioService, + playlist: playlist ) } else if let error = initError { Text("Failed to initialize database: \(error)") @@ -54,17 +57,22 @@ struct MusicApp: App { let scanner = ScannerService(db: db) let library = LibraryViewModel(db: db) let player = PlayerViewModel(audio: audioService, db: db) + let playlist = PlaylistViewModel(db: db) self.dbService = db self.scannerService = scanner self.libraryVM = library self.playerVM = player + self.playlistVM = playlist - if let savedFolder = UserDefaults.standard.string(forKey: "musicFolderPath"), - let url = URL(string: savedFolder) { + if let url = resolveBookmark() { Task { await scanner.rescan(url) } + } else { + DispatchQueue.main.async { + pickFolder() + } } } catch { initError = error.localizedDescription @@ -80,7 +88,7 @@ struct MusicApp: App { guard panel.runModal() == .OK, let url = panel.url else { return } - UserDefaults.standard.set(url.absoluteString, forKey: "musicFolderPath") + saveBookmark(for: url) if let scanner = scannerService { Task { @@ -88,4 +96,39 @@ struct MusicApp: App { } } } + + private func saveBookmark(for url: URL) { + do { + let data = try url.bookmarkData( + options: .withSecurityScope, + includingResourceValuesForKeys: nil, + relativeTo: nil + ) + UserDefaults.standard.set(data, forKey: "musicFolderBookmark") + } catch { + print("Failed to save bookmark: \(error)") + } + } + + private func resolveBookmark() -> URL? { + guard let data = UserDefaults.standard.data(forKey: "musicFolderBookmark") else { + return nil + } + do { + var isStale = false + let url = try URL( + resolvingBookmarkData: data, + options: .withSecurityScope, + relativeTo: nil, + bookmarkDataIsStale: &isStale + ) + guard url.startAccessingSecurityScopedResource() else { return nil } + if isStale { + saveBookmark(for: url) + } + return url + } catch { + return nil + } + } }