1use crate::disk::DiskInterface;
2use crate::network::{Network, NetworkStore, Token};
3use crate::tui::app::pages::token::TokenPage;
4use crate::tui::app::pages::Page;
5use crate::tui::app::widgets::confirm_popup::ConfirmPopup;
6use crate::tui::app::widgets::form::{Form, FormItemIndex, FormWidget};
7use crate::tui::app::SharedState;
8use crate::tui::traits::{Component, HandleResult};
9use crate::tui::Event;
10use alloy::primitives::Address;
11use ratatui::buffer::Buffer;
12use ratatui::layout::Rect;
13use std::sync::atomic::AtomicBool;
14use std::sync::mpsc::Sender;
15use std::sync::Arc;
16use strum_macros::EnumIter;
17
18#[derive(EnumIter, PartialEq)]
19pub enum FormItem {
20 Heading,
21 Name,
22 Symbol,
23 Decimals,
24 ContractAddress,
25 SaveButton,
26 RemoveButton,
27 ErrorText, }
29impl FormItemIndex for FormItem {
30 fn index(self) -> usize {
31 self as usize
32 }
33}
34impl TryFrom<FormItem> for FormWidget {
35 type Error = crate::Error;
36 fn try_from(value: FormItem) -> crate::Result<Self> {
37 let widget = match value {
38 FormItem::Heading => FormWidget::Heading("Edit Network"),
39 FormItem::Name => FormWidget::InputBox {
40 label: "Name",
41 text: String::new(),
42 empty_text: None,
43 currency: None,
44 },
45 FormItem::Symbol => FormWidget::InputBox {
46 label: "Symbol",
47 text: String::new(),
48 empty_text: None,
49 currency: None,
50 },
51 FormItem::Decimals => FormWidget::InputBox {
52 label: "Decimals",
53 text: String::new(),
54 empty_text: None,
55 currency: None,
56 },
57 FormItem::ContractAddress => FormWidget::InputBox {
58 label: "Contract Address",
59 text: String::new(),
60 empty_text: None,
61 currency: None,
62 },
63 FormItem::SaveButton => FormWidget::Button { label: "Save" },
64 FormItem::RemoveButton => FormWidget::Button { label: "Remove" },
65 FormItem::ErrorText => FormWidget::ErrorText(String::new()),
66 };
67 Ok(widget)
68 }
69}
70pub struct TokenCreatePage {
71 pub form: Form<FormItem>,
72 pub token: Token,
73 pub token_index: usize,
74 pub network: Network,
75 pub network_index: usize,
76 pub remove_popup: ConfirmPopup,
77}
78impl TokenCreatePage {
79 pub fn new(token_index: usize, network_index: usize, network: Network) -> crate::Result<Self> {
80 let token = network.tokens.get(token_index).cloned().unwrap_or_default();
81 Ok(Self {
82 form: Form::init(|form| {
83 *form.get_text_mut(FormItem::Name) = token.name.clone();
84 *form.get_text_mut(FormItem::Symbol) = token.symbol.clone();
85 *form.get_text_mut(FormItem::Decimals) = token.decimals.to_string();
86 *form.get_text_mut(FormItem::ContractAddress) = token.contract_address.to_string();
87 Ok(())
91 })?,
92 token_index,
93 remove_popup: ConfirmPopup::new(
94 "Remove Token",
95 "Are you sure you want to remove this token?".to_string(),
96 "Remove",
97 "Cancel",
98 ),
99 token,
100 network,
101 network_index,
102 })
103 }
104 pub fn token(form: &Form<FormItem>) -> Token {
105 Token {
106 name: form.get_text(FormItem::Name).clone(),
107 symbol: form.get_text(FormItem::Symbol).clone(),
108 decimals: form
109 .get_text(FormItem::Decimals)
110 .parse()
111 .unwrap_or_default(),
112 contract_address: form
113 .get_text(FormItem::ContractAddress)
114 .clone()
115 .parse::<Address>()
116 .unwrap_or_default(),
117 }
118 }
119}
120impl Component for TokenCreatePage {
121 fn handle_event(
122 &mut self,
123 event: &Event,
124 area: Rect,
125 _transmitter: &Sender<Event>,
126 _shutdown_signal: &Arc<AtomicBool>,
127 _shared_state: &SharedState,
128 ) -> crate::Result<HandleResult> {
129 let mut handle_result = HandleResult::default();
130 if self.remove_popup.is_open() {
131 let r = self.remove_popup.handle_event(
132 event,
133 area,
134 || {
135 if self.network.tokens.get(self.token_index).is_some() {
136 self.network.tokens.remove(self.token_index);
137 let mut config = NetworkStore::load()?;
138 config.networks[self.network_index] = self.network.clone();
139 config.save()?;
140 }
141 handle_result.page_pops = 1;
142 handle_result.page_inserts.push(Page::Token(TokenPage::new(
143 self.network_index,
144 self.network.clone(),
145 )?));
146 handle_result.reload = true;
147 Ok(())
148 },
149 || Ok(()),
150 )?;
151 handle_result.merge(r);
152 }
153 self.form.handle_event(
154 event,
155 |_, _| Ok(()),
156 |label, form| {
157 if label == FormItem::SaveButton {
158 let token = Self::token(form);
159 if self.network.tokens.get(self.token_index).is_some() {
160 self.network.tokens[self.token_index] = token;
161 } else {
162 self.network.tokens.push(token);
163 }
164
165 let mut config = NetworkStore::load()?;
166 config.networks[self.network_index] = self.network.clone();
167 config.save()?;
168
169 handle_result.page_pops = 1;
170 handle_result.page_inserts.push(Page::Token(TokenPage::new(
171 self.network_index,
172 self.network.clone(),
173 )?));
174 handle_result.reload = true;
175 }
176 if label == FormItem::RemoveButton {
177 self.remove_popup.open();
178 }
179
180 Ok(())
181 },
182 )?;
183 Ok(handle_result)
184 }
185 fn render_component(&self, area: Rect, buf: &mut Buffer, s: &SharedState) -> Rect
186 where
187 Self: Sized,
188 {
189 self.form.render(area, buf, &s.theme);
190 self.remove_popup.render(area, buf, &s.theme.popup());
191 area
192 }
193}