node_launchpad/components/popup/
upgrade_nodes.rs

1// Copyright 2024 MaidSafe.net limited.
2//
3// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3.
4// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed
5// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
6// KIND, either express or implied. Please review the Licences for the specific language governing
7// permissions and limitations relating to use of the SAFE Network Software.
8
9use super::super::utils::centered_rect_fixed;
10use super::super::Component;
11use crate::{
12    action::{Action, OptionsActions},
13    components::status,
14    mode::{InputMode, Scene},
15    style::{clear_area, EUCALYPTUS, GHOST_WHITE, LIGHT_PERIWINKLE, VIVID_SKY_BLUE},
16};
17use color_eyre::Result;
18use crossterm::event::{KeyCode, KeyEvent};
19use ratatui::{prelude::*, widgets::*};
20
21pub struct UpgradeNodesPopUp {
22    nodes_to_start: usize,
23    /// Whether the component is active right now, capturing keystrokes + draw things.
24    active: bool,
25}
26
27impl UpgradeNodesPopUp {
28    pub fn new(nodes_to_start: usize) -> Self {
29        Self {
30            nodes_to_start,
31            active: false,
32        }
33    }
34}
35
36impl Component for UpgradeNodesPopUp {
37    fn handle_key_events(&mut self, key: KeyEvent) -> Result<Vec<Action>> {
38        if !self.active {
39            return Ok(vec![]);
40        }
41        // while in entry mode, keybinds are not captured, so gotta exit entry mode from here
42        let send_back = match key.code {
43            KeyCode::Enter => {
44                debug!("Got Enter, Upgrading nodes...");
45                vec![
46                    Action::OptionsActions(OptionsActions::UpdateNodes),
47                    Action::SwitchScene(Scene::Status),
48                ]
49            }
50            KeyCode::Esc => {
51                debug!("Got Esc, Not upgrading nodes.");
52                vec![Action::SwitchScene(Scene::Options)]
53            }
54            _ => vec![],
55        };
56        Ok(send_back)
57    }
58
59    fn update(&mut self, action: Action) -> Result<Option<Action>> {
60        let send_back = match action {
61            Action::SwitchScene(scene) => match scene {
62                Scene::UpgradeNodesPopUp => {
63                    self.active = true;
64                    Some(Action::SwitchInputMode(InputMode::Entry))
65                }
66                _ => {
67                    self.active = false;
68                    None
69                }
70            },
71            Action::StoreNodesToStart(ref nodes_to_start) => {
72                self.nodes_to_start = *nodes_to_start;
73                None
74            }
75            _ => None,
76        };
77        Ok(send_back)
78    }
79
80    fn draw(&mut self, f: &mut crate::tui::Frame<'_>, area: Rect) -> Result<()> {
81        if !self.active {
82            return Ok(());
83        }
84
85        let layer_zero = centered_rect_fixed(52, 15, area);
86
87        let layer_one = Layout::new(
88            Direction::Vertical,
89            [
90                // for the pop_up_border
91                Constraint::Length(2),
92                // for the input field
93                Constraint::Min(1),
94                // for the pop_up_border
95                Constraint::Length(1),
96            ],
97        )
98        .split(layer_zero);
99
100        // layer zero
101        let pop_up_border = Paragraph::new("").block(
102            Block::default()
103                .borders(Borders::ALL)
104                .title(" Upgrade all nodes ")
105                .bold()
106                .title_style(Style::new().fg(VIVID_SKY_BLUE))
107                .padding(Padding::uniform(2))
108                .border_style(Style::new().fg(VIVID_SKY_BLUE)),
109        );
110        clear_area(f, layer_zero);
111
112        // split the area into 3 parts, for the lines, hypertext,  buttons
113        let layer_two = Layout::new(
114            Direction::Vertical,
115            [
116                // for the text
117                Constraint::Length(9),
118                // gap
119                Constraint::Length(4),
120                // for the buttons
121                Constraint::Length(1),
122            ],
123        )
124        .split(layer_one[1]);
125
126        let text = Paragraph::new(vec![
127            Line::from(Span::styled("\n\n", Style::default())),
128            Line::from(vec![
129                Span::styled("This will ", Style::default().fg(LIGHT_PERIWINKLE)),
130                Span::styled(
131                    "stop and upgrade all nodes. ",
132                    Style::default().fg(GHOST_WHITE),
133                ),
134            ]),
135            Line::from(Span::styled(
136                "No data will be lost.",
137                Style::default().fg(LIGHT_PERIWINKLE),
138            )),
139            Line::from(Span::styled(
140                format!(
141                    "Upgrade time ~ {:.1?} mins ({:?} nodes * {:?} secs)",
142                    self.nodes_to_start * (status::FIXED_INTERVAL / 1_000) as usize / 60,
143                    self.nodes_to_start,
144                    status::FIXED_INTERVAL / 1_000,
145                ),
146                Style::default().fg(LIGHT_PERIWINKLE),
147            )),
148            Line::from(Span::styled("\n\n", Style::default())),
149            Line::from(vec![
150                Span::styled("You’ll need to ", Style::default().fg(LIGHT_PERIWINKLE)),
151                Span::styled("Start ", Style::default().fg(GHOST_WHITE)),
152                Span::styled(
153                    "them again afterwards.",
154                    Style::default().fg(LIGHT_PERIWINKLE),
155                ),
156            ]),
157            Line::from(Span::styled(
158                "Are you sure you want to continue?",
159                Style::default(),
160            )),
161        ])
162        .block(Block::default().padding(Padding::horizontal(2)))
163        .alignment(Alignment::Center)
164        .wrap(Wrap { trim: true });
165
166        f.render_widget(text, layer_two[0]);
167
168        let dash = Block::new()
169            .borders(Borders::BOTTOM)
170            .border_style(Style::new().fg(GHOST_WHITE));
171        f.render_widget(dash, layer_two[1]);
172
173        let buttons_layer =
174            Layout::horizontal(vec![Constraint::Percentage(45), Constraint::Percentage(55)])
175                .split(layer_two[2]);
176
177        let button_no = Line::from(vec![Span::styled(
178            "  No, Cancel [Esc]",
179            Style::default().fg(LIGHT_PERIWINKLE),
180        )]);
181        f.render_widget(button_no, buttons_layer[0]);
182
183        let button_yes = Paragraph::new(Line::from(vec![Span::styled(
184            "Yes, Upgrade [Enter]  ",
185            Style::default().fg(EUCALYPTUS),
186        )]))
187        .alignment(Alignment::Right);
188        f.render_widget(button_yes, buttons_layer[1]);
189        f.render_widget(pop_up_border, layer_zero);
190
191        Ok(())
192    }
193}