From 145b778ff38eb25317483d34aa5c667838110c36 Mon Sep 17 00:00:00 2001 From: ChristianVisintin Date: Thu, 10 Dec 2020 10:28:13 +0100 Subject: [PATCH] Added FileInfo popup ('I') --- .../activities/filetransfer_activity/input.rs | 38 ++- .../filetransfer_activity/layout.rs | 231 +++++++++++++++++- .../activities/filetransfer_activity/mod.rs | 1 + 3 files changed, 261 insertions(+), 9 deletions(-) diff --git a/src/ui/activities/filetransfer_activity/input.rs b/src/ui/activities/filetransfer_activity/input.rs index 5d41ba0..82a1b5c 100644 --- a/src/ui/activities/filetransfer_activity/input.rs +++ b/src/ui/activities/filetransfer_activity/input.rs @@ -215,10 +215,11 @@ impl FileTransferActivity { } 'h' | 'H' => { // Show help - // If ctrl is enabled... - if key.modifiers.intersects(KeyModifiers::CONTROL) { - self.input_mode = InputMode::Popup(PopupType::Help); - } + self.input_mode = InputMode::Popup(PopupType::Help); + } + 'i' | 'I' => { + // Show file info + self.input_mode = InputMode::Popup(PopupType::FileInfo); } 'r' | 'R' => { // Rename @@ -431,10 +432,11 @@ impl FileTransferActivity { } 'h' | 'H' => { // Show help - // If ctrl is enabled... - if key.modifiers.intersects(KeyModifiers::CONTROL) { - self.input_mode = InputMode::Popup(PopupType::Help); - } + self.input_mode = InputMode::Popup(PopupType::Help); + } + 'i' | 'I' => { + // Show file info + self.input_mode = InputMode::Popup(PopupType::FileInfo); } 'r' | 'R' => { // Rename @@ -569,6 +571,7 @@ impl FileTransferActivity { pub(super) fn handle_input_event_mode_popup(&mut self, ev: &InputEvent, popup: PopupType) { match popup { PopupType::Alert(_, _) => self.handle_input_event_mode_popup_alert(ev), + PopupType::FileInfo => self.handle_input_event_mode_popup_fileinfo(ev), PopupType::Help => self.handle_input_event_mode_popup_help(ev), PopupType::Fatal(_) => self.handle_input_event_mode_popup_fatal(ev), PopupType::Input(_, cb) => self.handle_input_event_mode_popup_input(ev, cb), @@ -599,6 +602,25 @@ impl FileTransferActivity { } } + /// ### handle_input_event_mode_popup_fileinfo + /// + /// Input event handler for popup fileinfo + pub(super) fn handle_input_event_mode_popup_fileinfo(&mut self, ev: &InputEvent) { + // If enter, close popup + match ev { + InputEvent::Key(key) => { + match key.code { + KeyCode::Enter | KeyCode::Esc => { + // Set input mode back to explorer + self.input_mode = InputMode::Explorer; + } + _ => { /* Nothing to do */ } + } + } + _ => { /* Nothing to do */ } + } + } + /// ### handle_input_event_mode_popup_help /// /// Input event handler for popup help diff --git a/src/ui/activities/filetransfer_activity/layout.rs b/src/ui/activities/filetransfer_activity/layout.rs index 862deed..7287273 100644 --- a/src/ui/activities/filetransfer_activity/layout.rs +++ b/src/ui/activities/filetransfer_activity/layout.rs @@ -19,12 +19,18 @@ * */ +extern crate bytesize; extern crate hostname; +#[cfg(any(unix, macos, linux))] +extern crate users; use super::{ Context, DialogYesNoOption, FileExplorerTab, FileTransferActivity, FsEntry, InputField, InputMode, LogLevel, LogRecord, PopupType, }; +use crate::utils::time_to_str; + +use bytesize::ByteSize; use std::path::{Path, PathBuf}; use tui::{ layout::{Constraint, Corner, Direction, Layout, Rect}, @@ -33,6 +39,8 @@ use tui::{ widgets::{Block, Borders, Clear, Gauge, List, ListItem, ListState, Paragraph, Tabs}, }; use unicode_width::UnicodeWidthStr; +#[cfg(any(unix, macos, linux))] +use users::{get_group_by_gid, get_user_by_uid}; impl FileTransferActivity { /// ### draw @@ -96,8 +104,9 @@ impl FileTransferActivity { let (width, height): (u16, u16) = match popup { PopupType::Alert(_, _) => (50, 10), PopupType::Fatal(_) => (50, 10), + PopupType::FileInfo => (50, 50), PopupType::Help => (50, 70), - PopupType::Input(_, _) => (30, 10), + PopupType::Input(_, _) => (40, 10), PopupType::Progress(_) => (40, 10), PopupType::Wait(_) => (50, 10), PopupType::YesNo(_, _, _) => (30, 10), @@ -113,6 +122,7 @@ impl FileTransferActivity { self.draw_popup_fatal(txt.clone(), popup_area.width), popup_area, ), + PopupType::FileInfo => f.render_widget(self.draw_popup_fileinfo(), popup_area), PopupType::Help => f.render_widget(self.draw_popup_help(), popup_area), PopupType::Input(txt, _) => { f.render_widget(self.draw_popup_input(txt.clone()), popup_area); @@ -434,6 +444,225 @@ impl FileTransferActivity { ) } + /// ### draw_popup_fileinfo + /// + /// Draw popup containing info about selected fsentry + pub(super) fn draw_popup_fileinfo(&self) -> List { + let mut info: Vec = Vec::new(); + // Get current fsentry + let fsentry: Option<&FsEntry> = match self.tab { + FileExplorerTab::Local => { + // Get selected file + match self.local.files.get(self.local.index) { + Some(entry) => Some(entry), + None => None, + } + } + FileExplorerTab::Remote => match self.remote.files.get(self.remote.index) { + Some(entry) => Some(entry), + None => None, + }, + }; + // Get file_name and fill info list + let file_name: String = match fsentry { + Some(fsentry) => match fsentry { + FsEntry::Directory(dir) => { + // Push path + info.push(ListItem::new(Spans::from(vec![ + Span::styled("Path: ", Style::default()), + Span::styled( + match &dir.symlink { + Some(symlink) => { + format!("{} => {}", dir.abs_path.display(), symlink.display()) + } + None => dir.abs_path.to_string_lossy().to_string(), + }, + Style::default() + .fg(Color::LightYellow) + .add_modifier(Modifier::BOLD), + ), + ]))); + // Push creation time + info.push(ListItem::new(Spans::from(vec![ + Span::styled("Creation time: ", Style::default()), + Span::styled( + time_to_str(dir.creation_time, "%b %d %Y %H:%M:%S"), + Style::default() + .fg(Color::LightGreen) + .add_modifier(Modifier::BOLD), + ), + ]))); + // Push Last change + info.push(ListItem::new(Spans::from(vec![ + Span::styled("Last change time: ", Style::default()), + Span::styled( + time_to_str(dir.last_change_time, "%b %d %Y %H:%M:%S"), + Style::default().fg(Color::Red).add_modifier(Modifier::BOLD), + ), + ]))); + // Push Last access + info.push(ListItem::new(Spans::from(vec![ + Span::styled("Last access time: ", Style::default()), + Span::styled( + time_to_str(dir.last_access_time, "%b %d %Y %H:%M:%S"), + Style::default() + .fg(Color::LightMagenta) + .add_modifier(Modifier::BOLD), + ), + ]))); + // User + #[cfg(any(unix, macos, linux))] + let username: String = match dir.user { + Some(uid) => match get_user_by_uid(uid) { + Some(user) => user.name().to_string_lossy().to_string(), + None => uid.to_string(), + }, + None => String::from("0"), + }; + #[cfg(target_os = "windows")] + let username: format!("{}", dir.user.unwrap_or(0)); + info.push(ListItem::new(Spans::from(vec![ + Span::styled("User: ", Style::default()), + Span::styled( + username, + Style::default() + .fg(Color::LightRed) + .add_modifier(Modifier::BOLD), + ), + ]))); + // Group + #[cfg(any(unix, macos, linux))] + let group: String = match dir.group { + Some(gid) => match get_group_by_gid(gid) { + Some(group) => group.name().to_string_lossy().to_string(), + None => gid.to_string(), + }, + None => String::from("0"), + }; + #[cfg(target_os = "windows")] + let group: String = format!("{}", dir.group.unwrap_or(0)); + info.push(ListItem::new(Spans::from(vec![ + Span::styled("Group: ", Style::default()), + Span::styled( + group, + Style::default() + .fg(Color::LightBlue) + .add_modifier(Modifier::BOLD), + ), + ]))); + // Finally return file name + dir.name.clone() + } + FsEntry::File(file) => { + // Push path + info.push(ListItem::new(Spans::from(vec![ + Span::styled("Path: ", Style::default()), + Span::styled( + match &file.symlink { + Some(symlink) => { + format!("{} => {}", file.abs_path.display(), symlink.display()) + } + None => file.abs_path.to_string_lossy().to_string(), + }, + Style::default() + .fg(Color::LightYellow) + .add_modifier(Modifier::BOLD), + ), + ]))); + // Push size + info.push(ListItem::new(Spans::from(vec![ + Span::styled("Size: ", Style::default()), + Span::styled( + format!("{} ({})", ByteSize(file.size as u64), file.size), + Style::default() + .fg(Color::LightBlue) + .add_modifier(Modifier::BOLD), + ), + ]))); + // Push creation time + info.push(ListItem::new(Spans::from(vec![ + Span::styled("Creation time: ", Style::default()), + Span::styled( + time_to_str(file.creation_time, "%b %d %Y %H:%M:%S"), + Style::default() + .fg(Color::LightGreen) + .add_modifier(Modifier::BOLD), + ), + ]))); + // Push Last change + info.push(ListItem::new(Spans::from(vec![ + Span::styled("Last change time: ", Style::default()), + Span::styled( + time_to_str(file.last_change_time, "%b %d %Y %H:%M:%S"), + Style::default().fg(Color::Red).add_modifier(Modifier::BOLD), + ), + ]))); + // Push Last access + info.push(ListItem::new(Spans::from(vec![ + Span::styled("Last access time: ", Style::default()), + Span::styled( + time_to_str(file.last_access_time, "%b %d %Y %H:%M:%S"), + Style::default() + .fg(Color::LightMagenta) + .add_modifier(Modifier::BOLD), + ), + ]))); + // User + #[cfg(any(unix, macos, linux))] + let username: String = match file.user { + Some(uid) => match get_user_by_uid(uid) { + Some(user) => user.name().to_string_lossy().to_string(), + None => uid.to_string(), + }, + None => String::from("0"), + }; + #[cfg(target_os = "windows")] + let username: format!("{}", file.user.unwrap_or(0)); + info.push(ListItem::new(Spans::from(vec![ + Span::styled("User: ", Style::default()), + Span::styled( + username, + Style::default() + .fg(Color::LightRed) + .add_modifier(Modifier::BOLD), + ), + ]))); + // Group + #[cfg(any(unix, macos, linux))] + let group: String = match file.group { + Some(gid) => match get_group_by_gid(gid) { + Some(group) => group.name().to_string_lossy().to_string(), + None => gid.to_string(), + }, + None => String::from("0"), + }; + #[cfg(target_os = "windows")] + let group: String = format!("{}", file.group.unwrap_or(0)); + info.push(ListItem::new(Spans::from(vec![ + Span::styled("Group: ", Style::default()), + Span::styled( + group, + Style::default() + .fg(Color::LightBlue) + .add_modifier(Modifier::BOLD), + ), + ]))); + // Finally return file name + file.name.clone() + } + }, + None => String::from(""), + }; + List::new(info) + .block( + Block::default() + .borders(Borders::ALL) + .border_style(Style::default()) + .title(file_name), + ) + .start_corner(Corner::TopLeft) + } + /// ### draw_footer /// /// Draw authentication page footer diff --git a/src/ui/activities/filetransfer_activity/mod.rs b/src/ui/activities/filetransfer_activity/mod.rs index bbac3e5..772c3aa 100644 --- a/src/ui/activities/filetransfer_activity/mod.rs +++ b/src/ui/activities/filetransfer_activity/mod.rs @@ -97,6 +97,7 @@ enum DialogYesNoOption { enum PopupType { Alert(Color, String), // Block color; Block text Fatal(String), // Must quit after being hidden + FileInfo, // Show info about current file Help, // Show Help Input(String, OnInputSubmitCallback), // Input description; Callback for submit Progress(String), // Progress block text