s3tui 0.2.1

Simple TUI application for multiple s3 account operations
use crate::components::component::{Component, ComponentRender};
use crate::model::action::Action;
use crate::model::state::{ActivePage, State};
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use ratatui::{prelude::*, widgets::*};
use tokio::sync::mpsc::UnboundedSender;

struct Props {
    commands: Vec<Vec<String>>,
}

impl From<&State> for Props {
    fn from(_state: &State) -> Self {
        Props {
            commands: vec![
                vec![
                    "Tab/↔".to_string(),
                    "move between local and s3 panel".to_string(),
                ],
                vec![
                    "s".to_string(),
                    "select account currently in use".to_string(),
                ],
                vec![
                    "Esc".to_string(),
                    "move back to the file manager window".to_string(),
                ],
                vec![
                    "↕ / j / k".to_string(),
                    "move up/down on the lists".to_string(),
                ],
                vec![
                    "t".to_string(),
                    "select/deselect files to transfer".to_string(),
                ],
                vec!["c".to_string(), "create bucket".to_string()],
                vec!["⌫ / Del".to_string(), "delete item".to_string()],
                vec![
                    "l".to_string(),
                    "show currently selected files to transfer".to_string(),
                ],
                vec![
                    "r".to_string(),
                    "run currently selected transfers".to_string(),
                ],
                vec!["F5".to_string(), "refresh current view".to_string()],
                vec!["F1".to_string(), "sort by name".to_string()],
                vec!["F2".to_string(), "sort by size".to_string()],
                vec!["F3".to_string(), "sort by type".to_string()],
                vec!["/".to_string(), "search/filter files".to_string()],
                vec!["q".to_string(), "quit the application".to_string()],
                vec!["?".to_string(), "this help page".to_string()],
            ],
        }
    }
}

/// Page listing all available commands, accessible with '?'
pub struct HelpPage {
    pub action_tx: UnboundedSender<Action>,
    props: Props,
}

impl Component for HelpPage {
    fn new(state: &State, action_tx: UnboundedSender<Action>) -> Self
    where
        Self: Sized,
    {
        HelpPage {
            action_tx: action_tx.clone(),
            // set the props
            props: Props::from(state),
        }
        .move_with_state(state)
    }

    fn move_with_state(self, state: &State) -> Self
    where
        Self: Sized,
    {
        HelpPage {
            props: Props::from(state),
            ..self
        }
    }

    fn handle_key_event(&mut self, key: KeyEvent) {
        if key.kind != KeyEventKind::Press {
            return;
        }

        match key.code {
            KeyCode::Char('q') => {
                let _ = self.action_tx.send(Action::Exit);
            }
            KeyCode::Esc => {
                let _ = self.action_tx.send(Action::Navigate {
                    page: ActivePage::FileManager,
                });
            }
            _ => {}
        }
    }
}

impl ComponentRender<()> for HelpPage {
    fn render(&self, frame: &mut Frame, _props: ()) {
        let v_layout = Layout::default()
            .direction(Direction::Vertical)
            .constraints([
                Constraint::Percentage(3),
                Constraint::Percentage(94),
                Constraint::Percentage(3),
            ])
            .split(frame.area());
        let h_layout = Layout::default()
            .direction(Direction::Horizontal)
            .constraints([
                Constraint::Percentage(3),
                Constraint::Percentage(94),
                Constraint::Percentage(3),
            ])
            .split(v_layout[1]);
        let rows: Vec<Row> = self
            .props
            .commands
            .iter()
            .map(|c| Row::new(c.clone()))
            .collect();
        let header = Row::new(vec!["Command Name", "Description"])
            .bold()
            .underlined()
            .height(1)
            .bottom_margin(0);
        let table = Table::new(rows, [Constraint::Length(30), Constraint::Length(70)])
            .block(Block::new().borders(Borders::ALL))
            .header(header);
        frame.render_widget(table, h_layout[1]);
    }
}