diff --git a/src/app/mod.rs b/src/app/mod.rs index 5a06ad5..2f33a80 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -60,7 +60,7 @@ impl App { event_listener, screen: vec![CurrentScreen::Main], file_picker: FileExplorer::new().expect("could not create file explorer"), - text: Some("this is content".to_string()), + text: None, events: Default::default(), peers: Default::default(), peer_state: Default::default(), @@ -234,13 +234,18 @@ impl App { async fn send_content(&mut self) { debug!("sending content"); - if let Some(text) = self.text.to_owned() { - if let Some(idx) = self.peer_state.selected() - && let Some(peer) = self.peers.get(idx) - { - if let Err(e) = self.service.send_text(&peer.fingerprint, &text).await { - error!("got error sending \"{text}\" to {}: {e:?}", peer.alias); - } + let Some(peer_idx) = self.peer_state.selected() else { + warn!("no peer selected to send to"); + return; + }; + let Some(peer) = self.peers.get(peer_idx) else { + warn!("invalid peer index {peer_idx}"); + return; + }; + + if let Some(ref text) = self.text { + if let Err(e) = self.service.send_text(&peer.fingerprint, text).await { + error!("got error sending \"{text}\" to {}: {e:?}", peer.alias); } } else { let file = self.file_picker.current().path().clone(); @@ -248,12 +253,10 @@ impl App { && let Err(e) = self.file_picker.set_cwd(&file) { error!("could not list directory {file:?}: {e}"); + return; } - if let Some(idx) = self.peer_state.selected() - && let Some(peer) = self.peers.get(idx) - && file.is_file() - { + if file.is_file() { debug!("sending {file:?}"); if let Err(e) = self .service diff --git a/src/error.rs b/src/error.rs index dd9ed7b..0e38274 100644 --- a/src/error.rs +++ b/src/error.rs @@ -36,6 +36,9 @@ pub enum LocalSendError { #[error("Session inactive")] SessionInactive, + #[error("Session not found")] + SessionNotFound, + #[error("Cancel Failed")] CancelFailed, diff --git a/src/models.rs b/src/models.rs index 4dccbba..ed99fdf 100644 --- a/src/models.rs +++ b/src/models.rs @@ -63,22 +63,21 @@ impl FileMetadata { }) } - pub fn from_text(text: &str) -> Self { + pub fn from_text(text: &str) -> crate::error::Result { + let size = text.len() as u64; let id = Julid::new().as_string(); - let size = text.as_bytes().len() as u64; - let file_type = "text/plain".into(); let sha256 = Some(sha256::digest(text)); - FileMetadata { - id, - file_name: "".to_string(), + Ok(FileMetadata { + id: id.clone(), + file_name: format!("{id}.txt"), size, file_type, sha256, preview: Some(text.to_string()), metadata: None, - } + }) } } diff --git a/src/transfer.rs b/src/transfer.rs index dcf7c3a..faf7605 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -62,9 +62,7 @@ impl JoecalService { return Err(LocalSendError::PeerNotFound); }; - debug!("sending to peer at {addr:?}"); - - let response = self + let request = self .client .post(format!( "{}://{}/api/localsend/v2/prepare-upload", @@ -73,14 +71,24 @@ impl JoecalService { .json(&PrepareUploadRequest { info: self.device.clone(), files: files.clone(), - }) - .timeout(Duration::from_secs(30)) - .send() - .await?; + }); + + let r = request.timeout(Duration::from_secs(30)).build().unwrap(); + debug!("sending '{r:?}' to peer at {addr:?}"); + + let response = self.client.execute(r).await?; debug!("Response: {response:?}"); - let response: PrepareUploadResponse = response.json().await?; + let response: PrepareUploadResponse = match response.json().await { + Err(e) => { + error!("got error deserializing response: {e:?}"); + return Err(LocalSendError::RequestError(e)); + } + Ok(r) => r, + }; + + debug!("decoded response: {response:?}"); let session = Session { session_id: response.session_id.clone(), @@ -100,41 +108,6 @@ impl JoecalService { Ok(response) } - pub async fn send_bytes( - &self, - session_id: String, - content_id: String, - token: String, - body: Bytes, - ) -> Result<()> { - let sessions = self.sessions.lock().await; - let session = sessions.get(&session_id).unwrap(); - - if session.status != SessionStatus::Active { - return Err(LocalSendError::SessionInactive); - } - - if session.file_tokens.get(&content_id) != Some(&token) { - return Err(LocalSendError::InvalidToken); - } - - let request = self.client - .post(format!( - "{}://{}/api/localsend/v2/upload?sessionId={session_id}&fileId={content_id}&token={token}", - session.receiver.protocol, session.addr)) - .body(body); - - debug!("Uploading file: {request:?}"); - let response = request.send().await?; - - if response.status() != 200 { - warn!("Upload failed: {response:?}"); - return Err(LocalSendError::UploadFailed); - } - - Ok(()) - } - pub async fn send_file(&self, peer: &str, file_path: PathBuf) -> Result<()> { // Generate file metadata let file_metadata = FileMetadata::from_path(&file_path)?; @@ -158,9 +131,9 @@ impl JoecalService { // Upload file self.send_bytes( - prepare_response.session_id, - file_metadata.id, - token.clone(), + &prepare_response.session_id, + &file_metadata.id, + token, bytes, ) .await?; @@ -170,7 +143,7 @@ impl JoecalService { pub async fn send_text(&self, peer: &str, text: &str) -> Result<()> { // Generate file metadata - let file_metadata = FileMetadata::from_text(text); + let file_metadata = FileMetadata::from_text(text)?; // Prepare files map let mut files = BTreeMap::new(); @@ -189,9 +162,9 @@ impl JoecalService { // Upload file self.send_bytes( - prepare_response.session_id, - file_metadata.id, - token.clone(), + &prepare_response.session_id, + &file_metadata.id, + token, bytes, ) .await?; @@ -199,15 +172,17 @@ impl JoecalService { Ok(()) } - pub async fn cancel_upload(&self, session_id: String) -> Result<()> { + pub async fn cancel_upload(&self, session_id: &str) -> Result<()> { let sessions = self.sessions.lock().await; - let session = sessions.get(&session_id).unwrap(); + let session = sessions + .get(session_id) + .ok_or(LocalSendError::SessionNotFound)?; let request = self .client .post(format!( - "{}://{}/api/localsend/v2/cancel?sessionId={}", - session.receiver.protocol, session.addr, session_id + "{}://{}/api/localsend/v2/cancel?sessionId={session_id}", + session.receiver.protocol, session.addr )) .send() .await?; @@ -218,6 +193,41 @@ impl JoecalService { Ok(()) } + + async fn send_bytes( + &self, + session_id: &str, + content_id: &str, + token: &String, + body: Bytes, + ) -> Result<()> { + let sessions = self.sessions.lock().await; + let session = sessions.get(session_id).unwrap(); + + if session.status != SessionStatus::Active { + return Err(LocalSendError::SessionInactive); + } + + if session.file_tokens.get(content_id) != Some(token) { + return Err(LocalSendError::InvalidToken); + } + + let request = self.client + .post(format!( + "{}://{}/api/localsend/v2/upload?sessionId={session_id}&fileId={content_id}&token={token}", + session.receiver.protocol, session.addr)) + .body(body).build()?; + + debug!("Uploading bytes: {request:?}"); + let response = self.client.execute(request).await?; + + if response.status() != 200 { + warn!("Upload failed: {response:?}"); + return Err(LocalSendError::UploadFailed); + } + + Ok(()) + } } pub async fn prepare_upload(