add sending screen and keybinds

This commit is contained in:
Joe Ardent 2025-08-03 12:16:38 -07:00
parent ce87f62317
commit c8621da2f0
3 changed files with 110 additions and 40 deletions

View file

@ -28,12 +28,18 @@ pub struct App {
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CurrentScreen { pub enum CurrentScreen {
Main, Main,
Sending, Sending(SendingScreen),
Receiving, Receiving,
Stopping, Stopping,
Logging, Logging,
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SendingScreen {
Files,
Peers,
}
impl App { impl App {
pub fn new(service: JoecalService, event_listener: UnboundedReceiver<TransferEvent>) -> Self { pub fn new(service: JoecalService, event_listener: UnboundedReceiver<TransferEvent>) -> Self {
App { App {
@ -81,7 +87,7 @@ impl App {
} }
pub fn handle_key_event(&mut self, key_event: KeyEvent) { 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 { CurrentScreen::Logging => match key_event.code {
KeyCode::Esc => self.pop(), KeyCode::Esc => self.pop(),
KeyCode::Left => change_log_level(-1), KeyCode::Left => change_log_level(-1),
@ -98,6 +104,22 @@ impl App {
KeyCode::Char('q') => self.exit(), 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 { _ => match key_event.code {
KeyCode::Char('q') => self.exit(), KeyCode::Char('q') => self.exit(),
KeyCode::Char('s') => self.send(), KeyCode::Char('s') => self.send(),
@ -157,8 +179,10 @@ impl App {
pub fn send(&mut self) { pub fn send(&mut self) {
let last = self.screen.last(); let last = self.screen.last();
match last { match last {
Some(CurrentScreen::Sending) => {} Some(CurrentScreen::Sending(_)) => {}
_ => self.screen.push(CurrentScreen::Sending), _ => self
.screen
.push(CurrentScreen::Sending(SendingScreen::Files)),
} }
} }

View file

@ -12,7 +12,7 @@ use ratatui::{
}; };
use tui_logger::{TuiLoggerLevelOutput, TuiLoggerWidget, TuiWidgetState}; use tui_logger::{TuiLoggerLevelOutput, TuiLoggerWidget, TuiWidgetState};
use super::{App, CurrentScreen, Peers}; use super::{App, CurrentScreen, Peers, SendingScreen};
static MAIN_MENU: LazyLock<Line> = LazyLock::new(|| { static MAIN_MENU: LazyLock<Line> = LazyLock::new(|| {
Line::from(vec![ Line::from(vec![
@ -42,7 +42,7 @@ static LOGGING_MENU: LazyLock<Line> = LazyLock::new(|| {
]) ])
}); });
static UPLOADS_MENU: LazyLock<Line> = LazyLock::new(|| { static CONTENT_RECEIVE_MENU: LazyLock<Line> = LazyLock::new(|| {
Line::from(vec![ Line::from(vec![
" Select Previous ".into(), " Select Previous ".into(),
"<UP>".blue().bold(), "<UP>".blue().bold(),
@ -59,11 +59,48 @@ static UPLOADS_MENU: LazyLock<Line> = LazyLock::new(|| {
]) ])
}); });
static CONTENT_SEND_FILE_MENU: LazyLock<Line> = LazyLock::new(|| {
Line::from(vec![
" Select Previous ".into(),
"<UP>".blue().bold(),
" Select Next ".into(),
"<DOWN>".blue().bold(),
" Select ".into(),
"<ENTER>".blue().bold(),
" Parent Dir ".into(),
"<LEFT>".blue().bold(),
" Child Dir ".into(),
"<RIGHT>".blue().bold(),
" Peers ".into(),
"<TAB>".blue().bold(),
" Previous Screen ".into(),
"<ESC>".blue().bold(),
" Quit ".into(),
"<Q>".blue().bold(),
])
});
static CONTENT_SEND_PEERS_MENU: LazyLock<Line> = LazyLock::new(|| {
Line::from(vec![
" Select Previous ".into(),
"<UP>".blue().bold(),
" Select Next ".into(),
"<DOWN>".blue().bold(),
" Select ".into(),
"<ENTER>".blue().bold(),
" Files ".into(),
"<TAB>".blue().bold(),
" Previous Screen ".into(),
"<ESC>".blue().bold(),
" Quit ".into(),
"<Q>".blue().bold(),
])
});
impl Widget for &mut App { impl Widget for &mut App {
fn render(self, area: Rect, buf: &mut Buffer) { fn render(self, area: Rect, buf: &mut Buffer) {
let main_layout = let main_layout = Layout::vertical([Constraint::Min(5), Constraint::Min(3)]);
Layout::vertical([Constraint::Min(5), Constraint::Min(10), Constraint::Min(3)]); let [top, bottom] = main_layout.areas(area);
let [top, _middle, bottom] = main_layout.areas(area);
let footer_layout = let footer_layout =
Layout::horizontal([Constraint::Percentage(30), Constraint::Percentage(70)]); 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 subscreen_margin = Margin::new(2, 4);
let mode = self.screen.last().unwrap(); let current_screen = self.screen.last().unwrap();
let ups: Vec<_> = self.receive_requests.values().collect();
match mode { match current_screen {
CurrentScreen::Main => { 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); logger(header_right.inner(header_margin), buf);
let peers = PeersWidget { peers: &self.peers }; let peers = PeersWidget { peers: &self.peers };
peers.render(footer_right.inner(footer_margin), buf); peers.render(footer_right.inner(footer_margin), buf);
NetworkInfoWidget.render(footer_left.inner(footer_margin), buf); NetworkInfoWidget.render(footer_left.inner(footer_margin), buf);
upload_requests( receive_requests(
&ups, &rx_reqs,
&mut self.upload_state, &mut self.upload_state,
header_left.inner(header_margin), header_left.inner(header_margin),
buf, buf,
); );
} }
CurrentScreen::Logging => { CurrentScreen::Logging => {
main_page(*mode, &LOGGING_MENU, area, buf); outer_frame(*current_screen, &LOGGING_MENU, area, buf);
logger(area.inner(subscreen_margin), buf); logger(area.inner(subscreen_margin), buf);
} }
CurrentScreen::Receiving => { CurrentScreen::Receiving => {
main_page(*mode, &UPLOADS_MENU, area, buf); let rx_reqs: Vec<_> = self.receive_requests.values().collect();
upload_requests( outer_frame(*current_screen, &CONTENT_RECEIVE_MENU, area, buf);
&ups, receive_requests(
&rx_reqs,
&mut self.upload_state, &mut self.upload_state,
area.inner(subscreen_margin), area.inner(subscreen_margin),
buf, 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) { fn logger(area: Rect, buf: &mut Buffer) {
let title = Line::from(log::max_level().as_str()); let title = Line::from(log::max_level().as_str());
let logger = TuiLoggerWidget::default() let logger = TuiLoggerWidget::default()
@ -128,23 +191,7 @@ fn logger(area: Rect, buf: &mut Buffer) {
logger.render(area, buf); logger.render(area, buf);
} }
fn main_page(screen: CurrentScreen, menu: &Line, area: Rect, buf: &mut Buffer) { fn receive_requests(
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(
requests: &[&ReceiveRequest], requests: &[&ReceiveRequest],
state: &mut TableState, state: &mut TableState,
area: Rect, area: Rect,

View file

@ -215,10 +215,9 @@ pub async fn register_prepare_upload(
} }
} }
let confirmation = rx.recv().await; let Some(confirmation) = rx.recv().await else {
let Some(confirmation) = confirmation else {
// the frontend must have dropped the tx before trying to send a reply back // 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(); return StatusCode::INTERNAL_SERVER_ERROR.into_response();
}; };