You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
122 lines
4.1 KiB
122 lines
4.1 KiB
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 }
|
|
)
|
|
}
|
|
}
|
|
|