From b2a9e1664aa8323891e7d09887e807530a04b8b6 Mon Sep 17 00:00:00 2001 From: TheMoonThatRises <58153205+TheMoonThatRises@users.noreply.github.com> Date: Mon, 16 Dec 2024 11:06:28 -0700 Subject: [PATCH] add initial (very buggy) queue viewer --- Spwifiy/Localizations/Localizable.xcstrings | 6 + .../Views/MainElements/QueueElementView.swift | 116 ++++++++++++++++++ Spwifiy/Views/MainView.swift | 102 ++++++++------- 3 files changed, 180 insertions(+), 44 deletions(-) create mode 100644 Spwifiy/Views/MainElements/QueueElementView.swift diff --git a/Spwifiy/Localizations/Localizable.xcstrings b/Spwifiy/Localizations/Localizable.xcstrings index 28993f3..699ef6f 100644 --- a/Spwifiy/Localizations/Localizable.xcstrings +++ b/Spwifiy/Localizations/Localizable.xcstrings @@ -82,12 +82,18 @@ }, "My Library" : { "extractionState" : "manual" + }, + "Next up:" : { + }, "Pin to Home" : { }, "Pins" : { + }, + "Play some songs to populate the queue" : { + }, "Playlists" : { diff --git a/Spwifiy/Views/MainElements/QueueElementView.swift b/Spwifiy/Views/MainElements/QueueElementView.swift new file mode 100644 index 0000000..ebd58d2 --- /dev/null +++ b/Spwifiy/Views/MainElements/QueueElementView.swift @@ -0,0 +1,116 @@ +// +// QueueElementView.swift +// Spwifiy +// +// Created by Peter Duanmu on 12/12/24. +// + +import SwiftUI +import SpotifyWebAPI + +struct QueueElementView: View { + + enum CurrentView: String, CaseIterable { + case queueView = "Queue" + case previousView = "Recent" + } + + @ObservedObject var avAudioPlayer: AVAudioPlayer + + @Binding var selectedArtist: Artist? + + @State var currentView: CurrentView = .queueView + + var body: some View { + VStack(alignment: .leading) { + UnderlinedViewMenu(types: CurrentView.allCases, + currentOption: $currentView) + + if avAudioPlayer.trackQueue.count > 0 { + TrackQueueView(track: avAudioPlayer.trackQueue[avAudioPlayer.playingIndex], + selectedArtist: $selectedArtist) + + Spacer() + .frame(height: 40) + + Text("Next up:") + .foregroundStyle(.fgPrimary) + .font(.satoshiBlack(16)) + + Spacer() + .frame(height: 20) + + List { + ForEach(currentView == .queueView + ? avAudioPlayer.trackQueue + .suffix(avAudioPlayer.trackQueue.count - avAudioPlayer.playingIndex - 1) + : avAudioPlayer.previousQueue, + id: \.uri) { track in + TrackQueueView(track: track, + selectedArtist: $selectedArtist) + } + .onMove { indices, newOffset in + avAudioPlayer.trackQueue.move(fromOffsets: indices, + toOffset: newOffset) + } + } + } else { + Text("Play some songs to populate the queue") + .padding() + } + + Spacer() + } + .padding() + .frame(width: 300) + .foregroundStyle(.fgSecondary) + } + +} + +struct TrackQueueView: View { + + let track: Track + + @Binding var selectedArtist: Artist? + + var body: some View { + HStack { + CroppedCachedAsyncImage(url: track.album?.images?.first?.url, + width: 50, + height: 50, + alignment: .center, + clipShape: RoundedRectangle(cornerRadius: 5)) + + VStack(alignment: .leading) { + Text(track.name) + .foregroundStyle(.fgPrimary) + .font(.satoshiCustom(nil, 14)) + + Button { + selectedArtist = track.artists?.first + } label: { + Text(track.artists?.description ?? "Artist") + } + .buttonStyle(.plain) + .cursorHover(.pointingHand) + } + .lineLimit(1) + .frame(maxWidth: 150) + .fixedSize() + + Spacer() + + Button { + + } label: { + Image("spwifiy.close") + .resizable() + .frame(width: 40, height: 40) + } + .buttonStyle(.plain) + .cursorHover(.pointingHand) + } + } + +} diff --git a/Spwifiy/Views/MainView.swift b/Spwifiy/Views/MainView.swift index 5a57dc0..b3d3a36 100644 --- a/Spwifiy/Views/MainView.swift +++ b/Spwifiy/Views/MainView.swift @@ -47,58 +47,72 @@ struct MainView: View { Spacer() - Group { - switch mainViewModel.currentViewAnimated { - // default view - case .home: - HomeView(spotifyDataViewModel: spotifyDataViewModel, - mainViewModel: mainViewModel) - - // sidebar views - case .likedSongs: - LikedSongsView(spotifyCache: spotifyCache, - selectedArtist: $mainViewModel.selectedArtist, - selectedAlbum: $mainViewModel.selectedAlbum) - - // layers deep abstracted view - case .selectedPlaylist: - if let selectedPlaylist = mainViewModel.selectedPlaylist { - SelectedPlaylistView( - showFlags: PlaylistShowFlags.none, - spotifyCache: spotifyCache, - avAudioPlayer: avAudioPlayer, - playlist: selectedPlaylist, - selectedArtist: $mainViewModel.selectedArtist, - selectedAlbum: $mainViewModel.selectedAlbum - ) - } else { - Text("Unable to get selected playlist") - .font(.title) - } - case .selectedArtist: - if let artist = mainViewModel.selectedArtist { - ArtistView(spotifyCache: spotifyCache, artist: artist) - } else { - Text("Unable to get selected artist") + HStack { + Group { + switch mainViewModel.currentViewAnimated { + // default view + case .home: + HomeView(spotifyDataViewModel: spotifyDataViewModel, + mainViewModel: mainViewModel) + + // sidebar views + case .likedSongs: + LikedSongsView(spotifyCache: spotifyCache, + selectedArtist: $mainViewModel.selectedArtist, + selectedAlbum: $mainViewModel.selectedAlbum) + + // layers deep abstracted view + case .selectedPlaylist: + if let selectedPlaylist = mainViewModel.selectedPlaylist { + SelectedPlaylistView( + showFlags: PlaylistShowFlags.none, + spotifyCache: spotifyCache, + avAudioPlayer: avAudioPlayer, + playlist: selectedPlaylist, + selectedArtist: $mainViewModel.selectedArtist, + selectedAlbum: $mainViewModel.selectedAlbum + ) + } else { + Text("Unable to get selected playlist") + .font(.title) + } + case .selectedArtist: + if let artist = mainViewModel.selectedArtist { + ArtistView(spotifyCache: spotifyCache, artist: artist) + } else { + Text("Unable to get selected artist") + .font(.title) + } + + // unimplemented views + default: + Text("Unknown error") .font(.title) } + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .overlay { + RoundedRectangle(cornerRadius: 5) + .stroke(.fgTertiary, lineWidth: 0.5) + .allowsHitTesting(false) + } - // unimplemented views - default: - Text("Unknown error") - .font(.title) + if mainViewModel.showQueueView { + QueueElementView(avAudioPlayer: avAudioPlayer, + selectedArtist: $mainViewModel.selectedArtist) + .frame(maxHeight: .infinity) + .overlay { + RoundedRectangle(cornerRadius: 5) + .stroke(.fgTertiary, lineWidth: 0.5) + .allowsHitTesting(false) + } } } - .frame(maxWidth: .infinity, maxHeight: .infinity) - .overlay { - RoundedRectangle(cornerRadius: 5) - .stroke(.fgTertiary, lineWidth: 0.5) - .allowsHitTesting(false) - } PlayingElementView(avAudioPlayer: avAudioPlayer, selectedArtist: $mainViewModel.selectedArtist, - selectedAlbum: $mainViewModel.selectedAlbum) + selectedAlbum: $mainViewModel.selectedAlbum, + showQueueView: $mainViewModel.showQueueView) } } .padding()