Changed activity paths

This commit is contained in:
veeso
2021-05-08 19:28:47 +02:00
parent 7ba1b316f7
commit 300256b196
31 changed files with 9 additions and 10 deletions

View File

@@ -0,0 +1,276 @@
//! ## AuthActivity
//!
//! `auth_activity` is the module which implements the authentication activity
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// Dependencies
extern crate dirs;
// Locals
use super::{AuthActivity, FileTransferProtocol};
use crate::system::bookmarks_client::BookmarksClient;
use crate::system::environment;
// Ext
use std::path::PathBuf;
use tuirealm::components::{input::InputPropsBuilder, radio::RadioPropsBuilder};
use tuirealm::{Payload, PropsBuilder, Value};
impl AuthActivity {
/// ### del_bookmark
///
/// Delete bookmark
pub(super) fn del_bookmark(&mut self, idx: usize) {
if let Some(bookmarks_cli) = self.bookmarks_client.as_mut() {
// Iterate over kyes
let name: Option<&String> = self.bookmarks_list.get(idx);
if let Some(name) = name {
bookmarks_cli.del_bookmark(&name);
// Write bookmarks
self.write_bookmarks();
}
// Delete element from vec
self.bookmarks_list.remove(idx);
}
}
/// ### load_bookmark
///
/// Load selected bookmark (at index) to input fields
pub(super) fn load_bookmark(&mut self, idx: usize) {
if let Some(bookmarks_cli) = self.bookmarks_client.as_ref() {
// Iterate over bookmarks
if let Some(key) = self.bookmarks_list.get(idx) {
if let Some(bookmark) = bookmarks_cli.get_bookmark(&key) {
// Load parameters into components
self.load_bookmark_into_gui(
bookmark.0, bookmark.1, bookmark.2, bookmark.3, bookmark.4,
);
}
}
}
}
/// ### save_bookmark
///
/// Save current input fields as a bookmark
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 save_password {
true => match self
.view
.get_state(super::COMPONENT_RADIO_BOOKMARK_SAVE_PWD)
{
Some(Payload::One(Value::Usize(0))) => Some(password), // Yes
_ => None, // No such component / No
},
false => None,
};
bookmarks_cli.add_bookmark(name.clone(), address, port, protocol, username, password);
// Save bookmarks
self.write_bookmarks();
// Remove `name` from bookmarks if exists
self.bookmarks_list.retain(|b| b.as_str() != name.as_str());
// Push bookmark to list and sort
self.bookmarks_list.push(name);
self.sort_bookmarks();
}
}
/// ### del_recent
///
/// Delete recent
pub(super) fn del_recent(&mut self, idx: usize) {
if let Some(client) = self.bookmarks_client.as_mut() {
let name: Option<&String> = self.recents_list.get(idx);
if let Some(name) = name {
client.del_recent(&name);
// Write bookmarks
self.write_bookmarks();
}
// Delete element from vec
self.recents_list.remove(idx);
}
}
/// ### load_recent
///
/// Load selected recent (at index) to input fields
pub(super) fn load_recent(&mut self, idx: usize) {
if let Some(client) = self.bookmarks_client.as_ref() {
// Iterate over bookmarks
if let Some(key) = self.recents_list.get(idx) {
if let Some(bookmark) = client.get_recent(key) {
// Load parameters
self.load_bookmark_into_gui(
bookmark.0, bookmark.1, bookmark.2, bookmark.3, None,
);
}
}
}
}
/// ### save_recent
///
/// Save current input fields as a "recent"
pub(super) fn save_recent(&mut self) {
let (address, port, protocol, username, _password) = self.get_input();
if let Some(bookmarks_cli) = self.bookmarks_client.as_mut() {
bookmarks_cli.add_recent(address, port, protocol, username);
// Save bookmarks
self.write_bookmarks();
}
}
/// ### write_bookmarks
///
/// Write bookmarks to file
fn write_bookmarks(&mut self) {
if let Some(bookmarks_cli) = self.bookmarks_client.as_ref() {
if let Err(err) = bookmarks_cli.write_bookmarks() {
self.mount_error(format!("Could not write bookmarks: {}", err).as_str());
}
}
}
/// ### init_bookmarks_client
///
/// Initialize bookmarks client
pub(super) fn init_bookmarks_client(&mut self) {
// Get config dir
match environment::init_config_dir() {
Ok(path) => {
// If some configure client, otherwise do nothing; don't bother users telling them that bookmarks are not supported on their system.
if let Some(config_dir_path) = path {
let bookmarks_file: PathBuf =
environment::get_bookmarks_paths(config_dir_path.as_path());
// Initialize client
match BookmarksClient::new(
bookmarks_file.as_path(),
config_dir_path.as_path(),
16,
) {
Ok(cli) => {
// Load bookmarks into list
let mut bookmarks_list: Vec<String> =
Vec::with_capacity(cli.iter_bookmarks().count());
for bookmark in cli.iter_bookmarks() {
bookmarks_list.push(bookmark.clone());
}
// Load recents into list
let mut recents_list: Vec<String> =
Vec::with_capacity(cli.iter_recents().count());
for recent in cli.iter_recents() {
recents_list.push(recent.clone());
}
self.bookmarks_client = Some(cli);
self.bookmarks_list = bookmarks_list;
self.recents_list = recents_list;
// Sort bookmark list
self.sort_bookmarks();
self.sort_recents();
}
Err(err) => {
self.mount_error(
format!(
"Could not initialize bookmarks (at \"{}\", \"{}\"): {}",
bookmarks_file.display(),
config_dir_path.display(),
err
)
.as_str(),
);
}
}
}
}
Err(err) => {
self.mount_error(
format!("Could not initialize configuration directory: {}", err).as_str(),
);
}
}
}
// -- privates
/// ### sort_bookmarks
///
/// Sort bookmarks in list
fn sort_bookmarks(&mut self) {
// Conver to lowercase when sorting
self.bookmarks_list
.sort_by(|a, b| a.to_lowercase().as_str().cmp(b.to_lowercase().as_str()));
}
/// ### sort_recents
///
/// Sort recents in list
fn sort_recents(&mut self) {
// 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(props) = self.view.get_props(super::COMPONENT_INPUT_ADDR) {
let props = InputPropsBuilder::from(props).with_value(addr).build();
self.view.update(super::COMPONENT_INPUT_ADDR, props);
}
if let Some(props) = self.view.get_props(super::COMPONENT_INPUT_PORT) {
let props = InputPropsBuilder::from(props)
.with_value(port.to_string())
.build();
self.view.update(super::COMPONENT_INPUT_PORT, props);
}
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_PROTOCOL) {
let props = RadioPropsBuilder::from(props)
.with_value(Self::protocol_enum_to_opt(protocol))
.build();
self.view.update(super::COMPONENT_RADIO_PROTOCOL, props);
}
if let Some(props) = self.view.get_props(super::COMPONENT_INPUT_USERNAME) {
let props = InputPropsBuilder::from(props).with_value(username).build();
self.view.update(super::COMPONENT_INPUT_USERNAME, props);
}
if let Some(password) = password {
if let Some(props) = self.view.get_props(super::COMPONENT_INPUT_PASSWORD) {
let props = InputPropsBuilder::from(props).with_value(password).build();
self.view.update(super::COMPONENT_INPUT_PASSWORD, props);
}
}
}
}

View File

@@ -0,0 +1,71 @@
//! ## AuthActivity
//!
//! `auth_activity` is the module which implements the authentication activity
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use super::{AuthActivity, FileTransferProtocol};
impl AuthActivity {
/// ### protocol_opt_to_enum
///
/// Convert radio index for protocol into a `FileTransferProtocol`
pub(super) fn protocol_opt_to_enum(protocol: usize) -> FileTransferProtocol {
match protocol {
1 => FileTransferProtocol::Scp,
2 => FileTransferProtocol::Ftp(false),
3 => FileTransferProtocol::Ftp(true),
_ => FileTransferProtocol::Sftp,
}
}
/// ### protocol_enum_to_opt
///
/// Convert `FileTransferProtocol` enum into radio group index
pub(super) fn protocol_enum_to_opt(protocol: FileTransferProtocol) -> usize {
match protocol {
FileTransferProtocol::Sftp => 0,
FileTransferProtocol::Scp => 1,
FileTransferProtocol::Ftp(false) => 2,
FileTransferProtocol::Ftp(true) => 3,
}
}
/// ### get_default_port_for_protocol
///
/// Get the default port for protocol
pub(super) fn get_default_port_for_protocol(protocol: FileTransferProtocol) -> u16 {
match protocol {
FileTransferProtocol::Sftp | FileTransferProtocol::Scp => 22,
FileTransferProtocol::Ftp(_) => 21,
}
}
/// ### is_port_standard
///
/// Returns whether the port is standard or not
pub(super) fn is_port_standard(port: u16) -> bool {
port < 1024
}
}

View File

@@ -0,0 +1,227 @@
//! ## AuthActivity
//!
//! `auth_activity` is the module which implements the authentication activity
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// Sub modules
mod bookmarks;
mod misc;
mod update;
mod view;
// Dependencies
extern crate crossterm;
extern crate tuirealm;
// locals
use super::{Activity, Context, ExitReason};
use crate::filetransfer::FileTransferProtocol;
use crate::system::bookmarks_client::BookmarksClient;
use crate::ui::context::FileTransferParams;
use crate::utils::git;
// Includes
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
use tuirealm::View;
// -- components
const COMPONENT_TEXT_H1: &str = "TEXT_H1";
const COMPONENT_TEXT_H2: &str = "TEXT_H2";
const COMPONENT_TEXT_NEW_VERSION: &str = "TEXT_NEW_VERSION";
const COMPONENT_TEXT_FOOTER: &str = "TEXT_FOOTER";
const COMPONENT_TEXT_HELP: &str = "TEXT_HELP";
const COMPONENT_TEXT_ERROR: &str = "TEXT_ERROR";
const COMPONENT_INPUT_ADDR: &str = "INPUT_ADDRESS";
const COMPONENT_INPUT_PORT: &str = "INPUT_PORT";
const COMPONENT_INPUT_USERNAME: &str = "INPUT_USERNAME";
const COMPONENT_INPUT_PASSWORD: &str = "INPUT_PASSWORD";
const COMPONENT_INPUT_BOOKMARK_NAME: &str = "INPUT_BOOKMARK_NAME";
const COMPONENT_RADIO_PROTOCOL: &str = "RADIO_PROTOCOL";
const COMPONENT_RADIO_QUIT: &str = "RADIO_QUIT";
const COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK: &str = "RADIO_DELETE_BOOKMARK";
const COMPONENT_RADIO_BOOKMARK_DEL_RECENT: &str = "RADIO_DELETE_RECENT";
const COMPONENT_RADIO_BOOKMARK_SAVE_PWD: &str = "RADIO_SAVE_PASSWORD";
const COMPONENT_BOOKMARKS_LIST: &str = "BOOKMARKS_LIST";
const COMPONENT_RECENTS_LIST: &str = "RECENTS_LIST";
// Store keys
const STORE_KEY_LATEST_VERSION: &str = "AUTH_LATEST_VERSION";
/// ### AuthActivity
///
/// AuthActivity is the data holder for the authentication activity
pub struct AuthActivity {
exit_reason: Option<ExitReason>,
context: Option<Context>,
view: View,
bookmarks_client: Option<BookmarksClient>,
redraw: bool, // Should ui actually be redrawned?
bookmarks_list: Vec<String>, // List of bookmarks
recents_list: Vec<String>, // list of recents
}
impl Default for AuthActivity {
fn default() -> Self {
Self::new()
}
}
impl AuthActivity {
/// ### new
///
/// Instantiates a new AuthActivity
pub fn new() -> AuthActivity {
AuthActivity {
exit_reason: None,
context: None,
view: View::init(),
bookmarks_client: None,
redraw: true, // True at startup
bookmarks_list: Vec::new(),
recents_list: Vec::new(),
}
}
/// ### on_create
///
/// If enabled in configuration, check for updates from Github
fn check_for_updates(&mut self) {
// Check version only if unset in the store
let ctx: &Context = self.context.as_ref().unwrap();
if !ctx.store.isset(STORE_KEY_LATEST_VERSION) {
let mut new_version: Option<String> = match ctx.config_client.as_ref() {
Some(client) => {
if client.get_check_for_updates() {
// Send request
match git::check_for_updates(env!("CARGO_PKG_VERSION")) {
Ok(version) => version,
Err(err) => {
// Report error
self.mount_error(
format!("Could not check for new updates: {}", err).as_str(),
);
// None
None
}
}
} else {
None
}
}
None => None,
};
let ctx: &mut Context = self.context.as_mut().unwrap();
// Set version into the store (or just a flag)
match new_version.take() {
Some(new_version) => ctx.store.set_string(STORE_KEY_LATEST_VERSION, new_version), // If Some, set String
None => ctx.store.set(STORE_KEY_LATEST_VERSION), // If None, just set flag
}
}
}
}
impl Activity for AuthActivity {
/// ### on_create
///
/// `on_create` is the function which must be called to initialize the activity.
/// `on_create` must initialize all the data structures used by the activity
/// Context is taken from activity manager and will be released only when activity is destroyed
fn on_create(&mut self, mut context: Context) {
// Initialize file transfer params
context.ft_params = Some(FileTransferParams::default());
// Set context
self.context = Some(context);
// Clear terminal
self.context.as_mut().unwrap().clear_screen();
// Put raw mode on enabled
let _ = enable_raw_mode();
// Init bookmarks client
if self.bookmarks_client.is_none() {
self.init_bookmarks_client();
}
// Verify error state from context
if let Some(err) = self.context.as_mut().unwrap().get_error() {
self.mount_error(err.as_str());
}
// If check for updates is enabled, check for updates
self.check_for_updates();
// Initialize view
self.init();
}
/// ### on_draw
///
/// `on_draw` is the function which draws the graphical interface.
/// This function must be called at each tick to refresh the interface
fn on_draw(&mut self) {
// Context must be something
if self.context.is_none() {
return;
}
// Read one event
if let Ok(Some(event)) = self.context.as_ref().unwrap().input_hnd.read_event() {
// Set redraw to true
self.redraw = true;
// Handle event on view and update
let msg = self.view.on(event);
self.update(msg);
}
// Redraw if necessary
if self.redraw {
// View
self.view();
// Set redraw to false
self.redraw = false;
}
}
/// ### will_umount
///
/// `will_umount` is the method which must be able to report to the activity manager, whether
/// the activity should be terminated or not.
/// If not, the call will return `None`, otherwise return`Some(ExitReason)`
fn will_umount(&self) -> Option<&ExitReason> {
self.exit_reason.as_ref()
}
/// ### on_destroy
///
/// `on_destroy` is the function which cleans up runtime variables and data before terminating the activity.
/// This function must be called once before terminating the activity.
/// This function finally releases the context
fn on_destroy(&mut self) -> Option<Context> {
// Disable raw mode
let _ = disable_raw_mode();
self.context.as_ref()?;
// Clear terminal and return
match self.context.take() {
Some(mut ctx) => {
ctx.clear_screen();
Some(ctx)
}
None => None,
}
}
}

View File

@@ -0,0 +1,351 @@
//! ## AuthActivity
//!
//! `auth_activity` is the module which implements the authentication activity
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// locals
use super::{
AuthActivity, FileTransferParams, 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_RADIO_QUIT, COMPONENT_RECENTS_LIST, COMPONENT_TEXT_ERROR,
COMPONENT_TEXT_HELP,
};
use crate::ui::keymap::*;
use tuirealm::components::InputPropsBuilder;
use tuirealm::{Msg, Payload, PropsBuilder, Value};
// -- update
impl AuthActivity {
/// ### update
///
/// Update auth activity model based on msg
/// The function exits when returns None
pub(super) fn update(&mut self, msg: Option<(String, Msg)>) -> Option<(String, Msg)> {
let ref_msg: Option<(&str, &Msg)> = msg.as_ref().map(|(s, msg)| (s.as_str(), msg));
// Match msg
match ref_msg {
None => None, // Exit after None
Some(msg) => match msg {
// Focus ( DOWN )
(COMPONENT_RADIO_PROTOCOL, &MSG_KEY_DOWN) => {
// Give focus to port
self.view.active(COMPONENT_INPUT_ADDR);
None
}
(COMPONENT_INPUT_ADDR, &MSG_KEY_DOWN) => {
// Give focus to port
self.view.active(COMPONENT_INPUT_PORT);
None
}
(COMPONENT_INPUT_PORT, &MSG_KEY_DOWN) => {
// Give focus to port
self.view.active(COMPONENT_INPUT_USERNAME);
None
}
(COMPONENT_INPUT_USERNAME, &MSG_KEY_DOWN) => {
// Give focus to port
self.view.active(COMPONENT_INPUT_PASSWORD);
None
}
(COMPONENT_INPUT_PASSWORD, &MSG_KEY_DOWN) => {
// Give focus to port
self.view.active(COMPONENT_RADIO_PROTOCOL);
None
}
// Focus ( UP )
(COMPONENT_INPUT_PASSWORD, &MSG_KEY_UP) => {
// Give focus to port
self.view.active(COMPONENT_INPUT_USERNAME);
None
}
(COMPONENT_INPUT_USERNAME, &MSG_KEY_UP) => {
// Give focus to port
self.view.active(COMPONENT_INPUT_PORT);
None
}
(COMPONENT_INPUT_PORT, &MSG_KEY_UP) => {
// Give focus to port
self.view.active(COMPONENT_INPUT_ADDR);
None
}
(COMPONENT_INPUT_ADDR, &MSG_KEY_UP) => {
// Give focus to port
self.view.active(COMPONENT_RADIO_PROTOCOL);
None
}
(COMPONENT_RADIO_PROTOCOL, &MSG_KEY_UP) => {
// Give focus to port
self.view.active(COMPONENT_INPUT_PASSWORD);
None
}
// Protocol - On Change
(COMPONENT_RADIO_PROTOCOL, Msg::OnChange(Payload::One(Value::Usize(protocol)))) => {
// If port is standard, update the current port with default for selected protocol
let protocol: FileTransferProtocol = Self::protocol_opt_to_enum(*protocol);
// Get port
let port: u16 = self.get_input_port();
match Self::is_port_standard(port) {
false => None, // Return None
true => {
self.update_input_port(Self::get_default_port_for_protocol(protocol))
}
}
}
// <TAB> bookmarks
(COMPONENT_BOOKMARKS_LIST, &MSG_KEY_TAB)
| (COMPONENT_RECENTS_LIST, &MSG_KEY_TAB) => {
// Give focus to address
self.view.active(COMPONENT_INPUT_ADDR);
None
}
// Any <TAB>, go to bookmarks
(_, &MSG_KEY_TAB) => {
self.view.active(COMPONENT_BOOKMARKS_LIST);
None
}
// Bookmarks commands
// <RIGHT> / <LEFT>
(COMPONENT_BOOKMARKS_LIST, &MSG_KEY_RIGHT) => {
// Give focus to recents
self.view.active(COMPONENT_RECENTS_LIST);
None
}
(COMPONENT_RECENTS_LIST, &MSG_KEY_LEFT) => {
// Give focus to bookmarks
self.view.active(COMPONENT_BOOKMARKS_LIST);
None
}
// <DEL | 'E'>
(COMPONENT_BOOKMARKS_LIST, &MSG_KEY_DEL)
| (COMPONENT_BOOKMARKS_LIST, &MSG_KEY_CHAR_E) => {
// Show delete popup
self.mount_bookmark_del_dialog();
None
}
(COMPONENT_RECENTS_LIST, &MSG_KEY_DEL)
| (COMPONENT_RECENTS_LIST, &MSG_KEY_CHAR_E) => {
// Show delete popup
self.mount_recent_del_dialog();
None
}
// Enter
(COMPONENT_BOOKMARKS_LIST, Msg::OnSubmit(Payload::One(Value::Usize(idx)))) => {
self.load_bookmark(*idx);
// Give focus to input password
self.view.active(COMPONENT_INPUT_PASSWORD);
None
}
(COMPONENT_RECENTS_LIST, Msg::OnSubmit(Payload::One(Value::Usize(idx)))) => {
self.load_recent(*idx);
// Give focus to input password
self.view.active(COMPONENT_INPUT_PASSWORD);
None
}
// Bookmark radio
// Del bookmarks
(
COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK,
Msg::OnSubmit(Payload::One(Value::Usize(index))),
) => {
// hide bookmark delete
self.umount_bookmark_del_dialog();
// Index must be 0 => YES
match *index {
0 => {
// Get selected bookmark
match self.view.get_state(COMPONENT_BOOKMARKS_LIST) {
Some(Payload::One(Value::Usize(index))) => {
// Delete bookmark
self.del_bookmark(index);
// Update bookmarks
self.view_bookmarks()
}
_ => None,
}
}
_ => None,
}
}
(
COMPONENT_RADIO_BOOKMARK_DEL_RECENT,
Msg::OnSubmit(Payload::One(Value::Usize(index))),
) => {
// hide bookmark delete
self.umount_recent_del_dialog();
// Index must be 0 => YES
match *index {
0 => {
// Get selected bookmark
match self.view.get_state(COMPONENT_RECENTS_LIST) {
Some(Payload::One(Value::Usize(index))) => {
// Delete recent
self.del_recent(index);
// Update bookmarks
self.view_recent_connections()
}
_ => None,
}
}
_ => None,
}
}
// <ESC> hide tab
(COMPONENT_RADIO_BOOKMARK_DEL_RECENT, &MSG_KEY_ESC) => {
self.umount_recent_del_dialog();
None
}
(COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK, &MSG_KEY_ESC) => {
self.umount_bookmark_del_dialog();
None
}
// Help
(_, &MSG_KEY_CTRL_H) => {
// Show help
self.mount_help();
None
}
(COMPONENT_TEXT_HELP, &MSG_KEY_ENTER) | (COMPONENT_TEXT_HELP, &MSG_KEY_ESC) => {
// Hide text help
self.umount_help();
None
}
// Enter setup
(_, &MSG_KEY_CTRL_C) => {
self.exit_reason = Some(super::ExitReason::EnterSetup);
None
}
// Save bookmark; show popup
(_, &MSG_KEY_CTRL_S) => {
// Show popup
self.mount_bookmark_save_dialog();
// Give focus to bookmark name
self.view.active(COMPONENT_INPUT_BOOKMARK_NAME);
None
}
(COMPONENT_INPUT_BOOKMARK_NAME, &MSG_KEY_DOWN) => {
// Give focus to pwd
self.view.active(COMPONENT_RADIO_BOOKMARK_SAVE_PWD);
None
}
(COMPONENT_RADIO_BOOKMARK_SAVE_PWD, &MSG_KEY_UP) => {
// Give focus to pwd
self.view.active(COMPONENT_INPUT_BOOKMARK_NAME);
None
}
// Save bookmark
(COMPONENT_INPUT_BOOKMARK_NAME, Msg::OnSubmit(_))
| (COMPONENT_RADIO_BOOKMARK_SAVE_PWD, Msg::OnSubmit(_)) => {
// Get values
let bookmark_name: String =
match self.view.get_state(COMPONENT_INPUT_BOOKMARK_NAME) {
Some(Payload::One(Value::Str(s))) => s,
_ => String::new(),
};
let save_pwd: bool = matches!(
self.view.get_state(COMPONENT_RADIO_BOOKMARK_SAVE_PWD),
Some(Payload::One(Value::Usize(0)))
);
// Save bookmark
self.save_bookmark(bookmark_name, save_pwd);
// Umount popup
self.umount_bookmark_save_dialog();
// Reload bookmarks
self.view_bookmarks()
}
// Hide save bookmark
(COMPONENT_INPUT_BOOKMARK_NAME, &MSG_KEY_ESC)
| (COMPONENT_RADIO_BOOKMARK_SAVE_PWD, &MSG_KEY_ESC) => {
// Umount popup
self.umount_bookmark_save_dialog();
None
}
// Error message
(COMPONENT_TEXT_ERROR, &MSG_KEY_ENTER) => {
// Umount text error
self.umount_error();
None
}
// Quit dialog
(COMPONENT_RADIO_QUIT, Msg::OnSubmit(Payload::One(Value::Usize(choice)))) => {
// If choice is 0, quit termscp
if *choice == 0 {
self.exit_reason = Some(super::ExitReason::Quit);
}
self.umount_quit();
None
}
(COMPONENT_RADIO_QUIT, &MSG_KEY_ESC) => {
self.umount_quit();
None
}
// On submit on any unhandled (connect)
(_, Msg::OnSubmit(_)) | (_, &MSG_KEY_ENTER) => {
// Match <ENTER> key for all other components
self.save_recent();
let (address, port, protocol, username, password) = self.get_input();
// Set file transfer params to context
let mut ft_params: &mut FileTransferParams =
&mut self.context.as_mut().unwrap().ft_params.as_mut().unwrap();
ft_params.address = address;
ft_params.port = port;
ft_params.protocol = protocol;
ft_params.username = match username.is_empty() {
true => None,
false => Some(username),
};
ft_params.password = match password.is_empty() {
true => None,
false => Some(password),
};
// Set exit reason
self.exit_reason = Some(super::ExitReason::Connect);
// Return None
None
}
// <ESC> => Quit
(_, &MSG_KEY_ESC) => {
self.mount_quit();
None
}
(_, _) => None, // Ignore other events
},
}
}
fn update_input_port(&mut self, port: u16) -> Option<(String, Msg)> {
match self.view.get_props(COMPONENT_INPUT_PORT) {
None => None,
Some(props) => {
let props = InputPropsBuilder::from(props)
.with_value(port.to_string())
.build();
self.view.update(COMPONENT_INPUT_PORT, props)
}
}
}
}

View File

@@ -0,0 +1,756 @@
//! ## AuthActivity
//!
//! `auth_activity` is the module which implements the authentication activity
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// Locals
use super::{AuthActivity, Context, FileTransferProtocol};
use crate::ui::components::{
bookmark_list::{BookmarkList, BookmarkListPropsBuilder},
msgbox::{MsgBox, MsgBoxPropsBuilder},
};
use crate::utils::ui::draw_area_in;
// Ext
use tuirealm::components::{
input::{Input, InputPropsBuilder},
label::{Label, LabelPropsBuilder},
radio::{Radio, RadioPropsBuilder},
span::{Span, SpanPropsBuilder},
table::{Table, TablePropsBuilder},
};
use tuirealm::tui::{
layout::{Constraint, Direction, Layout},
style::Color,
widgets::{BorderType, Borders, Clear},
};
use tuirealm::{
props::{InputType, PropsBuilder, TableBuilder, TextSpan, TextSpanBuilder},
Msg, Payload, Value,
};
impl AuthActivity {
/// ### init
///
/// Initialize view, mounting all startup components inside the view
pub(super) fn init(&mut self) {
// Headers
self.view.mount(
super::COMPONENT_TEXT_H1,
Box::new(Label::new(
LabelPropsBuilder::default()
.bold()
.italic()
.with_text(String::from("$ termscp"))
.build(),
)),
);
self.view.mount(
super::COMPONENT_TEXT_H2,
Box::new(Label::new(
LabelPropsBuilder::default()
.bold()
.italic()
.with_text(format!("$ version {}", env!("CARGO_PKG_VERSION")))
.build(),
)),
);
// Footer
self.view.mount(
super::COMPONENT_TEXT_FOOTER,
Box::new(Span::new(
SpanPropsBuilder::default()
.with_spans(vec![
TextSpanBuilder::new("Press ").bold().build(),
TextSpanBuilder::new("<CTRL+H>")
.bold()
.with_foreground(Color::Cyan)
.build(),
TextSpanBuilder::new(" to show keybindings; ")
.bold()
.build(),
TextSpanBuilder::new("<CTRL+C>")
.bold()
.with_foreground(Color::Cyan)
.build(),
TextSpanBuilder::new(" to enter setup").bold().build(),
])
.build(),
)),
);
// Get default protocol
let default_protocol: FileTransferProtocol =
match self.context.as_ref().unwrap().config_client.as_ref() {
Some(cli) => cli.get_default_protocol(),
None => FileTransferProtocol::Sftp,
};
// Protocol
self.view.mount(
super::COMPONENT_RADIO_PROTOCOL,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::LightGreen)
.with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightGreen)
.with_options(
Some(String::from("Protocol")),
vec![
String::from("SFTP"),
String::from("SCP"),
String::from("FTP"),
String::from("FTPS"),
],
)
.with_value(Self::protocol_enum_to_opt(default_protocol))
.build(),
)),
);
// Address
self.view.mount(
super::COMPONENT_INPUT_ADDR,
Box::new(Input::new(
InputPropsBuilder::default()
.with_foreground(Color::Yellow)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightYellow)
.with_label(String::from("Remote address"))
.build(),
)),
);
// Port
self.view.mount(
super::COMPONENT_INPUT_PORT,
Box::new(Input::new(
InputPropsBuilder::default()
.with_foreground(Color::LightCyan)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightCyan)
.with_label(String::from("Port number"))
.with_input(InputType::Number)
.with_input_len(5)
.with_value(Self::get_default_port_for_protocol(default_protocol).to_string())
.build(),
)),
);
// Username
self.view.mount(
super::COMPONENT_INPUT_USERNAME,
Box::new(Input::new(
InputPropsBuilder::default()
.with_foreground(Color::LightMagenta)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightMagenta)
.with_label(String::from("Username"))
.build(),
)),
);
// Password
self.view.mount(
super::COMPONENT_INPUT_PASSWORD,
Box::new(Input::new(
InputPropsBuilder::default()
.with_foreground(Color::LightBlue)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightBlue)
.with_label(String::from("Password"))
.with_input(InputType::Password)
.build(),
)),
);
// Version notice
if let Some(version) = self
.context
.as_ref()
.unwrap()
.store
.get_string(super::STORE_KEY_LATEST_VERSION)
{
self.view.mount(
super::COMPONENT_TEXT_NEW_VERSION,
Box::new(Span::new(
SpanPropsBuilder::default()
.with_foreground(Color::Yellow)
.with_spans(
vec![
TextSpan::from("termscp "),
TextSpanBuilder::new(version).underlined().bold().build(),
TextSpan::from(" is now available! Download it from <https://github.com/veeso/termscp/releases/latest>")
]
)
.build()
))
);
}
// Bookmarks
self.view.mount(
super::COMPONENT_BOOKMARKS_LIST,
Box::new(BookmarkList::new(
BookmarkListPropsBuilder::default()
.with_background(Color::LightGreen)
.with_foreground(Color::Black)
.with_borders(Borders::ALL, BorderType::Plain, Color::LightGreen)
.with_bookmarks(Some(String::from("Bookmarks")), vec![])
.build(),
)),
);
let _ = self.view_bookmarks();
// Recents
self.view.mount(
super::COMPONENT_RECENTS_LIST,
Box::new(BookmarkList::new(
BookmarkListPropsBuilder::default()
.with_background(Color::LightBlue)
.with_foreground(Color::Black)
.with_borders(Borders::ALL, BorderType::Plain, Color::LightBlue)
.with_bookmarks(Some(String::from("Recent connections")), vec![])
.build(),
)),
);
let _ = self.view_recent_connections();
// Active protocol
self.view.active(super::COMPONENT_RADIO_PROTOCOL);
}
/// ### view
///
/// Display view on canvas
pub(super) fn view(&mut self) {
let mut ctx: Context = self.context.take().unwrap();
let _ = ctx.terminal.draw(|f| {
// Prepare chunks
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(1)
.constraints(
[
Constraint::Percentage(70), // Auth Form
Constraint::Percentage(30), // Bookmarks
]
.as_ref(),
)
.split(f.size());
// Create explorer chunks
let auth_chunks = Layout::default()
.constraints(
[
Constraint::Length(1), // h1
Constraint::Length(1), // h2
Constraint::Length(1), // Version
Constraint::Length(3), // protocol
Constraint::Length(3), // host
Constraint::Length(3), // port
Constraint::Length(3), // username
Constraint::Length(3), // password
Constraint::Length(3), // footer
]
.as_ref(),
)
.direction(Direction::Vertical)
.split(chunks[0]);
// Create bookmark chunks
let bookmark_chunks = Layout::default()
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.direction(Direction::Horizontal)
.split(chunks[1]);
// Render
// Auth chunks
self.view
.render(super::COMPONENT_TEXT_H1, f, auth_chunks[0]);
self.view
.render(super::COMPONENT_TEXT_H2, f, auth_chunks[1]);
self.view
.render(super::COMPONENT_TEXT_NEW_VERSION, f, auth_chunks[2]);
self.view
.render(super::COMPONENT_RADIO_PROTOCOL, f, auth_chunks[3]);
self.view
.render(super::COMPONENT_INPUT_ADDR, f, auth_chunks[4]);
self.view
.render(super::COMPONENT_INPUT_PORT, f, auth_chunks[5]);
self.view
.render(super::COMPONENT_INPUT_USERNAME, f, auth_chunks[6]);
self.view
.render(super::COMPONENT_INPUT_PASSWORD, f, auth_chunks[7]);
self.view
.render(super::COMPONENT_TEXT_FOOTER, f, auth_chunks[8]);
// Bookmark chunks
self.view
.render(super::COMPONENT_BOOKMARKS_LIST, f, bookmark_chunks[0]);
self.view
.render(super::COMPONENT_RECENTS_LIST, f, bookmark_chunks[1]);
// Popups
if let Some(props) = self.view.get_props(super::COMPONENT_TEXT_ERROR) {
if props.visible {
let popup = draw_area_in(f.size(), 50, 10);
f.render_widget(Clear, popup);
// make popup
self.view.render(super::COMPONENT_TEXT_ERROR, f, popup);
}
}
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_QUIT) {
if props.visible {
// make popup
let popup = draw_area_in(f.size(), 30, 10);
f.render_widget(Clear, popup);
self.view.render(super::COMPONENT_RADIO_QUIT, f, popup);
}
}
if let Some(props) = self
.view
.get_props(super::COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK)
{
if props.visible {
// make popup
let popup = draw_area_in(f.size(), 30, 10);
f.render_widget(Clear, popup);
self.view
.render(super::COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK, f, popup);
}
}
if let Some(props) = self
.view
.get_props(super::COMPONENT_RADIO_BOOKMARK_DEL_RECENT)
{
if props.visible {
// make popup
let popup = draw_area_in(f.size(), 30, 10);
f.render_widget(Clear, popup);
self.view
.render(super::COMPONENT_RADIO_BOOKMARK_DEL_RECENT, f, popup);
}
}
if let Some(props) = self.view.get_props(super::COMPONENT_TEXT_HELP) {
if props.visible {
// make popup
let popup = draw_area_in(f.size(), 50, 70);
f.render_widget(Clear, popup);
self.view.render(super::COMPONENT_TEXT_HELP, f, popup);
}
}
if let Some(props) = self
.view
.get_props(super::COMPONENT_RADIO_BOOKMARK_SAVE_PWD)
{
if props.visible {
// make popup
let popup = draw_area_in(f.size(), 20, 20);
f.render_widget(Clear, popup);
let popup_chunks = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Length(3), // Input form
Constraint::Length(2), // Yes/No
]
.as_ref(),
)
.split(popup);
self.view
.render(super::COMPONENT_INPUT_BOOKMARK_NAME, f, popup_chunks[0]);
self.view
.render(super::COMPONENT_RADIO_BOOKMARK_SAVE_PWD, f, popup_chunks[1]);
}
}
});
self.context = Some(ctx);
}
// -- partials
/// ### view_bookmarks
///
/// Make text span from bookmarks
pub(super) fn view_bookmarks(&mut self) -> Option<(String, Msg)> {
let bookmarks: Vec<String> = self
.bookmarks_list
.iter()
.map(|x| {
let entry: (String, u16, FileTransferProtocol, String, _) = self
.bookmarks_client
.as_ref()
.unwrap()
.get_bookmark(x)
.unwrap();
format!(
"{} ({}://{}@{}:{})",
x,
entry.2.to_string().to_lowercase(),
entry.3,
entry.0,
entry.1
)
})
.collect();
match self.view.get_props(super::COMPONENT_BOOKMARKS_LIST) {
None => None,
Some(props) => {
let msg = self.view.update(
super::COMPONENT_BOOKMARKS_LIST,
BookmarkListPropsBuilder::from(props)
.with_bookmarks(Some(String::from("Bookmarks")), bookmarks)
.build(),
);
msg
}
}
}
/// ### view_recent_connections
///
/// View recent connections
pub(super) fn view_recent_connections(&mut self) -> Option<(String, Msg)> {
let bookmarks: Vec<String> = self
.recents_list
.iter()
.map(|x| {
let entry: (String, u16, FileTransferProtocol, String) = self
.bookmarks_client
.as_ref()
.unwrap()
.get_recent(x)
.unwrap();
format!(
"{}://{}@{}:{}",
entry.2.to_string().to_lowercase(),
entry.3,
entry.0,
entry.1
)
})
.collect();
match self.view.get_props(super::COMPONENT_RECENTS_LIST) {
None => None,
Some(props) => {
let msg = self.view.update(
super::COMPONENT_RECENTS_LIST,
BookmarkListPropsBuilder::from(props)
.with_bookmarks(Some(String::from("Recent connections")), bookmarks)
.build(),
);
msg
}
}
}
// -- mount
/// ### mount_error
///
/// Mount error box
pub(super) fn mount_error(&mut self, text: &str) {
// Mount
self.view.mount(
super::COMPONENT_TEXT_ERROR,
Box::new(MsgBox::new(
MsgBoxPropsBuilder::default()
.with_foreground(Color::Red)
.with_borders(Borders::ALL, BorderType::Thick, Color::Red)
.bold()
.with_texts(None, vec![TextSpan::from(text)])
.build(),
)),
);
// Give focus to error
self.view.active(super::COMPONENT_TEXT_ERROR);
}
/// ### umount_error
///
/// Umount error message
pub(super) fn umount_error(&mut self) {
self.view.umount(super::COMPONENT_TEXT_ERROR);
}
/// ### mount_quit
///
/// Mount quit popup
pub(super) fn mount_quit(&mut self) {
// Protocol
self.view.mount(
super::COMPONENT_RADIO_QUIT,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::Yellow)
.with_borders(Borders::ALL, BorderType::Rounded, Color::Yellow)
.with_inverted_color(Color::Black)
.with_options(
Some(String::from("Quit termscp?")),
vec![String::from("Yes"), String::from("No")],
)
.build(),
)),
);
self.view.active(super::COMPONENT_RADIO_QUIT);
}
/// ### umount_quit
///
/// Umount quit popup
pub(super) fn umount_quit(&mut self) {
self.view.umount(super::COMPONENT_RADIO_QUIT);
}
/// ### mount_bookmark_del_dialog
///
/// Mount bookmark delete dialog
pub(super) fn mount_bookmark_del_dialog(&mut self) {
self.view.mount(
super::COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::Yellow)
.with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::Yellow)
.with_options(
Some(String::from("Delete bookmark?")),
vec![String::from("Yes"), String::from("No")],
)
.with_value(1)
.build(),
)),
);
// Active
self.view
.active(super::COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK);
}
/// ### umount_bookmark_del_dialog
///
/// umount delete bookmark dialog
pub(super) fn umount_bookmark_del_dialog(&mut self) {
self.view
.umount(super::COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK);
}
/// ### mount_bookmark_del_dialog
///
/// Mount recent delete dialog
pub(super) fn mount_recent_del_dialog(&mut self) {
self.view.mount(
super::COMPONENT_RADIO_BOOKMARK_DEL_RECENT,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::Yellow)
.with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::Yellow)
.with_options(
Some(String::from("Delete bookmark?")),
vec![String::from("Yes"), String::from("No")],
)
.with_value(1)
.build(),
)),
);
// Active
self.view.active(super::COMPONENT_RADIO_BOOKMARK_DEL_RECENT);
}
/// ### umount_recent_del_dialog
///
/// umount delete recent dialog
pub(super) fn umount_recent_del_dialog(&mut self) {
self.view.umount(super::COMPONENT_RADIO_BOOKMARK_DEL_RECENT);
}
/// ### mount_bookmark_save_dialog
///
/// Mount bookmark save dialog
pub(super) fn mount_bookmark_save_dialog(&mut self) {
self.view.mount(
super::COMPONENT_INPUT_BOOKMARK_NAME,
Box::new(Input::new(
InputPropsBuilder::default()
.with_foreground(Color::LightCyan)
.with_label(String::from("Save bookmark as..."))
.with_borders(
Borders::TOP | Borders::RIGHT | Borders::LEFT,
BorderType::Rounded,
Color::Reset,
)
.build(),
)),
);
self.view.mount(
super::COMPONENT_RADIO_BOOKMARK_SAVE_PWD,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::Red)
.with_borders(
Borders::BOTTOM | Borders::RIGHT | Borders::LEFT,
BorderType::Rounded,
Color::Reset,
)
.with_options(
Some(String::from("Save password?")),
vec![String::from("Yes"), String::from("No")],
)
.build(),
)),
);
// Give focus to input bookmark name
self.view.active(super::COMPONENT_INPUT_BOOKMARK_NAME);
}
/// ### umount_bookmark_save_dialog
///
/// Umount bookmark save dialog
pub(super) fn umount_bookmark_save_dialog(&mut self) {
self.view.umount(super::COMPONENT_RADIO_BOOKMARK_SAVE_PWD);
self.view.umount(super::COMPONENT_INPUT_BOOKMARK_NAME);
}
/// ### mount_help
///
/// Mount help
pub(super) fn mount_help(&mut self) {
self.view.mount(
super::COMPONENT_TEXT_HELP,
Box::new(Table::new(
TablePropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Rounded, Color::White)
.with_table(
Some(String::from("Help")),
TableBuilder::default()
.add_col(
TextSpanBuilder::new("<ESC>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Quit termscp"))
.add_row()
.add_col(
TextSpanBuilder::new("<TAB>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Switch from form and bookmarks"))
.add_row()
.add_col(
TextSpanBuilder::new("<RIGHT/LEFT>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Switch bookmark tab"))
.add_row()
.add_col(
TextSpanBuilder::new("<UP/DOWN>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Move up/down in current tab"))
.add_row()
.add_col(
TextSpanBuilder::new("<ENTER>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Connect/Load bookmark"))
.add_row()
.add_col(
TextSpanBuilder::new("<DEL|E>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Delete selected bookmark"))
.add_row()
.add_col(
TextSpanBuilder::new("<CTRL+C>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Enter setup"))
.add_row()
.add_col(
TextSpanBuilder::new("<CTRL+S>")
.bold()
.with_foreground(Color::Cyan)
.build(),
)
.add_col(TextSpan::from(" Save bookmark"))
.build(),
)
.build(),
)),
);
// Active help
self.view.active(super::COMPONENT_TEXT_HELP);
}
/// ### umount_help
///
/// Umount help
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 = self.get_input_addr();
let port: u16 = self.get_input_port();
let protocol: FileTransferProtocol = self.get_input_protocol();
let username: String = self.get_input_username();
let password: String = self.get_input_password();
(addr, port, protocol, username, password)
}
pub(super) fn get_input_addr(&self) -> String {
match self.view.get_state(super::COMPONENT_INPUT_ADDR) {
Some(Payload::One(Value::Str(x))) => x,
_ => String::new(),
}
}
pub(super) fn get_input_port(&self) -> u16 {
match self.view.get_state(super::COMPONENT_INPUT_PORT) {
Some(Payload::One(Value::Usize(x))) => x as u16,
_ => Self::get_default_port_for_protocol(FileTransferProtocol::Sftp),
}
}
pub(super) fn get_input_protocol(&self) -> FileTransferProtocol {
match self.view.get_state(super::COMPONENT_RADIO_PROTOCOL) {
Some(Payload::One(Value::Usize(x))) => Self::protocol_opt_to_enum(x),
_ => FileTransferProtocol::Sftp,
}
}
pub(super) fn get_input_username(&self) -> String {
match self.view.get_state(super::COMPONENT_INPUT_USERNAME) {
Some(Payload::One(Value::Str(x))) => x,
_ => String::new(),
}
}
pub(super) fn get_input_password(&self) -> String {
match self.view.get_state(super::COMPONENT_INPUT_PASSWORD) {
Some(Payload::One(Value::Str(x))) => x,
_ => String::new(),
}
}
}