notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

login_manager.rs (6322B)


      1 use crate::key_parsing::perform_key_retrieval;
      2 use crate::key_parsing::AcquireKeyError;
      3 use egui::{TextBuffer, TextEdit};
      4 use enostr::Keypair;
      5 use notedeck::{tr, Localization};
      6 use poll_promise::Promise;
      7 
      8 /// The state data for acquiring a nostr key
      9 #[derive(Default)]
     10 pub struct AcquireKeyState {
     11     desired_key: String,
     12     promise_query: Option<(String, Promise<Result<Keypair, AcquireKeyError>>)>,
     13     error: Option<AcquireKeyError>,
     14     key_on_error: Option<String>,
     15     should_create_new: bool,
     16     show_password: bool,
     17 }
     18 
     19 impl<'a> AcquireKeyState {
     20     pub fn new() -> Self {
     21         AcquireKeyState::default()
     22     }
     23 
     24     /// Get the textedit for the UI without exposing the key variable
     25     pub fn get_acquire_textedit(
     26         &'a mut self,
     27         textedit_closure: impl FnOnce(&'a mut dyn TextBuffer) -> TextEdit<'a>,
     28     ) -> TextEdit<'a> {
     29         textedit_closure(&mut self.desired_key)
     30     }
     31 
     32     pub fn input_buffer(&mut self) -> &mut String {
     33         &mut self.desired_key
     34     }
     35 
     36     /// User pressed the 'acquire' button
     37     pub fn apply_acquire(&'a mut self) {
     38         let new_promise = match &self.promise_query {
     39             Some((query, _)) => {
     40                 if query != &self.desired_key {
     41                     Some(perform_key_retrieval(&self.desired_key))
     42                 } else {
     43                     None
     44                 }
     45             }
     46             None => Some(perform_key_retrieval(&self.desired_key)),
     47         };
     48 
     49         if let Some(new_promise) = new_promise {
     50             self.promise_query = Some((self.desired_key.clone(), new_promise));
     51         }
     52     }
     53 
     54     pub fn is_awaiting_network(&self) -> bool {
     55         if let Some((_, promise)) = &self.promise_query {
     56             promise.ready().is_none()
     57         } else {
     58             false
     59         }
     60     }
     61 
     62     /// Whether to indicate to the user that a login error occured
     63     pub fn check_for_error(&'a mut self) -> Option<&'a AcquireKeyError> {
     64         if let Some(error_key) = &self.key_on_error {
     65             if self.desired_key != *error_key {
     66                 self.error = None;
     67                 self.key_on_error = None;
     68             }
     69         }
     70 
     71         self.error.as_ref()
     72     }
     73 
     74     /// Whether to indicate to the user that a successful login occured
     75     pub fn get_login_keypair(&mut self) -> Option<&Keypair> {
     76         if let Some((_, promise)) = &self.promise_query {
     77             match promise.poll() {
     78                 std::task::Poll::Ready(inner) => match inner {
     79                     Ok(kp) => Some(kp),
     80                     Err(e) => {
     81                         self.error = Some(e.clone());
     82                         self.key_on_error = Some(self.desired_key.clone());
     83                         None
     84                     }
     85                 },
     86                 std::task::Poll::Pending => None,
     87             }
     88         } else {
     89             None
     90         }
     91     }
     92 
     93     pub fn handle_input_change_after_acquire(&mut self) {
     94         if let Some((query, _)) = &self.promise_query {
     95             if *query != self.desired_key {
     96                 self.promise_query = None;
     97             }
     98         }
     99     }
    100 
    101     pub fn should_create_new(&mut self) {
    102         self.should_create_new = true;
    103     }
    104 
    105     pub fn check_for_create_new(&self) -> bool {
    106         self.should_create_new
    107     }
    108 
    109     pub fn loading_and_error_ui(&mut self, ui: &mut egui::Ui, i18n: &mut Localization) {
    110         ui.add_space(8.0);
    111 
    112         ui.vertical_centered(|ui| {
    113             if self.is_awaiting_network() {
    114                 ui.add(egui::Spinner::new());
    115             }
    116         });
    117 
    118         if let Some(err) = self.check_for_error() {
    119             show_error(ui, i18n, err);
    120         }
    121 
    122         ui.add_space(8.0);
    123     }
    124 
    125     pub fn toggle_password_visibility(&mut self) {
    126         self.show_password = !self.show_password;
    127     }
    128 
    129     pub fn password_visible(&self) -> bool {
    130         self.show_password
    131     }
    132 }
    133 
    134 fn show_error(ui: &mut egui::Ui, i18n: &mut Localization, err: &AcquireKeyError) {
    135     ui.horizontal(|ui| {
    136         let error_label = match err {
    137             AcquireKeyError::InvalidKey => egui::Label::new(
    138                 egui::RichText::new(tr!(
    139                     i18n,
    140                     "Invalid key.",
    141                     "Error message for invalid key input"
    142                 ))
    143                 .color(ui.visuals().error_fg_color),
    144             ),
    145             AcquireKeyError::Nip05Failed(e) => {
    146                 egui::Label::new(egui::RichText::new(e).color(ui.visuals().error_fg_color))
    147             }
    148         };
    149         ui.add(error_label.truncate());
    150     });
    151 }
    152 
    153 #[cfg(test)]
    154 mod tests {
    155     use enostr::Pubkey;
    156 
    157     use super::*;
    158     use std::time::{Duration, Instant};
    159 
    160     #[test]
    161     fn test_retrieve_key() {
    162         let mut manager = AcquireKeyState::new();
    163         let expected_str = "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681";
    164         let expected_key = Keypair::only_pubkey(Pubkey::from_hex(expected_str).unwrap());
    165 
    166         let start_time = Instant::now();
    167 
    168         while start_time.elapsed() < Duration::from_millis(50u64) {
    169             let cur_time = start_time.elapsed();
    170 
    171             if cur_time < Duration::from_millis(10u64) {
    172                 let _ = manager.get_acquire_textedit(|text| {
    173                     text.clear();
    174                     text.insert_text("test", 0);
    175                     egui::TextEdit::singleline(text)
    176                 });
    177                 manager.apply_acquire();
    178             } else if cur_time < Duration::from_millis(30u64) {
    179                 let _ = manager.get_acquire_textedit(|text| {
    180                     text.clear();
    181                     text.insert_text("test2", 0);
    182                     egui::TextEdit::singleline(text)
    183                 });
    184                 manager.apply_acquire();
    185             } else {
    186                 let _ = manager.get_acquire_textedit(|text| {
    187                     text.clear();
    188                     text.insert_text(
    189                         "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681",
    190                         0,
    191                     );
    192                     egui::TextEdit::singleline(text)
    193                 });
    194                 manager.apply_acquire();
    195             }
    196 
    197             if let Some(key) = manager.get_login_keypair() {
    198                 assert_eq!(expected_key, key.clone());
    199                 return;
    200             }
    201         }
    202 
    203         panic!();
    204     }
    205 }