Skip to main content

gm_lib/tui/app/pages/
config.rs

1use 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}