able to accept or reject upload requests from the tui
This commit is contained in:
parent
f9efd37d00
commit
a7cfe419b4
4 changed files with 102 additions and 22 deletions
|
@ -4,11 +4,10 @@ use crossterm::event::{Event, EventStream, KeyCode, KeyEvent, KeyEventKind};
|
|||
use futures::{FutureExt, StreamExt};
|
||||
use joecalsend::{
|
||||
Config, JoecalState, JoecalUploadRequest, Listeners, TransferEvent, UploadDialog,
|
||||
error::{LocalSendError, Result},
|
||||
models::Device,
|
||||
error::Result, models::Device,
|
||||
};
|
||||
use julid::Julid;
|
||||
use log::{LevelFilter, debug, error, info};
|
||||
use log::{LevelFilter, debug, error, info, warn};
|
||||
use ratatui::{DefaultTerminal, Frame, widgets::TableState};
|
||||
use tokio::{
|
||||
sync::mpsc::{UnboundedReceiver, unbounded_channel},
|
||||
|
@ -147,6 +146,15 @@ impl App {
|
|||
KeyCode::Char('q') => self.exit(),
|
||||
_ => {}
|
||||
},
|
||||
CurrentScreen::Receiving => match key_event.code {
|
||||
KeyCode::Up => self.upload_state.select_previous(),
|
||||
KeyCode::Down => self.upload_state.select_next(),
|
||||
KeyCode::Char('a') => self.accept(),
|
||||
KeyCode::Char('d') => self.deny(),
|
||||
KeyCode::Esc => self.pop(),
|
||||
KeyCode::Char('q') => self.exit(),
|
||||
_ => {}
|
||||
},
|
||||
_ => match key_event.code {
|
||||
KeyCode::Char('q') => self.exit(),
|
||||
KeyCode::Char('s') => self.send(),
|
||||
|
@ -158,6 +166,43 @@ impl App {
|
|||
}
|
||||
}
|
||||
|
||||
fn accept(&mut self) {
|
||||
let Some(idx) = self.upload_state.selected() else {
|
||||
return;
|
||||
};
|
||||
// keys are sorted, so we can use the table selection index
|
||||
let keys: Vec<_> = self.uploads.keys().collect();
|
||||
let Some(key) = keys.get(idx) else {
|
||||
warn!("could not get id from selection index {idx}");
|
||||
return;
|
||||
};
|
||||
let Some(req) = self.uploads.get(key) else {
|
||||
return;
|
||||
};
|
||||
if let Err(e) = req.tx.send(UploadDialog::UploadConfirm) {
|
||||
error!("got error sending upload confirmation: {e:?}");
|
||||
};
|
||||
}
|
||||
|
||||
fn deny(&mut self) {
|
||||
let Some(idx) = self.upload_state.selected() else {
|
||||
return;
|
||||
};
|
||||
// keys are sorted, so we can use the table selection index
|
||||
let keys: Vec<_> = self.uploads.keys().cloned().collect();
|
||||
let Some(key) = keys.get(idx) else {
|
||||
warn!("could not get id from selection index {idx}");
|
||||
return;
|
||||
};
|
||||
let Some(req) = self.uploads.get(key).cloned() else {
|
||||
return;
|
||||
};
|
||||
if let Err(e) = req.tx.send(UploadDialog::UploadDeny) {
|
||||
error!("got error sending upload confirmation: {e:?}");
|
||||
};
|
||||
self.uploads.remove(key);
|
||||
}
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame) {
|
||||
frame.render_widget(self, frame.area());
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use log::LevelFilter;
|
|||
use ratatui::{
|
||||
buffer::Buffer,
|
||||
layout::{Constraint, Layout, Margin, Rect},
|
||||
style::{Style, Stylize},
|
||||
style::{Color, Style, Stylize},
|
||||
symbols::border,
|
||||
text::{Line, Text, ToLine},
|
||||
widgets::{Block, Borders, List, ListItem, Padding, Paragraph, Row, Table, TableState, Widget},
|
||||
|
@ -42,6 +42,23 @@ static LOGGING_MENU: LazyLock<Line> = LazyLock::new(|| {
|
|||
])
|
||||
});
|
||||
|
||||
static UPLOADS_MENU: LazyLock<Line> = LazyLock::new(|| {
|
||||
Line::from(vec![
|
||||
" Select Previous ".into(),
|
||||
"<UP>".blue().bold(),
|
||||
" Select Next ".into(),
|
||||
"<DOWN>".blue().bold(),
|
||||
" Approve Selection ".into(),
|
||||
"<A>".blue().bold(),
|
||||
" Deny Selection ".into(),
|
||||
"<D>".blue().bold(),
|
||||
" Previous Screen ".into(),
|
||||
"<ESC>".blue().bold(),
|
||||
" Quit ".into(),
|
||||
"<Q>".blue().bold(),
|
||||
])
|
||||
});
|
||||
|
||||
impl Widget for &mut App {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let main_layout =
|
||||
|
@ -59,6 +76,7 @@ impl Widget for &mut App {
|
|||
let header_margin = Margin::new(1, 2);
|
||||
|
||||
let mode = self.screen.last().unwrap();
|
||||
let ups: Vec<_> = self.uploads.values().collect();
|
||||
match mode {
|
||||
CurrentScreen::Main => {
|
||||
main_page(*mode, &MAIN_MENU, area, buf);
|
||||
|
@ -66,7 +84,6 @@ impl Widget for &mut App {
|
|||
let peers = PeersWidget { peers: &self.peers };
|
||||
peers.render(footer_right.inner(footer_margin), buf);
|
||||
NetworkInfoWidget.render(footer_left.inner(footer_margin), buf);
|
||||
let ups: Vec<_> = self.uploads.values().collect();
|
||||
uploads(
|
||||
&ups,
|
||||
&mut self.upload_state,
|
||||
|
@ -79,7 +96,13 @@ impl Widget for &mut App {
|
|||
logger(area.inner(Margin::new(2, 4)), buf);
|
||||
}
|
||||
CurrentScreen::Receiving => {
|
||||
main_page(*mode, &MAIN_MENU, area, buf);
|
||||
main_page(*mode, &UPLOADS_MENU, area, buf);
|
||||
uploads(
|
||||
&ups,
|
||||
&mut self.upload_state,
|
||||
area.inner(Margin::new(2, 4)),
|
||||
buf,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
main_page(*mode, &MAIN_MENU, area, buf);
|
||||
|
@ -97,11 +120,7 @@ fn logger(area: Rect, buf: &mut Buffer) {
|
|||
.output_target(true)
|
||||
.output_file(false)
|
||||
.output_line(false)
|
||||
.block(
|
||||
Block::bordered()
|
||||
.border_set(border::THICK)
|
||||
.title(title.centered()),
|
||||
)
|
||||
.block(Block::bordered().title(title.centered()))
|
||||
.style(Style::default())
|
||||
.state(&TuiWidgetState::new().set_default_display_level(LevelFilter::Debug));
|
||||
logger.render(area, buf);
|
||||
|
@ -130,9 +149,8 @@ fn uploads(
|
|||
buf: &mut Buffer,
|
||||
) {
|
||||
let title = Line::from(" Upload Requests ").bold();
|
||||
let block = Block::bordered()
|
||||
.title(title.centered())
|
||||
.border_set(border::THICK);
|
||||
let block = Block::bordered().title(title.centered());
|
||||
|
||||
let mut rows = Vec::new();
|
||||
for &req in requests {
|
||||
let src = req.alias.to_line().left_aligned();
|
||||
|
@ -150,16 +168,27 @@ fn uploads(
|
|||
let size = Line::from(format!("{size}")).centered();
|
||||
rows.push(Row::new([src, size, files]));
|
||||
}
|
||||
|
||||
if state.selected().is_none() && !rows.is_empty() {
|
||||
state.select(Some(0));
|
||||
} else if rows.is_empty() {
|
||||
state.select(None);
|
||||
};
|
||||
|
||||
let widths = [
|
||||
Constraint::Max(20),
|
||||
Constraint::Max(15),
|
||||
Constraint::Min(50),
|
||||
];
|
||||
let table = Table::new(rows, widths).block(block).header(Row::new([
|
||||
"Sender".bold().into_left_aligned_line(),
|
||||
"Bytes".bold().into_centered_line(),
|
||||
"Files".bold().into_centered_line(),
|
||||
]));
|
||||
|
||||
let table = Table::new(rows, widths)
|
||||
.block(block)
|
||||
.header(Row::new([
|
||||
"Sender".bold().into_left_aligned_line(),
|
||||
"Bytes".bold().into_centered_line(),
|
||||
"Files".bold().into_centered_line(),
|
||||
]))
|
||||
.row_highlight_style(Style::new().bg(Color::Rgb(99, 99, 99)));
|
||||
|
||||
ratatui::widgets::StatefulWidget::render(table, area, buf, state);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use axum::{
|
|||
Json,
|
||||
extract::{ConnectInfo, State},
|
||||
};
|
||||
use log::{debug, error, warn};
|
||||
use log::{debug, error, trace, warn};
|
||||
use tokio::net::UdpSocket;
|
||||
|
||||
use crate::{Config, JoecalState, RunningState, models::Device};
|
||||
|
@ -19,7 +19,7 @@ impl JoecalState {
|
|||
socket: Option<SocketAddr>,
|
||||
config: &Config,
|
||||
) -> crate::error::Result<()> {
|
||||
debug!("announcing");
|
||||
trace!("announcing");
|
||||
announce_http(&self.device, socket, self.client.clone()).await?;
|
||||
announce_multicast(&self.device, config.multicast_addr, self.socket.clone()).await?;
|
||||
Ok(())
|
||||
|
@ -44,7 +44,7 @@ impl JoecalState {
|
|||
}
|
||||
},
|
||||
r = self.socket.recv_from(&mut buf) => {
|
||||
debug!("received multicast datagram");
|
||||
trace!("received multicast datagram");
|
||||
match r {
|
||||
Ok((size, src)) => {
|
||||
let received_msg = String::from_utf8_lossy(&buf[..size]);
|
||||
|
|
|
@ -321,6 +321,12 @@ pub async fn register_upload(
|
|||
.into_response();
|
||||
}
|
||||
|
||||
if let Ok(id) = Julid::from_str(session_id)
|
||||
&& let Err(e) = state.transfer_event_tx.send(TransferEvent::Received(id))
|
||||
{
|
||||
error!("got error sending upload received event: {e:?}");
|
||||
};
|
||||
|
||||
StatusCode::OK.into_response()
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue