1 use super::{ 2 super::app::{App, RecommendationsContext, TrackTable, TrackTableContext}, 3 common_key_events, 4 }; 5 use crate::event::Key; 6 use crate::network::IoEvent; 7 use rand::{thread_rng, Rng}; 8 use serde_json::from_value; 9 10 pub fn handler(key: Key, app: &mut App) { 11 match key { 12 k if common_key_events::left_event(k) => common_key_events::handle_left_event(app), 13 k if common_key_events::down_event(k) => { 14 let next_index = common_key_events::on_down_press_handler( 15 &app.track_table.tracks, 16 Some(app.track_table.selected_index), 17 ); 18 app.track_table.selected_index = next_index; 19 } 20 k if common_key_events::up_event(k) => { 21 let next_index = common_key_events::on_up_press_handler( 22 &app.track_table.tracks, 23 Some(app.track_table.selected_index), 24 ); 25 app.track_table.selected_index = next_index; 26 } 27 k if common_key_events::high_event(k) => { 28 let next_index = common_key_events::on_high_press_handler(); 29 app.track_table.selected_index = next_index; 30 } 31 k if common_key_events::middle_event(k) => { 32 let next_index = common_key_events::on_middle_press_handler(&app.track_table.tracks); 33 app.track_table.selected_index = next_index; 34 } 35 k if common_key_events::low_event(k) => { 36 let next_index = common_key_events::on_low_press_handler(&app.track_table.tracks); 37 app.track_table.selected_index = next_index; 38 } 39 Key::Enter => { 40 on_enter(app); 41 } 42 // Scroll down 43 k if k == app.user_config.keys.next_page => { 44 match &app.track_table.context { 45 Some(context) => match context { 46 TrackTableContext::MyPlaylists => { 47 if let (Some(playlists), Some(selected_playlist_index)) = 48 (&app.playlists, &app.selected_playlist_index) 49 { 50 if let Some(selected_playlist) = 51 playlists.items.get(selected_playlist_index.to_owned()) 52 { 53 if let Some(playlist_tracks) = &app.playlist_tracks { 54 if app.playlist_offset + app.large_search_limit < playlist_tracks.total { 55 app.playlist_offset += app.large_search_limit; 56 let playlist_id = selected_playlist.id.to_owned(); 57 app.dispatch(IoEvent::GetPlaylistTracks(playlist_id, app.playlist_offset)); 58 } 59 } 60 } 61 }; 62 } 63 TrackTableContext::RecommendedTracks => {} 64 TrackTableContext::SavedTracks => { 65 app.get_current_user_saved_tracks_next(); 66 } 67 TrackTableContext::AlbumSearch => {} 68 TrackTableContext::PlaylistSearch => {} 69 TrackTableContext::MadeForYou => { 70 let (playlists, selected_playlist_index) = 71 (&app.library.made_for_you_playlists, &app.made_for_you_index); 72 73 if let Some(selected_playlist) = playlists 74 .get_results(Some(0)) 75 .unwrap() 76 .items 77 .get(selected_playlist_index.to_owned()) 78 { 79 if let Some(playlist_tracks) = &app.made_for_you_tracks { 80 if app.made_for_you_offset + app.large_search_limit < playlist_tracks.total { 81 app.made_for_you_offset += app.large_search_limit; 82 let playlist_id = selected_playlist.id.to_owned(); 83 app.dispatch(IoEvent::GetMadeForYouPlaylistTracks( 84 playlist_id, 85 app.made_for_you_offset, 86 )); 87 } 88 } 89 } 90 } 91 }, 92 None => {} 93 }; 94 } 95 // Scroll up 96 k if k == app.user_config.keys.previous_page => { 97 match &app.track_table.context { 98 Some(context) => match context { 99 TrackTableContext::MyPlaylists => { 100 if let (Some(playlists), Some(selected_playlist_index)) = 101 (&app.playlists, &app.selected_playlist_index) 102 { 103 if app.playlist_offset >= app.large_search_limit { 104 app.playlist_offset -= app.large_search_limit; 105 }; 106 if let Some(selected_playlist) = 107 playlists.items.get(selected_playlist_index.to_owned()) 108 { 109 let playlist_id = selected_playlist.id.to_owned(); 110 app.dispatch(IoEvent::GetPlaylistTracks(playlist_id, app.playlist_offset)); 111 } 112 }; 113 } 114 TrackTableContext::RecommendedTracks => {} 115 TrackTableContext::SavedTracks => { 116 app.get_current_user_saved_tracks_previous(); 117 } 118 TrackTableContext::AlbumSearch => {} 119 TrackTableContext::PlaylistSearch => {} 120 TrackTableContext::MadeForYou => { 121 let (playlists, selected_playlist_index) = ( 122 &app 123 .library 124 .made_for_you_playlists 125 .get_results(Some(0)) 126 .unwrap(), 127 app.made_for_you_index, 128 ); 129 if app.made_for_you_offset >= app.large_search_limit { 130 app.made_for_you_offset -= app.large_search_limit; 131 } 132 if let Some(selected_playlist) = playlists.items.get(selected_playlist_index) { 133 let playlist_id = selected_playlist.id.to_owned(); 134 app.dispatch(IoEvent::GetMadeForYouPlaylistTracks( 135 playlist_id, 136 app.made_for_you_offset, 137 )); 138 } 139 } 140 }, 141 None => {} 142 }; 143 } 144 Key::Char('s') => handle_save_track_event(app), 145 Key::Char('S') => play_random_song(app), 146 k if k == app.user_config.keys.jump_to_end => jump_to_end(app), 147 k if k == app.user_config.keys.jump_to_start => jump_to_start(app), 148 //recommended song radio 149 Key::Char('r') => { 150 handle_recommended_tracks(app); 151 } 152 _ if key == app.user_config.keys.add_item_to_queue => on_queue(app), 153 _ => {} 154 } 155 } 156 157 fn play_random_song(app: &mut App) { 158 if let Some(context) = &app.track_table.context { 159 match context { 160 TrackTableContext::MyPlaylists => { 161 let (context_uri, track_json) = match (&app.selected_playlist_index, &app.playlists) { 162 (Some(selected_playlist_index), Some(playlists)) => { 163 if let Some(selected_playlist) = playlists.items.get(selected_playlist_index.to_owned()) 164 { 165 ( 166 Some(selected_playlist.uri.to_owned()), 167 selected_playlist.tracks.get("total"), 168 ) 169 } else { 170 (None, None) 171 } 172 } 173 _ => (None, None), 174 }; 175 176 if let Some(val) = track_json { 177 let num_tracks: usize = from_value(val.clone()).unwrap(); 178 app.dispatch(IoEvent::StartPlayback( 179 context_uri, 180 None, 181 Some(thread_rng().gen_range(0..num_tracks)), 182 )); 183 } 184 } 185 TrackTableContext::RecommendedTracks => {} 186 TrackTableContext::SavedTracks => { 187 if let Some(saved_tracks) = &app.library.saved_tracks.get_results(None) { 188 let track_uris: Vec<String> = saved_tracks 189 .items 190 .iter() 191 .map(|item| item.track.uri.to_owned()) 192 .collect(); 193 let rand_idx = thread_rng().gen_range(0..track_uris.len()); 194 app.dispatch(IoEvent::StartPlayback( 195 None, 196 Some(track_uris), 197 Some(rand_idx), 198 )) 199 } 200 } 201 TrackTableContext::AlbumSearch => {} 202 TrackTableContext::PlaylistSearch => { 203 let (context_uri, playlist_track_json) = match ( 204 &app.search_results.selected_playlists_index, 205 &app.search_results.playlists, 206 ) { 207 (Some(selected_playlist_index), Some(playlist_result)) => { 208 if let Some(selected_playlist) = playlist_result 209 .items 210 .get(selected_playlist_index.to_owned()) 211 { 212 ( 213 Some(selected_playlist.uri.to_owned()), 214 selected_playlist.tracks.get("total"), 215 ) 216 } else { 217 (None, None) 218 } 219 } 220 _ => (None, None), 221 }; 222 if let Some(val) = playlist_track_json { 223 let num_tracks: usize = from_value(val.clone()).unwrap(); 224 app.dispatch(IoEvent::StartPlayback( 225 context_uri, 226 None, 227 Some(thread_rng().gen_range(0..num_tracks)), 228 )) 229 } 230 } 231 TrackTableContext::MadeForYou => { 232 if let Some(playlist) = &app 233 .library 234 .made_for_you_playlists 235 .get_results(Some(0)) 236 .and_then(|playlist| playlist.items.get(app.made_for_you_index)) 237 { 238 if let Some(num_tracks) = &playlist 239 .tracks 240 .get("total") 241 .and_then(|total| -> Option<usize> { from_value(total.clone()).ok() }) 242 { 243 let uri = Some(playlist.uri.clone()); 244 app.dispatch(IoEvent::StartPlayback( 245 uri, 246 None, 247 Some(thread_rng().gen_range(0..*num_tracks)), 248 )) 249 }; 250 }; 251 } 252 } 253 }; 254 } 255 256 fn handle_save_track_event(app: &mut App) { 257 let (selected_index, tracks) = (&app.track_table.selected_index, &app.track_table.tracks); 258 if let Some(track) = tracks.get(*selected_index) { 259 if let Some(id) = &track.id { 260 let id = id.to_string(); 261 app.dispatch(IoEvent::ToggleSaveTrack(id)); 262 }; 263 }; 264 } 265 266 fn handle_recommended_tracks(app: &mut App) { 267 let (selected_index, tracks) = (&app.track_table.selected_index, &app.track_table.tracks); 268 if let Some(track) = tracks.get(*selected_index) { 269 let first_track = track.clone(); 270 let track_id_list = track.id.as_ref().map(|id| vec![id.to_string()]); 271 272 app.recommendations_context = Some(RecommendationsContext::Song); 273 app.recommendations_seed = first_track.name.clone(); 274 app.get_recommendations_for_seed(None, track_id_list, Some(first_track)); 275 }; 276 } 277 278 fn jump_to_end(app: &mut App) { 279 match &app.track_table.context { 280 Some(context) => match context { 281 TrackTableContext::MyPlaylists => { 282 if let (Some(playlists), Some(selected_playlist_index)) = 283 (&app.playlists, &app.selected_playlist_index) 284 { 285 if let Some(selected_playlist) = playlists.items.get(selected_playlist_index.to_owned()) { 286 let total_tracks = selected_playlist 287 .tracks 288 .get("total") 289 .and_then(|total| total.as_u64()) 290 .expect("playlist.tracks object should have a total field") 291 as u32; 292 293 if app.large_search_limit < total_tracks { 294 app.playlist_offset = total_tracks - (total_tracks % app.large_search_limit); 295 let playlist_id = selected_playlist.id.to_owned(); 296 app.dispatch(IoEvent::GetPlaylistTracks(playlist_id, app.playlist_offset)); 297 } 298 } 299 } 300 } 301 TrackTableContext::RecommendedTracks => {} 302 TrackTableContext::SavedTracks => {} 303 TrackTableContext::AlbumSearch => {} 304 TrackTableContext::PlaylistSearch => {} 305 TrackTableContext::MadeForYou => {} 306 }, 307 None => {} 308 } 309 } 310 311 fn on_enter(app: &mut App) { 312 let TrackTable { 313 context, 314 selected_index, 315 tracks, 316 } = &app.track_table; 317 match &context { 318 Some(context) => match context { 319 TrackTableContext::MyPlaylists => { 320 if let Some(_track) = tracks.get(*selected_index) { 321 let context_uri = match (&app.active_playlist_index, &app.playlists) { 322 (Some(active_playlist_index), Some(playlists)) => playlists 323 .items 324 .get(active_playlist_index.to_owned()) 325 .map(|selected_playlist| selected_playlist.uri.to_owned()), 326 _ => None, 327 }; 328 329 app.dispatch(IoEvent::StartPlayback( 330 context_uri, 331 None, 332 Some(app.track_table.selected_index + app.playlist_offset as usize), 333 )); 334 }; 335 } 336 TrackTableContext::RecommendedTracks => { 337 app.dispatch(IoEvent::StartPlayback( 338 None, 339 Some( 340 app 341 .recommended_tracks 342 .iter() 343 .map(|x| x.uri.clone()) 344 .collect::<Vec<String>>(), 345 ), 346 Some(app.track_table.selected_index), 347 )); 348 } 349 TrackTableContext::SavedTracks => { 350 if let Some(saved_tracks) = &app.library.saved_tracks.get_results(None) { 351 let track_uris: Vec<String> = saved_tracks 352 .items 353 .iter() 354 .map(|item| item.track.uri.to_owned()) 355 .collect(); 356 357 app.dispatch(IoEvent::StartPlayback( 358 None, 359 Some(track_uris), 360 Some(app.track_table.selected_index), 361 )); 362 }; 363 } 364 TrackTableContext::AlbumSearch => {} 365 TrackTableContext::PlaylistSearch => { 366 let TrackTable { 367 selected_index, 368 tracks, 369 .. 370 } = &app.track_table; 371 if let Some(_track) = tracks.get(*selected_index) { 372 let context_uri = match ( 373 &app.search_results.selected_playlists_index, 374 &app.search_results.playlists, 375 ) { 376 (Some(selected_playlist_index), Some(playlist_result)) => playlist_result 377 .items 378 .get(selected_playlist_index.to_owned()) 379 .map(|selected_playlist| selected_playlist.uri.to_owned()), 380 _ => None, 381 }; 382 383 app.dispatch(IoEvent::StartPlayback( 384 context_uri, 385 None, 386 Some(app.track_table.selected_index), 387 )); 388 }; 389 } 390 TrackTableContext::MadeForYou => { 391 if let Some(_track) = tracks.get(*selected_index) { 392 let context_uri = Some( 393 app 394 .library 395 .made_for_you_playlists 396 .get_results(Some(0)) 397 .unwrap() 398 .items 399 .get(app.made_for_you_index) 400 .unwrap() 401 .uri 402 .to_owned(), 403 ); 404 405 app.dispatch(IoEvent::StartPlayback( 406 context_uri, 407 None, 408 Some(app.track_table.selected_index + app.made_for_you_offset as usize), 409 )); 410 } 411 } 412 }, 413 None => {} 414 }; 415 } 416 417 fn on_queue(app: &mut App) { 418 let TrackTable { 419 context, 420 selected_index, 421 tracks, 422 } = &app.track_table; 423 match &context { 424 Some(context) => match context { 425 TrackTableContext::MyPlaylists => { 426 if let Some(track) = tracks.get(*selected_index) { 427 let uri = track.uri.clone(); 428 app.dispatch(IoEvent::AddItemToQueue(uri)); 429 }; 430 } 431 TrackTableContext::RecommendedTracks => { 432 if let Some(full_track) = app.recommended_tracks.get(app.track_table.selected_index) { 433 let uri = full_track.uri.clone(); 434 app.dispatch(IoEvent::AddItemToQueue(uri)); 435 } 436 } 437 TrackTableContext::SavedTracks => { 438 if let Some(page) = app.library.saved_tracks.get_results(None) { 439 if let Some(saved_track) = page.items.get(app.track_table.selected_index) { 440 let uri = saved_track.track.uri.clone(); 441 app.dispatch(IoEvent::AddItemToQueue(uri)); 442 } 443 } 444 } 445 TrackTableContext::AlbumSearch => {} 446 TrackTableContext::PlaylistSearch => { 447 let TrackTable { 448 selected_index, 449 tracks, 450 .. 451 } = &app.track_table; 452 if let Some(track) = tracks.get(*selected_index) { 453 let uri = track.uri.clone(); 454 app.dispatch(IoEvent::AddItemToQueue(uri)); 455 }; 456 } 457 TrackTableContext::MadeForYou => { 458 if let Some(track) = tracks.get(*selected_index) { 459 let uri = track.uri.clone(); 460 app.dispatch(IoEvent::AddItemToQueue(uri)); 461 } 462 } 463 }, 464 None => {} 465 }; 466 } 467 468 fn jump_to_start(app: &mut App) { 469 match &app.track_table.context { 470 Some(context) => match context { 471 TrackTableContext::MyPlaylists => { 472 if let (Some(playlists), Some(selected_playlist_index)) = 473 (&app.playlists, &app.selected_playlist_index) 474 { 475 if let Some(selected_playlist) = playlists.items.get(selected_playlist_index.to_owned()) { 476 app.playlist_offset = 0; 477 let playlist_id = selected_playlist.id.to_owned(); 478 app.dispatch(IoEvent::GetPlaylistTracks(playlist_id, app.playlist_offset)); 479 } 480 } 481 } 482 TrackTableContext::RecommendedTracks => {} 483 TrackTableContext::SavedTracks => {} 484 TrackTableContext::AlbumSearch => {} 485 TrackTableContext::PlaylistSearch => {} 486 TrackTableContext::MadeForYou => {} 487 }, 488 None => {} 489 } 490 } 491