use std::sync::{atomic::AtomicBool, mpsc, Arc};
use crossterm::event::{KeyCode, KeyEventKind};
use ratatui::{layout::Rect, widgets::Widget};
use strum::{Display, EnumIter, IntoEnumIterator};
use super::{
account::AccountPage, address_book::AddressBookPage, assets::AssetsPage,
complete_setup::CompleteSetupPage, config::ConfigPage, dev_key_capture::DevKeyCapturePage,
send_message::SendMessagePage, sign_message::SignMessagePage, walletconnect::WalletConnectPage,
Page,
};
use crate::tui::app::pages::network::NetworkPage;
use crate::{
disk::Config,
tui::{
app::{widgets::select::Select, SharedState},
events::Event,
traits::{Component, HandleResult},
},
utils::cursor::Cursor,
};
#[derive(Display, EnumIter)]
pub enum MainMenuItem {
CompleteSetup,
Portfolio,
Accounts,
AddressBook,
Networks,
WalletConnect,
SignMessage,
SendMessage,
DevKeyInput,
Config,
}
impl MainMenuItem {
pub fn get_page(&self) -> crate::Result<Page> {
Ok(match self {
MainMenuItem::CompleteSetup => Page::CompleteSetup(CompleteSetupPage::new()?),
MainMenuItem::Portfolio => Page::Assets(AssetsPage::default()),
MainMenuItem::Accounts => Page::Account(AccountPage::new()?),
MainMenuItem::AddressBook => Page::AddressBook(AddressBookPage::new()?),
MainMenuItem::Networks => Page::Network(NetworkPage::new()?),
MainMenuItem::WalletConnect => Page::WalletConnect(WalletConnectPage::new()?),
MainMenuItem::SignMessage => Page::SignMessage(SignMessagePage::new()?),
MainMenuItem::SendMessage => Page::SendMessage(SendMessagePage::new()?),
MainMenuItem::DevKeyInput => Page::DevKeyCapture(DevKeyCapturePage::default()),
MainMenuItem::Config => Page::Config(ConfigPage::new()?),
})
}
pub fn depends_on_current_account(&self) -> bool {
match self {
MainMenuItem::CompleteSetup
| MainMenuItem::AddressBook
| MainMenuItem::Networks
| MainMenuItem::Accounts
| MainMenuItem::WalletConnect
| MainMenuItem::DevKeyInput
| MainMenuItem::Config => false,
MainMenuItem::Portfolio | MainMenuItem::SignMessage | MainMenuItem::SendMessage => true,
}
}
pub fn only_on_developer_mode(&self) -> bool {
match self {
MainMenuItem::CompleteSetup
| MainMenuItem::Portfolio
| MainMenuItem::Accounts
| MainMenuItem::AddressBook
| MainMenuItem::Networks
| MainMenuItem::WalletConnect
| MainMenuItem::SignMessage
| MainMenuItem::SendMessage
| MainMenuItem::Config => false,
MainMenuItem::DevKeyInput => true,
}
}
pub fn get_menu(developer_mode: bool) -> crate::Result<Vec<MainMenuItem>> {
let mut all_options: Vec<MainMenuItem> = MainMenuItem::iter().collect();
let temp_setup_page = CompleteSetupPage::new()?;
if temp_setup_page.form.visible_count() == 0 {
all_options.remove(0);
}
let current_account_exists = Config::current_account()?.is_some();
let mut options = vec![];
for option in all_options {
if (!option.depends_on_current_account() || current_account_exists)
&& (!option.only_on_developer_mode() || developer_mode)
{
options.push(option);
}
}
Ok(options)
}
}
pub struct MainMenuPage {
pub cursor: Cursor,
pub list: Vec<MainMenuItem>,
}
impl MainMenuPage {
pub fn get_focussed_item(&self) -> &MainMenuItem {
self.list
.get(self.cursor.current)
.expect("Invalid cursor position in MainMenuPage")
}
}
impl MainMenuPage {
pub fn new(developer_mode: bool) -> crate::Result<Self> {
Ok(Self {
list: MainMenuItem::get_menu(developer_mode)?,
cursor: Cursor::default(),
})
}
}
impl Component for MainMenuPage {
fn reload(&mut self, shared_state: &SharedState) -> crate::Result<()> {
let fresh = Self::new(shared_state.developer_mode)?;
self.list = fresh.list;
if self.cursor.current >= self.list.len() {
self.cursor.current = self.list.len() - 1;
}
Ok(())
}
fn handle_event(
&mut self,
event: &Event,
_area: Rect,
_transmitter: &mpsc::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 result = HandleResult::default();
if let Event::Input(key_event) = event {
if key_event.kind == KeyEventKind::Press {
#[allow(clippy::single_match)]
match key_event.code {
KeyCode::Enter => {
let mut page = self.list[self.cursor.current].get_page()?;
page.set_focus(true);
result.page_inserts.push(page);
}
_ => {}
}
}
};
Ok(result)
}
fn render_component(
&self,
area: Rect,
buf: &mut ratatui::prelude::Buffer,
shared_state: &SharedState,
) -> ratatui::prelude::Rect
where
Self: Sized,
{
Select {
list: &self.list,
cursor: &self.cursor,
focus: true,
focus_style: shared_state.theme.select(),
}
.render(area, buf);
area
}
}