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 }