Something is working, but it is still unusable

This commit is contained in:
veeso
2021-03-14 15:31:49 +01:00
parent fe6e0eeab5
commit 5bc46dd720
5 changed files with 262 additions and 227 deletions

View File

@@ -27,9 +27,11 @@
extern crate dirs;
// Locals
use super::{AuthActivity, Color, DialogYesNoOption, Popup};
use super::{AuthActivity, FileTransferProtocol};
use crate::system::bookmarks_client::BookmarksClient;
use crate::system::environment;
use crate::ui::layout::props::PropValue;
use crate::ui::layout::Payload;
// Ext
use std::path::PathBuf;
@@ -60,14 +62,10 @@ impl AuthActivity {
// Iterate over bookmarks
if let Some(key) = self.bookmarks_list.get(idx) {
if let Some(bookmark) = bookmarks_cli.get_bookmark(&key) {
// Load parameters
self.address = bookmark.0;
self.port = bookmark.1.to_string();
self.protocol = bookmark.2;
self.username = bookmark.3;
if let Some(password) = bookmark.4 {
self.password = password;
}
// Load parameters into components
self.load_bookmark_into_gui(
bookmark.0, bookmark.1, bookmark.2, bookmark.3, bookmark.4,
);
}
}
}
@@ -76,41 +74,24 @@ impl AuthActivity {
/// ### save_bookmark
///
/// Save current input fields as a bookmark
pub(super) fn save_bookmark(&mut self, name: String) {
// Check port
let port: u16 = match self.port.parse::<usize>() {
Ok(val) => {
if val > 65535 {
self.popup = Some(Popup::Alert(
Color::Red,
String::from("Specified port must be in range 0-65535"),
));
return;
}
val as u16
}
Err(_) => {
self.popup = Some(Popup::Alert(
Color::Red,
String::from("Specified port is not a number"),
));
return;
}
};
pub(super) fn save_bookmark(&mut self, name: String, save_password: bool) {
let (address, port, protocol, username, password) = self.get_input();
if let Some(bookmarks_cli) = self.bookmarks_client.as_mut() {
// Check if password must be saved
let password: Option<String> = match self.choice_opt {
DialogYesNoOption::Yes => Some(self.password.clone()),
DialogYesNoOption::No => None,
let password: Option<String> = match save_password {
true => match self
.view
.get_value(super::COMPONENT_RADIO_BOOKMARK_SAVE_PWD)
{
Some(Payload::Unsigned(choice)) => match choice {
0 => Some(password), // Yes
_ => None, // No
},
_ => None, // No such component
},
false => None,
};
bookmarks_cli.add_bookmark(
name.clone(),
self.address.clone(),
port,
self.protocol,
self.username.clone(),
password,
);
bookmarks_cli.add_bookmark(name.clone(), address, port, protocol, username, password);
// Save bookmarks
self.write_bookmarks();
// Push bookmark to list and sort
@@ -143,10 +124,9 @@ impl AuthActivity {
if let Some(key) = self.recents_list.get(idx) {
if let Some(bookmark) = client.get_recent(key) {
// Load parameters
self.address = bookmark.0;
self.port = bookmark.1.to_string();
self.protocol = bookmark.2;
self.username = bookmark.3;
self.load_bookmark_into_gui(
bookmark.0, bookmark.1, bookmark.2, bookmark.3, None,
);
}
}
}
@@ -156,33 +136,9 @@ impl AuthActivity {
///
/// Save current input fields as a "recent"
pub(super) fn save_recent(&mut self) {
// Check port
let port: u16 = match self.port.parse::<usize>() {
Ok(val) => {
if val > 65535 {
self.popup = Some(Popup::Alert(
Color::Red,
String::from("Specified port must be in range 0-65535"),
));
return;
}
val as u16
}
Err(_) => {
self.popup = Some(Popup::Alert(
Color::Red,
String::from("Specified port is not a number"),
));
return;
}
};
let (address, port, protocol, username, _password) = self.get_input();
if let Some(bookmarks_cli) = self.bookmarks_client.as_mut() {
bookmarks_cli.add_recent(
self.address.clone(),
port,
self.protocol,
self.username.clone(),
);
bookmarks_cli.add_recent(address, port, protocol, username);
// Save bookmarks
self.write_bookmarks();
}
@@ -194,10 +150,7 @@ impl AuthActivity {
fn write_bookmarks(&mut self) {
if let Some(bookmarks_cli) = self.bookmarks_client.as_ref() {
if let Err(err) = bookmarks_cli.write_bookmarks() {
self.popup = Some(Popup::Alert(
Color::Red,
format!("Could not write bookmarks: {}", err),
));
self.mount_error(format!("Could not write bookmarks: {}", err).as_str());
}
}
}
@@ -240,28 +193,29 @@ impl AuthActivity {
self.sort_recents();
}
Err(err) => {
self.popup = Some(Popup::Alert(
Color::Red,
self.mount_error(
format!(
"Could not initialize bookmarks (at \"{}\", \"{}\"): {}",
bookmarks_file.display(),
config_dir_path.display(),
err
),
))
)
.as_str(),
);
}
}
}
}
Err(err) => {
self.popup = Some(Popup::Alert(
Color::Red,
format!("Could not initialize configuration directory: {}", err),
))
self.mount_error(
format!("Could not initialize configuration directory: {}", err).as_str(),
);
}
}
}
// -- privates
/// ### sort_bookmarks
///
/// Sort bookmarks in list
@@ -278,4 +232,47 @@ impl AuthActivity {
// Reverse order
self.recents_list.sort_by(|a, b| b.cmp(a));
}
/// ### load_bookmark_into_gui
///
/// Load bookmark data into the gui components
fn load_bookmark_into_gui(
&mut self,
addr: String,
port: u16,
protocol: FileTransferProtocol,
username: String,
password: Option<String>,
) {
// Load parameters into components
if let Some(mut props) = self.view.get_props(super::COMPONENT_INPUT_ADDR) {
let props = props.with_value(PropValue::Str(addr)).build();
self.view.update(super::COMPONENT_INPUT_ADDR, props);
}
if let Some(mut props) = self.view.get_props(super::COMPONENT_INPUT_PORT) {
let props = props.with_value(PropValue::Unsigned(port as usize)).build();
self.view.update(super::COMPONENT_INPUT_PORT, props);
}
if let Some(mut props) = self.view.get_props(super::COMPONENT_RADIO_PROTOCOL) {
let props = props
.with_value(PropValue::Unsigned(match protocol {
FileTransferProtocol::Sftp => 0,
FileTransferProtocol::Scp => 1,
FileTransferProtocol::Ftp(false) => 2,
FileTransferProtocol::Ftp(true) => 3,
}))
.build();
self.view.update(super::COMPONENT_RADIO_PROTOCOL, props);
}
if let Some(mut props) = self.view.get_props(super::COMPONENT_INPUT_USERNAME) {
let props = props.with_value(PropValue::Str(username)).build();
self.view.update(super::COMPONENT_INPUT_USERNAME, props);
}
if let Some(password) = password {
if let Some(mut props) = self.view.get_props(super::COMPONENT_INPUT_PASSWORD) {
let props = props.with_value(PropValue::Str(password)).build();
self.view.update(super::COMPONENT_INPUT_PASSWORD, props);
}
}
}
}

View File

@@ -45,10 +45,6 @@ use crate::utils::git;
// Includes
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
use std::path::PathBuf;
use tui::style::Color;
// Types
type DialogCallback = fn(&mut AuthActivity);
// -- components
const COMPONENT_TEXT_HEADER: &str = "TEXT_HEADER";
@@ -68,48 +64,6 @@ const COMPONENT_RADIO_BOOKMARK_SAVE_PWD: &str = "RADIO_SAVE_PASSWORD";
const COMPONENT_BOOKMARKS_LIST: &str = "BOOKMARKS_LIST";
const COMPONENT_RECENTS_LIST: &str = "RECENTS_LIST";
/// ### InputField
///
/// InputField describes the current input field to edit
#[derive(std::cmp::PartialEq)]
enum InputField {
Address,
Port,
Protocol,
Username,
Password,
}
/// ### DialogYesNoOption
///
/// Current yes/no dialog option
#[derive(std::cmp::PartialEq, Clone)]
enum DialogYesNoOption {
Yes,
No,
}
/// ### Popup
///
/// Popup describes the type of the popup displayed
#[derive(Clone)]
enum Popup {
Alert(Color, String), // Show a message displaying text with the provided color
Help, // Help page
SaveBookmark,
YesNo(String, DialogCallback, DialogCallback), // Yes, no callback
}
#[derive(std::cmp::PartialEq)]
/// ### InputForm
///
/// InputForm describes the selected input form
enum InputForm {
AuthCredentials,
Bookmarks,
Recents,
}
/// ### AuthActivity
///
/// AuthActivity is the data holder for the authentication activity
@@ -126,17 +80,9 @@ pub struct AuthActivity {
view: View,
bookmarks_client: Option<BookmarksClient>,
config_client: Option<ConfigClient>,
selected_field: InputField, // Selected field in AuthCredentials Form
popup: Option<Popup>,
input_form: InputForm,
password_placeholder: String,
redraw: bool, // Should ui actually be redrawned?
input_txt: String, // Input text
choice_opt: DialogYesNoOption, // Dialog popup selected option
bookmarks_idx: usize, // Index of selected bookmark
bookmarks_list: Vec<String>, // List of bookmarks
recents_idx: usize, // Index of selected recent
recents_list: Vec<String>, // list of recents
redraw: bool, // Should ui actually be redrawned?
bookmarks_list: Vec<String>, // List of bookmarks
recents_list: Vec<String>, // list of recents
// misc
new_version: Option<String>, // Contains new version of termscp
}
@@ -165,16 +111,8 @@ impl AuthActivity {
view: View::init(),
bookmarks_client: None,
config_client: None,
selected_field: InputField::Address,
popup: None,
input_form: InputForm::AuthCredentials,
password_placeholder: String::new(),
redraw: true, // True at startup
input_txt: String::new(),
choice_opt: DialogYesNoOption::Yes,
bookmarks_idx: 0,
bookmarks_list: Vec::new(),
recents_idx: 0,
recents_list: Vec::new(),
new_version: None,
}
@@ -199,19 +137,18 @@ impl AuthActivity {
self.config_client = Some(cli);
}
Err(err) => {
self.popup = Some(Popup::Alert(
Color::Red,
format!("Could not initialize user configuration: {}", err),
))
self.mount_error(
format!("Could not initialize user configuration: {}", err)
.as_str(),
);
}
}
}
}
Err(err) => {
self.popup = Some(Popup::Alert(
Color::Red,
format!("Could not initialize configuration directory: {}", err),
))
self.mount_error(
format!("Could not initialize configuration directory: {}", err).as_str(),
);
}
}
}
@@ -227,10 +164,9 @@ impl AuthActivity {
Ok(version) => self.new_version = version,
Err(err) => {
// Report error
self.popup = Some(Popup::Alert(
Color::Red,
format!("Could not check for new updates: {}", err),
))
self.mount_error(
format!("Could not check for new updates: {}", err).as_str(),
);
}
}
}
@@ -251,7 +187,6 @@ impl Activity for AuthActivity {
self.context.as_mut().unwrap().clear_screen();
// Put raw mode on enabled
let _ = enable_raw_mode();
self.popup = None;
// Init bookmarks client
if self.bookmarks_client.is_none() {
self.init_bookmarks_client();

View File

@@ -25,16 +25,13 @@
// locals
use super::{
AuthActivity, FileTransferProtocol, COMPONENT_BOOKMARKS_LIST, COMPONENT_INPUT_ADDR,
COMPONENT_INPUT_BOOKMARK_NAME, COMPONENT_INPUT_PASSWORD, COMPONENT_INPUT_PORT,
COMPONENT_INPUT_USERNAME, COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK,
COMPONENT_RADIO_BOOKMARK_DEL_RECENT, COMPONENT_RADIO_BOOKMARK_SAVE_PWD,
COMPONENT_RADIO_PROTOCOL, COMPONENT_RECENTS_LIST, COMPONENT_TEXT_ERROR, COMPONENT_TEXT_HELP,
};
use crate::ui::layout::{
props::{TextParts, TextSpan},
Msg, Payload,
AuthActivity, COMPONENT_BOOKMARKS_LIST, COMPONENT_INPUT_ADDR, COMPONENT_INPUT_BOOKMARK_NAME,
COMPONENT_INPUT_PASSWORD, COMPONENT_INPUT_PORT, COMPONENT_INPUT_USERNAME,
COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK, COMPONENT_RADIO_BOOKMARK_DEL_RECENT,
COMPONENT_RADIO_BOOKMARK_SAVE_PWD, COMPONENT_RADIO_PROTOCOL, COMPONENT_RECENTS_LIST,
COMPONENT_TEXT_ERROR, COMPONENT_TEXT_HELP,
};
use crate::ui::layout::{props::TextParts, Msg, Payload};
// ext
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
@@ -193,6 +190,15 @@ impl AuthActivity {
self.mount_recent_del_dialog();
None
}
// Enter
(COMPONENT_BOOKMARKS_LIST, Msg::OnSubmit(Payload::Unsigned(idx))) => {
self.load_bookmark(*idx);
None
}
(COMPONENT_RECENTS_LIST, Msg::OnSubmit(Payload::Unsigned(idx))) => {
self.load_recent(*idx);
None
}
// Bookmark radio
// Del bookmarks
(
@@ -209,16 +215,19 @@ impl AuthActivity {
Some(Payload::Unsigned(index)) => {
// Delete bookmark
self.del_bookmark(index);
// TODO: view bookmarks
// Update bookmarks
match self.view.get_props(COMPONENT_BOOKMARKS_LIST).as_mut() {
None => None,
Some(props) => {
let msg = self
.view
.update(COMPONENT_BOOKMARKS_LIST, props.with_texts(
TextParts::new(Some(String::from("Bookmarks")), Some(self.view_bookmarks()))
).build()); // TODO: set rows
let msg = self.view.update(
COMPONENT_BOOKMARKS_LIST,
props
.with_texts(TextParts::new(
Some(String::from("Bookmarks")),
Some(self.view_bookmarks()),
))
.build(),
);
self.update(msg)
}
}
@@ -240,16 +249,19 @@ impl AuthActivity {
Some(Payload::Unsigned(index)) => {
// Delete recent
self.del_recent(index);
// TODO: view recents
// Update bookmarks
match self.view.get_props(COMPONENT_RECENTS_LIST).as_mut() {
None => None,
Some(props) => {
let msg = self
.view
.update(COMPONENT_RECENTS_LIST, props.with_texts(
TextParts::new(Some(String::from("Recent connections")), Some(self.view_recent_connections()))
).build()); // TODO: set rows
let msg = self.view.update(
COMPONENT_RECENTS_LIST,
props
.with_texts(TextParts::new(
Some(String::from("Recent connections")),
Some(self.view_recent_connections()),
))
.build(),
);
self.update(msg)
}
}
@@ -344,7 +356,8 @@ impl AuthActivity {
},
_ => false,
};
// TODO: save bookmark
// Save bookmark
self.save_bookmark(bookmark_name, save_pwd);
// Umount popup
self.umount_bookmark_save_dialog();
None
@@ -365,44 +378,8 @@ impl AuthActivity {
// On submit on any unhandled (connect)
(_, Msg::OnSubmit(_)) | (_, &MSG_KEY_ENTER) => {
// Match <ENTER> key for all other components
// Collect values from inputs
let address: String = match self.view.get_value(COMPONENT_INPUT_ADDR) {
Some(Payload::Text(addr)) => addr,
_ => {
// Show error
self.mount_error("Invalid address!");
return None;
}
};
let port: u16 = match self.view.get_value(COMPONENT_INPUT_PORT) {
Some(Payload::Unsigned(p)) => p as u16,
_ => {
// Show error
self.mount_error("Invalid port number!");
// Return None
return None;
}
};
let username: String = match self.view.get_value(COMPONENT_INPUT_USERNAME) {
Some(Payload::Text(u)) => u,
_ => String::new(),
};
let password: String = match self.view.get_value(COMPONENT_INPUT_PASSWORD) {
Some(Payload::Text(p)) => p,
_ => String::new(),
};
let protocol: FileTransferProtocol =
match self.view.get_value(COMPONENT_RADIO_PROTOCOL) {
Some(Payload::Unsigned(choice)) => match choice {
1 => FileTransferProtocol::Scp,
2 => FileTransferProtocol::Ftp(false),
3 => FileTransferProtocol::Ftp(true),
_ => FileTransferProtocol::Sftp,
},
_ => FileTransferProtocol::Sftp,
};
// FIXME: save recent, pass attributes
self.save_recent();
let (address, port, protocol, username, password) = self.get_input();
// TOREM: remove this after removing states
self.address = address;
self.port = port.to_string();

View File

@@ -24,17 +24,19 @@
*/
// Locals
use super::{AuthActivity, Context};
use super::{AuthActivity, Context, FileTransferProtocol};
use crate::ui::layout::components::{
bookmark_list::BookmarkList, input::Input, radio_group::RadioGroup, table::Table, text::Text,
bookmark_list::BookmarkList, ctext::CText, input::Input, radio_group::RadioGroup, table::Table,
text::Text,
};
use crate::ui::layout::props::{
InputType, PropValue, Props, PropsBuilder, TableBuilder, TextParts, TextSpan, TextSpanBuilder,
InputType, PropValue, PropsBuilder, TableBuilder, TextParts, TextSpan, TextSpanBuilder,
};
use crate::utils::fmt::align_text_center;
use crate::ui::layout::utils::draw_area_in;
use crate::ui::layout::Payload;
// Ext
use tui::{
layout::{Constraint, Corner, Direction, Layout},
layout::{Constraint, Direction, Layout},
style::Color,
};
@@ -153,6 +155,34 @@ impl AuthActivity {
))
);
}
// Bookmarks
self.view.mount(
super::COMPONENT_BOOKMARKS_LIST,
Box::new(BookmarkList::new(
PropsBuilder::default()
.with_foreground(Color::LightGreen)
.with_texts(TextParts::new(
Some(String::from("Bookmarks")),
Some(self.view_bookmarks()),
))
.build(),
)),
);
// Recents
self.view.mount(
super::COMPONENT_RECENTS_LIST,
Box::new(BookmarkList::new(
PropsBuilder::default()
.with_foreground(Color::LightBlue)
.with_texts(TextParts::new(
Some(String::from("Recent connections")),
Some(self.view_recent_connections()),
))
.build(),
)),
);
// Active address
self.view.active(super::COMPONENT_INPUT_ADDR);
}
/// ### view
@@ -218,8 +248,68 @@ impl AuthActivity {
.render(super::COMPONENT_BOOKMARKS_LIST, f, bookmark_chunks[0]);
self.view
.render(super::COMPONENT_RECENTS_LIST, f, bookmark_chunks[1]);
// Popup
// Popups
if let Some(mut props) = self.view.get_props(super::COMPONENT_TEXT_ERROR) {
if props.build().visible {
// make popup
self.view.render(
super::COMPONENT_TEXT_ERROR,
f,
draw_area_in(f.size(), 50, 10),
);
}
}
if let Some(mut props) = self
.view
.get_props(super::COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK)
{
if props.build().visible {
// make popup
self.view.render(
super::COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK,
f,
draw_area_in(f.size(), 30, 10),
);
}
}
if let Some(mut props) = self
.view
.get_props(super::COMPONENT_RADIO_BOOKMARK_DEL_RECENT)
{
if props.build().visible {
// make popup
self.view.render(
super::COMPONENT_RADIO_BOOKMARK_DEL_RECENT,
f,
draw_area_in(f.size(), 30, 10),
);
}
}
if let Some(mut props) = self.view.get_props(super::COMPONENT_TEXT_HELP) {
if props.build().visible {
// make popup
self.view.render(
super::COMPONENT_TEXT_HELP,
f,
draw_area_in(f.size(), 50, 70),
);
}
}
if let Some(mut props) = self
.view
.get_props(super::COMPONENT_RADIO_BOOKMARK_SAVE_PWD)
{
if props.build().visible {
// make popup
let popup = draw_area_in(f.size(), 20, 20);
self.view
.render(super::COMPONENT_INPUT_BOOKMARK_NAME, f, popup);
self.view
.render(super::COMPONENT_RADIO_BOOKMARK_SAVE_PWD, f, popup);
}
}
});
self.context = Some(ctx);
}
// -- partials
@@ -253,7 +343,7 @@ impl AuthActivity {
// Mount
self.view.mount(
super::COMPONENT_TEXT_ERROR,
Box::new(Text::new(
Box::new(CText::new(
PropsBuilder::default()
.with_foreground(Color::Red)
.bold()
@@ -459,4 +549,37 @@ impl AuthActivity {
pub(super) fn umount_help(&mut self) {
self.view.umount(super::COMPONENT_TEXT_HELP);
}
/// ### get_input
///
/// Collect input values from view
pub(super) fn get_input(&self) -> (String, u16, FileTransferProtocol, String, String) {
let addr: String = match self.view.get_value(super::COMPONENT_INPUT_ADDR) {
Some(Payload::Text(a)) => a,
_ => String::new(),
};
let port: u16 = match self.view.get_value(super::COMPONENT_INPUT_PORT) {
Some(Payload::Unsigned(p)) => p as u16,
_ => 0,
};
let protocol: FileTransferProtocol =
match self.view.get_value(super::COMPONENT_RADIO_PROTOCOL) {
Some(Payload::Unsigned(p)) => match p {
1 => FileTransferProtocol::Scp,
2 => FileTransferProtocol::Ftp(false),
3 => FileTransferProtocol::Ftp(true),
_ => FileTransferProtocol::Sftp,
},
_ => FileTransferProtocol::Sftp,
};
let username: String = match self.view.get_value(super::COMPONENT_INPUT_USERNAME) {
Some(Payload::Text(a)) => a,
_ => String::new(),
};
let password: String = match self.view.get_value(super::COMPONENT_INPUT_PASSWORD) {
Some(Payload::Text(a)) => a,
_ => String::new(),
};
(addr, port, protocol, username, password)
}
}