Added file replace prompt also for 'Many'

This commit is contained in:
veeso 2021-09-25 20:18:50 +02:00
parent 3c3d681ca0
commit bcc6fcd2e7
6 changed files with 200 additions and 38 deletions

View file

@ -130,9 +130,28 @@ impl FileTransferActivity {
dest_path.push(save_as); dest_path.push(save_as);
} }
// Iter files // Iter files
let entries = entries.iter().map(|x| x.get_realfile()).collect(); let entries: Vec<FsEntry> = entries.iter().map(|x| x.get_realfile()).collect();
match self.browser.tab() { match self.browser.tab() {
FileExplorerTab::FindLocal | FileExplorerTab::Local => { FileExplorerTab::FindLocal | FileExplorerTab::Local => {
if opts.check_replace && self.config().get_prompt_on_file_replace() {
// Check which file would be replaced
let existing_files: Vec<&FsEntry> = entries
.iter()
.filter(|x| {
self.remote_file_exists(
Self::file_to_check_many(x, dest_path.as_path()).as_path(),
)
})
.collect();
// Save pending transfer
if !existing_files.is_empty() {
self.set_pending_transfer_many(
existing_files,
&dest_path.to_string_lossy().to_owned(),
);
return;
}
}
if let Err(err) = self.filetransfer_send( if let Err(err) = self.filetransfer_send(
TransferPayload::Many(entries), TransferPayload::Many(entries),
dest_path.as_path(), dest_path.as_path(),
@ -148,6 +167,26 @@ impl FileTransferActivity {
} }
} }
FileExplorerTab::FindRemote | FileExplorerTab::Remote => { FileExplorerTab::FindRemote | FileExplorerTab::Remote => {
if opts.check_replace && self.config().get_prompt_on_file_replace() {
// Check which file would be replaced
let existing_files: Vec<&FsEntry> = entries
.iter()
.filter(|x| {
self.local_file_exists(
Self::file_to_check_many(x, dest_path.as_path()).as_path(),
)
})
.collect();
// Save pending transfer
// Save pending transfer
if !existing_files.is_empty() {
self.set_pending_transfer_many(
existing_files,
&dest_path.to_string_lossy().to_owned(),
);
return;
}
}
if let Err(err) = self.filetransfer_recv( if let Err(err) = self.filetransfer_recv(
TransferPayload::Many(entries), TransferPayload::Many(entries),
dest_path.as_path(), dest_path.as_path(),

View file

@ -30,15 +30,15 @@ use super::{
super::STORAGE_PENDING_TRANSFER, FileExplorerTab, FileTransferActivity, FsEntry, LogLevel, super::STORAGE_PENDING_TRANSFER, FileExplorerTab, FileTransferActivity, FsEntry, LogLevel,
SelectedEntry, TransferOpts, TransferPayload, SelectedEntry, TransferOpts, TransferPayload,
}; };
use std::path::PathBuf; use std::path::{Path, PathBuf};
impl FileTransferActivity { impl FileTransferActivity {
pub(crate) fn action_local_saveas(&mut self, input: String) { pub(crate) fn action_local_saveas(&mut self, input: String) {
self.local_send_file(TransferOpts::default().save_as(input)); self.local_send_file(TransferOpts::default().save_as(Some(input)));
} }
pub(crate) fn action_remote_saveas(&mut self, input: String) { pub(crate) fn action_remote_saveas(&mut self, input: String) {
self.remote_recv_file(TransferOpts::default().save_as(input)); self.remote_recv_file(TransferOpts::default().save_as(Some(input)));
} }
pub(crate) fn action_local_send(&mut self) { pub(crate) fn action_local_send(&mut self) {
@ -57,11 +57,10 @@ impl FileTransferActivity {
/// NOTE: Panics if `STORAGE_PENDING_TRANSFER` is undefined /// NOTE: Panics if `STORAGE_PENDING_TRANSFER` is undefined
pub(crate) fn action_finalize_pending_transfer(&mut self) { pub(crate) fn action_finalize_pending_transfer(&mut self) {
// Retrieve pending transfer // Retrieve pending transfer
let file_name: String = self let file_name = self
.context_mut() .context_mut()
.store_mut() .store_mut()
.take_string(STORAGE_PENDING_TRANSFER) .take_string(STORAGE_PENDING_TRANSFER);
.unwrap();
// Send file // Send file
match self.browser.tab() { match self.browser.tab() {
FileExplorerTab::Local => self.local_send_file( FileExplorerTab::Local => self.local_send_file(
@ -82,9 +81,12 @@ impl FileTransferActivity {
} }
// Reload browsers // Reload browsers
match self.browser.tab() { match self.browser.tab() {
FileExplorerTab::Local => self.reload_remote_dir(), FileExplorerTab::Local | FileExplorerTab::FindLocal => {
FileExplorerTab::Remote => self.reload_local_dir(), self.update_remote_filelist();
FileExplorerTab::FindLocal | FileExplorerTab::FindRemote => {} }
FileExplorerTab::Remote | FileExplorerTab::FindRemote => {
self.update_local_filelist();
}
} }
} }
@ -122,7 +124,26 @@ impl FileTransferActivity {
dest_path.push(save_as); dest_path.push(save_as);
} }
// Iter files // Iter files
let entries = entries.iter().map(|x| x.get_realfile()).collect(); let entries: Vec<FsEntry> = entries.iter().map(|x| x.get_realfile()).collect();
if opts.check_replace && self.config().get_prompt_on_file_replace() {
// Check which file would be replaced
let existing_files: Vec<&FsEntry> = entries
.iter()
.filter(|x| {
self.remote_file_exists(
Self::file_to_check_many(x, dest_path.as_path()).as_path(),
)
})
.collect();
// Save pending transfer
if !existing_files.is_empty() {
self.set_pending_transfer_many(
existing_files,
&dest_path.to_string_lossy().to_owned(),
);
return;
}
}
if let Err(err) = self.filetransfer_send( if let Err(err) = self.filetransfer_send(
TransferPayload::Many(entries), TransferPayload::Many(entries),
dest_path.as_path(), dest_path.as_path(),
@ -175,7 +196,26 @@ impl FileTransferActivity {
dest_path.push(save_as); dest_path.push(save_as);
} }
// Iter files // Iter files
let entries = entries.iter().map(|x| x.get_realfile()).collect(); let entries: Vec<FsEntry> = entries.iter().map(|x| x.get_realfile()).collect();
if opts.check_replace && self.config().get_prompt_on_file_replace() {
// Check which file would be replaced
let existing_files: Vec<&FsEntry> = entries
.iter()
.filter(|x| {
self.local_file_exists(
Self::file_to_check_many(x, dest_path.as_path()).as_path(),
)
})
.collect();
// Save pending transfer
if !existing_files.is_empty() {
self.set_pending_transfer_many(
existing_files,
&dest_path.to_string_lossy().to_owned(),
);
return;
}
}
if let Err(err) = self.filetransfer_recv( if let Err(err) = self.filetransfer_recv(
TransferPayload::Many(entries), TransferPayload::Many(entries),
dest_path.as_path(), dest_path.as_path(),
@ -205,6 +245,17 @@ impl FileTransferActivity {
.set_string(STORAGE_PENDING_TRANSFER, file_name.to_string()); .set_string(STORAGE_PENDING_TRANSFER, file_name.to_string());
} }
/// ### set_pending_transfer_many
///
/// Set pending transfer for many files into storage and mount radio
pub(crate) fn set_pending_transfer_many(&mut self, files: Vec<&FsEntry>, dest_path: &str) {
let file_names: Vec<&str> = files.iter().map(|x| x.get_name()).collect();
self.mount_radio_replace_many(file_names.as_slice());
self.context_mut()
.store_mut()
.set_string(STORAGE_PENDING_TRANSFER, dest_path.to_string());
}
/// ### file_to_check /// ### file_to_check
/// ///
/// Get file to check for path /// Get file to check for path
@ -214,4 +265,10 @@ impl FileTransferActivity {
None => PathBuf::from(e.get_name()), None => PathBuf::from(e.get_name()),
} }
} }
pub(crate) fn file_to_check_many(e: &FsEntry, wrkdir: &Path) -> PathBuf {
let mut p = wrkdir.to_path_buf();
p.push(e.get_name());
p
}
} }

View file

@ -222,8 +222,8 @@ impl TransferOpts {
/// ### save_as /// ### save_as
/// ///
/// Define the name of the file to be saved /// Define the name of the file to be saved
pub fn save_as<S: AsRef<str>>(mut self, n: S) -> Self { pub fn save_as<S: AsRef<str>>(mut self, n: Option<S>) -> Self {
self.save_as = Some(n.as_ref().to_string()); self.save_as = n.map(|x| x.as_ref().to_string());
self self
} }
@ -314,7 +314,7 @@ mod test {
assert!(opts.save_as.is_none()); assert!(opts.save_as.is_none());
let opts = TransferOpts::default() let opts = TransferOpts::default()
.check_replace(false) .check_replace(false)
.save_as("omar.txt"); .save_as(Some("omar.txt"));
assert_eq!(opts.save_as.as_deref().unwrap(), "omar.txt"); assert_eq!(opts.save_as.as_deref().unwrap(), "omar.txt");
assert_eq!(opts.check_replace, false); assert_eq!(opts.check_replace, false);
} }

View file

@ -88,6 +88,7 @@ const COMPONENT_RADIO_SORTING: &str = "RADIO_SORTING";
const COMPONENT_SPAN_STATUS_BAR_LOCAL: &str = "STATUS_BAR_LOCAL"; const COMPONENT_SPAN_STATUS_BAR_LOCAL: &str = "STATUS_BAR_LOCAL";
const COMPONENT_SPAN_STATUS_BAR_REMOTE: &str = "STATUS_BAR_REMOTE"; const COMPONENT_SPAN_STATUS_BAR_REMOTE: &str = "STATUS_BAR_REMOTE";
const COMPONENT_LIST_FILEINFO: &str = "LIST_FILEINFO"; const COMPONENT_LIST_FILEINFO: &str = "LIST_FILEINFO";
const COMPONENT_LIST_REPLACING_FILES: &str = "LIST_REPLACING_FILES"; // NOTE: used for file transfers, to list files which are going to be replaced
/// ## LogLevel /// ## LogLevel
/// ///

View file

@ -31,10 +31,11 @@ use super::{
COMPONENT_EXPLORER_FIND, COMPONENT_EXPLORER_LOCAL, COMPONENT_EXPLORER_REMOTE, COMPONENT_EXPLORER_FIND, COMPONENT_EXPLORER_LOCAL, COMPONENT_EXPLORER_REMOTE,
COMPONENT_INPUT_COPY, COMPONENT_INPUT_EXEC, COMPONENT_INPUT_FIND, COMPONENT_INPUT_GOTO, COMPONENT_INPUT_COPY, COMPONENT_INPUT_EXEC, COMPONENT_INPUT_FIND, COMPONENT_INPUT_GOTO,
COMPONENT_INPUT_MKDIR, COMPONENT_INPUT_NEWFILE, COMPONENT_INPUT_OPEN_WITH, COMPONENT_INPUT_MKDIR, COMPONENT_INPUT_NEWFILE, COMPONENT_INPUT_OPEN_WITH,
COMPONENT_INPUT_RENAME, COMPONENT_INPUT_SAVEAS, COMPONENT_LIST_FILEINFO, COMPONENT_LOG_BOX, COMPONENT_INPUT_RENAME, COMPONENT_INPUT_SAVEAS, COMPONENT_LIST_FILEINFO,
COMPONENT_PROGRESS_BAR_FULL, COMPONENT_PROGRESS_BAR_PARTIAL, COMPONENT_RADIO_DELETE, COMPONENT_LIST_REPLACING_FILES, COMPONENT_LOG_BOX, COMPONENT_PROGRESS_BAR_FULL,
COMPONENT_RADIO_DISCONNECT, COMPONENT_RADIO_QUIT, COMPONENT_RADIO_REPLACE, COMPONENT_PROGRESS_BAR_PARTIAL, COMPONENT_RADIO_DELETE, COMPONENT_RADIO_DISCONNECT,
COMPONENT_RADIO_SORTING, COMPONENT_TEXT_ERROR, COMPONENT_TEXT_FATAL, COMPONENT_TEXT_HELP, COMPONENT_RADIO_QUIT, COMPONENT_RADIO_REPLACE, COMPONENT_RADIO_SORTING, COMPONENT_TEXT_ERROR,
COMPONENT_TEXT_FATAL, COMPONENT_TEXT_HELP,
}; };
use crate::fs::explorer::FileSorting; use crate::fs::explorer::FileSorting;
use crate::fs::FsEntry; use crate::fs::FsEntry;
@ -583,7 +584,7 @@ impl Update for FileTransferActivity {
FileExplorerTab::Remote => self.action_remote_saveas(input.to_string()), FileExplorerTab::Remote => self.action_remote_saveas(input.to_string()),
FileExplorerTab::FindLocal | FileExplorerTab::FindRemote => { FileExplorerTab::FindLocal | FileExplorerTab::FindRemote => {
// Get entry // Get entry
self.action_find_transfer(TransferOpts::default().save_as(input)); self.action_find_transfer(TransferOpts::default().save_as(Some(input)));
} }
} }
self.umount_saveas(); self.umount_saveas();
@ -661,6 +662,12 @@ impl Update for FileTransferActivity {
self.umount_radio_replace(); self.umount_radio_replace();
None None
} }
(COMPONENT_RADIO_REPLACE, key) if key == &MSG_KEY_TAB => {
if self.is_radio_replace_extended() {
self.view.active(COMPONENT_LIST_REPLACING_FILES);
}
None
}
(COMPONENT_RADIO_REPLACE, Msg::OnSubmit(Payload::One(Value::Usize(0)))) => { (COMPONENT_RADIO_REPLACE, Msg::OnSubmit(Payload::One(Value::Usize(0)))) => {
// Choice is 'YES' // Choice is 'YES'
self.umount_radio_replace(); self.umount_radio_replace();
@ -668,6 +675,15 @@ impl Update for FileTransferActivity {
None None
} }
(COMPONENT_RADIO_REPLACE, _) => None, (COMPONENT_RADIO_REPLACE, _) => None,
(COMPONENT_LIST_REPLACING_FILES, key) if key == &MSG_KEY_TAB => {
self.view.active(COMPONENT_RADIO_REPLACE);
None
}
(COMPONENT_LIST_REPLACING_FILES, key) if key == &MSG_KEY_ESC => {
self.umount_radio_replace();
None
}
(COMPONENT_LIST_REPLACING_FILES, _) => None,
// -- disconnect // -- disconnect
(COMPONENT_RADIO_DISCONNECT, key) (COMPONENT_RADIO_DISCONNECT, key)
if key == &MSG_KEY_ESC if key == &MSG_KEY_ESC

View file

@ -310,10 +310,30 @@ impl FileTransferActivity {
} }
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_REPLACE) { if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_REPLACE) {
if props.visible { if props.visible {
let popup = draw_area_in(f.size(), 50, 10); // NOTE: handle extended / normal modes
f.render_widget(Clear, popup); if self.is_radio_replace_extended() {
// make popup let popup = draw_area_in(f.size(), 50, 50);
self.view.render(super::COMPONENT_RADIO_REPLACE, f, popup); f.render_widget(Clear, popup);
let popup_chunks = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Percentage(85), // List
Constraint::Percentage(15), // Radio
]
.as_ref(),
)
.split(popup);
self.view
.render(super::COMPONENT_LIST_REPLACING_FILES, f, popup_chunks[0]);
self.view
.render(super::COMPONENT_RADIO_REPLACE, f, popup_chunks[1]);
} else {
let popup = draw_area_in(f.size(), 50, 10);
f.render_widget(Clear, popup);
// make popup
self.view.render(super::COMPONENT_RADIO_REPLACE, f, popup);
}
} }
} }
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_DISCONNECT) { if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_DISCONNECT) {
@ -708,28 +728,57 @@ impl FileTransferActivity {
pub(super) fn mount_radio_replace(&mut self, file_name: &str) { pub(super) fn mount_radio_replace(&mut self, file_name: &str) {
let warn_color = self.theme().misc_warn_dialog; let warn_color = self.theme().misc_warn_dialog;
self.view.mount( self.mount_radio_dialog(
super::COMPONENT_RADIO_REPLACE, super::COMPONENT_RADIO_REPLACE,
Box::new(Radio::new( format!("File '{}' already exists. Overwrite file?", file_name),
RadioPropsBuilder::default() &["Yes", "No"],
.with_color(warn_color) 0,
.with_inverted_color(Color::Black) warn_color,
.with_borders(Borders::ALL, BorderType::Plain, warn_color) );
}
pub(super) fn mount_radio_replace_many(&mut self, files: &[&str]) {
let warn_color = self.theme().misc_warn_dialog;
// Make rows
let rows = files.iter().map(|x| vec![TextSpan::new(x)]).collect();
self.view.mount(
super::COMPONENT_LIST_REPLACING_FILES,
Box::new(List::new(
ListPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, warn_color)
.scrollable(true)
.with_highlighted_color(warn_color)
.with_highlighted_str(Some("> "))
.with_title( .with_title(
format!("File '{}' already exists. Overwrite file?", file_name), "The following files are going to be replaced",
Alignment::Center, Alignment::Center,
) )
.with_options(&[String::from("Yes"), String::from("No")]) .with_foreground(warn_color)
.with_value(0) .with_rows(rows)
.rewind(true)
.build(), .build(),
)), )),
); );
self.view.active(super::COMPONENT_RADIO_REPLACE); self.mount_radio_dialog(
super::COMPONENT_RADIO_REPLACE,
"Overwrite files?",
&["Yes", "No"],
0,
warn_color,
);
}
/// ### is_radio_replace_extended
///
/// Returns whether radio replace is in "extended" mode (for many files)
pub(super) fn is_radio_replace_extended(&self) -> bool {
self.view
.get_state(super::COMPONENT_LIST_REPLACING_FILES)
.is_some()
} }
pub(super) fn umount_radio_replace(&mut self) { pub(super) fn umount_radio_replace(&mut self) {
self.view.umount(super::COMPONENT_RADIO_REPLACE); self.view.umount(super::COMPONENT_RADIO_REPLACE);
self.view.umount(super::COMPONENT_LIST_REPLACING_FILES); // NOTE: replace anyway
} }
pub(super) fn mount_file_info(&mut self, file: &FsEntry) { pub(super) fn mount_file_info(&mut self, file: &FsEntry) {
@ -1056,10 +1105,10 @@ impl FileTransferActivity {
self.view.active(id); self.view.active(id);
} }
fn mount_radio_dialog( fn mount_radio_dialog<S: AsRef<str>>(
&mut self, &mut self,
id: &str, id: &str,
text: &str, text: S,
opts: &[&str], opts: &[&str],
default: usize, default: usize,
color: Color, color: Color,
@ -1071,7 +1120,7 @@ impl FileTransferActivity {
.with_color(color) .with_color(color)
.with_inverted_color(Color::Black) .with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, color) .with_borders(Borders::ALL, BorderType::Rounded, color)
.with_title(text, Alignment::Center) .with_title(text.as_ref(), Alignment::Center)
.with_options(opts) .with_options(opts)
.with_value(default) .with_value(default)
.rewind(true) .rewind(true)