diff --git a/src/app/mod.rs b/src/app/mod.rs index 22e1b08..e100cf0 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -67,7 +67,7 @@ impl App { match evt { Event::Key(key) if key.kind == KeyEventKind::Press - => self.handle_key_event(key, evt), + => self.handle_key_event(key, evt).await, Event::Mouse(_) => {} Event::Resize(_, _) => {} _ => {} @@ -93,7 +93,7 @@ impl App { Ok(()) } - pub fn handle_key_event(&mut self, key_event: KeyEvent, event: crossterm::event::Event) { + pub async fn handle_key_event(&mut self, key_event: KeyEvent, event: crossterm::event::Event) { match self.screen.last_mut().unwrap() { CurrentScreen::Logging => match key_event.code { KeyCode::Esc => self.pop(), @@ -116,14 +116,14 @@ impl App { KeyCode::Esc => self.pop(), KeyCode::Char('q') => self.exit(), KeyCode::Tab => *s = SendingScreen::Peers, - KeyCode::Enter => todo!("send the selected file or enter directory"), + KeyCode::Enter => self.send_content().await, _ => self.file_picker.handle(&event).unwrap_or_default(), }, 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"), + KeyCode::Enter => self.send_content().await, _ => {} }, SendingScreen::Text => {} @@ -139,43 +139,6 @@ impl App { } } - pub fn accept(&mut self) { - let Some(idx) = self.receiving_state.selected() else { - return; - }; - // keys are sorted, so we can use the table selection index - let keys: Vec<_> = self.receive_requests.keys().collect(); - let Some(key) = keys.get(idx) else { - warn!("could not get id from selection index {idx}"); - return; - }; - let Some(req) = self.receive_requests.get(key) else { - return; - }; - if let Err(e) = req.tx.send(ReceiveDialog::Approve) { - error!("got error sending upload confirmation: {e:?}"); - }; - } - - pub fn deny(&mut self) { - let Some(idx) = self.receiving_state.selected() else { - return; - }; - // keys are sorted, so we can use the table selection index - let keys: Vec<_> = self.receive_requests.keys().cloned().collect(); - let Some(key) = keys.get(idx) else { - warn!("could not get id from selection index {idx}"); - return; - }; - let Some(req) = self.receive_requests.get(key).cloned() else { - return; - }; - if let Err(e) = req.tx.send(ReceiveDialog::Deny) { - error!("got error sending upload confirmation: {e:?}"); - }; - self.receive_requests.remove(key); - } - pub fn draw(&mut self, frame: &mut Frame) { frame.render_widget(self, frame.area()); } @@ -216,6 +179,66 @@ impl App { self.screen.push(CurrentScreen::Main); } } + + // accept a content receive request + fn accept(&mut self) { + let Some(idx) = self.receiving_state.selected() else { + return; + }; + // keys are sorted, so we can use the table selection index + let keys: Vec<_> = self.receive_requests.keys().collect(); + let Some(key) = keys.get(idx) else { + warn!("could not get id from selection index {idx}"); + return; + }; + let Some(req) = self.receive_requests.get(key) else { + return; + }; + if let Err(e) = req.tx.send(ReceiveDialog::Approve) { + error!("got error sending upload confirmation: {e:?}"); + }; + } + + // reject an content receive request + fn deny(&mut self) { + let Some(idx) = self.receiving_state.selected() else { + return; + }; + // keys are sorted, so we can use the table selection index + let keys: Vec<_> = self.receive_requests.keys().cloned().collect(); + let Some(key) = keys.get(idx) else { + warn!("could not get id from selection index {idx}"); + return; + }; + let Some(req) = self.receive_requests.get(key).cloned() else { + return; + }; + if let Err(e) = req.tx.send(ReceiveDialog::Deny) { + error!("got error sending upload confirmation: {e:?}"); + }; + self.receive_requests.remove(key); + } + + // send content to selected peer, or change directories in the file explorer + async fn send_content(&mut self) { + if let Some(_bytes) = self.content.iter().next().cloned() { + // self.service.send_bytes(session_id, content_id, token, body) + warn!("entering text is not yet supported"); + } else { + let file = self.file_picker.current().path().clone(); + + if file.is_dir() + && let Err(e) = self.file_picker.set_cwd(&file) + { + error!("could not list directory {file:?}: {e}"); + } else if let Some((peer, _)) = self.service.peers.lock().await.first_key_value() + && file.is_file() + && let Err(e) = self.service.send_file(peer.to_owned(), file.clone()).await + { + error!("got error sending content: {e:?}"); + } + } + } } fn change_log_level(delta: isize) { diff --git a/src/lib.rs b/src/lib.rs index a4a01c7..fadbe4e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ pub mod models; pub mod transfer; use std::{ - collections::HashMap, + collections::BTreeMap, net::{Ipv4Addr, SocketAddr, SocketAddrV4}, sync::{Arc, OnceLock}, }; @@ -54,7 +54,7 @@ pub enum TransferEvent { #[derive(Clone, Debug)] pub struct ReceiveRequest { pub alias: String, - pub files: HashMap, + pub files: BTreeMap, pub tx: UnboundedSender, } @@ -62,8 +62,8 @@ pub struct ReceiveRequest { #[derive(Clone)] pub struct JoecalService { pub device: Device, - pub peers: Arc>>, - pub sessions: Arc>>, // Session ID to Session + pub peers: Arc>>, + pub sessions: Arc>>, // Session ID to Session pub running_state: Arc>, pub socket: Arc, pub client: reqwest::Client, diff --git a/src/transfer.rs b/src/transfer.rs index 3e7da5a..294da79 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, net::SocketAddr, path::PathBuf}; +use std::{collections::BTreeMap, net::SocketAddr, path::PathBuf}; use axum::{ Json, @@ -21,8 +21,8 @@ use crate::{ #[derive(Deserialize, Serialize)] pub struct Session { pub session_id: String, - pub files: HashMap, - pub file_tokens: HashMap, + pub files: BTreeMap, + pub file_tokens: BTreeMap, pub receiver: Device, pub sender: Device, pub status: SessionStatus, @@ -42,21 +42,21 @@ pub enum SessionStatus { #[serde(rename_all = "camelCase")] pub struct PrepareUploadResponse { pub session_id: String, - pub files: HashMap, + pub files: BTreeMap, } #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PrepareUploadRequest { pub info: Device, - pub files: HashMap, + pub files: BTreeMap, } impl JoecalService { pub async fn prepare_upload( &self, peer: String, - files: HashMap, + files: BTreeMap, ) -> Result { if !self.peers.lock().await.contains_key(&peer) { return Err(LocalSendError::PeerNotFound); @@ -140,7 +140,7 @@ impl JoecalService { let file_metadata = FileMetadata::from_path(&file_path)?; // Prepare files map - let mut files = HashMap::new(); + let mut files = BTreeMap::new(); files.insert(file_metadata.id.clone(), file_metadata.clone()); // Prepare upload @@ -227,7 +227,7 @@ pub async fn prepare_upload( let session_id = id.as_string(); - let file_tokens: HashMap = req + let file_tokens: BTreeMap = req .files .keys() .map(|id| (id.clone(), Julid::new().to_string())) // Replace with actual token logic