gm-tui 0.0.1

TUI-based Ethereum wallet
Documentation
use crate::network::{Network, Token};
use crate::tui::app::pages::network_create::NetworkCreatePage;
use crate::tui::app::pages::token_create::TokenCreatePage;
use crate::tui::app::pages::Page;
use crate::tui::app::widgets::select::Select;
use crate::tui::app::SharedState;
use crate::tui::traits::{Component, HandleResult};
use crate::tui::Event;
use crate::utils::cursor::Cursor;
use crossterm::event::{KeyCode, KeyEventKind};
use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
use ratatui::prelude::Widget;
use std::fmt::{Display, Formatter};
use std::sync::atomic::AtomicBool;
use std::sync::mpsc::Sender;
use std::sync::Arc;

enum TokenSelect {
    Create,
    Existing(Box<Token>),
}

impl Display for TokenSelect {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            TokenSelect::Create => write!(f, "Create new token"),
            TokenSelect::Existing(token) => write!(f, "{}", token.name),
        }
    }
}
pub struct TokenPage {
    cursor: Cursor,
    focus: bool,
    list: Vec<TokenSelect>,
    network: Network,
    network_index: usize,
}
impl TokenPage {
    pub fn new(network_index: usize, network: Network) -> crate::Result<Self> {
        let mut list = vec![TokenSelect::Create];
        let tokens = network.tokens.clone();
        list.extend(
            tokens
                .into_iter()
                .map(|t| TokenSelect::Existing(Box::new(t)))
                .collect::<Vec<_>>(),
        );
        Ok(Self {
            cursor: Cursor::default(),
            focus: true,
            list,
            network,
            network_index,
        })
    }
}
impl Component for TokenPage {
    fn reload(&mut self, _ss: &SharedState) -> crate::Result<()> {
        let fresh = Self::new(self.network_index, self.network.clone())?;
        self.list = fresh.list;
        Ok(())
    }

    fn set_focus(&mut self, focus: bool) {
        self.focus = focus;
    }

    fn handle_event(
        &mut self,
        event: &Event,
        _area: Rect,
        _transmitter: &Sender<Event>,
        _shutdown_signal: &Arc<AtomicBool>,
        _shared_state: &SharedState,
    ) -> crate::Result<HandleResult> {
        let cursor_max = self.list.len();
        self.cursor.handle(event, cursor_max);

        let mut handle_result = HandleResult {
            esc_ignores: 1,
            ..Default::default()
        };
        if let Event::Input(key_event) = event {
            if key_event.kind == KeyEventKind::Press {
                #[allow(clippy::single_match)]
                match key_event.code {
                    KeyCode::Enter => match &self.list[self.cursor.current] {
                        TokenSelect::Create => {
                            let token_index = self.network.tokens.len();
                            handle_result.page_pops = 1;
                            handle_result.page_inserts.push(Page::TokenCreate(
                                TokenCreatePage::new(
                                    token_index,
                                    self.network_index,
                                    self.network.clone(),
                                )?,
                            ));
                            handle_result.reload = true;
                        }

                        TokenSelect::Existing(token) => {
                            let token_index = self
                                .network
                                .tokens
                                .iter()
                                .position(|t| t.contract_address == token.contract_address)
                                .unwrap();
                            handle_result.page_pops = 1;
                            handle_result.page_inserts.push(Page::TokenCreate(
                                TokenCreatePage::new(
                                    token_index,
                                    self.network_index,
                                    self.network.clone(),
                                )?,
                            ));
                            handle_result.reload = true;
                        }
                    },
                    KeyCode::Esc => {
                        handle_result.page_pops = 1;
                        handle_result.page_inserts.push(Page::NetworkCreate(
                            NetworkCreatePage::new(self.network_index, self.network.clone())?,
                        ));
                    }
                    _ => {}
                }
            }
        };
        Ok(handle_result)
    }

    fn render_component(&self, area: Rect, buf: &mut Buffer, shared_state: &SharedState) -> Rect
    where
        Self: Sized,
    {
        Select {
            list: &self.list,
            cursor: &self.cursor,
            focus: self.focus,
            focus_style: shared_state.theme.select(),
        }
        .render(area, buf);
        area
    }
}