diff --git a/src/app.rs b/src/app.rs index 8b06be8..88f6efc 100755 --- a/src/app.rs +++ b/src/app.rs @@ -22,6 +22,10 @@ pub struct App { pub inputmode: InputMode, pub search_input: String, pub cursor_position: usize, + + // add to playlists + pub playlist_popup: bool, + pub append_list: ContentList, } #[derive(Debug, PartialEq, Clone)] @@ -38,6 +42,7 @@ impl App { let mut pl_list = ContentList::new(); pl_list.list = Self::get_playlist(&mut conn.conn)?; + let append_list = Self::get_append_list(&mut conn.conn)?; Self::get_queue(&mut conn, &mut queue_list.list); let browser = FileBrowser::new(); @@ -51,6 +56,8 @@ impl App { inputmode: InputMode::Normal, search_input: String::new(), cursor_position: 0, + playlist_popup: false, + append_list, }) } @@ -91,6 +98,16 @@ impl App { Ok(list) } + pub fn get_append_list(conn: &mut Client) -> AppResult> { + let mut list = ContentList::new(); + list.list.push("Current Playlist".to_string()); + for item in Self::get_playlist(conn)? { + list.list.push(item.to_string()); + } + + Ok(list) + } + pub fn update_playlist(&mut self) -> AppResult<()> { Self::get_playlist(&mut self.conn.conn)?; Ok(()) @@ -105,21 +122,9 @@ impl App { } pub fn search_song(&mut self) -> AppResult<()> { - let list = self - .conn - .songs_filenames - .iter() - .map(|f| f.as_str()) - .collect::>(); - let (filename, _) = - rust_fuzzy_search::fuzzy_search_sorted(self.search_input.as_str(), &list) - .get(0) - .unwrap() - .clone(); - - let song = self.conn.get_song_with_only_filename(filename); + let filename = self.conn.get_full_path(&self.search_input)?; + let song = self.conn.get_song_with_only_filename(&filename); self.conn.push(&song)?; - Ok(()) } diff --git a/src/connection.rs b/src/connection.rs index b84d429..51444dd 100755 --- a/src/connection.rs +++ b/src/connection.rs @@ -4,6 +4,8 @@ use simple_dmenu::dmenu; use std::process::Command; use std::time::Duration; +use crate::app::AppResult; + pub type Result = core::result::Result; pub type Error = Box; @@ -143,6 +145,12 @@ impl Connection { Ok(()) } + /// Add given song to playlist + pub fn add_to_playlist(&mut self, playlist: &str, song: &Song) -> Result<()> { + self.conn.pl_push(playlist, song)?; + Ok(()) + } + /// Given a filename, get instance of Song with only filename pub fn get_song_with_only_filename(&self, filename: &str) -> Song { Song { @@ -158,6 +166,21 @@ impl Connection { } } + /// Given a song name from a directory, it returns the full path of the song in the database + pub fn get_full_path(&self, short_path: &str) -> AppResult { + let list = self + .songs_filenames + .iter() + .map(|f| f.as_str()) + .collect::>(); + let (filename, _) = rust_fuzzy_search::fuzzy_search_sorted(&short_path, &list) + .get(0) + .unwrap() + .clone(); + + Ok(filename.to_string()) + } + /// Print status to stdout pub fn status(&mut self) { let current_song = self.conn.currentsong(); diff --git a/src/handler.rs b/src/handler.rs index cb7c292..c74a3a8 100755 --- a/src/handler.rs +++ b/src/handler.rs @@ -6,7 +6,6 @@ use crate::{ }; use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; use rust_fuzzy_search::{self, fuzzy_search_sorted}; -use simple_dmenu::dmenu; pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { if app.inputmode == InputMode::Editing { @@ -68,6 +67,52 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { app.move_cursor_right(); } + _ => {} + } + } else if app.playlist_popup { + match key_event.code { + KeyCode::Char('q') | KeyCode::Esc => { + app.playlist_popup = false; + } + + KeyCode::Char('j') | KeyCode::Down => app.append_list.next(), + KeyCode::Char('k') | KeyCode::Up => app.append_list.prev(), + + KeyCode::Enter => { + let pl_index = app.append_list.index; + let pl_name = app.append_list.list.get(pl_index).unwrap(); + + let s_index: usize; + let mut short_path: String = String::new(); + match app.selected_tab { + SelectedTab::Queue => { + s_index = app.queue_list.index; + short_path = app.queue_list.list.get(s_index).unwrap().to_string(); + } + + SelectedTab::DirectoryBrowser => { + let (t, f) = app.browser.filetree.get(app.browser.selected).unwrap(); + if t == "file" { + short_path = f.to_string(); + } + } + _ => {} + } + + let full_path = app.conn.get_full_path(&short_path)?; + let song = app.conn.get_song_with_only_filename(&full_path); + + if pl_name == "Current Playlist" { + app.conn.conn.push(&song)?; + app.update_queue(); + } else { + app.conn.add_to_playlist(pl_name, &song)?; + } + + // hide the playlist popup + app.playlist_popup = false; + app.append_list.index = 0; + } _ => {} } } else { @@ -160,32 +205,34 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { // add to queue KeyCode::Char('a') => { - let list = app - .conn - .songs_filenames - .iter() - .map(|f| f.as_str()) - .collect::>(); + // let list = app + // .conn + // .songs_filenames + // .iter() + // .map(|f| f.as_str()) + // .collect::>(); + // + // let files: Vec = app + // .browser + // .filetree + // .clone() + // .into_iter() + // .map(|(_, f)| f) + // .collect(); + // + // let (filename, _) = rust_fuzzy_search::fuzzy_search_sorted( + // &files.get(app.browser.selected).unwrap(), + // &list, + // ) + // .get(0) + // .unwrap() + // .clone(); + // + // let song = app.conn.get_song_with_only_filename(filename); + // + // app.conn.conn.push(&song)?; - let files: Vec = app - .browser - .filetree - .clone() - .into_iter() - .map(|(_, f)| f) - .collect(); - - let (filename, _) = rust_fuzzy_search::fuzzy_search_sorted( - &files.get(app.browser.selected).unwrap(), - &list, - ) - .get(0) - .unwrap() - .clone(); - - let song = app.conn.get_song_with_only_filename(filename); - - app.conn.conn.push(&song)?; + app.playlist_popup = true; } KeyCode::Right => { diff --git a/src/ui.rs b/src/ui.rs index 2010362..0bd87af 100755 --- a/src/ui.rs +++ b/src/ui.rs @@ -21,11 +21,6 @@ impl InputMode { /// Renders the user interface widgets pub fn render(app: &mut App, frame: &mut Frame) { - // This is where you add new widgets. - // See the following resources: - // - https://docs.rs/ratatui/latest/ratatui/widgets/index.html - // - https://github.com/ratatui-org/ratatui/tree/master/examples - // Layout let layout = Layout::default() .direction(Direction::Vertical) @@ -46,6 +41,10 @@ pub fn render(app: &mut App, frame: &mut Frame) { draw_search_bar(frame, app, layout[1]); } } + + if app.playlist_popup { + draw_add_to_playlist(frame, app, layout[0]); + } } /// Draws the file tree browser @@ -238,3 +237,42 @@ fn draw_progress_bar(frame: &mut Frame, app: &mut App, size: Rect) { frame.render_widget(progress_bar, size); } + +fn draw_add_to_playlist(frame: &mut Frame, app: &mut App, area: Rect) { + let area = centered_rect(40, 50, area); + let mut state = ListState::default(); + let title = Block::default() + .title(Title::from("Add Selected Item to: ")) + .title(Title::from(" to Cancel".green().bold()).alignment(Alignment::Right)); + let list = List::new(app.append_list.list.clone()) + .block(title.borders(Borders::ALL)) + .highlight_style( + Style::new() + .fg(Color::Cyan) + .bg(Color::Black) + .add_modifier(Modifier::BOLD) + .add_modifier(Modifier::REVERSED), + ) + .highlight_symbol(">>") + .repeat_highlight_symbol(true); + + state.select(Some(app.append_list.index)); + frame.render_widget(Clear, area); //this clears out the background + frame.render_stateful_widget(list, area, &mut state); +} + +fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect { + let popup_layout = Layout::vertical([ + Constraint::Percentage((100 - percent_y) / 2), + Constraint::Percentage(percent_y), + Constraint::Percentage((100 - percent_y) / 2), + ]) + .split(r); + + Layout::horizontal([ + Constraint::Percentage((100 - percent_x) / 2), + Constraint::Percentage(percent_x), + Constraint::Percentage((100 - percent_x) / 2), + ]) + .split(popup_layout[1])[1] +}