From a8a9cb9d2ea6e69953c60008f12433f39540b591 Mon Sep 17 00:00:00 2001 From: ChristianVisintin Date: Sat, 12 Dec 2020 22:00:12 +0100 Subject: [PATCH] Abort upload/download pressing Ctrl+C --- CHANGELOG.md | 3 +- README.md | 1 + .../activities/filetransfer_activity/input.rs | 35 +++++++++- .../activities/filetransfer_activity/mod.rs | 12 +--- .../filetransfer_activity/session.rs | 67 ++++++++++++++++--- 5 files changed, 95 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e5e92a..2217c1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,12 +9,13 @@ ## 0.1.2 -Released on ?? +Released on 13/12/2020 - General performance and code improvements - Improved symlinks management - Keybindings: - `E`: Delete file (Same as `DEL`); added because some keyboards don't have `DEL` (hey, that's my MacBook Air's keyboard!) + - `Ctrl+C`: Abort transfer process ## 0.1.1 diff --git a/README.md b/README.md index 0310af3..c97bf44 100644 --- a/README.md +++ b/README.md @@ -202,6 +202,7 @@ Password can be basically provided through 3 ways when address argument is provi | `` | Rename file | | `` | Go to parent directory | | `` | Delete file | +| `CTRL+C` | Abort file transfer process | --- diff --git a/src/ui/activities/filetransfer_activity/input.rs b/src/ui/activities/filetransfer_activity/input.rs index 7d64bfc..9559a68 100644 --- a/src/ui/activities/filetransfer_activity/input.rs +++ b/src/ui/activities/filetransfer_activity/input.rs @@ -24,11 +24,32 @@ use super::{ InputField, InputMode, LogLevel, OnInputSubmitCallback, PopupType, }; -use crossterm::event::KeyCode; +use crossterm::event::{KeyCode, KeyModifiers}; use std::path::PathBuf; use tui::style::Color; impl FileTransferActivity { + + /// ### read_input_event + /// + /// Read one event. + /// Returns whether at least one event has been handled + pub(super) fn read_input_event(&mut self) -> bool { + if let Ok(event) = self.context.as_ref().unwrap().input_hnd.read_event() { + // Iterate over input events + if let Some(event) = event { + // Handle event + self.handle_input_event(&event); + // Return true + true + } else { // No event + false + } + } else { // Error + false + } + } + /// ### handle_input_event /// /// Handle input event based on current input mode @@ -612,8 +633,16 @@ impl FileTransferActivity { /// ### handle_input_event_mode_explorer_alert /// /// Input event handler for popup alert - pub(super) fn handle_input_event_mode_popup_progress(&mut self, _ev: &InputEvent) { - // There's nothing you can do here I guess... maybe ctrl+c in the future idk + pub(super) fn handle_input_event_mode_popup_progress(&mut self, ev: &InputEvent) { + if let InputEvent::Key(key) = ev { + if let KeyCode::Char(ch) = key.code { + // If is 'C' and CTRL + if matches!(ch, 'c' | 'C') && key.modifiers.intersects(KeyModifiers::CONTROL) { + // Abort transfer + self.transfer.aborted = true; + } + } + } } /// ### handle_input_event_mode_explorer_alert diff --git a/src/ui/activities/filetransfer_activity/mod.rs b/src/ui/activities/filetransfer_activity/mod.rs index 44ee3cd..ce7f5c3 100644 --- a/src/ui/activities/filetransfer_activity/mod.rs +++ b/src/ui/activities/filetransfer_activity/mod.rs @@ -342,16 +342,8 @@ impl Activity for FileTransferActivity { // Redraw redraw = true; } - // Handle input events - if let Ok(event) = self.context.as_ref().unwrap().input_hnd.read_event() { - // Iterate over input events - if let Some(event) = event { - // Handle event - self.handle_input_event(&event); - // Set redraw to true - redraw = true; - } - } + // Handle input events (if false, becomes true; otherwise remains true) + redraw |= self.read_input_event(); // @! draw interface if redraw { self.draw(); diff --git a/src/ui/activities/filetransfer_activity/session.rs b/src/ui/activities/filetransfer_activity/session.rs index e3e4c12..d8ede16 100644 --- a/src/ui/activities/filetransfer_activity/session.rs +++ b/src/ui/activities/filetransfer_activity/session.rs @@ -23,6 +23,7 @@ use super::{FileTransferActivity, FsEntry, InputMode, LogLevel, PopupType}; use std::io::{Read, Seek, Write}; use std::path::{Path, PathBuf}; +use std::time::Instant; use tui::style::Color; impl FileTransferActivity { @@ -157,7 +158,17 @@ impl FileTransferActivity { // Reset transfer states self.transfer.reset(); let mut last_progress_val: f64 = 0.0; - while total_bytes_written < file_size { + let mut last_input_event_fetch: Instant = Instant::now(); + // While the entire file hasn't been completely written, + // Or filetransfer has been aborted + while total_bytes_written < file_size && !self.transfer.aborted { + // Handle input events (each 500ms) + if last_input_event_fetch.elapsed().as_millis() >= 500 { + // Read events + self.read_input_event(); + // Reset instant + last_input_event_fetch = Instant::now(); + } // Read till you can let mut buffer: [u8; 65536] = [0; 65536]; match fhnd.read(&mut buffer) { @@ -296,6 +307,10 @@ impl FileTransferActivity { Ok(entries) => { // Iterate over files for entry in entries.iter() { + // If aborted; break + if self.transfer.aborted { + break; + } // Send entry; name is always None after first call self.filetransfer_send(&entry, remote_path.as_path(), None); } @@ -347,13 +362,22 @@ impl FileTransferActivity { if let Ok(path) = self.client.pwd() { self.remote_scan(path.as_path()); } - // Eventually, Reset input mode to explorer (if input mode is wait or progress) - if let InputMode::Popup(ptype) = &self.input_mode { - match ptype { - PopupType::Wait(_) | PopupType::Progress(_) => { + // If aborted; show popup + if self.transfer.aborted { + // Show alert + self.input_mode = InputMode::Popup(PopupType::Alert( + Color::Red, + String::from("Upload aborted!"), + )); + // Set aborted to false + self.transfer.aborted = false; + } else { + // @! Successful + // Eventually, Reset input mode to explorer (if input mode is wait or progress) + if let InputMode::Popup(ptype) = &self.input_mode { + if matches!(ptype, PopupType::Wait(_) | PopupType::Progress(_)) { self.input_mode = InputMode::Explorer } - _ => { /* Nothing to do */ } } } } @@ -410,7 +434,17 @@ impl FileTransferActivity { self.transfer.reset(); // Write local file let mut last_progress_val: f64 = 0.0; - while total_bytes_written < file.size { + let mut last_input_event_fetch: Instant = Instant::now(); + // While the entire file hasn't been completely read, + // Or filetransfer has been aborted + while total_bytes_written < file.size && !self.transfer.aborted { + // Handle input events (each 500 ms) + if last_input_event_fetch.elapsed().as_millis() >= 500 { + // Read events + self.read_input_event(); + // Reset instant + last_input_event_fetch = Instant::now(); + } // Read till you can let mut buffer: [u8; 8192] = [0; 8192]; match rhnd.read(&mut buffer) { @@ -558,6 +592,10 @@ impl FileTransferActivity { Ok(entries) => { // Iterate over files for entry in entries.iter() { + // If transfer has been aborted; break + if self.transfer.aborted { + break; + } // Receive entry; name is always None after first call // Local path becomes local_dir_path self.filetransfer_recv(&entry, local_dir_path.as_path(), None); @@ -600,8 +638,19 @@ impl FileTransferActivity { } // Reload directory on local self.local_scan(local_path); - // Eventually, Reset input mode to explorer - self.input_mode = InputMode::Explorer; + // if aborted; show alert + if self.transfer.aborted { + // Show alert + self.input_mode = InputMode::Popup(PopupType::Alert( + Color::Red, + String::from("Download aborted!"), + )); + // Reset aborted to false + self.transfer.aborted = false; + } else { + // Eventually, Reset input mode to explorer + self.input_mode = InputMode::Explorer; + } } /// ### local_scan