This commit is contained in:
Joe Ardent 2025-08-04 17:20:28 -07:00
parent 35b053be79
commit 5161f3db75
4 changed files with 88 additions and 73 deletions

View file

@ -60,7 +60,7 @@ impl App {
event_listener, event_listener,
screen: vec![CurrentScreen::Main], screen: vec![CurrentScreen::Main],
file_picker: FileExplorer::new().expect("could not create file explorer"), file_picker: FileExplorer::new().expect("could not create file explorer"),
text: Some("this is content".to_string()), text: None,
events: Default::default(), events: Default::default(),
peers: Default::default(), peers: Default::default(),
peer_state: Default::default(), peer_state: Default::default(),
@ -234,26 +234,29 @@ impl App {
async fn send_content(&mut self) { async fn send_content(&mut self) {
debug!("sending content"); debug!("sending content");
if let Some(text) = self.text.to_owned() { let Some(peer_idx) = self.peer_state.selected() else {
if let Some(idx) = self.peer_state.selected() warn!("no peer selected to send to");
&& let Some(peer) = self.peers.get(idx) return;
{ };
if let Err(e) = self.service.send_text(&peer.fingerprint, &text).await { 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); error!("got error sending \"{text}\" to {}: {e:?}", peer.alias);
} }
}
} else { } else {
let file = self.file_picker.current().path().clone(); let file = self.file_picker.current().path().clone();
if file.is_dir() if file.is_dir()
&& let Err(e) = self.file_picker.set_cwd(&file) && let Err(e) = self.file_picker.set_cwd(&file)
{ {
error!("could not list directory {file:?}: {e}"); error!("could not list directory {file:?}: {e}");
return;
} }
if let Some(idx) = self.peer_state.selected() if file.is_file() {
&& let Some(peer) = self.peers.get(idx)
&& file.is_file()
{
debug!("sending {file:?}"); debug!("sending {file:?}");
if let Err(e) = self if let Err(e) = self
.service .service

View file

@ -36,6 +36,9 @@ pub enum LocalSendError {
#[error("Session inactive")] #[error("Session inactive")]
SessionInactive, SessionInactive,
#[error("Session not found")]
SessionNotFound,
#[error("Cancel Failed")] #[error("Cancel Failed")]
CancelFailed, CancelFailed,

View file

@ -63,22 +63,21 @@ impl FileMetadata {
}) })
} }
pub fn from_text(text: &str) -> Self { pub fn from_text(text: &str) -> crate::error::Result<Self> {
let size = text.len() as u64;
let id = Julid::new().as_string(); let id = Julid::new().as_string();
let size = text.as_bytes().len() as u64;
let file_type = "text/plain".into(); let file_type = "text/plain".into();
let sha256 = Some(sha256::digest(text)); let sha256 = Some(sha256::digest(text));
FileMetadata { Ok(FileMetadata {
id, id: id.clone(),
file_name: "".to_string(), file_name: format!("{id}.txt"),
size, size,
file_type, file_type,
sha256, sha256,
preview: Some(text.to_string()), preview: Some(text.to_string()),
metadata: None, metadata: None,
} })
} }
} }

View file

@ -62,9 +62,7 @@ impl JoecalService {
return Err(LocalSendError::PeerNotFound); return Err(LocalSendError::PeerNotFound);
}; };
debug!("sending to peer at {addr:?}"); let request = self
let response = self
.client .client
.post(format!( .post(format!(
"{}://{}/api/localsend/v2/prepare-upload", "{}://{}/api/localsend/v2/prepare-upload",
@ -73,14 +71,24 @@ impl JoecalService {
.json(&PrepareUploadRequest { .json(&PrepareUploadRequest {
info: self.device.clone(), info: self.device.clone(),
files: files.clone(), files: files.clone(),
}) });
.timeout(Duration::from_secs(30))
.send() let r = request.timeout(Duration::from_secs(30)).build().unwrap();
.await?; debug!("sending '{r:?}' to peer at {addr:?}");
let response = self.client.execute(r).await?;
debug!("Response: {response:?}"); 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 { let session = Session {
session_id: response.session_id.clone(), session_id: response.session_id.clone(),
@ -100,41 +108,6 @@ impl JoecalService {
Ok(response) 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<()> { pub async fn send_file(&self, peer: &str, file_path: PathBuf) -> Result<()> {
// Generate file metadata // Generate file metadata
let file_metadata = FileMetadata::from_path(&file_path)?; let file_metadata = FileMetadata::from_path(&file_path)?;
@ -158,9 +131,9 @@ impl JoecalService {
// Upload file // Upload file
self.send_bytes( self.send_bytes(
prepare_response.session_id, &prepare_response.session_id,
file_metadata.id, &file_metadata.id,
token.clone(), token,
bytes, bytes,
) )
.await?; .await?;
@ -170,7 +143,7 @@ impl JoecalService {
pub async fn send_text(&self, peer: &str, text: &str) -> Result<()> { pub async fn send_text(&self, peer: &str, text: &str) -> Result<()> {
// Generate file metadata // Generate file metadata
let file_metadata = FileMetadata::from_text(text); let file_metadata = FileMetadata::from_text(text)?;
// Prepare files map // Prepare files map
let mut files = BTreeMap::new(); let mut files = BTreeMap::new();
@ -189,9 +162,9 @@ impl JoecalService {
// Upload file // Upload file
self.send_bytes( self.send_bytes(
prepare_response.session_id, &prepare_response.session_id,
file_metadata.id, &file_metadata.id,
token.clone(), token,
bytes, bytes,
) )
.await?; .await?;
@ -199,15 +172,17 @@ impl JoecalService {
Ok(()) 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 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 let request = self
.client .client
.post(format!( .post(format!(
"{}://{}/api/localsend/v2/cancel?sessionId={}", "{}://{}/api/localsend/v2/cancel?sessionId={session_id}",
session.receiver.protocol, session.addr, session_id session.receiver.protocol, session.addr
)) ))
.send() .send()
.await?; .await?;
@ -218,6 +193,41 @@ impl JoecalService {
Ok(()) 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( pub async fn prepare_upload(