diff --git a/src/app/components/artist_details/artist_details_model.rs b/src/app/components/artist_details/artist_details_model.rs index c8ccecec..7c073413 100644 --- a/src/app/components/artist_details/artist_details_model.rs +++ b/src/app/components/artist_details/artist_details_model.rs @@ -113,6 +113,16 @@ impl PlaylistModel for ArtistDetailsModel { group.add_action(&song.make_album_action(self.dispatcher.box_clone(), None)); group.add_action(&song.make_link_action(None)); group.add_action(&song.make_queue_action(self.dispatcher.box_clone(), None)); + group.add_action(&song.make_like_action( + self.dispatcher.box_clone(), + self.app_model.clone(), + None, + )); + group.add_action(&song.make_unlike_action( + self.dispatcher.box_clone(), + self.app_model.clone(), + None, + )); Some(group.upcast()) } @@ -132,6 +142,8 @@ impl PlaylistModel for ArtistDetailsModel { menu.append(Some(&*labels::COPY_LINK), Some("song.copy_link")); menu.append(Some(&*labels::ADD_TO_QUEUE), Some("song.queue")); + menu.append(Some(&*labels::ADD_TO_LIBRARY), Some("song.like")); + menu.append(Some(&*labels::REMOVE_FROM_LIBRARY), Some("song.unlike")); Some(menu.upcast()) } diff --git a/src/app/components/details/details_model.rs b/src/app/components/details/details_model.rs index 12d98315..58ea3b6c 100644 --- a/src/app/components/details/details_model.rs +++ b/src/app/components/details/details_model.rs @@ -195,6 +195,16 @@ impl PlaylistModel for DetailsModel { } group.add_action(&song.make_link_action(None)); group.add_action(&song.make_queue_action(self.dispatcher.box_clone(), None)); + group.add_action(&song.make_like_action( + self.dispatcher.box_clone(), + self.app_model.clone(), + None, + )); + group.add_action(&song.make_unlike_action( + self.dispatcher.box_clone(), + self.app_model.clone(), + None, + )); Some(group.upcast()) } @@ -213,6 +223,8 @@ impl PlaylistModel for DetailsModel { menu.append(Some(&*labels::COPY_LINK), Some("song.copy_link")); menu.append(Some(&*labels::ADD_TO_QUEUE), Some("song.queue")); + menu.append(Some(&*labels::ADD_TO_LIBRARY), Some("song.like")); + menu.append(Some(&*labels::REMOVE_FROM_LIBRARY), Some("song.unlike")); Some(menu.upcast()) } } diff --git a/src/app/components/labels.rs b/src/app/components/labels.rs index 3bb216f6..e1955308 100644 --- a/src/app/components/labels.rs +++ b/src/app/components/labels.rs @@ -12,6 +12,12 @@ lazy_static! { // translators: This is part of a contextual menu attached to a single track; this entry removes a track from the play queue. pub static ref REMOVE_FROM_QUEUE: String = gettext("Remove from queue"); + + // translators: This is part of a contextual menu attached to a single track; this entry adds a track to the library. + pub static ref ADD_TO_LIBRARY: String = gettext("Add to library"); + + // translators: This is part of a contextual menu attached to a single track; this entry removes a track from the library. + pub static ref REMOVE_FROM_LIBRARY: String = gettext("Remove from library"); } pub fn add_to_playlist_label(playlist: &str) -> String { diff --git a/src/app/components/now_playing/now_playing_model.rs b/src/app/components/now_playing/now_playing_model.rs index 0e32558a..2384726b 100644 --- a/src/app/components/now_playing/now_playing_model.rs +++ b/src/app/components/now_playing/now_playing_model.rs @@ -79,6 +79,16 @@ impl PlaylistModel for NowPlayingModel { group.add_action(&song.make_album_action(self.dispatcher.box_clone(), None)); group.add_action(&song.make_link_action(None)); group.add_action(&song.make_dequeue_action(self.dispatcher.box_clone(), None)); + group.add_action(&song.make_like_action( + self.dispatcher.box_clone(), + self.app_model.clone(), + None, + )); + group.add_action(&song.make_unlike_action( + self.dispatcher.box_clone(), + self.app_model.clone(), + None, + )); Some(group.upcast()) } @@ -99,6 +109,8 @@ impl PlaylistModel for NowPlayingModel { menu.append(Some(&*labels::COPY_LINK), Some("song.copy_link")); menu.append(Some(&*labels::REMOVE_FROM_QUEUE), Some("song.dequeue")); + menu.append(Some(&*labels::ADD_TO_LIBRARY), Some("song.like")); + menu.append(Some(&*labels::REMOVE_FROM_LIBRARY), Some("song.unlike")); Some(menu.upcast()) } diff --git a/src/app/components/playlist/song_actions.rs b/src/app/components/playlist/song_actions.rs index c5a45ee6..8a3d6e45 100644 --- a/src/app/components/playlist/song_actions.rs +++ b/src/app/components/playlist/song_actions.rs @@ -1,9 +1,11 @@ use gdk::prelude::*; +use gettextrs::gettext; use gio::SimpleAction; +use std::rc::Rc; use crate::app::models::SongDescription; -use crate::app::state::{AppAction, PlaybackAction}; -use crate::app::ActionDispatcher; +use crate::app::state::{AppAction, PlaybackAction, SelectionAction}; +use crate::app::{ActionDispatcher, AppModel}; impl SongDescription { pub fn make_queue_action( @@ -79,4 +81,54 @@ impl SongDescription { }) .collect() } + + pub fn make_like_action( + &self, + dispatcher: Box, + app_model: Rc, + name: Option<&str>, + ) -> SimpleAction { + let track_id = self.id.clone(); + let song = self.clone(); + let like_track = SimpleAction::new(name.unwrap_or("like"), None); + like_track.connect_activate(move |_, _| { + let track_id = track_id.clone(); + let song = song.clone(); + let api = app_model.get_spotify(); + dispatcher.dispatch(SelectionAction::Select(vec![song]).into()); + dispatcher.call_spotify_and_dispatch_many(move || async move { + api.save_tracks(vec![track_id]).await?; + Ok(vec![ + AppAction::SaveSelection, + AppAction::ShowNotification(gettext("Track saved!")), + ]) + }); + }); + like_track + } + + pub fn make_unlike_action( + &self, + dispatcher: Box, + app_model: Rc, + name: Option<&str>, + ) -> SimpleAction { + let track_id = self.id.clone(); + let song = self.clone(); + let unlike_track = SimpleAction::new(name.unwrap_or("unlike"), None); + unlike_track.connect_activate(move |_, _| { + let track_id = track_id.clone(); + let song = song.clone(); + let api = app_model.get_spotify(); + dispatcher.dispatch(SelectionAction::Select(vec![song]).into()); + dispatcher.call_spotify_and_dispatch_many(move || async move { + api.remove_saved_tracks(vec![track_id]).await?; + Ok(vec![ + AppAction::UnsaveSelection, + AppAction::ShowNotification(gettext("Track unsaved!")), + ]) + }); + }); + unlike_track + } } diff --git a/src/app/components/playlist_details/playlist_details_model.rs b/src/app/components/playlist_details/playlist_details_model.rs index 9349f8a1..a5ef43e0 100644 --- a/src/app/components/playlist_details/playlist_details_model.rs +++ b/src/app/components/playlist_details/playlist_details_model.rs @@ -132,6 +132,16 @@ impl PlaylistModel for PlaylistDetailsModel { group.add_action(&song.make_album_action(self.dispatcher.box_clone(), None)); group.add_action(&song.make_link_action(None)); group.add_action(&song.make_queue_action(self.dispatcher.box_clone(), None)); + group.add_action(&song.make_like_action( + self.dispatcher.box_clone(), + self.app_model.clone(), + None, + )); + group.add_action(&song.make_unlike_action( + self.dispatcher.box_clone(), + self.app_model.clone(), + None, + )); Some(group.upcast()) } @@ -151,6 +161,8 @@ impl PlaylistModel for PlaylistDetailsModel { menu.append(Some(&*labels::COPY_LINK), Some("song.copy_link")); menu.append(Some(&*labels::ADD_TO_QUEUE), Some("song.queue")); + menu.append(Some(&*labels::ADD_TO_LIBRARY), Some("song.like")); + menu.append(Some(&*labels::REMOVE_FROM_LIBRARY), Some("song.unlike")); Some(menu.upcast()) } diff --git a/src/app/components/saved_tracks/saved_tracks_model.rs b/src/app/components/saved_tracks/saved_tracks_model.rs index 5bc8349a..50d7e711 100644 --- a/src/app/components/saved_tracks/saved_tracks_model.rs +++ b/src/app/components/saved_tracks/saved_tracks_model.rs @@ -97,6 +97,11 @@ impl PlaylistModel for SavedTracksModel { } group.add_action(&song.make_album_action(self.dispatcher.box_clone(), None)); group.add_action(&song.make_link_action(None)); + group.add_action(&song.make_unlike_action( + self.dispatcher.box_clone(), + self.app_model.clone(), + None, + )); Some(group.upcast()) } @@ -115,6 +120,7 @@ impl PlaylistModel for SavedTracksModel { } menu.append(Some(&*labels::COPY_LINK), Some("song.copy_link")); + menu.append(Some(&*labels::REMOVE_FROM_LIBRARY), Some("song.unlike")); Some(menu.upcast()) }