diff --git a/src/action.rs b/src/action.rs index 4ec55ba..44f43d9 100644 --- a/src/action.rs +++ b/src/action.rs @@ -29,6 +29,9 @@ pub enum Action { UsedAccount(AccountId32), NewAccount(String), + RenameAccount(String), + UpdateAccountName(String), + NewBestBlock(u32), NewBestHash(H256), NewFinalizedBlock(u32), diff --git a/src/components/wallet/accounts.rs b/src/components/wallet/accounts.rs index f3451f8..bc60e75 100644 --- a/src/components/wallet/accounts.rs +++ b/src/components/wallet/accounts.rs @@ -72,6 +72,23 @@ impl Accounts { self.save_to_file(); } + fn rename_account(&mut self, new_name: String) { + if let Some(index) = self.table_state.selected() { + self.wallet_keys[index].0 = new_name; + self.save_to_file(); + } + } + + fn update_account_name(&mut self) { + if let Some(index) = self.table_state.selected() { + if let Some(action_tx) = &self.action_tx { + let _ = action_tx.send(Action::RenameAccount( + self.wallet_keys[index].0.clone() + )); + } + } + } + fn swap_up(&mut self) { if let Some(src_index) = self.table_state.selected() { let dst_index = src_index.saturating_sub(1); @@ -231,11 +248,7 @@ impl PartialComponent for Accounts { fn set_active(&mut self, current_tab: CurrentTab) { match current_tab { CurrentTab::Accounts => self.is_active = true, - _ => { - self.is_active = false; - self.table_state.select(None); - self.scroll_state = self.scroll_state.position(0); - } + _ => self.is_active = false, } } } @@ -269,6 +282,7 @@ impl Component for Accounts { fn update(&mut self, action: Action) -> Result> { match action { Action::NewAccount(name) => self.create_new_account(name), + Action::UpdateAccountName(new_name) => self.rename_account(new_name), _ => {} }; Ok(None) @@ -283,6 +297,7 @@ impl Component for Accounts { KeyCode::Char('g') if self.is_active => self.first_row(), KeyCode::Char('G') if self.is_active => self.last_row(), KeyCode::Char('D') if self.is_active => self.delete_row(), + KeyCode::Char('R') if self.is_active => self.update_account_name(), _ => {}, }; Ok(None) diff --git a/src/components/wallet/add_account.rs b/src/components/wallet/add_account.rs index 05f0d22..5b61b9d 100644 --- a/src/components/wallet/add_account.rs +++ b/src/components/wallet/add_account.rs @@ -66,10 +66,11 @@ impl AddAccount { impl PartialComponent for AddAccount { fn set_active(&mut self, current_tab: CurrentTab) { + self.name = Input::new(String::new()); match current_tab { CurrentTab::AddAccount => self.is_active = true, _ => self.is_active = false, - } + }; } } @@ -89,7 +90,7 @@ impl Component for AddAccount { } fn handle_key_event(&mut self, key: KeyEvent) -> Result> { - if key.kind == KeyEventKind::Press { + if self.is_active && key.kind == KeyEventKind::Press { match key.code { KeyCode::Enter => self.submit_message(), KeyCode::Char(to_insert) => self.enter_char(to_insert), diff --git a/src/components/wallet/mod.rs b/src/components/wallet/mod.rs index c4a7325..630cfaf 100644 --- a/src/components/wallet/mod.rs +++ b/src/components/wallet/mod.rs @@ -9,6 +9,7 @@ mod balance; mod transfer; mod address_book; mod add_account; +mod rename_account; mod event_logs; mod accounts; mod overview; @@ -18,6 +19,7 @@ use tokio::sync::mpsc::UnboundedSender; use transfer::Transfer; use address_book::AddressBook; use add_account::AddAccount; +use rename_account::RenameAccount; use event_logs::EventLogs; use accounts::Accounts; use overview::Overview; @@ -31,6 +33,7 @@ pub enum CurrentTab { Accounts, AddressBook, AddAccount, + RenameAccount, } pub trait PartialComponent: Component { @@ -56,6 +59,7 @@ impl Default for Wallet { Box::new(EventLogs::default()), Box::new(Transfer::default()), Box::new(AddAccount::default()), + Box::new(RenameAccount::default()), ], } } @@ -114,6 +118,20 @@ impl Component for Wallet { } } } + } else if self.current_tab == CurrentTab::RenameAccount { + match key.code { + KeyCode::Esc => { + self.current_tab = CurrentTab::Accounts; + for component in self.components.iter_mut() { + component.set_active(self.current_tab.clone()); + } + }, + _ => { + for component in self.components.iter_mut() { + component.handle_key_event(key)?; + } + } + } } else { match key.code { KeyCode::Esc => { @@ -124,7 +142,7 @@ impl Component for Wallet { } return Ok(Some(Action::SetActiveScreen(Mode::Menu))); }, - KeyCode::Char('w') => { + KeyCode::Char('W') => { self.current_tab = CurrentTab::AddAccount; for component in self.components.iter_mut() { component.set_active(self.current_tab.clone()); @@ -167,6 +185,18 @@ impl Component for Wallet { component.set_active(self.current_tab.clone()); } } + if let Action::UpdateAccountName(_) = action { + self.current_tab = CurrentTab::Accounts; + for component in self.components.iter_mut() { + component.set_active(self.current_tab.clone()); + } + } + if let Action::RenameAccount(_) = action { + self.current_tab = CurrentTab::RenameAccount; + for component in self.components.iter_mut() { + component.set_active(self.current_tab.clone()); + } + } for component in self.components.iter_mut() { component.update(action.clone())?; } diff --git a/src/components/wallet/rename_account.rs b/src/components/wallet/rename_account.rs new file mode 100644 index 0000000..1202d9d --- /dev/null +++ b/src/components/wallet/rename_account.rs @@ -0,0 +1,140 @@ +use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; +use color_eyre::Result; +use ratatui::{ + layout::{Position, Alignment, Constraint, Flex, Layout, Rect}, + widgets::{Block, Clear, Paragraph}, + Frame +}; +use tokio::sync::mpsc::UnboundedSender; + +use super::{Component, PartialComponent, CurrentTab}; +use crate::{ + widgets::{Input, InputRequest}, + action::Action, + config::Config, + palette::StylePalette, +}; + +#[derive(Debug)] +pub struct RenameAccount { + is_active: bool, + action_tx: Option>, + old_name: String, + name: Input, + palette: StylePalette +} + +impl Default for RenameAccount { + fn default() -> Self { + Self::new() + } +} + +impl RenameAccount { + pub fn new() -> Self { + Self { + is_active: false, + old_name: String::new(), + action_tx: None, + name: Input::new(String::new()), + palette: StylePalette::default(), + } + } +} + +impl RenameAccount { + fn submit_message(&mut self) { + if let Some(action_tx) = &self.action_tx { + let _ = action_tx.send(Action::UpdateAccountName(self.name.value().to_string())); + } + } + + fn enter_char(&mut self, new_char: char) { + let _ = self.name.handle(InputRequest::InsertChar(new_char)); + } + + fn delete_char(&mut self) { + let _ = self.name.handle(InputRequest::DeletePrevChar); + } + + fn move_cursor_right(&mut self) { + let _ = self.name.handle(InputRequest::GoToNextChar); + } + + fn move_cursor_left(&mut self) { + let _ = self.name.handle(InputRequest::GoToPrevChar); + } +} + +impl PartialComponent for RenameAccount { + fn set_active(&mut self, current_tab: CurrentTab) { + match current_tab { + CurrentTab::RenameAccount => self.is_active = true, + _ => { + self.is_active = false; + self.old_name = String::new(); + } + }; + self.name = Input::new(String::new()); + } +} + +impl Component for RenameAccount { + fn register_action_handler(&mut self, tx: UnboundedSender) -> Result<()> { + self.action_tx = Some(tx); + Ok(()) + } + + fn register_config_handler(&mut self, config: Config) -> Result<()> { + if let Some(style) = config.styles.get(&crate::app::Mode::Explorer) { + self.palette.with_normal_style(style.get("normal_style").copied()); + self.palette.with_normal_border_style(style.get("normal_border_style").copied()); + self.palette.with_normal_title_style(style.get("normal_title_style").copied()); + } + Ok(()) + } + + fn update(&mut self, action: Action) -> Result> { + match action { + Action::RenameAccount(old_name) => self.old_name = old_name, + _ => {} + }; + Ok(None) + } + + fn handle_key_event(&mut self, key: KeyEvent) -> Result> { + if self.is_active && key.kind == KeyEventKind::Press { + match key.code { + KeyCode::Enter => self.submit_message(), + KeyCode::Char(to_insert) => self.enter_char(to_insert), + KeyCode::Backspace => self.delete_char(), + KeyCode::Left => self.move_cursor_left(), + KeyCode::Right => self.move_cursor_right(), + KeyCode::Esc => self.is_active = false, + _ => {}, + }; + } + Ok(None) + } + + fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { + if self.is_active { + let input = Paragraph::new(self.name.value()) + .block(Block::bordered() + .title_alignment(Alignment::Right) + .title(format!("New name for '{}'", self.old_name))); + let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center); + let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center); + let [area] = v.areas(area); + let [area] = h.areas(area); + + frame.render_widget(Clear, area); + frame.render_widget(input, area); + frame.set_cursor_position(Position::new( + area.x + self.name.cursor() as u16 + 1, + area.y + 1 + )); + } + Ok(()) + } +}