Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(launchpad): upgrade nodes version #2388

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions node-launchpad/.config/config.json5
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
"<Ctrl-l>": {"OptionsActions":"TriggerAccessLogs"},
"<Ctrl-L>": {"OptionsActions":"TriggerAccessLogs"},
"<Ctrl-Shift-l>": {"OptionsActions":"TriggerAccessLogs"},
"<Ctrl-u>": {"OptionsActions":"TriggerUpdateNodes"},
"<Ctrl-U>": {"OptionsActions":"TriggerUpdateNodes"},
"<Ctrl-r>": {"OptionsActions":"TriggerResetNodes"},
"<Ctrl-R>": {"OptionsActions":"TriggerResetNodes"},
"<Ctrl-Shift-r>": {"OptionsActions":"TriggerResetNodes"},
Expand Down
4 changes: 4 additions & 0 deletions node-launchpad/src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,15 @@ pub enum StatusActions {
StartNodesCompleted,
StopNodesCompleted,
ResetNodesCompleted { trigger_start_node: bool },
UpdateNodesCompleted,
SuccessfullyDetectedNatStatus,
ErrorWhileRunningNatDetection,
ErrorLoadingNodeRegistry { raw_error: String },
ErrorGettingNodeRegistryPath { raw_error: String },
ErrorScalingUpNodes { raw_error: String },
ErrorStoppingNodes { raw_error: String },
ErrorResettingNodes { raw_error: String },
ErrorUpdatingNodes { raw_error: String },
NodesStatsObtained(NodeStats),

TriggerManageNodes,
Expand All @@ -67,11 +69,13 @@ pub enum StatusActions {
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Display, Deserialize)]
pub enum OptionsActions {
ResetNodes,
UpdateNodes,

TriggerChangeDrive,
TriggerChangeConnectionMode,
TriggerChangePortRange,
TriggerRewardsAddress,
TriggerUpdateNodes,
TriggerResetNodes,
TriggerAccessLogs,
UpdateConnectionMode(ConnectionMode),
Expand Down
4 changes: 3 additions & 1 deletion node-launchpad/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::{
popup::{
change_drive::ChangeDrivePopup, connection_mode::ChangeConnectionModePopUp,
manage_nodes::ManageNodes, port_range::PortRangePopUp, reset_nodes::ResetNodesPopup,
rewards_address::RewardsAddress,
rewards_address::RewardsAddress, upgrade_nodes::UpgradeNodesPopUp,
},
status::{Status, StatusConfig},
Component,
Expand Down Expand Up @@ -120,6 +120,7 @@ impl App {
let change_connection_mode = ChangeConnectionModePopUp::new(connection_mode)?;
let port_range = PortRangePopUp::new(connection_mode, port_from, port_to);
let rewards_address = RewardsAddress::new(app_data.discord_username.clone());
let upgrade_nodes = UpgradeNodesPopUp::default();

Ok(Self {
config,
Expand All @@ -146,6 +147,7 @@ impl App {
Box::new(rewards_address),
Box::new(reset_nodes),
Box::new(manage_nodes),
Box::new(upgrade_nodes),
],
should_quit: false,
should_suspend: false,
Expand Down
73 changes: 50 additions & 23 deletions node-launchpad/src/components/options.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::path::PathBuf;
use std::{cmp::max, path::PathBuf};

use color_eyre::eyre::{eyre, Result};
use color_eyre::eyre::{eyre, Ok, Result};
use ratatui::{
layout::{Alignment, Constraint, Direction, Layout, Rect},
style::{Style, Stylize},
Expand Down Expand Up @@ -74,7 +74,7 @@ impl Component for Options {
Constraint::Length(7),
Constraint::Length(3),
Constraint::Length(3),
Constraint::Length(3),
Constraint::Length(4),
Constraint::Length(3),
]
.as_ref(),
Expand Down Expand Up @@ -271,35 +271,58 @@ impl Component for Options {
.block(block3)
.style(Style::default().fg(GHOST_WHITE));

// Reset All Nodes
// Update Nodes
let reset_legend = " Begin Reset ";
let reset_key = " [Ctrl+R] ";
let upgrade_legend = " Begin Upgrade ";
let upgrade_key = " [Ctrl+U] ";
let block4 = Block::default()
.title(" Reset All Nodes ")
.title(" Update Nodes ")
.title_style(Style::default().bold().fg(GHOST_WHITE))
.style(Style::default().fg(GHOST_WHITE))
.borders(Borders::ALL)
.border_style(Style::default().fg(EUCALYPTUS));
let reset_nodes = Table::new(
vec![Row::new(vec![
Cell::from(
Line::from(vec![Span::styled(
" Remove and Reset all Nodes on this device ",
Style::default().fg(LIGHT_PERIWINKLE),
)])
.alignment(Alignment::Left),
),
Cell::from(
Line::from(vec![
Span::styled(reset_legend, Style::default().fg(EUCALYPTUS)),
Span::styled(reset_key, Style::default().fg(GHOST_WHITE)),
])
.alignment(Alignment::Right),
),
])],
vec![
Row::new(vec![
Cell::from(
Line::from(vec![Span::styled(
" Upgrade all Nodes ",
Style::default().fg(LIGHT_PERIWINKLE),
)])
.alignment(Alignment::Left),
),
Cell::from(
Line::from(vec![
Span::styled(upgrade_legend, Style::default().fg(EUCALYPTUS)),
Span::styled(upgrade_key, Style::default().fg(GHOST_WHITE)),
])
.alignment(Alignment::Right),
),
]),
Row::new(vec![
Cell::from(
Line::from(vec![Span::styled(
" Reset all Nodes on this device ",
Style::default().fg(LIGHT_PERIWINKLE),
)])
.alignment(Alignment::Left),
),
Cell::from(
Line::from(vec![
Span::styled(reset_legend, Style::default().fg(EUCALYPTUS)),
Span::styled(reset_key, Style::default().fg(GHOST_WHITE)),
])
.alignment(Alignment::Right),
),
]),
],
&[
Constraint::Fill(1),
Constraint::Length((reset_legend.len() + reset_key.len()) as u16),
Constraint::Length(
(max(reset_legend.len(), upgrade_legend.len())
+ max(reset_key.len(), upgrade_key.len())) as u16,
),
],
)
.block(block4)
Expand Down Expand Up @@ -355,7 +378,8 @@ impl Component for Options {
| Scene::ChangeConnectionModePopUp
| Scene::ChangePortsPopUp { .. }
| Scene::OptionsRewardsAddressPopUp
| Scene::ResetNodesPopUp => {
| Scene::ResetNodesPopUp
| Scene::UpgradeNodesPopUp => {
self.active = true;
// make sure we're in navigation mode
return Ok(Some(Action::SwitchInputMode(InputMode::Navigation)));
Expand Down Expand Up @@ -402,6 +426,9 @@ impl Component for Options {
error!("Failed to open folder: {}", e);
}
}
OptionsActions::TriggerUpdateNodes => {
return Ok(Some(Action::SwitchScene(Scene::UpgradeNodesPopUp)));
}
OptionsActions::TriggerResetNodes => {
return Ok(Some(Action::SwitchScene(Scene::ResetNodesPopUp)))
}
Expand Down
1 change: 1 addition & 0 deletions node-launchpad/src/components/popup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ pub mod manage_nodes;
pub mod port_range;
pub mod reset_nodes;
pub mod rewards_address;
pub mod upgrade_nodes;
1 change: 0 additions & 1 deletion node-launchpad/src/components/popup/rewards_address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ pub struct RewardsAddress {
can_save: bool,
}

#[allow(dead_code)]
enum RewardsAddressState {
RewardsAddressAlreadySet,
ShowTCs,
Expand Down
182 changes: 182 additions & 0 deletions node-launchpad/src/components/popup/upgrade_nodes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
// Copyright 2024 MaidSafe.net limited.
//
// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3.
// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed
// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. Please review the Licences for the specific language governing
// permissions and limitations relating to use of the SAFE Network Software.

use super::super::utils::centered_rect_fixed;
use super::super::Component;
use crate::{
action::{Action, OptionsActions},
mode::{InputMode, Scene},
style::{clear_area, EUCALYPTUS, GHOST_WHITE, LIGHT_PERIWINKLE, VIVID_SKY_BLUE},
};
use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent};
use ratatui::{prelude::*, widgets::*};

pub struct UpgradeNodesPopUp {
/// Whether the component is active right now, capturing keystrokes + draw things.
active: bool,
}

impl UpgradeNodesPopUp {
pub fn new() -> Self {
Self { active: false }
}
}

impl Default for UpgradeNodesPopUp {
fn default() -> Self {
Self::new()
}
}

impl Component for UpgradeNodesPopUp {
fn handle_key_events(&mut self, key: KeyEvent) -> Result<Vec<Action>> {
if !self.active {
return Ok(vec![]);
}
// while in entry mode, keybinds are not captured, so gotta exit entry mode from here
let send_back = match key.code {
KeyCode::Enter => {
debug!("Got Enter, Upgrading nodes...");
vec![
Action::OptionsActions(OptionsActions::UpdateNodes),
Action::SwitchScene(Scene::Status),
]
}
KeyCode::Esc => {
debug!("Got Esc, Not upgrading nodes.");
vec![Action::SwitchScene(Scene::Options)]
}
_ => vec![],
};
Ok(send_back)
}

fn update(&mut self, action: Action) -> Result<Option<Action>> {
let send_back = match action {
Action::SwitchScene(scene) => match scene {
Scene::UpgradeNodesPopUp => {
self.active = true;
Some(Action::SwitchInputMode(InputMode::Entry))
}
_ => {
self.active = false;
None
}
},
_ => None,
};
Ok(send_back)
}

fn draw(&mut self, f: &mut crate::tui::Frame<'_>, area: Rect) -> Result<()> {
if !self.active {
return Ok(());
}

let layer_zero = centered_rect_fixed(52, 15, area);

let layer_one = Layout::new(
Direction::Vertical,
[
// for the pop_up_border
Constraint::Length(2),
// for the input field
Constraint::Min(1),
// for the pop_up_border
Constraint::Length(1),
],
)
.split(layer_zero);

// layer zero
let pop_up_border = Paragraph::new("").block(
Block::default()
.borders(Borders::ALL)
.title(" Upgrade all nodes ")
.bold()
.title_style(Style::new().fg(VIVID_SKY_BLUE))
.padding(Padding::uniform(2))
.border_style(Style::new().fg(VIVID_SKY_BLUE)),
);
clear_area(f, layer_zero);

// split the area into 3 parts, for the lines, hypertext, buttons
let layer_two = Layout::new(
Direction::Vertical,
[
// for the text
Constraint::Length(9),
// gap
Constraint::Length(4),
// for the buttons
Constraint::Length(1),
],
)
.split(layer_one[1]);

let text = Paragraph::new(vec![
Line::from(Span::styled("\n\n", Style::default())),
Line::from(vec![
Span::styled("This will ", Style::default().fg(LIGHT_PERIWINKLE)),
Span::styled(
"stop and upgrade all nodes. ",
Style::default().fg(GHOST_WHITE),
),
]),
Line::from(Span::styled(
"No data will be lost.",
Style::default().fg(LIGHT_PERIWINKLE),
)),
Line::from(Span::styled("\n\n", Style::default())),
Line::from(Span::styled("\n\n", Style::default())),
Line::from(vec![
Span::styled("You’ll need to ", Style::default().fg(LIGHT_PERIWINKLE)),
Span::styled("Start ", Style::default().fg(GHOST_WHITE)),
Span::styled(
"them again afterwards.",
Style::default().fg(LIGHT_PERIWINKLE),
),
]),
Line::from(Span::styled(
"Are you sure you want to continue?",
Style::default(),
)),
])
.block(Block::default().padding(Padding::horizontal(2)))
.alignment(Alignment::Center)
.wrap(Wrap { trim: true });

f.render_widget(text, layer_two[0]);

let dash = Block::new()
.borders(Borders::BOTTOM)
.border_style(Style::new().fg(GHOST_WHITE));
f.render_widget(dash, layer_two[1]);

let buttons_layer =
Layout::horizontal(vec![Constraint::Percentage(45), Constraint::Percentage(55)])
.split(layer_two[2]);

let button_no = Line::from(vec![Span::styled(
" No, Cancel [Esc]",
Style::default().fg(LIGHT_PERIWINKLE),
)]);
f.render_widget(button_no, buttons_layer[0]);

let button_yes = Paragraph::new(Line::from(vec![Span::styled(
"Yes, Upgrade [Enter] ",
Style::default().fg(EUCALYPTUS),
)]))
.alignment(Alignment::Right);
f.render_widget(button_yes, buttons_layer[1]);
f.render_widget(pop_up_border, layer_zero);

Ok(())
}
}
Loading
Loading