feat: Added <CTRL+S> keybinding to get the total size of selected paths. (#367)
Some checks failed
Linux / build (push) Has been cancelled
MacOS / build (push) Has been cancelled
Windows / build (push) Has been cancelled

* feat: Added `<CTRL+S>` keybinding to get the total size of selected paths.

closes #297
This commit is contained in:
Christian Visintin
2025-11-09 21:14:42 +01:00
committed by GitHub
parent 75943f2b93
commit a0b357cf8c
14 changed files with 166 additions and 38 deletions

View File

@@ -0,0 +1,94 @@
use remotefs::File;
use super::{FileTransferActivity, LogLevel};
use crate::ui::activities::filetransfer::lib::browser::FileExplorerTab;
#[derive(Debug, Copy, Clone)]
enum Host {
HostBridge,
Remote,
}
impl FileTransferActivity {
pub(crate) fn action_get_file_size(&mut self) {
// Get selected file
self.mount_blocking_wait("Getting total path size...");
let total_size = match self.browser.tab() {
FileExplorerTab::HostBridge => {
let files = self.get_local_selected_entries().get_files();
self.get_files_size(files, Host::HostBridge)
}
FileExplorerTab::Remote => {
let files = self.get_remote_selected_entries().get_files();
self.get_files_size(files, Host::Remote)
}
FileExplorerTab::FindHostBridge => {
let files = self.get_found_selected_entries().get_files();
self.get_files_size(files, Host::HostBridge)
}
FileExplorerTab::FindRemote => {
let files = self.get_found_selected_entries().get_files();
self.get_files_size(files, Host::Remote)
}
};
self.umount_wait();
self.mount_info(format!(
"Total file size: {size}",
size = bytesize::ByteSize::b(total_size)
));
}
fn get_files_size(&mut self, files: Vec<File>, host: Host) -> u64 {
files.into_iter().map(|f| self.get_file_size(f, host)).sum()
}
fn get_file_size(&mut self, file: File, host: Host) -> u64 {
if let Some(symlink) = &file.metadata().symlink {
// stat
let stat_res = match host {
Host::HostBridge => self.host_bridge.stat(symlink).map_err(|e| e.to_string()),
Host::Remote => self.client.stat(symlink).map_err(|e| e.to_string()),
};
match stat_res {
Ok(stat) => stat.metadata().size,
Err(err_msg) => {
self.log(
LogLevel::Error,
format!(
"Failed to stat symlink target {path}: {err_msg}",
path = symlink.display(),
),
);
0
}
}
} else if file.is_dir() {
// list and sum
let list_res = match host {
Host::HostBridge => self
.host_bridge
.list_dir(&file.path)
.map_err(|e| e.to_string()),
Host::Remote => self.client.list_dir(&file.path).map_err(|e| e.to_string()),
};
match list_res {
Ok(list) => list.into_iter().map(|f| self.get_file_size(f, host)).sum(),
Err(err_msg) => {
self.log(
LogLevel::Error,
format!(
"Failed to list directory {path}: {err_msg}",
path = file.path.display(),
),
);
0
}
}
} else {
file.metadata().size
}
}
}

View File

@@ -23,6 +23,7 @@ pub(crate) mod copy;
pub(crate) mod delete;
pub(crate) mod edit;
pub(crate) mod exec;
pub(crate) mod file_size;
pub(crate) mod filter;
pub(crate) mod find;
pub(crate) mod mark;

View File

@@ -644,6 +644,11 @@ impl KeybindingsPopup {
.add_col(TextSpan::new("<CTRL+C>").bold().fg(key_color))
.add_col(TextSpan::from(" Interrupt file transfer"))
.add_row()
.add_col(TextSpan::new("<CTRL+S>").bold().fg(key_color))
.add_col(TextSpan::from(
" Get total path size of selected files",
))
.add_row()
.add_col(TextSpan::new("<CTRL+T>").bold().fg(key_color))
.add_col(TextSpan::from(" Show watched paths"))
.build(),

View File

@@ -192,6 +192,10 @@ impl ExplorerFuzzy {
code: Key::Char('i'),
modifiers: KeyModifiers::NONE,
}) => Some(Msg::Ui(UiMsg::ShowFileInfoPopup)),
Event::Keyboard(KeyEvent {
code: Key::Char('s'),
modifiers: KeyModifiers::CONTROL,
}) => Some(Msg::Transfer(TransferMsg::GetFileSize)),
Event::Keyboard(KeyEvent {
code: Key::Char('s') | Key::Function(2),
modifiers: KeyModifiers::NONE,
@@ -338,6 +342,10 @@ impl Component<Msg, NoUserEvent> for ExplorerFind {
code: Key::Char('i'),
modifiers: KeyModifiers::NONE,
}) => Some(Msg::Ui(UiMsg::ShowFileInfoPopup)),
Event::Keyboard(KeyEvent {
code: Key::Char('s'),
modifiers: KeyModifiers::CONTROL,
}) => Some(Msg::Transfer(TransferMsg::GetFileSize)),
Event::Keyboard(KeyEvent {
code: Key::Char('s') | Key::Function(2),
modifiers: KeyModifiers::NONE,
@@ -528,6 +536,10 @@ impl Component<Msg, NoUserEvent> for ExplorerLocal {
code: Key::Char('r') | Key::Function(6),
modifiers: KeyModifiers::NONE,
}) => Some(Msg::Ui(UiMsg::ShowRenamePopup)),
Event::Keyboard(KeyEvent {
code: Key::Char('s'),
modifiers: KeyModifiers::CONTROL,
}) => Some(Msg::Transfer(TransferMsg::GetFileSize)),
Event::Keyboard(KeyEvent {
code: Key::Char('s') | Key::Function(2),
modifiers: KeyModifiers::NONE,
@@ -742,6 +754,10 @@ impl Component<Msg, NoUserEvent> for ExplorerRemote {
code: Key::Char('r') | Key::Function(6),
modifiers: KeyModifiers::NONE,
}) => Some(Msg::Ui(UiMsg::ShowRenamePopup)),
Event::Keyboard(KeyEvent {
code: Key::Char('s'),
modifiers: KeyModifiers::CONTROL,
}) => Some(Msg::Transfer(TransferMsg::GetFileSize)),
Event::Keyboard(KeyEvent {
code: Key::Char('s') | Key::Function(2),
modifiers: KeyModifiers::NONE,

View File

@@ -117,6 +117,7 @@ enum TransferMsg {
DeleteFile,
EnterDirectory,
ExecuteCmd(String),
GetFileSize,
GoTo(String),
GoToParentDirectory,
GoToPreviousDirectory,

View File

@@ -152,6 +152,9 @@ impl FileTransferActivity {
_ => panic!("Found tab doesn't support EXEC"),
};
}
TransferMsg::GetFileSize => {
self.action_get_file_size();
}
TransferMsg::GoTo(dir) => {
match self.browser.tab() {
FileExplorerTab::HostBridge => self.action_change_local_dir(dir),