From c8621da2f085df7c1b6331f491318457a69518ed Mon Sep 17 00:00:00 2001 From: Joe Ardent Date: Sun, 3 Aug 2025 12:16:38 -0700 Subject: [PATCH] add sending screen and keybinds --- src/app/mod.rs | 32 +++++++++++-- src/app/widgets.rs | 113 ++++++++++++++++++++++++++++++++------------- src/transfer.rs | 5 +- 3 files changed, 110 insertions(+), 40 deletions(-) diff --git a/src/app/mod.rs b/src/app/mod.rs index e875419..72a4e37 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -28,12 +28,18 @@ pub struct App { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum CurrentScreen { Main, - Sending, + Sending(SendingScreen), Receiving, Stopping, Logging, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SendingScreen { + Files, + Peers, +} + impl App { pub fn new(service: JoecalService, event_listener: UnboundedReceiver) -> Self { App { @@ -81,7 +87,7 @@ impl App { } pub fn handle_key_event(&mut self, key_event: KeyEvent) { - match self.screen.last().unwrap() { + match self.screen.last_mut().unwrap() { CurrentScreen::Logging => match key_event.code { KeyCode::Esc => self.pop(), KeyCode::Left => change_log_level(-1), @@ -98,6 +104,22 @@ impl App { KeyCode::Char('q') => self.exit(), _ => {} }, + CurrentScreen::Sending(s) => match s { + SendingScreen::Files => match key_event.code { + KeyCode::Esc => self.pop(), + KeyCode::Char('q') => self.exit(), + KeyCode::Tab => *s = SendingScreen::Peers, + KeyCode::Enter => todo!("send the selected file or enter directory"), + _ => todo!("have the file picker handle it"), + }, + SendingScreen::Peers => match key_event.code { + KeyCode::Esc => self.pop(), + KeyCode::Char('q') => self.exit(), + KeyCode::Tab => *s = SendingScreen::Files, + KeyCode::Enter => todo!("send to the selected peer"), + _ => {} + }, + }, _ => match key_event.code { KeyCode::Char('q') => self.exit(), KeyCode::Char('s') => self.send(), @@ -157,8 +179,10 @@ impl App { pub fn send(&mut self) { let last = self.screen.last(); match last { - Some(CurrentScreen::Sending) => {} - _ => self.screen.push(CurrentScreen::Sending), + Some(CurrentScreen::Sending(_)) => {} + _ => self + .screen + .push(CurrentScreen::Sending(SendingScreen::Files)), } } diff --git a/src/app/widgets.rs b/src/app/widgets.rs index d0c5399..1a39df9 100644 --- a/src/app/widgets.rs +++ b/src/app/widgets.rs @@ -12,7 +12,7 @@ use ratatui::{ }; use tui_logger::{TuiLoggerLevelOutput, TuiLoggerWidget, TuiWidgetState}; -use super::{App, CurrentScreen, Peers}; +use super::{App, CurrentScreen, Peers, SendingScreen}; static MAIN_MENU: LazyLock = LazyLock::new(|| { Line::from(vec![ @@ -42,7 +42,7 @@ static LOGGING_MENU: LazyLock = LazyLock::new(|| { ]) }); -static UPLOADS_MENU: LazyLock = LazyLock::new(|| { +static CONTENT_RECEIVE_MENU: LazyLock = LazyLock::new(|| { Line::from(vec![ " Select Previous ".into(), "".blue().bold(), @@ -59,11 +59,48 @@ static UPLOADS_MENU: LazyLock = LazyLock::new(|| { ]) }); +static CONTENT_SEND_FILE_MENU: LazyLock = LazyLock::new(|| { + Line::from(vec![ + " Select Previous ".into(), + "".blue().bold(), + " Select Next ".into(), + "".blue().bold(), + " Select ".into(), + "".blue().bold(), + " Parent Dir ".into(), + "".blue().bold(), + " Child Dir ".into(), + "".blue().bold(), + " Peers ".into(), + "".blue().bold(), + " Previous Screen ".into(), + "".blue().bold(), + " Quit ".into(), + "".blue().bold(), + ]) +}); + +static CONTENT_SEND_PEERS_MENU: LazyLock = LazyLock::new(|| { + Line::from(vec![ + " Select Previous ".into(), + "".blue().bold(), + " Select Next ".into(), + "".blue().bold(), + " Select ".into(), + "".blue().bold(), + " Files ".into(), + "".blue().bold(), + " Previous Screen ".into(), + "".blue().bold(), + " Quit ".into(), + "".blue().bold(), + ]) +}); + impl Widget for &mut App { fn render(self, area: Rect, buf: &mut Buffer) { - let main_layout = - Layout::vertical([Constraint::Min(5), Constraint::Min(10), Constraint::Min(3)]); - let [top, _middle, bottom] = main_layout.areas(area); + let main_layout = Layout::vertical([Constraint::Min(5), Constraint::Min(3)]); + let [top, bottom] = main_layout.areas(area); let footer_layout = Layout::horizontal([Constraint::Percentage(30), Constraint::Percentage(70)]); @@ -77,42 +114,68 @@ impl Widget for &mut App { let subscreen_margin = Margin::new(2, 4); - let mode = self.screen.last().unwrap(); - let ups: Vec<_> = self.receive_requests.values().collect(); - match mode { + let current_screen = self.screen.last().unwrap(); + + match current_screen { CurrentScreen::Main => { - main_page(*mode, &MAIN_MENU, area, buf); + let rx_reqs: Vec<_> = self.receive_requests.values().collect(); + outer_frame(*current_screen, &MAIN_MENU, area, buf); logger(header_right.inner(header_margin), buf); let peers = PeersWidget { peers: &self.peers }; peers.render(footer_right.inner(footer_margin), buf); NetworkInfoWidget.render(footer_left.inner(footer_margin), buf); - upload_requests( - &ups, + receive_requests( + &rx_reqs, &mut self.upload_state, header_left.inner(header_margin), buf, ); } CurrentScreen::Logging => { - main_page(*mode, &LOGGING_MENU, area, buf); + outer_frame(*current_screen, &LOGGING_MENU, area, buf); logger(area.inner(subscreen_margin), buf); } CurrentScreen::Receiving => { - main_page(*mode, &UPLOADS_MENU, area, buf); - upload_requests( - &ups, + let rx_reqs: Vec<_> = self.receive_requests.values().collect(); + outer_frame(*current_screen, &CONTENT_RECEIVE_MENU, area, buf); + receive_requests( + &rx_reqs, &mut self.upload_state, area.inner(subscreen_margin), buf, ); } + CurrentScreen::Sending(s) => match s { + SendingScreen::Files => { + outer_frame(*current_screen, &CONTENT_SEND_FILE_MENU, area, buf) + } + SendingScreen::Peers => { + outer_frame(*current_screen, &CONTENT_SEND_PEERS_MENU, area, buf) + } + }, _ => { - main_page(*mode, &MAIN_MENU, area, buf); + outer_frame(*current_screen, &MAIN_MENU, area, buf); } } } } +fn outer_frame(screen: CurrentScreen, menu: &Line, area: Rect, buf: &mut Buffer) { + let title = Line::from(" Joecalsend ".bold()); + let block = Block::bordered() + .title(title.centered()) + .title_bottom(menu.clone().centered()) + .border_set(border::THICK); + + let current_screen = format!("{screen:?}",); + let text = Text::from(Line::from(current_screen.yellow())); + + Paragraph::new(text) + .centered() + .block(block) + .render(area, buf); +} + fn logger(area: Rect, buf: &mut Buffer) { let title = Line::from(log::max_level().as_str()); let logger = TuiLoggerWidget::default() @@ -128,23 +191,7 @@ fn logger(area: Rect, buf: &mut Buffer) { logger.render(area, buf); } -fn main_page(screen: CurrentScreen, menu: &Line, area: Rect, buf: &mut Buffer) { - let title = Line::from(" Joecalsend ".bold()); - let block = Block::bordered() - .title(title.centered()) - .title_bottom(menu.clone().centered()) - .border_set(border::THICK); - - let current_screen = format!("{screen:?}",); - let text = Text::from(Line::from(current_screen.yellow())); - - Paragraph::new(text) - .centered() - .block(block) - .render(area, buf); -} - -fn upload_requests( +fn receive_requests( requests: &[&ReceiveRequest], state: &mut TableState, area: Rect, diff --git a/src/transfer.rs b/src/transfer.rs index ca885e9..62c15a0 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -215,10 +215,9 @@ pub async fn register_prepare_upload( } } - let confirmation = rx.recv().await; - - let Some(confirmation) = confirmation else { + let Some(confirmation) = rx.recv().await else { // the frontend must have dropped the tx before trying to send a reply back + warn!("could not read content receive response from the frontend"); return StatusCode::INTERNAL_SERVER_ERROR.into_response(); };