mirror of
https://github.com/localsend/localsend.git
synced 2026-08-07 07:14:52 +00:00
feat(cli): --file mode
This commit is contained in:
+12
-4
@@ -65,22 +65,30 @@ impl App {
|
||||
rows
|
||||
}
|
||||
|
||||
pub(super) fn handle_device_list_key(&mut self, key: KeyEvent) {
|
||||
/// Handles a key while the list is open; returns `true` when the
|
||||
/// application should quit (the list was cancelled in `--file` mode).
|
||||
pub(super) fn handle_device_list_key(&mut self, key: KeyEvent) -> bool {
|
||||
let Some(list) = &mut self.device_list else {
|
||||
return;
|
||||
return false;
|
||||
};
|
||||
match list.handle_key(key) {
|
||||
DeviceListOutcome::Open => {}
|
||||
DeviceListOutcome::Closed => self.close_device_list(),
|
||||
DeviceListOutcome::Closed => {
|
||||
self.close_device_list();
|
||||
return !self.preselected.is_empty();
|
||||
}
|
||||
DeviceListOutcome::Send { fingerprint } => {
|
||||
self.close_device_list();
|
||||
if let Some(device) = self.registry.by_fingerprint(&fingerprint).cloned() {
|
||||
if !self.preselected.is_empty() {
|
||||
self.start_send(&fingerprint, self.preselected.clone());
|
||||
} else if let Some(device) = self.registry.by_fingerprint(&fingerprint).cloned() {
|
||||
self.open_picker(device);
|
||||
}
|
||||
}
|
||||
DeviceListOutcome::Pair { fingerprint } => self.pair(&fingerprint),
|
||||
DeviceListOutcome::Unpair { fingerprint } => self.unpair(&fingerprint),
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub(super) fn close_device_list(&mut self) {
|
||||
|
||||
+17
-5
@@ -20,6 +20,7 @@ use localsend::multicast::{
|
||||
};
|
||||
use receive::{Answer, PendingReceive, ReceiveSession};
|
||||
use sending::SendState;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
@@ -73,10 +74,16 @@ struct App {
|
||||
picker: Option<Picker>,
|
||||
device_list: Option<DeviceList>,
|
||||
|
||||
/// Files given via `-f`/`--file`; sending uses these instead of the picker.
|
||||
preselected: Vec<PathBuf>,
|
||||
|
||||
events_tx: mpsc::Sender<AppEvent>,
|
||||
}
|
||||
|
||||
pub async fn run(args: Args) -> anyhow::Result<()> {
|
||||
for path in &args.file {
|
||||
anyhow::ensure!(path.is_file(), "Not a file: {}", path.display());
|
||||
}
|
||||
let storage = storage::Repository::load(&args)?;
|
||||
let identity = storage.identity.clone();
|
||||
let (events_tx, mut events_rx) = mpsc::channel::<AppEvent>(64);
|
||||
@@ -159,10 +166,14 @@ pub async fn run(args: Args) -> anyhow::Result<()> {
|
||||
send: None,
|
||||
picker: None,
|
||||
device_list: None,
|
||||
preselected: args.file,
|
||||
events_tx: events_tx.clone(),
|
||||
};
|
||||
|
||||
app.ui.log_plain(&crate::banner::render(&app.storage));
|
||||
match app.preselected.is_empty() {
|
||||
true => app.ui.log_plain(&crate::banner::render(&app.storage)),
|
||||
false => app.open_device_list(),
|
||||
}
|
||||
|
||||
let mut tick = tokio::time::interval(Duration::from_millis(250));
|
||||
tick.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
|
||||
@@ -200,7 +211,6 @@ pub async fn run(args: Args) -> anyhow::Result<()> {
|
||||
if let Some(multicast) = &multicast {
|
||||
let _ = tokio::time::timeout(Duration::from_secs(1), multicast.wait_stopped()).await;
|
||||
}
|
||||
println!("Bye!");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -241,6 +251,8 @@ impl App {
|
||||
AppEvent::SendEnded => {
|
||||
self.send = None;
|
||||
self.render_status();
|
||||
// In `--file` mode the transfer is the whole program.
|
||||
return !self.preselected.is_empty();
|
||||
}
|
||||
AppEvent::Log { category, text } => self.ui.log(category, &text),
|
||||
}
|
||||
@@ -258,8 +270,7 @@ impl App {
|
||||
return false;
|
||||
}
|
||||
if self.device_list.is_some() {
|
||||
self.handle_device_list_key(key);
|
||||
return false;
|
||||
return self.handle_device_list_key(key);
|
||||
}
|
||||
|
||||
if let KeyCode::Char(c) = key.code {
|
||||
@@ -286,7 +297,8 @@ impl App {
|
||||
}
|
||||
if self.device_list.is_some() {
|
||||
self.close_device_list();
|
||||
return false;
|
||||
// In `--file` mode the device list is the whole program.
|
||||
return !self.preselected.is_empty();
|
||||
}
|
||||
let mut cancelled = false;
|
||||
if self.pending.is_some() {
|
||||
|
||||
@@ -33,6 +33,10 @@ impl App {
|
||||
.log(Category::Send, &format!("No device on [{slot}]"));
|
||||
return;
|
||||
};
|
||||
if !self.preselected.is_empty() {
|
||||
self.start_send(&device.fingerprint, self.preselected.clone());
|
||||
return;
|
||||
}
|
||||
self.open_picker(device);
|
||||
}
|
||||
|
||||
@@ -76,7 +80,11 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
fn start_send(&mut self, fingerprint: &str, picked: Vec<PathBuf>) {
|
||||
pub(super) fn start_send(&mut self, fingerprint: &str, picked: Vec<PathBuf>) {
|
||||
if self.send.is_some() {
|
||||
self.ui.log(Category::Send, "A send is already in progress");
|
||||
return;
|
||||
}
|
||||
let Some(device) = self.registry.by_fingerprint(fingerprint).cloned() else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -26,6 +26,10 @@ pub struct Args {
|
||||
/// Directory where received files are saved [default: config.toml, else the Downloads folder]
|
||||
#[arg(long, env = "LOCALSEND_DESTINATION")]
|
||||
pub destination: Option<PathBuf>,
|
||||
|
||||
/// File to send: opens the device list on start, selecting a device starts the transfer (repeatable)
|
||||
#[arg(short, long = "file", value_name = "PATH")]
|
||||
pub file: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
const HELP_SECTIONS: &str = "Events:\n \
|
||||
|
||||
Reference in New Issue
Block a user