parent
1cf5353339
commit
5b8cdf603c
@ -0,0 +1,122 @@ |
||||
import Foundation |
||||
import Observation |
||||
import GRDB |
||||
|
||||
@Observable |
||||
final class PlaylistViewModel { |
||||
var playlists: [Playlist] = [] |
||||
var selectedPlaylist: Playlist? |
||||
var playlistTracks: [Track] = [] |
||||
|
||||
var lastUsedPlaylistId: Int64? { |
||||
get { UserDefaults.standard.object(forKey: "lastUsedPlaylistId") as? Int64 } |
||||
set { UserDefaults.standard.set(newValue, forKey: "lastUsedPlaylistId") } |
||||
} |
||||
|
||||
var lastUsedPlaylistName: String? { |
||||
guard let id = lastUsedPlaylistId else { return nil } |
||||
return playlists.first(where: { $0.id == id })?.name |
||||
} |
||||
|
||||
private let db: DatabaseService |
||||
private var playlistsCancellable: AnyDatabaseCancellable? |
||||
private var tracksCancellable: AnyDatabaseCancellable? |
||||
private var searchTask: Task<Void, Never>? |
||||
private var searchText = "" |
||||
|
||||
init(db: DatabaseService) { |
||||
self.db = db |
||||
observePlaylists() |
||||
} |
||||
|
||||
func createPlaylist(name: String) throws { |
||||
_ = try db.createPlaylist(name: name) |
||||
} |
||||
|
||||
func renamePlaylist(_ playlist: Playlist, to name: String) throws { |
||||
guard let id = playlist.id else { return } |
||||
try db.renamePlaylist(id: id, name: name) |
||||
} |
||||
|
||||
func deletePlaylist(_ playlist: Playlist) throws { |
||||
guard let id = playlist.id else { return } |
||||
if selectedPlaylist?.id == id { |
||||
deselectPlaylist() |
||||
} |
||||
if lastUsedPlaylistId == id { |
||||
lastUsedPlaylistId = nil |
||||
} |
||||
try db.deletePlaylist(id: id) |
||||
} |
||||
|
||||
func addTrack(_ track: Track, to playlist: Playlist) throws { |
||||
guard let trackId = track.id, let playlistId = playlist.id else { return } |
||||
try db.addTrackToPlaylist(trackId: trackId, playlistId: playlistId) |
||||
lastUsedPlaylistId = playlistId |
||||
} |
||||
|
||||
func addTrackToLastUsedPlaylist(_ track: Track) throws { |
||||
guard let playlistId = lastUsedPlaylistId, |
||||
let playlist = playlists.first(where: { $0.id == playlistId }) else { return } |
||||
try addTrack(track, to: playlist) |
||||
} |
||||
|
||||
func removeTrack(_ track: Track, from playlist: Playlist) throws { |
||||
guard let trackId = track.id, let playlistId = playlist.id else { return } |
||||
try db.removeTrackFromPlaylist(trackId: trackId, playlistId: playlistId) |
||||
} |
||||
|
||||
func moveTrack(in playlist: Playlist, from source: Int, to destination: Int) throws { |
||||
guard let playlistId = playlist.id else { return } |
||||
try db.reorderPlaylistTrack(playlistId: playlistId, fromPosition: source, toPosition: destination) |
||||
} |
||||
|
||||
func search(_ text: String) { |
||||
searchText = text |
||||
searchTask?.cancel() |
||||
searchTask = Task { @MainActor [weak self] in |
||||
try? await Task.sleep(for: .milliseconds(150)) |
||||
guard !Task.isCancelled else { return } |
||||
self?.observePlaylistTracks() |
||||
} |
||||
} |
||||
|
||||
func selectPlaylist(_ playlist: Playlist) { |
||||
selectedPlaylist = playlist |
||||
observePlaylistTracks() |
||||
} |
||||
|
||||
func deselectPlaylist() { |
||||
selectedPlaylist = nil |
||||
tracksCancellable?.cancel() |
||||
tracksCancellable = nil |
||||
playlistTracks = [] |
||||
searchText = "" |
||||
} |
||||
|
||||
private func observePlaylists() { |
||||
let observation = ValueObservation.tracking { [db] dbAccess in |
||||
try db.fetchPlaylists(db: dbAccess) |
||||
} |
||||
playlistsCancellable = observation.start( |
||||
in: db.dbPool, |
||||
onError: { error in print("Playlists observation error: \(error)") }, |
||||
onChange: { [weak self] playlists in self?.playlists = playlists } |
||||
) |
||||
} |
||||
|
||||
private func observePlaylistTracks() { |
||||
tracksCancellable?.cancel() |
||||
guard let playlistId = selectedPlaylist?.id else { return } |
||||
let search = searchText |
||||
|
||||
let observation = ValueObservation.tracking { [db] dbAccess in |
||||
try db.fetchPlaylistTracks(db: dbAccess, playlistId: playlistId, search: search) |
||||
} |
||||
tracksCancellable = observation.start( |
||||
in: db.dbPool, |
||||
onError: { error in print("Playlist tracks observation error: \(error)") }, |
||||
onChange: { [weak self] tracks in self?.playlistTracks = tracks } |
||||
) |
||||
} |
||||
} |
||||
Loading…
Reference in new issue