diff --git a/gui/src/installer/step/descriptor.rs b/gui/src/installer/step/descriptor.rs index e7aa99aa..07d472ae 100644 --- a/gui/src/installer/step/descriptor.rs +++ b/gui/src/installer/step/descriptor.rs @@ -49,6 +49,7 @@ pub struct RecoveryPath { keys: Vec, threshold: usize, sequence: u16, + duplicate_sequence: bool, } impl RecoveryPath { @@ -57,11 +58,14 @@ impl RecoveryPath { keys: vec![DescriptorKey::default()], threshold: 1, sequence: u16::MAX, + duplicate_sequence: false, } } fn valid(&self) -> bool { - !self.keys.is_empty() && !self.keys.iter().any(|k| k.key.is_none()) + !self.keys.is_empty() + && !self.keys.iter().any(|k| k.key.is_none()) + && !self.duplicate_sequence } fn check_network(&mut self, network: Network) { @@ -73,6 +77,7 @@ impl RecoveryPath { fn view(&self) -> Element { view::recovery_path_view( self.sequence, + self.duplicate_sequence, self.threshold, self.keys .iter() @@ -143,6 +148,8 @@ impl DefineDescriptor { let mut duplicate_keys = HashSet::new(); let mut all_names: HashMap = HashMap::new(); let mut duplicate_names = HashSet::new(); + let mut all_sequence = HashSet::new(); + let mut duplicate_sequence = HashSet::new(); for spending_key in &self.spending_keys { if let Some(key) = &spending_key.key { if let Some(fg) = all_names.get(&spending_key.name) { @@ -159,7 +166,12 @@ impl DefineDescriptor { } } } - for path in &self.recovery_paths { + for path in &mut self.recovery_paths { + if all_sequence.contains(&path.sequence) { + duplicate_sequence.insert(path.sequence); + } else { + all_sequence.insert(path.sequence); + } for recovery_key in &path.keys { if let Some(key) = &recovery_key.key { if let Some(fg) = all_names.get(&recovery_key.name) { @@ -185,6 +197,7 @@ impl DefineDescriptor { } for path in &mut self.recovery_paths { + path.duplicate_sequence = duplicate_sequence.contains(&path.sequence); for recovery_key in path.keys.iter_mut() { if let Some(key) = &recovery_key.key { recovery_key.duplicate_key = duplicate_keys.contains(key); @@ -342,6 +355,7 @@ impl Step for DefineDescriptor { if let Some(path) = self.recovery_paths.get_mut(i) { path.sequence = seq; } + self.check_for_duplicate(); } message::DefinePath::EditSequence => { if let Some(path) = self.recovery_paths.get(i) { diff --git a/gui/src/installer/view.rs b/gui/src/installer/view.rs index a2a4c55d..2f4a0f90 100644 --- a/gui/src/installer/view.rs +++ b/gui/src/installer/view.rs @@ -278,46 +278,49 @@ pub fn define_descriptor<'a>( pub fn recovery_path_view( sequence: u16, + duplicate_sequence: bool, recovery_threshold: usize, recovery_keys: Vec>, ) -> Element { Container::new( - Column::new().push(defined_sequence(sequence)).push( - Row::new() - .align_items(Alignment::Center) - .push_maybe(if recovery_keys.len() > 1 { - Some(threshsold_input::threshsold_input( - recovery_threshold, - recovery_keys.len(), - message::DefinePath::ThresholdEdited, - )) - } else { - None - }) - .push( - scrollable( - Row::new() - .spacing(5) - .align_items(Alignment::Center) - .push(Row::with_children(recovery_keys).spacing(5)) - .push( - Button::new( - Container::new(icon::plus_icon().size(50)) - .width(Length::Units(150)) - .height(Length::Units(150)) - .align_y(alignment::Vertical::Center) - .align_x(alignment::Horizontal::Center), + Column::new() + .push(defined_sequence(sequence, duplicate_sequence)) + .push( + Row::new() + .align_items(Alignment::Center) + .push_maybe(if recovery_keys.len() > 1 { + Some(threshsold_input::threshsold_input( + recovery_threshold, + recovery_keys.len(), + message::DefinePath::ThresholdEdited, + )) + } else { + None + }) + .push( + scrollable( + Row::new() + .spacing(5) + .align_items(Alignment::Center) + .push(Row::with_children(recovery_keys).spacing(5)) + .push( + Button::new( + Container::new(icon::plus_icon().size(50)) + .width(Length::Units(150)) + .height(Length::Units(150)) + .align_y(alignment::Vertical::Center) + .align_x(alignment::Horizontal::Center), + ) + .width(Length::Units(150)) + .height(Length::Units(150)) + .style(theme::Button::TransparentBorder) + .on_press(message::DefinePath::AddKey), ) - .width(Length::Units(150)) - .height(Length::Units(150)) - .style(theme::Button::TransparentBorder) - .on_press(message::DefinePath::AddKey), - ) - .padding(5), - ) - .horizontal_scroll(Properties::new().width(3).scroller_width(3)), - ), - ), + .padding(5), + ) + .horizontal_scroll(Properties::new().width(3).scroller_width(3)), + ), + ), ) .padding(5) .style(theme::Container::Card(theme::Card::Border)) @@ -1032,46 +1035,61 @@ pub fn install<'a>( ) } -pub fn defined_sequence<'a>(sequence: u16) -> Element<'a, message::DefinePath> { +pub fn defined_sequence<'a>( + sequence: u16, + duplicate_sequence: bool, +) -> Element<'a, message::DefinePath> { let (n_years, n_months, n_days, n_hours, n_minutes) = duration_from_sequence(sequence); Container::new( - Row::new() - .width(Length::Fill) - .align_items(Alignment::Center) - .push( - Container::new( - Column::new() - .spacing(5) - .push(text(format!("Available after {} blocks", sequence)).bold()) - .push( - [ - (n_years, "y"), - (n_months, "m"), - (n_days, "d"), - (n_hours, "h"), - (n_minutes, "mn"), - ] - .iter() - .fold( - Row::new().spacing(5), - |row, (n, unit)| { - row.push_maybe(if *n > 0 { - Some(text(format!("{}{}", n, unit,))) - } else { - None - }) - }, - ), - ), + Column::new() + .spacing(5) + .push_maybe(if duplicate_sequence { + Some( + text("No two recovery paths may become available at the very same date.") + .small() + .style(color::legacy::ALERT), ) - .padding(5) - .align_y(alignment::Vertical::Center), - ) + } else { + None + }) .push( - button::border(Some(icon::pencil_icon()), "Edit") - .on_press(message::DefinePath::EditSequence), - ) - .spacing(15), + Row::new() + .align_items(Alignment::Center) + .push( + Container::new( + Column::new() + .spacing(5) + .push(text(format!("Available after {} blocks", sequence)).bold()) + .push( + [ + (n_years, "y"), + (n_months, "m"), + (n_days, "d"), + (n_hours, "h"), + (n_minutes, "mn"), + ] + .iter() + .fold( + Row::new().spacing(5), + |row, (n, unit)| { + row.push_maybe(if *n > 0 { + Some(text(format!("{}{}", n, unit,))) + } else { + None + }) + }, + ), + ), + ) + .padding(5) + .align_y(alignment::Vertical::Center), + ) + .push( + button::border(Some(icon::pencil_icon()), "Edit") + .on_press(message::DefinePath::EditSequence), + ) + .spacing(15), + ), ) .padding(5) .into()