login_manager.rs (4697B)
1 use crate::key_parsing::perform_key_retrieval; 2 use crate::key_parsing::LoginError; 3 use egui::{TextBuffer, TextEdit}; 4 use enostr::Keypair; 5 use poll_promise::Promise; 6 7 /// The UI view interface to log in to a nostr account. 8 #[derive(Default)] 9 pub struct LoginManager { 10 login_key: String, 11 promise_query: Option<(String, Promise<Result<Keypair, LoginError>>)>, 12 error: Option<LoginError>, 13 key_on_error: Option<String>, 14 } 15 16 impl<'a> LoginManager { 17 pub fn new() -> Self { 18 LoginManager { 19 login_key: String::new(), 20 promise_query: None, 21 error: None, 22 key_on_error: None, 23 } 24 } 25 26 /// Get the textedit for the login UI without exposing the key variable 27 pub fn get_login_textedit( 28 &'a mut self, 29 textedit_closure: fn(&'a mut dyn TextBuffer) -> TextEdit<'a>, 30 ) -> TextEdit<'a> { 31 textedit_closure(&mut self.login_key) 32 } 33 34 /// User pressed the 'login' button 35 pub fn apply_login(&'a mut self) { 36 let new_promise = match &self.promise_query { 37 Some((query, _)) => { 38 if query != &self.login_key { 39 Some(perform_key_retrieval(&self.login_key)) 40 } else { 41 None 42 } 43 } 44 None => Some(perform_key_retrieval(&self.login_key)), 45 }; 46 47 if let Some(new_promise) = new_promise { 48 self.promise_query = Some((self.login_key.clone(), new_promise)); 49 } 50 } 51 52 /// Whether to indicate to the user that there is a network operation occuring 53 pub fn is_awaiting_network(&self) -> bool { 54 self.promise_query.is_some() 55 } 56 57 /// Whether to indicate to the user that a login error occured 58 pub fn check_for_error(&'a mut self) -> Option<&'a LoginError> { 59 if let Some(error_key) = &self.key_on_error { 60 if self.login_key != *error_key { 61 self.error = None; 62 self.key_on_error = None; 63 } 64 } 65 66 self.error.as_ref() 67 } 68 69 /// Whether to indicate to the user that a successful login occured 70 pub fn check_for_successful_login(&mut self) -> Option<Keypair> { 71 if let Some((_, promise)) = &mut self.promise_query { 72 if promise.ready().is_some() { 73 if let Some((_, promise)) = self.promise_query.take() { 74 match promise.block_and_take() { 75 Ok(key) => { 76 return Some(key); 77 } 78 Err(e) => { 79 self.error = Some(e); 80 self.key_on_error = Some(self.login_key.clone()); 81 } 82 }; 83 } 84 } 85 } 86 None 87 } 88 } 89 90 #[cfg(test)] 91 mod tests { 92 use enostr::Pubkey; 93 94 use super::*; 95 use std::time::{Duration, Instant}; 96 97 #[tokio::test(flavor = "multi_thread", worker_threads = 2)] 98 async fn test_retrieve_key() { 99 let mut manager = LoginManager::new(); 100 let expected_str = "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"; 101 let expected_key = Keypair::only_pubkey(Pubkey::from_hex(expected_str).unwrap()); 102 103 let start_time = Instant::now(); 104 105 while start_time.elapsed() < Duration::from_millis(50u64) { 106 let cur_time = start_time.elapsed(); 107 108 if cur_time < Duration::from_millis(10u64) { 109 let _ = manager.get_login_textedit(|text| { 110 text.clear(); 111 text.insert_text("test", 0); 112 egui::TextEdit::singleline(text) 113 }); 114 manager.apply_login(); 115 } else if cur_time < Duration::from_millis(30u64) { 116 let _ = manager.get_login_textedit(|text| { 117 text.clear(); 118 text.insert_text("test2", 0); 119 egui::TextEdit::singleline(text) 120 }); 121 manager.apply_login(); 122 } else { 123 let _ = manager.get_login_textedit(|text| { 124 text.clear(); 125 text.insert_text( 126 "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681", 127 0, 128 ); 129 egui::TextEdit::singleline(text) 130 }); 131 manager.apply_login(); 132 } 133 134 if let Some(key) = manager.check_for_successful_login() { 135 assert_eq!(expected_key, key); 136 return; 137 } 138 } 139 140 panic!("Test failed to get expected key."); 141 } 142 }