From 5f2e2f3eb2ca359601ac2b23d94c73a4f327527f Mon Sep 17 00:00:00 2001 From: Joe Ardent Date: Fri, 1 Aug 2025 09:16:05 -0700 Subject: [PATCH] remove native dialog dep --- Cargo.lock | 280 +-------------------------------------------- Cargo.toml | 1 - src/app/mod.rs | 153 ++----------------------- src/app/widgets.rs | 150 ++++++++++++++++++++++-- src/lib.rs | 28 +++-- src/transfer.rs | 24 ++-- 6 files changed, 188 insertions(+), 448 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 52e693c..b5fb2e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -97,12 +97,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "ascii" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" - [[package]] name = "async-trait" version = "0.1.88" @@ -237,15 +231,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "block2" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2" -dependencies = [ - "objc2", -] - [[package]] name = "bumpalo" version = "3.19.0" @@ -449,15 +434,6 @@ dependencies = [ "dirs-sys", ] -[[package]] -name = "dirs" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" -dependencies = [ - "dirs-sys", -] - [[package]] name = "dirs-sys" version = "0.5.0" @@ -470,18 +446,6 @@ dependencies = [ "windows-sys 0.60.2", ] -[[package]] -name = "dispatch2" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" -dependencies = [ - "bitflags", - "block2", - "libc", - "objc2", -] - [[package]] name = "displaydoc" version = "0.2.5" @@ -518,12 +482,6 @@ dependencies = [ "regex", ] -[[package]] -name = "env_home" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" - [[package]] name = "env_logger" version = "0.11.8" @@ -611,12 +569,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "formatx" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8866fac38f53fc87fa3ae1b09ddd723e0482f8fa74323518b4c59df2c55a00a" - [[package]] name = "futures" version = "0.3.31" @@ -1135,15 +1087,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.15" @@ -1190,7 +1133,6 @@ dependencies = [ "log", "mime", "mime_guess", - "native-dialog", "network-interface", "ratatui", "reqwest", @@ -1349,30 +1291,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "native-dialog" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f006431cea71a83e6668378cb5abc2d52af299cbac6dca1780c6eeca90822df" -dependencies = [ - "ascii", - "block2", - "dirs", - "dispatch2", - "formatx", - "objc2", - "objc2-app-kit", - "objc2-core-foundation", - "objc2-core-graphics", - "objc2-foundation", - "raw-window-handle", - "thiserror 2.0.12", - "versions", - "wfd", - "which", - "winapi", -] - [[package]] name = "native-tls" version = "0.2.14" @@ -1428,15 +1346,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "nom" -version = "8.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" -dependencies = [ - "memchr", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -1446,147 +1355,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "objc2" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551" -dependencies = [ - "objc2-encode", -] - -[[package]] -name = "objc2-app-kit" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" -dependencies = [ - "bitflags", - "block2", - "libc", - "objc2", - "objc2-cloud-kit", - "objc2-core-data", - "objc2-core-foundation", - "objc2-core-graphics", - "objc2-core-image", - "objc2-foundation", - "objc2-quartz-core", -] - -[[package]] -name = "objc2-cloud-kit" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17614fdcd9b411e6ff1117dfb1d0150f908ba83a7df81b1f118005fe0a8ea15d" -dependencies = [ - "bitflags", - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-core-data" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291fbbf7d29287518e8686417cf7239c74700fd4b607623140a7d4a3c834329d" -dependencies = [ - "bitflags", - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-core-foundation" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" -dependencies = [ - "bitflags", - "block2", - "dispatch2", - "libc", - "objc2", -] - -[[package]] -name = "objc2-core-graphics" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4" -dependencies = [ - "bitflags", - "block2", - "dispatch2", - "libc", - "objc2", - "objc2-core-foundation", - "objc2-io-surface", - "objc2-metal", -] - -[[package]] -name = "objc2-core-image" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79b3dc0cc4386b6ccf21c157591b34a7f44c8e75b064f85502901ab2188c007e" -dependencies = [ - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-encode" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" - -[[package]] -name = "objc2-foundation" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" -dependencies = [ - "bitflags", - "block2", - "libc", - "objc2", - "objc2-core-foundation", -] - -[[package]] -name = "objc2-io-surface" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7282e9ac92529fa3457ce90ebb15f4ecbc383e8338060960760fa2cf75420c3c" -dependencies = [ - "bitflags", - "objc2", - "objc2-core-foundation", -] - -[[package]] -name = "objc2-metal" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f246c183239540aab1782457b35ab2040d4259175bd1d0c58e46ada7b47a874" -dependencies = [ - "bitflags", - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-quartz-core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ffb6a0cd5f182dc964334388560b12a57f7b74b3e2dec5e2722aa2dfb2ccd5" -dependencies = [ - "bitflags", - "objc2", - "objc2-foundation", -] - [[package]] name = "object" version = "0.36.7" @@ -1846,7 +1614,7 @@ dependencies = [ "crossterm", "indoc", "instability", - "itertools 0.13.0", + "itertools", "lru", "paste", "strum", @@ -1855,12 +1623,6 @@ dependencies = [ "unicode-width 0.2.0", ] -[[package]] -name = "raw-window-handle" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" - [[package]] name = "redox_syscall" version = "0.5.13" @@ -2627,7 +2389,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" dependencies = [ - "itertools 0.13.0", + "itertools", "unicode-segmentation", "unicode-width 0.1.14", ] @@ -2685,16 +2447,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "versions" -version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80a7e511ce1795821207a837b7b1c8d8aca0c648810966ad200446ae58f6667f" -dependencies = [ - "itertools 0.14.0", - "nom", -] - [[package]] name = "want" version = "0.3.1" @@ -2800,28 +2552,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "wfd" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e713040b67aae5bf1a0ae3e1ebba8cc29ab2b90da9aa1bff6e09031a8a41d7a8" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "which" -version = "7.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762" -dependencies = [ - "either", - "env_home", - "rustix 1.0.7", - "winsafe", -] - [[package]] name = "winapi" version = "0.3.9" @@ -3078,12 +2808,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winsafe" -version = "0.0.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" - [[package]] name = "wit-bindgen-rt" version = "0.39.0" diff --git a/Cargo.toml b/Cargo.toml index 329e998..4769820 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,6 @@ local-ip-address = "0.6" log = "0.4" mime = "0.3" mime_guess = "2" -native-dialog = "0.9" network-interface = { version = "2", features = ["serde"] } ratatui = "0.29" reqwest = { version = "0.12", features = ["json"] } diff --git a/src/app/mod.rs b/src/app/mod.rs index d972972..83f3e5b 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -1,36 +1,21 @@ -use std::{ - collections::BTreeMap, - net::SocketAddr, - sync::{LazyLock, OnceLock}, - time::Duration, -}; +use std::{collections::BTreeMap, net::SocketAddr, sync::OnceLock, time::Duration}; use crossterm::event::{Event, EventStream, KeyCode, KeyEvent, KeyEventKind}; use futures::{FutureExt, StreamExt}; use joecalsend::{ - Config, JoecalState, Listeners, TransferEvent, UploadDialog, + Config, JoecalState, JoecalUploadRequest, Listeners, TransferEvent, UploadDialog, error::{LocalSendError, Result}, models::Device, }; +use julid::Julid; use log::{LevelFilter, error, info}; -use native_dialog::MessageDialogBuilder; -use ratatui::{ - DefaultTerminal, Frame, - buffer::Buffer, - layout::{Constraint, Layout, Margin, Rect}, - style::{Style, Stylize}, - symbols::border, - text::{Line, Text}, - widgets::{Block, Paragraph, Widget}, -}; +use ratatui::{DefaultTerminal, Frame}; use tokio::{ sync::mpsc::{UnboundedReceiver, unbounded_channel}, task::JoinSet, }; -use tui_logger::{TuiLoggerLevelOutput, TuiLoggerWidget, TuiWidgetState}; pub mod widgets; -use widgets::*; pub type Peers = BTreeMap; @@ -40,6 +25,7 @@ pub struct App { pub events: EventStream, // addr -> (alias, fingerprint) pub peers: Peers, + pub uploads: BTreeMap, // for getting messages back from the web server or web client about things we've done; the // other end is held by the state transfer_event_rx: OnceLock>, @@ -67,6 +53,7 @@ impl App { screen: vec![CurrentScreen::Main], peers: Default::default(), events: Default::default(), + uploads: Default::default(), transfer_event_rx: Default::default(), } } @@ -131,31 +118,13 @@ impl App { transfer_event = self.transfer_event_rx.get_mut().unwrap().recv() => { if let Some(event) = transfer_event { match event { - TransferEvent::UploadRequest { alias, id } => { - let sender = - self - .state - .get() - .unwrap() - .get_upload_request(id) - .await - .ok_or(LocalSendError::SessionInactive)?; - - // TODO: replace this with ratatui widget dialog - let upload_confirmed = MessageDialogBuilder::default() - .set_title(&alias) - .set_text("Do you want to receive files from this device?") - .confirm() - .show() - .unwrap(); - - if upload_confirmed { - let _ = sender.send(UploadDialog::UploadConfirm); - } else { - let _ = sender.send(UploadDialog::UploadDeny); - } + TransferEvent::UploadRequest { id, request } => { + self.uploads.insert(id, request); } TransferEvent::Sent => {} + TransferEvent::Received(id) => { + self.uploads.remove(&id); + } _ => {} } } @@ -236,106 +205,6 @@ fn change_log_level(delta: isize) { log::set_max_level(level); } -static MAIN_MENU: LazyLock = LazyLock::new(|| { - Line::from(vec![ - " Send ".into(), - "".blue().bold(), - " Receive ".into(), - "".blue().bold(), - " Logs ".into(), - "".blue().bold(), - " Previous Screen ".into(), - "".blue().bold(), - " Quit ".into(), - "".blue().bold(), - ]) -}); - -static LOGGING_MENU: LazyLock = LazyLock::new(|| { - Line::from(vec![ - " Reduce Logging Level ".into(), - "".blue().bold(), - " Increase Logging Level ".into(), - "".blue().bold(), - " Previous Screen ".into(), - "".blue().bold(), - " Quit ".into(), - "".blue().bold(), - ]) -}); - -impl Widget for &App { - fn render(self, area: Rect, buf: &mut Buffer) { - let main_layout = - Layout::vertical([Constraint::Min(5), Constraint::Min(10), Constraint::Min(3)]); - let [top, _middle, bottom] = main_layout.areas(area); - - let footer_layout = - Layout::horizontal([Constraint::Percentage(30), Constraint::Percentage(70)]); - let [footer_left, footer_right] = footer_layout.areas(bottom); - - let header_layout = - Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)]); - let [_header_left, header_right] = header_layout.areas(top); - - let mode = self.screen.last().unwrap(); - match mode { - CurrentScreen::Main => { - main_page(*mode, &MAIN_MENU, area, buf); - logger(header_right.inner(Margin::new(1, 2)), buf); - let peers = PeersWidget { peers: &self.peers }; - peers.render(footer_right.inner(Margin::new(1, 1)), buf); - NetworkInfoWidget.render(footer_left.inner(Margin::new(1, 1)), buf); - } - CurrentScreen::Logging => { - main_page(*mode, &LOGGING_MENU, area, buf); - logger(area.inner(Margin::new(2, 4)), buf); - } - CurrentScreen::Receiving => { - main_page(*mode, &MAIN_MENU, area, buf); - } - _ => { - main_page(*mode, &MAIN_MENU, area, buf); - } - } - } -} - -fn logger(area: Rect, buf: &mut Buffer) { - let title = Line::from(log::max_level().as_str()); - let logger = TuiLoggerWidget::default() - .output_separator('|') - .output_timestamp(Some("%H:%M:%S%.3f".to_string())) - .output_level(Some(TuiLoggerLevelOutput::Abbreviated)) - .output_target(true) - .output_file(false) - .output_line(false) - .block( - Block::bordered() - .border_set(border::THICK) - .title(title.centered()), - ) - .style(Style::default()) - .state(&TuiWidgetState::new().set_default_display_level(LevelFilter::Debug)); - logger.render(area, buf); -} - -fn main_page(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); -} - async fn shutdown(handles: &mut JoinSet) { let mut alarm = tokio::time::interval(tokio::time::Duration::from_secs(5)); alarm.tick().await; diff --git a/src/app/widgets.rs b/src/app/widgets.rs index b6a5790..96fe96c 100644 --- a/src/app/widgets.rs +++ b/src/app/widgets.rs @@ -1,12 +1,147 @@ +use std::sync::LazyLock; + +use joecalsend::JoecalUploadRequest; +use log::LevelFilter; use ratatui::{ buffer::Buffer, - layout::{Constraint, Rect}, - style::Stylize, - text::{Line, ToLine}, - widgets::{Block, Borders, List, ListItem, Padding, Row, Table, Widget}, + layout::{Constraint, Layout, Margin, Rect}, + style::{Style, Stylize}, + symbols::border, + text::{Line, Text, ToLine}, + widgets::{Block, Borders, List, ListItem, Padding, Paragraph, Row, Table, Widget}, }; +use tui_logger::{TuiLoggerLevelOutput, TuiLoggerWidget, TuiWidgetState}; -use super::Peers; +use super::{App, CurrentScreen, Peers}; + +static MAIN_MENU: LazyLock = LazyLock::new(|| { + Line::from(vec![ + " Send ".into(), + "".blue().bold(), + " Receive ".into(), + "".blue().bold(), + " Logs ".into(), + "".blue().bold(), + " Previous Screen ".into(), + "".blue().bold(), + " Quit ".into(), + "".blue().bold(), + ]) +}); + +static LOGGING_MENU: LazyLock = LazyLock::new(|| { + Line::from(vec![ + " Reduce Logging Level ".into(), + "".blue().bold(), + " Increase Logging Level ".into(), + "".blue().bold(), + " Previous Screen ".into(), + "".blue().bold(), + " Quit ".into(), + "".blue().bold(), + ]) +}); + +impl Widget for &App { + fn render(self, area: Rect, buf: &mut Buffer) { + let main_layout = + Layout::vertical([Constraint::Min(5), Constraint::Min(10), Constraint::Min(3)]); + let [top, _middle, bottom] = main_layout.areas(area); + + let footer_layout = + Layout::horizontal([Constraint::Percentage(30), Constraint::Percentage(70)]); + let [footer_left, footer_right] = footer_layout.areas(bottom); + let footer_margin = Margin::new(1, 1); + + let header_layout = + Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)]); + let [header_left, header_right] = header_layout.areas(top); + let header_margin = Margin::new(1, 2); + + let mode = self.screen.last().unwrap(); + match mode { + CurrentScreen::Main => { + main_page(*mode, &MAIN_MENU, area, buf); + logger(header_right.inner(header_margin), buf); + 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, header_left.inner(header_margin), buf); + } + CurrentScreen::Logging => { + main_page(*mode, &LOGGING_MENU, area, buf); + logger(area.inner(Margin::new(2, 4)), buf); + } + CurrentScreen::Receiving => { + main_page(*mode, &MAIN_MENU, area, buf); + } + _ => { + main_page(*mode, &MAIN_MENU, area, buf); + } + } + } +} + +fn logger(area: Rect, buf: &mut Buffer) { + let title = Line::from(log::max_level().as_str()); + let logger = TuiLoggerWidget::default() + .output_separator('|') + .output_timestamp(Some("%H:%M:%S%.3f".to_string())) + .output_level(Some(TuiLoggerLevelOutput::Abbreviated)) + .output_target(true) + .output_file(false) + .output_line(false) + .block( + Block::bordered() + .border_set(border::THICK) + .title(title.centered()), + ) + .style(Style::default()) + .state(&TuiWidgetState::new().set_default_display_level(LevelFilter::Debug)); + logger.render(area, buf); +} + +fn main_page(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 uploads(requests: &[&JoecalUploadRequest], area: Rect, buf: &mut Buffer) { + //let mut lines = Vec::new(); + let title = Line::from(" Upload Requests ").bold(); + let block = Block::bordered() + .title(title.centered()) + .border_set(border::THICK); + let mut rows = Vec::new(); + for &req in requests { + let src = req.alias.to_line().left_aligned(); + let files = req + .files + .values() + .map(|f| f.file_name.clone()) + .collect::>(); + let files = files.join(", "); + let files = Line::from(files); + rows.push(Row::new([src, files.centered()])); + } + let widths = [Constraint::Max(20), Constraint::Min(50)]; + let table = Table::new(rows, widths) + .block(block) + .header(Row::new(["Sender".bold(), "Files".bold()])); + table.render(area, buf); +} #[derive(Debug, Clone)] pub struct PeersWidget<'p> { @@ -44,13 +179,10 @@ impl Widget for NetworkInfoWidget { where Self: Sized, { - // let rows = Rows::new(rect); - // let mut table = Table::default(); - let udp = "UDP socket".yellow(); let udp = udp.to_line().left_aligned(); let uaddr = format!("{:?}", joecalsend::LISTENING_SOCKET_ADDR).yellow(); - let udp = Row::new(vec![udp.into(), uaddr.to_line().right_aligned()]); + let udp = Row::new(vec![udp, uaddr.to_line().right_aligned()]); let mip = format!( "{:?}:{:?}", diff --git a/src/lib.rs b/src/lib.rs index 446e549..6b46556 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,7 @@ use std::{ use julid::Julid; use log::error; -use models::Device; +use models::{Device, FileMetadata}; use serde::{Deserialize, Serialize}; use tokio::{ net::UdpSocket, @@ -46,9 +46,19 @@ pub enum UploadDialog { pub enum TransferEvent { Sent, - Received, + Received(Julid), Failed, - UploadRequest { alias: String, id: Julid }, + UploadRequest { + id: Julid, + request: JoecalUploadRequest, + }, +} + +#[derive(Clone, Debug)] +pub struct JoecalUploadRequest { + pub alias: String, + pub files: HashMap, + pub tx: UnboundedSender, } /// Contains the main network and backend state for an application session. @@ -60,7 +70,7 @@ pub struct JoecalState { pub running_state: Arc>, pub socket: Arc, pub client: reqwest::Client, - upload_requests: Arc>>>, + pub upload_requests: Arc>>, shutdown_sender: OnceLock, // the receiving end will be held by the application so it can update the UI based on backend // events @@ -152,7 +162,7 @@ impl JoecalState { peers.clear(); } - pub async fn get_upload_request(&self, id: Julid) -> Option> { + pub async fn get_upload_request(&self, id: Julid) -> Option { self.upload_requests.lock().await.get(&id).cloned() } @@ -166,8 +176,12 @@ impl JoecalState { /// /// IMPORTANT! Be sure to call `clear_upload_request(id)` when you're done /// getting an answer back/before you exit! - pub async fn add_upload_request(&self, id: Julid, tx: UnboundedSender) { - self.upload_requests.lock().await.entry(id).insert_entry(tx); + pub async fn add_upload_request(&self, id: Julid, request: JoecalUploadRequest) { + self.upload_requests + .lock() + .await + .entry(id) + .insert_entry(request); } } diff --git a/src/transfer.rs b/src/transfer.rs index e5c454a..3057dd7 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -8,12 +8,12 @@ use axum::{ response::IntoResponse, }; use julid::Julid; -use log::{debug, info, warn}; +use log::{debug, error, info, warn}; use serde::{Deserialize, Serialize}; use tokio::sync::mpsc::unbounded_channel; use crate::{ - JoecalState, TransferEvent, UploadDialog, + JoecalState, JoecalUploadRequest, TransferEvent, UploadDialog, error::{LocalSendError, Result}, models::{Device, FileMetadata}, }; @@ -199,22 +199,24 @@ pub async fn register_prepare_upload( let id = Julid::new(); let (tx, mut rx) = unbounded_channel(); // be sure to clear this request before this function exits! - state.add_upload_request(id, tx).await; - - let dialog_send = state.transfer_event_tx.send(TransferEvent::UploadRequest { + let request = JoecalUploadRequest { alias: req.info.alias.clone(), - id, - }); - match dialog_send { + files: req.files.clone(), + tx, + }; + + match state + .transfer_event_tx + .send(TransferEvent::UploadRequest { id, request }) + { Ok(_) => {} - Err(_e) => { - state.clear_upload_request(id).await; + Err(e) => { + error!("error sending transfer event to app: {e:?}"); return StatusCode::INTERNAL_SERVER_ERROR.into_response(); } } let confirmation = rx.recv().await; - state.clear_upload_request(id).await; let Some(confirmation) = confirmation else { // the frontend must have dropped the tx before trying to send a reply back