gm_lib/tui/app/pages/
config.rs1use std::{
2 str::FromStr,
3 sync::{atomic::AtomicBool, mpsc, Arc},
4};
5
6use ratatui::{buffer::Buffer, layout::Rect};
7use strum::EnumIter;
8
9use crate::{
10 disk::{Config, DiskInterface},
11 tui::{
12 app::{
13 widgets::{
14 filter_select_popup::FilterSelectPopup,
15 form::{Form, FormItemIndex, FormWidget},
16 },
17 SharedState,
18 },
19 events::Event,
20 theme::{self, ThemeName},
21 traits::{Component, HandleResult},
22 },
23};
24
25#[derive(EnumIter, PartialEq)]
26pub enum FormItem {
27 Heading,
28 AlchemyApiKey,
29 TestnetMode,
30 DeveloperMode,
31 Theme,
32 SaveButton,
33 DisplayText,
34}
35impl FormItemIndex for FormItem {
36 fn index(self) -> usize {
37 self as usize
38 }
39}
40impl TryFrom<FormItem> for FormWidget {
41 type Error = crate::Error;
42 fn try_from(value: FormItem) -> crate::Result<Self> {
43 let widget = match value {
44 FormItem::Heading => FormWidget::Heading("Configuration"),
45 FormItem::AlchemyApiKey => FormWidget::InputBox {
46 label: "Alchemy API key",
47 text: String::new(),
48 empty_text: Some("Please get an Alchemy API key from https://www.alchemy.com/"),
49 currency: None,
50 },
51 FormItem::TestnetMode => FormWidget::BooleanInput {
52 label: "Testnet Mode",
53 value: false,
54 },
55 FormItem::DeveloperMode => FormWidget::BooleanInput {
56 label: "Developer Mode",
57 value: false,
58 },
59 FormItem::Theme => FormWidget::SelectInput {
60 label: "Theme",
61 text: String::new(),
62 empty_text: Some("Select a theme"),
63 popup: FilterSelectPopup::new("Select a theme", Some("No themes available")),
64 },
65 FormItem::SaveButton => FormWidget::Button { label: "Save" },
66 FormItem::DisplayText => FormWidget::DisplayText(String::new()),
67 };
68 Ok(widget)
69 }
70}
71
72pub struct ConfigPage {
73 pub form: Form<FormItem>,
74}
75
76impl ConfigPage {
77 pub fn new() -> crate::Result<Self> {
78 let form = Form::init(|form| {
79 let config = Config::load()?;
80 *form.get_text_mut(FormItem::AlchemyApiKey) =
81 config.alchemy_api_key.clone().unwrap_or_default();
82 *form.get_boolean_mut(FormItem::TestnetMode) = config.testnet_mode;
83 *form.get_boolean_mut(FormItem::DeveloperMode) = config.developer_mode;
84 *form.get_text_mut(FormItem::Theme) = config.theme_name.clone();
85 let popup = form.get_popup_mut(FormItem::Theme);
86 popup.set_items(Some(ThemeName::list()));
87 popup.set_cursor(&config.theme_name);
88 Ok(())
89 })?;
90
91 Ok(Self { form })
92 }
93}
94
95impl Component for ConfigPage {
96 fn set_focus(&mut self, focus: bool) {
97 self.form.set_form_focus(focus);
98 }
99
100 fn handle_event(
101 &mut self,
102 event: &Event,
103 _area: Rect,
104 _transmitter: &mpsc::Sender<Event>,
105 _shutdown_signal: &Arc<AtomicBool>,
106 _shared_state: &SharedState,
107 ) -> crate::Result<HandleResult> {
108 let display_text = self.form.get_text_mut(FormItem::DisplayText);
109 *display_text = "".to_string();
110
111 let mut handle_result = HandleResult::default();
112
113 self.form.handle_event(
114 event,
115 |_, _| Ok(()),
116 |_, form| {
117 handle_result.reload = true;
118
119 let mut config = Config::load()?;
120 config.alchemy_api_key = Some(form.get_text(FormItem::AlchemyApiKey).clone());
121 config.testnet_mode = form.get_boolean(FormItem::TestnetMode);
122 config.developer_mode = form.get_boolean(FormItem::DeveloperMode);
123 let theme_name = form.get_text(FormItem::Theme).clone();
124 config.theme_name = theme::ThemeName::from_str(&theme_name)?.to_string();
125
126 config.save()?;
127
128 let display_text = form.get_text_mut(FormItem::DisplayText);
129 *display_text = "Configuration saved".to_string();
130
131 Ok(())
132 },
133 )?;
134
135 Ok(handle_result)
136 }
137
138 fn render_component(&self, area: Rect, buf: &mut Buffer, ss: &SharedState) -> Rect
139 where
140 Self: Sized,
141 {
142 self.form.render(area, buf, &ss.theme);
143 area
144 }
145}