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

Issue:
Jump to currently playing track in the list
**Describe the solution you'd like**
For those who have more than 1000 tracks in the playlist and use shuffle, it would be nice to have an action to navigate to the currently playing track in the current context. This action can be bound to the key sequence `g c` or `g 0`. Actually in Vim there's `z z` to center the current line under the cursor, but I see `z` already bound to queue.

 - If nothing is currently playing -> do nothing
 - If the current context doesn't contain a track -> either do nothing, or call `CurrentlyPlayingContextPage` action before jumping.

**Describe alternatives you've considered**
As an alternative, I suggest adding a way to jump to the track by it's number. Possibly by pressing the `#` key and typing in a track number. This also requires adding `{trackno}` to the playback format configuration, so the user can look at the track number, press `#` (or `g space` to switch to a context and then `#`) and jump to that track.


Discussion / hints:

Fix patch:
diff --git a/README.md b/README.md
index df6bf463..153df65d 100644
--- a/README.md
+++ b/README.md
@@ -369,6 +369,7 @@ List of supported commands:
 | `MovePlaylistItemUp`           | move playlist item up one position                                      | `C-k`              |
 | `MovePlaylistItemDown`         | move playlist item down one position                                    | `C-j`              |
 | `CreatePlaylist`               | create a new playlist                                                   | `N`                |
+| `JumpToCurrentTrackInContext`  | jump to the current track in the context                                | `g c`              |
 
 To add new shortcuts or modify the default shortcuts, please refer to the [keymaps section](docs/config.md#keymaps) in the configuration documentation.
 
diff --git a/spotify_player/src/command.rs b/spotify_player/src/command.rs
index 08c26145..52ff9617 100644
--- a/spotify_player/src/command.rs
+++ b/spotify_player/src/command.rs
@@ -30,6 +30,7 @@ pub enum Command {
     SelectFirstOrScrollToTop,
     SelectLastOrScrollToBottom,
 
+    JumpToCurrentTrackInContext,
     ChooseSelected,
 
     RefreshPlayback,
@@ -251,6 +252,7 @@ impl Command {
                 "select the last item in a list/table or scroll to the bottom"
             }
             Self::ChooseSelected => "choose the selected item and act on it",
+            Self::JumpToCurrentTrackInContext => "jump to the current track in the context",
             Self::RefreshPlayback => "manually refresh the current playback",
             Self::ShowActionsOnSelectedItem => "open a popup showing actions on a selected item",
             Self::ShowActionsOnCurrentTrack => "open a popup showing actions on the current track",
diff --git a/spotify_player/src/config/keymap.rs b/spotify_player/src/config/keymap.rs
index 3a3e3e49..9eac25fb 100644
--- a/spotify_player/src/config/keymap.rs
+++ b/spotify_player/src/config/keymap.rs
@@ -312,6 +312,10 @@ impl Default for KeymapConfig {
                     key_sequence: "N".into(),
                     command: Command::CreatePlaylist,
                 },
+                Keymap {
+                    key_sequence: "g c".into(),
+                    command: Command::JumpToCurrentTrackInContext,
+                },
             ],
         }
     }
diff --git a/spotify_player/src/event/mod.rs b/spotify_player/src/event/mod.rs
index b86c69f6..70d845e3 100644
--- a/spotify_player/src/event/mod.rs
+++ b/spotify_player/src/event/mod.rs
@@ -593,6 +593,33 @@ fn handle_global_command(
                 current_field: PlaylistCreateCurrentField::Name,
             });
         }
+        Command::JumpToCurrentTrackInContext => {
+            let track_id = match state
+                .player
+                .read()
+                .current_playing_track()
+                .and_then(|track| track.id.clone())
+            {
+                Some(id) => id,
+                None => return Ok(false),
+            };
+
+            if let PageState::Context {
+                id: Some(context_id),
+                ..
+            } = ui.current_page()
+            {
+                let context_track_pos = state
+                    .data
+                    .read()
+                    .context_tracks(context_id)
+                    .and_then(|tracks| tracks.iter().position(|t| t.id == track_id));
+
+                if let Some(p) = context_track_pos {
+                    ui.current_page_mut().select(p);
+                }
+            }
+        }
         Command::ClosePopup => {
             ui.popup = None;
         }
diff --git a/spotify_player/src/event/window.rs b/spotify_player/src/event/window.rs
index 5596ad19..c489cd64 100644
--- a/spotify_player/src/event/window.rs
+++ b/spotify_player/src/event/window.rs
@@ -132,7 +132,7 @@ pub fn handle_command_for_focused_context_window(
         // sort ordering commands
         if let Some(order) = order {
             let mut data = state.data.write();
-            if let Some(tracks) = data.context_tracks(context_id) {
+            if let Some(tracks) = data.context_tracks_mut(context_id) {
                 tracks.sort_by(|x, y| order.compare(x, y));
             }
             return Ok(true);
@@ -140,7 +140,7 @@ pub fn handle_command_for_focused_context_window(
         // reverse ordering command
         if command == Command::ReverseTrackOrder {
             let mut data = state.data.write();
-            if let Some(tracks) = data.context_tracks(context_id) {
+            if let Some(tracks) = data.context_tracks_mut(context_id) {
                 tracks.reverse();
             }
             return Ok(true);
diff --git a/spotify_player/src/state/data.rs b/spotify_player/src/state/data.rs
index c4ca04d5..f72e18d2 100644
--- a/spotify_player/src/state/data.rs
+++ b/spotify_player/src/state/data.rs
@@ -76,7 +76,7 @@ impl AppData {
     }
 
     /// Get a list of tracks inside a given context
-    pub fn context_tracks(&mut self, id: &ContextId) -> Option<&mut Vec<Track>> {
+    pub fn context_tracks_mut(&mut self, id: &ContextId) -> Option<&mut Vec<Track>> {
         self.caches.context.get_mut(&id.uri()).map(|c| match c {
             Context::Album { tracks, .. } => tracks,
             Context::Playlist { tracks, .. } => tracks,
@@ -86,6 +86,17 @@ impl AppData {
             Context::Tracks { tracks, .. } => tracks,
         })
     }
+
+    pub fn context_tracks(&self, id: &ContextId) -> Option<&Vec<Track>> {
+        self.caches.context.get(&id.uri()).map(|c| match c {
+            Context::Album { tracks, .. } => tracks,
+            Context::Playlist { tracks, .. } => tracks,
+            Context::Artist {
+                top_tracks: tracks, ..
+            } => tracks,
+            Context::Tracks { tracks, .. } => tracks,
+        })
+    }
 }
 
 impl UserData {


Test patch: