diff --git a/src/app.rs b/src/app.rs index 7e14bfa9..89477f61 100644 --- a/src/app.rs +++ b/src/app.rs @@ -570,12 +570,7 @@ impl App { None, None, ) { - self.track_table.tracks = playlist_tracks - .items - .clone() - .into_iter() - .map(|item| item.track) - .collect::>(); + self.set_playlist_tracks_to_table(&playlist_tracks); self.playlist_tracks = playlist_tracks.items; self.push_navigation_stack(RouteId::TrackTable, ActiveBlock::TrackTable); @@ -628,13 +623,38 @@ impl App { } } - fn set_saved_tracks_to_table(&mut self, saved_tracks: &Page) { - self.track_table.tracks = saved_tracks - .items - .clone() - .into_iter() - .map(|item| item.track) - .collect::>(); + fn set_saved_tracks_to_table(&mut self, saved_track_page: &Page) { + self.set_tracks_to_table( + saved_track_page + .items + .clone() + .into_iter() + .map(|item| item.track) + .collect::>(), + ); + } + + fn set_playlist_tracks_to_table(&mut self, playlist_track_page: &Page) { + self.set_tracks_to_table( + playlist_track_page + .items + .clone() + .into_iter() + .map(|item| item.track) + .collect::>(), + ); + } + + pub fn set_tracks_to_table(&mut self, tracks: Vec) { + self.track_table.tracks = tracks.clone(); + + self.current_user_saved_tracks_contains( + tracks + .clone() + .into_iter() + .filter_map(|item| item.id) + .collect::>(), + ); } pub fn get_current_user_saved_tracks(&mut self, offset: Option) { @@ -692,10 +712,18 @@ impl App { Ok(tracks) => { self.selected_album = Some(SelectedAlbum { album, - tracks, + tracks: tracks.clone(), selected_index: Some(0), }); + self.current_user_saved_tracks_contains( + tracks + .items + .into_iter() + .filter_map(|item| item.id) + .collect::>(), + ); + self.album_table_context = AlbumTableContext::Simplified; self.push_navigation_stack(RouteId::AlbumTracks, ActiveBlock::AlbumTracks); } diff --git a/src/handlers/input.rs b/src/handlers/input.rs index f1a09d0c..c0c82175 100644 --- a/src/handlers/input.rs +++ b/src/handlers/input.rs @@ -56,7 +56,7 @@ pub fn handler(key: Key, app: &mut App) { country.to_owned(), ) { Ok(result) => { - app.track_table.tracks = result.tracks.items.clone(); + app.set_tracks_to_table(result.tracks.items.clone()); app.search_results.tracks = Some(result); } Err(e) => { diff --git a/src/handlers/library.rs b/src/handlers/library.rs index 4cbdc964..bdf8c0d9 100644 --- a/src/handlers/library.rs +++ b/src/handlers/library.rs @@ -36,7 +36,16 @@ pub fn handler(key: Key, app: &mut App) { .current_user_recently_played(app.large_search_limit) { Ok(result) => { - app.recently_played.result = Some(result); + app.recently_played.result = Some(result.clone()); + + app.current_user_saved_tracks_contains( + result + .items + .iter() + .filter_map(|item| item.track.id.clone()) + .collect::>(), + ); + app.push_navigation_stack( RouteId::RecentlyPlayed, ActiveBlock::RecentlyPlayed, diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 8fbe68c2..c30a29b2 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -16,16 +16,50 @@ use util::{ get_search_results_highlight_state, millis_to_minutes, }; -pub struct TableItem { - id: String, - format: Vec, +pub enum TableId { + Album, + AlbumList, + Artist, + Song, + RecentlyPlayed, +} + +#[derive(PartialEq)] +pub enum ColumnId { + None, + SongTitle, + Liked, +} + +impl Default for ColumnId { + fn default() -> Self { + ColumnId::None + } } pub struct TableHeader<'a> { + id: TableId, + items: Vec>, +} + +impl TableHeader<'_> { + pub fn get_index(&self, id: ColumnId) -> Option { + self.items.iter().position(|item| item.id == id) + } +} + +#[derive(Default)] +pub struct TableHeaderItem<'a> { + id: ColumnId, text: &'a str, width: u16, } +pub struct TableItem { + id: String, + format: Vec, +} + pub fn draw_help_menu(f: &mut Frame) where B: Backend, @@ -350,10 +384,14 @@ pub fn draw_artist_table(f: &mut Frame, app: &App, layout_chunk: Rect) where B: Backend, { - let header = [TableHeader { - text: "Artist", - width: get_percentage_width(layout_chunk.width, 1.0), - }]; + let header = TableHeader { + id: TableId::Artist, + items: vec![TableHeaderItem { + text: "Artist", + width: get_percentage_width(layout_chunk.width, 1.0), + ..Default::default() + }], + }; let current_route = app.get_current_route(); let highlight_state = ( @@ -384,20 +422,31 @@ pub fn draw_album_table(f: &mut Frame, app: &App, layout_chunk: Rect) where B: Backend, { - let header = [ - TableHeader { - text: "#", - width: 3, - }, - TableHeader { - text: "Title", - width: get_percentage_width(layout_chunk.width, 0.80), - }, - TableHeader { - text: "Length", - width: get_percentage_width(layout_chunk.width, 0.15), - }, - ]; + let header = TableHeader { + id: TableId::Album, + items: vec![ + TableHeaderItem { + id: ColumnId::Liked, + text: "", + width: 2, + }, + TableHeaderItem { + text: "#", + width: 3, + ..Default::default() + }, + TableHeaderItem { + id: ColumnId::SongTitle, + text: "Title", + width: get_percentage_width(layout_chunk.width, 0.80), + }, + TableHeaderItem { + text: "Length", + width: get_percentage_width(layout_chunk.width, 0.15), + ..Default::default() + }, + ], + }; let current_route = app.get_current_route(); let highlight_state = ( @@ -415,6 +464,7 @@ where .map(|item| TableItem { id: item.id.clone().unwrap_or_else(|| "".to_string()), format: vec![ + "".to_string(), item.track_number.to_string(), item.name.to_owned(), millis_to_minutes(u128::from(item.duration_ms)), @@ -441,6 +491,7 @@ where .map(|item| TableItem { id: item.id.clone().unwrap_or_else(|| "".to_string()), format: vec![ + "".to_string(), item.track_number.to_string(), item.name.to_owned(), millis_to_minutes(u128::from(item.duration_ms)), @@ -477,24 +528,36 @@ pub fn draw_song_table(f: &mut Frame, app: &App, layout_chunk: Rect) where B: Backend, { - let header = [ - TableHeader { - text: "Title", - width: get_percentage_width(layout_chunk.width, 0.3), - }, - TableHeader { - text: "Artist", - width: get_percentage_width(layout_chunk.width, 0.3), - }, - TableHeader { - text: "AlbumTracks", - width: get_percentage_width(layout_chunk.width, 0.3), - }, - TableHeader { - text: "Length", - width: get_percentage_width(layout_chunk.width, 0.1), - }, - ]; + let header = TableHeader { + id: TableId::Song, + items: vec![ + TableHeaderItem { + id: ColumnId::Liked, + text: "", + width: 2, + }, + TableHeaderItem { + id: ColumnId::SongTitle, + text: "Title", + width: get_percentage_width(layout_chunk.width, 0.3), + }, + TableHeaderItem { + text: "Artist", + width: get_percentage_width(layout_chunk.width, 0.3), + ..Default::default() + }, + TableHeaderItem { + text: "AlbumTracks", + width: get_percentage_width(layout_chunk.width, 0.3), + ..Default::default() + }, + TableHeaderItem { + text: "Length", + width: get_percentage_width(layout_chunk.width, 0.1), + ..Default::default() + }, + ], + }; let current_route = app.get_current_route(); let highlight_state = ( @@ -509,6 +572,7 @@ where .map(|item| TableItem { id: item.id.clone().unwrap_or_else(|| "".to_string()), format: vec![ + "".to_string(), item.name.to_owned(), create_artist_string(&item.artists), item.album.name.to_owned(), @@ -862,20 +926,26 @@ pub fn draw_album_list(f: &mut Frame, app: &App, layout_chunk: Rect) where B: Backend, { - let header = [ - TableHeader { - text: "Name", - width: get_percentage_width(layout_chunk.width, 2.0 / 5.0), - }, - TableHeader { - text: "Artists", - width: get_percentage_width(layout_chunk.width, 2.0 / 5.0), - }, - TableHeader { - text: "Release Date", - width: get_percentage_width(layout_chunk.width, 1.0 / 3.0), - }, - ]; + let header = TableHeader { + id: TableId::AlbumList, + items: vec![ + TableHeaderItem { + text: "Name", + width: get_percentage_width(layout_chunk.width, 2.0 / 5.0), + ..Default::default() + }, + TableHeaderItem { + text: "Artists", + width: get_percentage_width(layout_chunk.width, 2.0 / 5.0), + ..Default::default() + }, + TableHeaderItem { + text: "Release Date", + width: get_percentage_width(layout_chunk.width, 1.0 / 3.0), + ..Default::default() + }, + ], + }; let current_route = app.get_current_route(); @@ -916,20 +986,31 @@ pub fn draw_recently_played_table(f: &mut Frame, app: &App, layout_chunk: where B: Backend, { - let header = [ - TableHeader { - text: "Title", - width: get_percentage_width(layout_chunk.width, 2.0 / 5.0), - }, - TableHeader { - text: "Artist", - width: get_percentage_width(layout_chunk.width, 2.0 / 5.0), - }, - TableHeader { - text: "Length", - width: get_percentage_width(layout_chunk.width, 1.0 / 5.0), - }, - ]; + let header = TableHeader { + id: TableId::RecentlyPlayed, + items: vec![ + TableHeaderItem { + id: ColumnId::Liked, + text: "", + width: 2, + }, + TableHeaderItem { + id: ColumnId::SongTitle, + text: "Title", + width: get_percentage_width(layout_chunk.width, 2.0 / 5.0), + }, + TableHeaderItem { + text: "Artist", + width: get_percentage_width(layout_chunk.width, 2.0 / 5.0), + ..Default::default() + }, + TableHeaderItem { + text: "Length", + width: get_percentage_width(layout_chunk.width, 1.0 / 5.0), + ..Default::default() + }, + ], + }; if let Some(recently_played) = &app.recently_played.result { let current_route = app.get_current_route(); @@ -947,6 +1028,7 @@ where .map(|item| TableItem { id: item.track.id.clone().unwrap_or_else(|| "".to_string()), format: vec![ + "".to_string(), item.track.name.to_owned(), create_artist_string(&item.track.artists), millis_to_minutes(u128::from(item.track.duration_ms)), @@ -996,7 +1078,7 @@ fn draw_table( f: &mut Frame, app: &App, layout_chunk: Rect, - table_layout: (&str, &[TableHeader]), // (title, header colums) + table_layout: (&str, &TableHeader), // (title, header colums) items: &[TableItem], // The nested vector must have the same length as the `header_columns` selected_index: usize, highlight_state: (bool, bool), @@ -1013,17 +1095,33 @@ fn draw_table( None => None, }; + let (title, header) = table_layout; + let rows = items.iter().enumerate().map(|(i, item)| { - // Show this ♥ if the song is liked let mut formatted_row = item.format.clone(); let mut style = Style::default().fg(Color::White); // default styling - // First check if the song should be highlighted because it is currently playing - if let Some(_track_playing_index) = track_playing_index { - if i == _track_playing_index { - formatted_row[0] = format!("|> {}", &formatted_row[0]); - style = Style::default().fg(Color::Cyan).modifier(Modifier::BOLD); + // if table displays songs + match header.id { + TableId::Song | TableId::RecentlyPlayed | TableId::Album => { + // First check if the song should be highlighted because it is currently playing + if let Some(title_idx) = header.get_index(ColumnId::SongTitle) { + if let Some(_track_playing_index) = track_playing_index { + if i == _track_playing_index { + formatted_row[title_idx] = format!("|> {}", &formatted_row[title_idx]); + style = Style::default().fg(Color::Cyan).modifier(Modifier::BOLD); + } + } + } + + // Show this ♥ if the song is liked + if let Some(liked_idx) = header.get_index(ColumnId::Liked) { + if app.liked_song_ids_set.contains(item.id.as_str()) { + formatted_row[liked_idx] = " ♥".to_string(); + } + } } + _ => {} } // Next check if the item is under selection @@ -1035,11 +1133,9 @@ fn draw_table( Row::StyledData(formatted_row.into_iter(), style) }); - let (title, header_columns) = table_layout; - - let widths = header_columns.iter().map(|h| h.width).collect::>(); + let widths = header.items.iter().map(|h| h.width).collect::>(); - Table::new(header_columns.iter().map(|h| h.text), rows) + Table::new(header.items.iter().map(|h| h.text), rows) .block( Block::default() .borders(Borders::ALL)