notedeck

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

commit b8229fb9a9951bb75d53acef7a027d942bb4e081
parent 5396085d961940cd5e7154d42ff511b89e304869
Author: kernelkind <kernelkind@gmail.com>
Date:   Sat, 27 Apr 2024 12:12:35 -0400

Move login business logic to LoginManager

Signed-off-by: kernelkind <kernelkind@gmail.com>
Signed-off-by: William Casarin <jb55@jb55.com>

Diffstat:
Msrc/account_login_view.rs | 40++++++++++++++++++----------------------
Msrc/login_manager.rs | 105++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
2 files changed, 117 insertions(+), 28 deletions(-)

diff --git a/src/account_login_view.rs b/src/account_login_view.rs @@ -1,5 +1,5 @@ use crate::app_style::NotedeckTextStyle; -use crate::key_parsing::{perform_key_retrieval, LoginError}; +use crate::key_parsing::LoginError; use crate::login_manager::LoginManager; use crate::ui; use crate::ui::{Preview, View}; @@ -7,7 +7,7 @@ use egui::{ Align, Align2, Button, Color32, Frame, Id, LayerId, Margin, Pos2, Rect, RichText, Rounding, Ui, Vec2, Window, }; -use egui::{Image, TextBuffer, TextEdit}; +use egui::{Image, TextEdit}; pub struct AccountLoginView<'a> { manager: &'a mut LoginManager, @@ -92,10 +92,10 @@ impl<'a> AccountLoginView<'a> { }); ui.vertical_centered_justified(|ui| { - ui.add(login_textedit(&mut self.manager.login_key)); + ui.add(login_textedit(self.manager)); if ui.add(login_button()).clicked() { - self.manager.promise = Some(perform_key_retrieval(&self.manager.login_key)); + self.manager.apply_login(); } }); @@ -217,23 +217,17 @@ impl<'a> AccountLoginView<'a> { ui.add_space(8f32); - ui.add(login_textedit(&mut self.manager.login_key).min_size(Vec2::new(440.0, 40.0))); + ui.add(login_textedit(self.manager).min_size(Vec2::new(440.0, 40.0))); ui.add_space(8.0); ui.vertical_centered(|ui| { - if self.manager.promise.is_some() { + if self.manager.is_awaiting_network() { ui.add(egui::Spinner::new()); } }); - if let Some(error_key) = &self.manager.key_on_error { - if self.manager.login_key != *error_key { - self.manager.error = None; - self.manager.key_on_error = None; - } - } - if let Some(err) = &self.manager.error { + if let Some(err) = self.manager.check_for_error() { ui.horizontal(|ui| { let error_label = match err { LoginError::InvalidKey => egui::Label::new( @@ -252,7 +246,7 @@ impl<'a> AccountLoginView<'a> { let login_button = login_button().min_size(Vec2::new(442.0, 40.0)); if ui.add(login_button).clicked() { - self.manager.promise = Some(perform_key_retrieval(&self.manager.login_key)); + self.manager.apply_login() } }); } @@ -332,14 +326,16 @@ fn login_button() -> Button<'static> { .min_size(Vec2::new(0.0, 40.0)) } -fn login_textedit(text: &mut dyn TextBuffer) -> TextEdit { - egui::TextEdit::singleline(text) - .hint_text( - RichText::new("Your key here...").text_style(NotedeckTextStyle::Body.text_style()), - ) - .vertical_align(Align::Center) - .min_size(Vec2::new(0.0, 40.0)) - .margin(Margin::same(12.0)) +fn login_textedit(manager: &mut LoginManager) -> TextEdit { + manager.get_login_textedit(|text| { + egui::TextEdit::singleline(text) + .hint_text( + RichText::new("Your key here...").text_style(NotedeckTextStyle::Body.text_style()), + ) + .vertical_align(Align::Center) + .min_size(Vec2::new(0.0, 40.0)) + .margin(Margin::same(12.0)) + }) } pub struct AccountLoginPreview { diff --git a/src/login_manager.rs b/src/login_manager.rs @@ -1,4 +1,6 @@ +use crate::key_parsing::perform_key_retrieval; use crate::key_parsing::LoginError; +use egui::{TextBuffer, TextEdit}; use nostr_sdk::Keys; use poll_promise::Promise; @@ -6,14 +8,105 @@ use poll_promise::Promise; /// nostr-sdk Keys object if possible. #[derive(Default)] pub struct LoginManager { - pub login_key: String, - pub promise: Option<Promise<Result<Keys, LoginError>>>, - pub error: Option<LoginError>, - pub key_on_error: Option<String>, + login_key: String, + promise: Option<Promise<Result<Keys, LoginError>>>, + error: Option<LoginError>, + key_on_error: Option<String>, } -impl LoginManager { +impl<'a> LoginManager { pub fn new() -> Self { - LoginManager::default() + LoginManager { + login_key: String::new(), + promise: None, + error: None, + key_on_error: None, + } + } + + pub fn get_login_textedit( + &'a mut self, + textedit_closure: fn(&'a mut dyn TextBuffer) -> TextEdit<'a>, + ) -> TextEdit<'a> { + textedit_closure(&mut self.login_key) + } + + pub fn apply_login(&'a mut self) { + self.promise = Some(perform_key_retrieval(&self.login_key)); + } + + pub fn is_awaiting_network(&self) -> bool { + self.promise.is_some() + } + + pub fn check_for_error(&'a mut self) -> Option<&'a LoginError> { + if let Some(error_key) = &self.key_on_error { + if self.login_key != *error_key { + self.error = None; + self.key_on_error = None; + } + } + + self.error.as_ref() + } + + pub fn check_for_successful_login(&mut self) -> Option<Keys> { + if let Some(promise) = &mut self.promise { + if promise.ready().is_some() { + if let Some(promise) = self.promise.take() { + match promise.block_and_take() { + Ok(key) => { + return Some(key); + } + Err(e) => { + self.error = Some(e); + self.key_on_error = Some(self.login_key.clone()); + } + }; + } + } + } + None + } +} + +#[cfg(test)] +mod tests { + use super::*; + use nostr_sdk::PublicKey; + use std::time::{Duration, Instant}; + + #[test] + fn test_retrieve_key() { + let mut manager = LoginManager::new(); + let manager_ref = &mut manager; + let expected_str = "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"; + let expected_key = Keys::from_public_key(PublicKey::from_hex(expected_str).unwrap()); + + let start_time = Instant::now(); + + while start_time.elapsed() < Duration::from_millis(50u64) { + let cur_time = start_time.elapsed(); + + if cur_time < Duration::from_millis(10u64) { + let key = "test"; + manager_ref.login_key = String::from(key); + manager_ref.promise = Some(perform_key_retrieval(key)); + } else if cur_time < Duration::from_millis(30u64) { + let key = "test2"; + manager_ref.login_key = String::from(key); + manager_ref.promise = Some(perform_key_retrieval(key)); + } else { + manager_ref.login_key = String::from(expected_str); + manager_ref.promise = Some(perform_key_retrieval(expected_str)); + } + + if let Some(key) = manager_ref.check_for_successful_login() { + assert_eq!(expected_key, key); + return; + } + } + + panic!(); } }