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