|
|
|
|
@ -8,6 +8,7 @@ struct ContentView: View { |
|
|
|
|
var audio: AudioService |
|
|
|
|
var playlist: PlaylistViewModel |
|
|
|
|
var shazam: ShazamService |
|
|
|
|
var db: DatabaseService |
|
|
|
|
@Binding var showNewPlaylistAlert: Bool |
|
|
|
|
@State private var showRenameAlert = false |
|
|
|
|
@State private var showEditQueryAlert = false |
|
|
|
|
@ -17,6 +18,10 @@ struct ContentView: View { |
|
|
|
|
@State private var smartPlaylistToEdit: SmartPlaylist? |
|
|
|
|
@State private var scrollToPlayingTrigger = UUID() |
|
|
|
|
@State private var searchText = "" |
|
|
|
|
@State private var showHome = true |
|
|
|
|
@State private var recentTracks: [Track] = [] |
|
|
|
|
@State private var totalDuration: Double = 0 |
|
|
|
|
@State private var monthlyAdditions: [MonthlyCount] = [] |
|
|
|
|
|
|
|
|
|
var body: some View { |
|
|
|
|
VStack(spacing: 0) { |
|
|
|
|
@ -24,6 +29,11 @@ struct ContentView: View { |
|
|
|
|
searchText: $searchText, |
|
|
|
|
trackCount: playlist.selectedItem != nil ? playlist.playlistTracks.count : library.trackCount, |
|
|
|
|
onSearch: { text in |
|
|
|
|
if text.isEmpty { |
|
|
|
|
showHome = true |
|
|
|
|
} else { |
|
|
|
|
showHome = false |
|
|
|
|
} |
|
|
|
|
library.search(text) |
|
|
|
|
if playlist.selectedPlaylist != nil { |
|
|
|
|
playlist.search(text) |
|
|
|
|
@ -52,6 +62,7 @@ struct ContentView: View { |
|
|
|
|
Button(action: { |
|
|
|
|
playlist.deselectPlaylist() |
|
|
|
|
searchText = "" |
|
|
|
|
showHome = true |
|
|
|
|
}) { |
|
|
|
|
HStack(spacing: 2) { |
|
|
|
|
Image(systemName: "chevron.left") |
|
|
|
|
@ -75,43 +86,60 @@ struct ContentView: View { |
|
|
|
|
.frame(maxWidth: .infinity, alignment: .leading) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TrackTableView( |
|
|
|
|
tracks: playlist.selectedItem != nil ? playlist.playlistTracks : library.tracks, |
|
|
|
|
playingTrackId: player.currentTrack?.id, |
|
|
|
|
sortColumn: library.sortColumn, |
|
|
|
|
sortAscending: library.sortAscending, |
|
|
|
|
onSort: { column in |
|
|
|
|
if playlist.selectedItem == nil { |
|
|
|
|
library.sort(by: column) |
|
|
|
|
if showHome && playlist.selectedItem == nil && searchText.isEmpty { |
|
|
|
|
HomeView( |
|
|
|
|
recentTracks: recentTracks, |
|
|
|
|
trackCount: library.trackCount, |
|
|
|
|
totalDuration: totalDuration, |
|
|
|
|
monthlyAdditions: monthlyAdditions, |
|
|
|
|
onTrackDoubleClick: { track in |
|
|
|
|
player.setQueue(recentTracks) |
|
|
|
|
player.play(track) |
|
|
|
|
}, |
|
|
|
|
onShowAll: { |
|
|
|
|
showHome = false |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
onDoubleClick: { track in |
|
|
|
|
let trackList = playlist.selectedItem != nil ? playlist.playlistTracks : library.tracks |
|
|
|
|
player.setQueue(trackList) |
|
|
|
|
player.play(track) |
|
|
|
|
}, |
|
|
|
|
onPlayPause: { audio.togglePlayPause() }, |
|
|
|
|
playlists: playlist.playlists, |
|
|
|
|
lastUsedPlaylistName: playlist.lastUsedPlaylistName, |
|
|
|
|
selectedPlaylist: playlist.selectedPlaylist, |
|
|
|
|
onAddToPlaylist: { track, targetPlaylist in |
|
|
|
|
try? playlist.addTrack(track, to: targetPlaylist) |
|
|
|
|
}, |
|
|
|
|
onAddToLastPlaylist: { track in |
|
|
|
|
try? playlist.addTrackToLastUsedPlaylist(track) |
|
|
|
|
}, |
|
|
|
|
onRemoveFromPlaylist: playlist.selectedPlaylist != nil ? { track in |
|
|
|
|
if let selected = playlist.selectedPlaylist { |
|
|
|
|
try? playlist.removeTrack(track, from: selected) |
|
|
|
|
} |
|
|
|
|
} : nil, |
|
|
|
|
onReorder: playlist.selectedPlaylist != nil ? { from, to in |
|
|
|
|
if let selected = playlist.selectedPlaylist { |
|
|
|
|
try? playlist.moveTrack(in: selected, from: from, to: to) |
|
|
|
|
} |
|
|
|
|
} : nil, |
|
|
|
|
scrollToPlayingTrigger: scrollToPlayingTrigger |
|
|
|
|
) |
|
|
|
|
) |
|
|
|
|
.onAppear { loadHomeData() } |
|
|
|
|
} else { |
|
|
|
|
TrackTableView( |
|
|
|
|
tracks: playlist.selectedItem != nil ? playlist.playlistTracks : library.tracks, |
|
|
|
|
playingTrackId: player.currentTrack?.id, |
|
|
|
|
sortColumn: library.sortColumn, |
|
|
|
|
sortAscending: library.sortAscending, |
|
|
|
|
onSort: { column in |
|
|
|
|
if playlist.selectedItem == nil { |
|
|
|
|
library.sort(by: column) |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
onDoubleClick: { track in |
|
|
|
|
let trackList = playlist.selectedItem != nil ? playlist.playlistTracks : library.tracks |
|
|
|
|
player.setQueue(trackList) |
|
|
|
|
player.play(track) |
|
|
|
|
}, |
|
|
|
|
onPlayPause: { audio.togglePlayPause() }, |
|
|
|
|
playlists: playlist.playlists, |
|
|
|
|
lastUsedPlaylistName: playlist.lastUsedPlaylistName, |
|
|
|
|
selectedPlaylist: playlist.selectedPlaylist, |
|
|
|
|
onAddToPlaylist: { track, targetPlaylist in |
|
|
|
|
try? playlist.addTrack(track, to: targetPlaylist) |
|
|
|
|
}, |
|
|
|
|
onAddToLastPlaylist: { track in |
|
|
|
|
try? playlist.addTrackToLastUsedPlaylist(track) |
|
|
|
|
}, |
|
|
|
|
onRemoveFromPlaylist: playlist.selectedPlaylist != nil ? { track in |
|
|
|
|
if let selected = playlist.selectedPlaylist { |
|
|
|
|
try? playlist.removeTrack(track, from: selected) |
|
|
|
|
} |
|
|
|
|
} : nil, |
|
|
|
|
onReorder: playlist.selectedPlaylist != nil ? { from, to in |
|
|
|
|
if let selected = playlist.selectedPlaylist { |
|
|
|
|
try? playlist.moveTrack(in: selected, from: from, to: to) |
|
|
|
|
} |
|
|
|
|
} : nil, |
|
|
|
|
scrollToPlayingTrigger: scrollToPlayingTrigger |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
PlaylistBarView( |
|
|
|
|
playlists: playlist.allPlaylists, |
|
|
|
|
@ -126,6 +154,7 @@ struct ContentView: View { |
|
|
|
|
onDeselect: { |
|
|
|
|
playlist.deselectPlaylist() |
|
|
|
|
searchText = "" |
|
|
|
|
showHome = true |
|
|
|
|
}, |
|
|
|
|
onRename: { item in |
|
|
|
|
itemToRename = item |
|
|
|
|
@ -148,6 +177,14 @@ struct ContentView: View { |
|
|
|
|
|
|
|
|
|
playerControls |
|
|
|
|
} |
|
|
|
|
.onKeyPress(.leftArrow) { |
|
|
|
|
player.previous() |
|
|
|
|
return .handled |
|
|
|
|
} |
|
|
|
|
.onKeyPress(.rightArrow) { |
|
|
|
|
player.next() |
|
|
|
|
return .handled |
|
|
|
|
} |
|
|
|
|
.onDrop(of: [.fileURL], isTargeted: nil) { providers in |
|
|
|
|
handleDrop(providers) |
|
|
|
|
return true |
|
|
|
|
@ -155,6 +192,9 @@ struct ContentView: View { |
|
|
|
|
.onChange(of: audio.currentTime) { _, _ in |
|
|
|
|
player.checkHalfway() |
|
|
|
|
} |
|
|
|
|
.onChange(of: library.trackCount) { _, _ in |
|
|
|
|
if showHome { loadHomeData() } |
|
|
|
|
} |
|
|
|
|
.alert("New Playlist", isPresented: $showNewPlaylistAlert) { |
|
|
|
|
TextField("Playlist name", text: $playlistNameInput) |
|
|
|
|
Button("Cancel", role: .cancel) { playlistNameInput = "" } |
|
|
|
|
@ -241,6 +281,12 @@ struct ContentView: View { |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private func loadHomeData() { |
|
|
|
|
recentTracks = (try? db.fetchRecentlyAdded(limit: 50)) ?? [] |
|
|
|
|
totalDuration = (try? db.totalDuration()) ?? 0 |
|
|
|
|
monthlyAdditions = (try? db.fetchMonthlyAdditions(months: 12)) ?? [] |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private func handleDrop(_ providers: [NSItemProvider]) { |
|
|
|
|
for provider in providers { |
|
|
|
|
provider.loadItem(forTypeIdentifier: "public.file-url") { data, _ in |
|
|
|
|
|