Repository: aome510/spotify-player (language: rust)

Issue:
Add album to user library
I'm not sure if I'm missing the keybinding to do this, so I didn't to label it a feature request prematurely. Is there a way to add an entire album to the user library from either the album page or from an individual track?

An example of what I'd like to do is to select a track from my Release Radar playlist and add the album it's from to my library. This could either be an action on an individual track or an action from the album page. This would help me out greatly to build up what I want to check out.

If this doesn't currently exist, feel free to label as a feature request :) Thanks in advance!


Discussion / hints:
> I'm not sure if I'm missing the keybinding to do this, so I didn't to label it a feature request prematurely. Is there a way to add an entire album to the user library from either the album page or from an individual track?

No there isn't unfortunately. The only way to add album to library now is to select an album and invoke `ShowActionsOnSelectedItem`. It shouldn't be too difficult to add support for showing actions on current track's album or current album page.

Fix patch:
diff --git a/spotify_player/src/command.rs b/spotify_player/src/command.rs
index 23f03ef9..244dc6a8 100644
--- a/spotify_player/src/command.rs
+++ b/spotify_player/src/command.rs
@@ -1,4 +1,4 @@
-use crate::state::{DataReadGuard, Track};
+use crate::state::{Album, Artist, DataReadGuard, Playlist, Track};
 use serde::Deserialize;
 
 #[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
@@ -81,8 +81,8 @@ pub enum TrackAction {
     GoToArtist,
     GoToAlbum,
     GoToTrackRadio,
-    GoToArtistRadio,
-    GoToAlbumRadio,
+    ShowActionsOnAlbum,
+    ShowActionsOnArtist,
     AddToQueue,
     AddToPlaylist,
     DeleteFromCurrentPlaylist,
@@ -94,8 +94,8 @@ pub enum TrackAction {
 #[derive(Debug, Copy, Clone)]
 pub enum AlbumAction {
     GoToArtist,
-    GoToArtistRadio,
     GoToAlbumRadio,
+    ShowActionsOnArtist,
     AddToLibrary,
     DeleteFromLibrary,
     CopyAlbumLink,
@@ -117,14 +117,14 @@ pub enum PlaylistAction {
     CopyPlaylistLink,
 }
 
-/// constructs a default list of actions on a track
+/// constructs a list of actions on a track
 pub fn construct_track_actions(track: &Track, data: &DataReadGuard) -> Vec<TrackAction> {
     let mut actions = vec![
         TrackAction::GoToArtist,
         TrackAction::GoToAlbum,
         TrackAction::GoToTrackRadio,
-        TrackAction::GoToArtistRadio,
-        TrackAction::GoToAlbumRadio,
+        TrackAction::ShowActionsOnAlbum,
+        TrackAction::ShowActionsOnArtist,
         TrackAction::CopyTrackLink,
         TrackAction::AddToPlaylist,
         TrackAction::AddToQueue,
@@ -140,6 +140,55 @@ pub fn construct_track_actions(track: &Track, data: &DataReadGuard) -> Vec<Track
     actions
 }
 
+/// constructs a list of actions on an album
+pub fn construct_album_actions(album: &Album, data: &DataReadGuard) -> Vec<AlbumAction> {
+    let mut actions = vec![
+        AlbumAction::GoToArtist,
+        AlbumAction::GoToAlbumRadio,
+        AlbumAction::ShowActionsOnArtist,
+        AlbumAction::CopyAlbumLink,
+    ];
+    if data.user_data.saved_albums.iter().any(|a| a.id == album.id) {
+        actions.push(AlbumAction::DeleteFromLibrary);
+    } else {
+        actions.push(AlbumAction::AddToLibrary);
+    }
+    actions
+}
+
+/// constructs a list of actions on an artist
+pub fn construct_artist_actions(artist: &Artist, data: &DataReadGuard) -> Vec<ArtistAction> {
+    let mut actions = vec![ArtistAction::GoToArtistRadio, ArtistAction::CopyArtistLink];
+    if data
+        .user_data
+        .followed_artists
+        .iter()
+        .any(|a| a.id == artist.id)
+    {
+        actions.push(ArtistAction::Unfollow);
+    } else {
+        actions.push(ArtistAction::Follow);
+    }
+    actions
+}
+
+/// constructs a list of actions on an playlist
+pub fn construct_playlist_actions(
+    playlist: &Playlist,
+    data: &DataReadGuard,
+) -> Vec<PlaylistAction> {
+    let mut actions = vec![
+        PlaylistAction::GoToPlaylistRadio,
+        PlaylistAction::CopyPlaylistLink,
+    ];
+    if data.user_data.playlists.iter().any(|a| a.id == playlist.id) {
+        actions.push(PlaylistAction::DeleteFromLibrary);
+    } else {
+        actions.push(PlaylistAction::AddToLibrary);
+    }
+    actions
+}
+
 impl Command {
     pub fn desc(&self) -> &'static str {
         match self {
diff --git a/spotify_player/src/event/popup.rs b/spotify_player/src/event/popup.rs
index bb1f17db..648710f9 100644
--- a/spotify_player/src/event/popup.rs
+++ b/spotify_player/src/event/popup.rs
@@ -2,7 +2,10 @@ use std::io::Write;
 
 use super::*;
 use crate::{
-    command::{AlbumAction, ArtistAction, PlaylistAction, TrackAction},
+    command::{
+        construct_album_actions, construct_artist_actions, AlbumAction, ArtistAction,
+        PlaylistAction, TrackAction,
+    },
     config,
 };
 use anyhow::Context;
@@ -76,14 +79,15 @@ pub fn handle_key_sequence_for_popup(
                                 state: None,
                             });
                         }
-                        ArtistPopupAction::GoToRadio => {
-                            let uri = artists[id].id.uri();
-                            let name = artists[id].name.to_owned();
-                            ui.create_new_radio_page(&uri);
-                            client_pub.send(ClientRequest::GetRadioTracks {
-                                seed_uri: uri,
-                                seed_name: name,
-                            })?;
+                        ArtistPopupAction::ShowActions => {
+                            let actions = {
+                                let data = state.data.read();
+                                construct_artist_actions(&artists[id], &data)
+                            };
+                            ui.popup = Some(PopupState::ActionList(
+                                ActionListItem::Artist(artists[id].clone(), actions),
+                                new_list_state(),
+                            ));
                         }
                     }
 
@@ -583,22 +587,23 @@ fn handle_nth_action(
                     seed_name: name,
                 })?;
             }
-            TrackAction::GoToArtistRadio => {
+            TrackAction::ShowActionsOnArtist => {
                 ui.popup = Some(PopupState::ArtistList(
-                    ArtistPopupAction::GoToRadio,
+                    ArtistPopupAction::ShowActions,
                     track.artists,
                     new_list_state(),
                 ));
             }
-            TrackAction::GoToAlbumRadio => {
+            TrackAction::ShowActionsOnAlbum => {
                 if let Some(album) = track.album {
-                    let uri = album.id.uri();
-                    let name = album.name;
-                    ui.create_new_radio_page(&uri);
-                    client_pub.send(ClientRequest::GetRadioTracks {
-                        seed_uri: uri,
-                        seed_name: name,
-                    })?;
+                    let actions = {
+                        let data = state.data.read();
+                        construct_album_actions(&album, &data)
+                    };
+                    ui.popup = Some(PopupState::ActionList(
+                        ActionListItem::Album(album, actions),
+                        new_list_state(),
+                    ));
                 }
             }
             TrackAction::DeleteFromLikedTracks => {
@@ -636,9 +641,9 @@ fn handle_nth_action(
                     seed_name: name,
                 })?;
             }
-            AlbumAction::GoToArtistRadio => {
+            AlbumAction::ShowActionsOnArtist => {
                 ui.popup = Some(PopupState::ArtistList(
-                    ArtistPopupAction::GoToRadio,
+                    ArtistPopupAction::ShowActions,
                     album.artists,
                     new_list_state(),
                 ));
diff --git a/spotify_player/src/event/window.rs b/spotify_player/src/event/window.rs
index 64220d10..d4afe79f 100644
--- a/spotify_player/src/event/window.rs
+++ b/spotify_player/src/event/window.rs
@@ -1,7 +1,9 @@
 use super::page::handle_navigation_commands_for_page;
 use super::*;
 use crate::{
-    command::{AlbumAction, ArtistAction, PlaylistAction, TrackAction},
+    command::{
+        construct_album_actions, construct_artist_actions, construct_playlist_actions, TrackAction,
+    },
     state::UIStateGuard,
 };
 use rand::Rng;
@@ -312,17 +314,7 @@ pub fn handle_command_for_artist_list_window(
             });
         }
         Command::ShowActionsOnSelectedItem => {
-            let mut actions = vec![ArtistAction::GoToArtistRadio, ArtistAction::CopyArtistLink];
-            if data
-                .user_data
-                .followed_artists
-                .iter()
-                .any(|a| a.id == artists[id].id)
-            {
-                actions.push(ArtistAction::Unfollow);
-            } else {
-                actions.push(ArtistAction::Follow);
-            }
+            let actions = construct_artist_actions(artists[id], data);
             ui.popup = Some(PopupState::ActionList(
                 ActionListItem::Artist(artists[id].clone(), actions),
                 new_list_state(),
@@ -356,22 +348,7 @@ pub fn handle_command_for_album_list_window(
             });
         }
         Command::ShowActionsOnSelectedItem => {
-            let mut actions = vec![
-                AlbumAction::GoToArtist,
-                AlbumAction::GoToAlbumRadio,
-                AlbumAction::GoToArtistRadio,
-                AlbumAction::CopyAlbumLink,
-            ];
-            if data
-                .user_data
-                .saved_albums
-                .iter()
-                .any(|a| a.id == albums[id].id)
-            {
-                actions.push(AlbumAction::DeleteFromLibrary);
-            } else {
-                actions.push(AlbumAction::AddToLibrary);
-            }
+            let actions = construct_album_actions(albums[id], data);
             ui.popup = Some(PopupState::ActionList(
                 ActionListItem::Album(albums[id].clone(), actions),
                 new_list_state(),
@@ -411,20 +388,7 @@ pub fn handle_command_for_playlist_list_window(
             });
         }
         Command::ShowActionsOnSelectedItem => {
-            let mut actions = vec![
-                PlaylistAction::GoToPlaylistRadio,
-                PlaylistAction::CopyPlaylistLink,
-            ];
-            if data
-                .user_data
-                .playlists
-                .iter()
-                .any(|a| a.id == playlists[id].id)
-            {
-                actions.push(PlaylistAction::DeleteFromLibrary);
-            } else {
-                actions.push(PlaylistAction::AddToLibrary);
-            }
+            let actions = construct_playlist_actions(playlists[id], data);
             ui.popup = Some(PopupState::ActionList(
                 ActionListItem::Playlist(playlists[id].clone(), actions),
                 new_list_state(),
diff --git a/spotify_player/src/state/ui/popup.rs b/spotify_player/src/state/ui/popup.rs
index 0cf8634b..aa21ea44 100644
--- a/spotify_player/src/state/ui/popup.rs
+++ b/spotify_player/src/state/ui/popup.rs
@@ -34,7 +34,7 @@ pub enum PlaylistPopupAction {
 #[derive(Copy, Clone, Debug)]
 pub enum ArtistPopupAction {
     Browse,
-    GoToRadio,
+    ShowActions,
 }
 
 impl PopupState {


Test patch: