mirror of
https://github.com/veeso/termscp.git
synced 2025-12-07 09:36:00 -08:00
Thanks to latest commit I can finally draw progress bar :D
This commit is contained in:
@@ -44,6 +44,7 @@ use crossterm::event::Event as InputEvent;
|
|||||||
use crossterm::event::{KeyCode, KeyModifiers};
|
use crossterm::event::{KeyCode, KeyModifiers};
|
||||||
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
|
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
use std::io::{Read, Seek, Write};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use tui::{
|
use tui::{
|
||||||
layout::{Constraint, Corner, Direction, Layout, Rect},
|
layout::{Constraint, Corner, Direction, Layout, Rect},
|
||||||
@@ -209,7 +210,7 @@ pub struct FileTransferActivity {
|
|||||||
input_field: InputField, // Current selected input mode
|
input_field: InputField, // Current selected input mode
|
||||||
input_txt: String, // Input text
|
input_txt: String, // Input text
|
||||||
choice_opt: DialogYesNoOption, // Dialog popup selected option
|
choice_opt: DialogYesNoOption, // Dialog popup selected option
|
||||||
transfer_prog: f64, // Current write/read progress (percentage)
|
transfer_progress: f64, // Current write/read progress (percentage)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileTransferActivity {
|
impl FileTransferActivity {
|
||||||
@@ -237,7 +238,7 @@ impl FileTransferActivity {
|
|||||||
input_field: InputField::Explorer,
|
input_field: InputField::Explorer,
|
||||||
input_txt: String::new(),
|
input_txt: String::new(),
|
||||||
choice_opt: DialogYesNoOption::Yes,
|
choice_opt: DialogYesNoOption::Yes,
|
||||||
transfer_prog: 0.0,
|
transfer_progress: 0.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,8 +303,7 @@ impl FileTransferActivity {
|
|||||||
FsEntry::Directory(dir) => dir.name.clone(),
|
FsEntry::Directory(dir) => dir.name.clone(),
|
||||||
FsEntry::File(file) => file.name.clone(),
|
FsEntry::File(file) => file.name.clone(),
|
||||||
};
|
};
|
||||||
self.input_mode =
|
self.input_mode = InputMode::Popup(PopupType::Wait(format!("Uploading \"{}\"", file_name)));
|
||||||
InputMode::Popup(PopupType::Wait(format!("Uploading \"{}\"...", file_name)));
|
|
||||||
// Draw
|
// Draw
|
||||||
self.draw();
|
self.draw();
|
||||||
// Get remote path
|
// Get remote path
|
||||||
@@ -323,16 +323,66 @@ impl FileTransferActivity {
|
|||||||
.local
|
.local
|
||||||
.open_file_read(file.abs_path.as_path())
|
.open_file_read(file.abs_path.as_path())
|
||||||
{
|
{
|
||||||
Ok(mut f) => match self.client.send_file(remote_path.as_path(), &mut f) {
|
Ok(mut fhnd) => match self.client.send_file(remote_path.as_path()) {
|
||||||
Ok(_) => self.log(
|
Ok(mut rhnd) => {
|
||||||
LogLevel::Info,
|
// Write file
|
||||||
format!(
|
let file_size: usize =
|
||||||
"Saved file \"{}\" to \"{}\"",
|
fhnd.seek(std::io::SeekFrom::End(0)).unwrap_or(0) as usize;
|
||||||
file.abs_path.display(),
|
// rewind
|
||||||
remote_path.display()
|
if let Err(err) = fhnd.seek(std::io::SeekFrom::Start(0)) {
|
||||||
)
|
self.log(
|
||||||
.as_ref(),
|
LogLevel::Error,
|
||||||
),
|
format!("Could not rewind local file: {}", err).as_ref(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Write remote file
|
||||||
|
let mut total_bytes_written: usize = 0;
|
||||||
|
// Set input state to popup progress
|
||||||
|
self.input_mode = InputMode::Popup(PopupType::Progress(format!(
|
||||||
|
"Uploading \"{}\"",
|
||||||
|
file_name
|
||||||
|
)));
|
||||||
|
loop {
|
||||||
|
// Read till you can
|
||||||
|
let mut buffer: [u8; 8192] = [0; 8192];
|
||||||
|
match fhnd.read(&mut buffer) {
|
||||||
|
Ok(bytes_read) => {
|
||||||
|
total_bytes_written += bytes_read;
|
||||||
|
if bytes_read == 0 {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
// Write bytes
|
||||||
|
if let Err(err) = rhnd.write(&buffer[0..bytes_read]) {
|
||||||
|
self.log(
|
||||||
|
LogLevel::Error,
|
||||||
|
format!("Could not write remote file: {}", err)
|
||||||
|
.as_ref(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
self.log(
|
||||||
|
LogLevel::Error,
|
||||||
|
format!("Could not read local file: {}", err).as_ref(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Increase progress
|
||||||
|
self.set_progress(total_bytes_written, file_size);
|
||||||
|
// Draw
|
||||||
|
self.draw();
|
||||||
|
}
|
||||||
|
self.log(
|
||||||
|
LogLevel::Info,
|
||||||
|
format!(
|
||||||
|
"Saved file \"{}\" to \"{}\"",
|
||||||
|
file.abs_path.display(),
|
||||||
|
remote_path.display()
|
||||||
|
)
|
||||||
|
.as_ref(),
|
||||||
|
);
|
||||||
|
}
|
||||||
Err(err) => self.log(
|
Err(err) => self.log(
|
||||||
LogLevel::Error,
|
LogLevel::Error,
|
||||||
format!(
|
format!(
|
||||||
@@ -446,19 +496,60 @@ impl FileTransferActivity {
|
|||||||
{
|
{
|
||||||
Ok(mut local_file) => {
|
Ok(mut local_file) => {
|
||||||
// Download file from remote
|
// Download file from remote
|
||||||
match self
|
match self.client.recv_file(file.abs_path.as_path()) {
|
||||||
.client
|
Ok((mut rhnd, file_size)) => {
|
||||||
.recv_file(file.abs_path.as_path(), &mut local_file)
|
// Set popup progress
|
||||||
{
|
self.input_mode = InputMode::Popup(PopupType::Progress(format!(
|
||||||
Ok(_) => self.log(
|
"Downloading \"{}\"...",
|
||||||
LogLevel::Info,
|
file_name
|
||||||
format!(
|
)));
|
||||||
"Saved file \"{}\" to \"{}\"",
|
let mut total_bytes_written: usize = 0;
|
||||||
file.abs_path.display(),
|
// Write local file
|
||||||
local_file_path.display()
|
loop {
|
||||||
)
|
// Read till you can
|
||||||
.as_ref(),
|
let mut buffer: [u8; 8192] = [0; 8192];
|
||||||
),
|
match rhnd.read(&mut buffer) {
|
||||||
|
Ok(bytes_read) => {
|
||||||
|
total_bytes_written += bytes_read;
|
||||||
|
if bytes_read == 0 {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
// Write bytes
|
||||||
|
if let Err(err) =
|
||||||
|
local_file.write(&buffer[0..bytes_read])
|
||||||
|
{
|
||||||
|
self.log(
|
||||||
|
LogLevel::Error,
|
||||||
|
format!(
|
||||||
|
"Could not write local file: {}",
|
||||||
|
err
|
||||||
|
)
|
||||||
|
.as_ref(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => self.log(
|
||||||
|
LogLevel::Error,
|
||||||
|
format!("Could not read remote file: {}", err).as_ref(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
// Set progress
|
||||||
|
self.set_progress(total_bytes_written, file_size);
|
||||||
|
// Draw
|
||||||
|
self.draw();
|
||||||
|
}
|
||||||
|
// Log
|
||||||
|
self.log(
|
||||||
|
LogLevel::Info,
|
||||||
|
format!(
|
||||||
|
"Saved file \"{}\" to \"{}\"",
|
||||||
|
file.abs_path.display(),
|
||||||
|
local_file_path.display()
|
||||||
|
)
|
||||||
|
.as_ref(),
|
||||||
|
);
|
||||||
|
}
|
||||||
Err(err) => self.log(
|
Err(err) => self.log(
|
||||||
LogLevel::Error,
|
LogLevel::Error,
|
||||||
format!(
|
format!(
|
||||||
@@ -596,7 +687,10 @@ impl FileTransferActivity {
|
|||||||
.change_wrkdir(PathBuf::from(path))
|
.change_wrkdir(PathBuf::from(path))
|
||||||
{
|
{
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
self.log(LogLevel::Info, format!("Changed directory on local: {}", path.display()).as_str());
|
self.log(
|
||||||
|
LogLevel::Info,
|
||||||
|
format!("Changed directory on local: {}", path.display()).as_str(),
|
||||||
|
);
|
||||||
// Reload files
|
// Reload files
|
||||||
self.local_scan(path);
|
self.local_scan(path);
|
||||||
// Push prev_dir to stack
|
// Push prev_dir to stack
|
||||||
@@ -621,7 +715,10 @@ impl FileTransferActivity {
|
|||||||
// Change directory
|
// Change directory
|
||||||
match self.client.change_dir(path) {
|
match self.client.change_dir(path) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
self.log(LogLevel::Info, format!("Changed directory on remote: {}", path.display()).as_str());
|
self.log(
|
||||||
|
LogLevel::Info,
|
||||||
|
format!("Changed directory on remote: {}", path.display()).as_str(),
|
||||||
|
);
|
||||||
// Update files
|
// Update files
|
||||||
self.remote_scan(path);
|
self.remote_scan(path);
|
||||||
// Push prev_dir to stack
|
// Push prev_dir to stack
|
||||||
@@ -636,7 +733,7 @@ impl FileTransferActivity {
|
|||||||
format!("Could not change working directory: {}", err),
|
format!("Could not change working directory: {}", err),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
// Report err
|
// Report err
|
||||||
@@ -685,6 +782,13 @@ impl FileTransferActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ### set_progress
|
||||||
|
///
|
||||||
|
/// Calculate progress percentage based on current progress
|
||||||
|
fn set_progress(&mut self, it: usize, sz: usize) {
|
||||||
|
self.transfer_progress = ((it as f64) * 100.0) / (sz as f64);
|
||||||
|
}
|
||||||
|
|
||||||
// @! input listeners
|
// @! input listeners
|
||||||
|
|
||||||
/// ### handle_input_event
|
/// ### handle_input_event
|
||||||
@@ -969,12 +1073,13 @@ impl FileTransferActivity {
|
|||||||
self.remote_changedir(parent, true);
|
self.remote_changedir(parent, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => self.input_mode = InputMode::Popup(PopupType::Alert(
|
Err(err) => {
|
||||||
Color::Red,
|
self.input_mode = InputMode::Popup(PopupType::Alert(
|
||||||
format!("Could not change working directory: {}", err),
|
Color::Red,
|
||||||
))
|
format!("Could not change working directory: {}", err),
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
' ' => {
|
' ' => {
|
||||||
@@ -1567,7 +1672,7 @@ impl FileTransferActivity {
|
|||||||
PopupType::Help => (50, 70),
|
PopupType::Help => (50, 70),
|
||||||
PopupType::Input(_, _) => (30, 10),
|
PopupType::Input(_, _) => (30, 10),
|
||||||
PopupType::Progress(_) => (40, 10),
|
PopupType::Progress(_) => (40, 10),
|
||||||
PopupType::Wait(_) => (30, 10),
|
PopupType::Wait(_) => (50, 10),
|
||||||
PopupType::YesNo(_, _, _) => (10, 10),
|
PopupType::YesNo(_, _, _) => (10, 10),
|
||||||
};
|
};
|
||||||
let popup_area: Rect = self.draw_popup_area(f.size(), width, height);
|
let popup_area: Rect = self.draw_popup_area(f.size(), width, height);
|
||||||
@@ -1580,9 +1685,7 @@ impl FileTransferActivity {
|
|||||||
PopupType::Fatal(txt) => {
|
PopupType::Fatal(txt) => {
|
||||||
f.render_widget(self.draw_popup_fatal(txt.clone()), popup_area)
|
f.render_widget(self.draw_popup_fatal(txt.clone()), popup_area)
|
||||||
}
|
}
|
||||||
PopupType::Help => {
|
PopupType::Help => f.render_widget(self.draw_popup_help(), popup_area),
|
||||||
f.render_widget(self.draw_popup_help(), popup_area)
|
|
||||||
}
|
|
||||||
PopupType::Input(txt, _) => {
|
PopupType::Input(txt, _) => {
|
||||||
f.render_widget(self.draw_popup_input(txt.clone()), popup_area);
|
f.render_widget(self.draw_popup_input(txt.clone()), popup_area);
|
||||||
// Set cursor
|
// Set cursor
|
||||||
@@ -1781,9 +1884,9 @@ impl FileTransferActivity {
|
|||||||
///
|
///
|
||||||
/// Draw progress popup
|
/// Draw progress popup
|
||||||
fn draw_popup_progress(&self, text: String) -> Gauge {
|
fn draw_popup_progress(&self, text: String) -> Gauge {
|
||||||
let label = format!("{:.2}%", self.transfer_prog);
|
let label = format!("{:.2}%", self.transfer_progress);
|
||||||
Gauge::default()
|
Gauge::default()
|
||||||
.block(Block::default().title(text))
|
.block(Block::default().borders(Borders::ALL).title(text))
|
||||||
.gauge_style(
|
.gauge_style(
|
||||||
Style::default()
|
Style::default()
|
||||||
.fg(Color::Magenta)
|
.fg(Color::Magenta)
|
||||||
@@ -1791,7 +1894,7 @@ impl FileTransferActivity {
|
|||||||
.add_modifier(Modifier::BOLD),
|
.add_modifier(Modifier::BOLD),
|
||||||
)
|
)
|
||||||
.label(label)
|
.label(label)
|
||||||
.ratio(self.transfer_prog / 100.0)
|
.ratio(self.transfer_progress / 100.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ### draw_popup_wait
|
/// ### draw_popup_wait
|
||||||
@@ -1800,11 +1903,7 @@ impl FileTransferActivity {
|
|||||||
fn draw_popup_wait(&self, text: String) -> Paragraph {
|
fn draw_popup_wait(&self, text: String) -> Paragraph {
|
||||||
Paragraph::new(text)
|
Paragraph::new(text)
|
||||||
.style(Style::default().add_modifier(Modifier::BOLD))
|
.style(Style::default().add_modifier(Modifier::BOLD))
|
||||||
.block(
|
.block(Block::default().borders(Borders::ALL).title("Please wait"))
|
||||||
Block::default()
|
|
||||||
.borders(Borders::ALL)
|
|
||||||
.title("Please wait"),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ### draw_popup_yesno
|
/// ### draw_popup_yesno
|
||||||
@@ -1833,126 +1932,106 @@ impl FileTransferActivity {
|
|||||||
fn draw_popup_help(&self) -> List {
|
fn draw_popup_help(&self) -> List {
|
||||||
// Write header
|
// Write header
|
||||||
let cmds: Vec<ListItem> = vec![
|
let cmds: Vec<ListItem> = vec![
|
||||||
ListItem::new(
|
ListItem::new(Spans::from(vec![
|
||||||
Spans::from(vec![
|
Span::styled(
|
||||||
Span::styled(
|
"<ESC> ",
|
||||||
"<ESC> ",
|
Style::default()
|
||||||
Style::default()
|
.bg(Color::Cyan)
|
||||||
.bg(Color::Cyan)
|
.fg(Color::White)
|
||||||
.fg(Color::White)
|
.add_modifier(Modifier::BOLD),
|
||||||
.add_modifier(Modifier::BOLD),
|
),
|
||||||
),
|
Span::raw("quit"),
|
||||||
Span::raw("quit")
|
])),
|
||||||
])
|
ListItem::new(Spans::from(vec![
|
||||||
),
|
Span::styled(
|
||||||
ListItem::new(
|
"<TAB> ",
|
||||||
Spans::from(vec![
|
Style::default()
|
||||||
Span::styled(
|
.bg(Color::Cyan)
|
||||||
"<TAB> ",
|
.fg(Color::White)
|
||||||
Style::default()
|
.add_modifier(Modifier::BOLD),
|
||||||
.bg(Color::Cyan)
|
),
|
||||||
.fg(Color::White)
|
Span::raw("change input field"),
|
||||||
.add_modifier(Modifier::BOLD),
|
])),
|
||||||
),
|
ListItem::new(Spans::from(vec![
|
||||||
Span::raw("change input field")
|
Span::styled(
|
||||||
])
|
"<RIGHT/LEFT> ",
|
||||||
),
|
Style::default()
|
||||||
ListItem::new(
|
.bg(Color::Cyan)
|
||||||
Spans::from(vec![
|
.fg(Color::White)
|
||||||
Span::styled(
|
.add_modifier(Modifier::BOLD),
|
||||||
"<RIGHT/LEFT> ",
|
),
|
||||||
Style::default()
|
Span::raw("change explorer tab"),
|
||||||
.bg(Color::Cyan)
|
])),
|
||||||
.fg(Color::White)
|
ListItem::new(Spans::from(vec![
|
||||||
.add_modifier(Modifier::BOLD),
|
Span::styled(
|
||||||
),
|
"<ENTER> ",
|
||||||
Span::raw("change explorer tab")
|
Style::default()
|
||||||
])
|
.bg(Color::Cyan)
|
||||||
),
|
.fg(Color::White)
|
||||||
ListItem::new(
|
.add_modifier(Modifier::BOLD),
|
||||||
Spans::from(vec![
|
),
|
||||||
Span::styled(
|
Span::raw("enter directory"),
|
||||||
"<ENTER> ",
|
])),
|
||||||
Style::default()
|
ListItem::new(Spans::from(vec![
|
||||||
.bg(Color::Cyan)
|
Span::styled(
|
||||||
.fg(Color::White)
|
"<SPACE> ",
|
||||||
.add_modifier(Modifier::BOLD),
|
Style::default()
|
||||||
),
|
.bg(Color::Cyan)
|
||||||
Span::raw("enter directory")
|
.fg(Color::White)
|
||||||
])
|
.add_modifier(Modifier::BOLD),
|
||||||
),
|
),
|
||||||
ListItem::new(
|
Span::raw("upload/download file"),
|
||||||
Spans::from(vec![
|
])),
|
||||||
Span::styled(
|
ListItem::new(Spans::from(vec![
|
||||||
"<SPACE> ",
|
Span::styled(
|
||||||
Style::default()
|
"<CTRL+D> ",
|
||||||
.bg(Color::Cyan)
|
Style::default()
|
||||||
.fg(Color::White)
|
.bg(Color::Cyan)
|
||||||
.add_modifier(Modifier::BOLD),
|
.fg(Color::White)
|
||||||
),
|
.add_modifier(Modifier::BOLD),
|
||||||
Span::raw("upload/download file")
|
),
|
||||||
])
|
Span::raw("make directory"),
|
||||||
),
|
])),
|
||||||
ListItem::new(
|
ListItem::new(Spans::from(vec![
|
||||||
Spans::from(vec![
|
Span::styled(
|
||||||
Span::styled(
|
"<CTRL+G> ",
|
||||||
"<CTRL+D> ",
|
Style::default()
|
||||||
Style::default()
|
.bg(Color::Cyan)
|
||||||
.bg(Color::Cyan)
|
.fg(Color::White)
|
||||||
.fg(Color::White)
|
.add_modifier(Modifier::BOLD),
|
||||||
.add_modifier(Modifier::BOLD),
|
),
|
||||||
),
|
Span::raw("goto path"),
|
||||||
Span::raw("make directory")
|
])),
|
||||||
])
|
ListItem::new(Spans::from(vec![
|
||||||
),
|
Span::styled(
|
||||||
ListItem::new(
|
"<CTRL+R> ",
|
||||||
Spans::from(vec![
|
Style::default()
|
||||||
Span::styled(
|
.bg(Color::Cyan)
|
||||||
"<CTRL+G> ",
|
.fg(Color::White)
|
||||||
Style::default()
|
.add_modifier(Modifier::BOLD),
|
||||||
.bg(Color::Cyan)
|
),
|
||||||
.fg(Color::White)
|
Span::raw("rename file"),
|
||||||
.add_modifier(Modifier::BOLD),
|
])),
|
||||||
),
|
ListItem::new(Spans::from(vec![
|
||||||
Span::raw("goto path")
|
Span::styled(
|
||||||
])
|
"<CTRL+U> ",
|
||||||
),
|
Style::default()
|
||||||
ListItem::new(
|
.bg(Color::Cyan)
|
||||||
Spans::from(vec![
|
.fg(Color::White)
|
||||||
Span::styled(
|
.add_modifier(Modifier::BOLD),
|
||||||
"<CTRL+R> ",
|
),
|
||||||
Style::default()
|
Span::raw("go to parent directory"),
|
||||||
.bg(Color::Cyan)
|
])),
|
||||||
.fg(Color::White)
|
ListItem::new(Spans::from(vec![
|
||||||
.add_modifier(Modifier::BOLD),
|
Span::styled(
|
||||||
),
|
"<CANC> ",
|
||||||
Span::raw("rename file")
|
Style::default()
|
||||||
])
|
.bg(Color::Cyan)
|
||||||
),
|
.fg(Color::White)
|
||||||
ListItem::new(
|
.add_modifier(Modifier::BOLD),
|
||||||
Spans::from(vec![
|
),
|
||||||
Span::styled(
|
Span::raw("delete file"),
|
||||||
"<CTRL+U> ",
|
])),
|
||||||
Style::default()
|
|
||||||
.bg(Color::Cyan)
|
|
||||||
.fg(Color::White)
|
|
||||||
.add_modifier(Modifier::BOLD),
|
|
||||||
),
|
|
||||||
Span::raw("go to parent directory")
|
|
||||||
])
|
|
||||||
),
|
|
||||||
ListItem::new(
|
|
||||||
Spans::from(vec![
|
|
||||||
Span::styled(
|
|
||||||
"<CANC> ",
|
|
||||||
Style::default()
|
|
||||||
.bg(Color::Cyan)
|
|
||||||
.fg(Color::White)
|
|
||||||
.add_modifier(Modifier::BOLD),
|
|
||||||
),
|
|
||||||
Span::raw("delete file")
|
|
||||||
])
|
|
||||||
)
|
|
||||||
];
|
];
|
||||||
List::new(cmds)
|
List::new(cmds)
|
||||||
.block(
|
.block(
|
||||||
|
|||||||
Reference in New Issue
Block a user