account_login_view.rs (6976B)
1 use crate::login_manager::AcquireKeyState; 2 use crate::ui::onboarding::FollowPacksResponse; 3 use crate::ui::{Preview, PreviewConfig}; 4 use egui::{ 5 Align, Button, Color32, Frame, InnerResponse, Layout, Margin, RichText, TextEdit, Vec2, 6 }; 7 use egui_winit::clipboard::Clipboard; 8 use enostr::Keypair; 9 use notedeck::{fonts::get_font_size, tr, Localization, NotedeckTextStyle}; 10 use notedeck_ui::{ 11 app_images, 12 context_menu::{input_context, PasteBehavior}, 13 }; 14 15 pub struct AccountLoginView<'a> { 16 manager: &'a mut AcquireKeyState, 17 clipboard: &'a mut Clipboard, 18 i18n: &'a mut Localization, 19 } 20 21 pub enum AccountLoginResponse { 22 CreatingNew, 23 Onboarding(FollowPacksResponse), 24 LoginWith(Keypair), 25 } 26 27 impl<'a> AccountLoginView<'a> { 28 pub fn new( 29 manager: &'a mut AcquireKeyState, 30 clipboard: &'a mut Clipboard, 31 i18n: &'a mut Localization, 32 ) -> Self { 33 AccountLoginView { 34 manager, 35 clipboard, 36 i18n, 37 } 38 } 39 40 pub fn ui(&mut self, ui: &mut egui::Ui) -> InnerResponse<Option<AccountLoginResponse>> { 41 Frame::new().outer_margin(12.0).show(ui, |ui| self.show(ui)) 42 } 43 44 fn show(&mut self, ui: &mut egui::Ui) -> Option<AccountLoginResponse> { 45 ui.vertical(|ui| { 46 ui.vertical_centered(|ui| { 47 ui.add_space(32.0); 48 ui.label(login_title_text(self.i18n)); 49 }); 50 51 ui.horizontal(|ui| { 52 ui.label(login_textedit_info_text(self.i18n)); 53 }); 54 55 ui.vertical_centered_justified(|ui| { 56 ui.horizontal(|ui| { 57 let available_width = ui.available_width(); 58 let button_width = 32.0; 59 let text_edit_width = available_width - button_width; 60 61 let textedit_resp = ui.add_sized([text_edit_width, 40.0], login_textedit(self.manager, self.i18n)); 62 input_context(ui, &textedit_resp, self.clipboard, self.manager.input_buffer(), PasteBehavior::Clear); 63 64 if eye_button(ui, self.manager.password_visible()).clicked() { 65 self.manager.toggle_password_visibility(); 66 } 67 }); 68 ui.with_layout(Layout::left_to_right(Align::TOP), |ui| { 69 let help_text_style = NotedeckTextStyle::Small; 70 ui.add(egui::Label::new( 71 RichText::new(tr!(self.i18n, "Enter your public key (npub), nostr address (e.g. {address}), or private key (nsec). You must enter your private key to be able to post, reply, etc.", "Instructions for entering Nostr credentials", address="vrod@damus.io")) 72 .text_style(help_text_style.text_style()) 73 .size(get_font_size(ui.ctx(), &help_text_style)).color(ui.visuals().weak_text_color()), 74 ).wrap()) 75 }); 76 77 self.manager.loading_and_error_ui(ui, self.i18n); 78 79 if ui.add(login_button(self.i18n)).clicked() { 80 self.manager.apply_acquire(); 81 } 82 }); 83 84 ui.horizontal(|ui| { 85 ui.label( 86 RichText::new(tr!(self.i18n,"New to Nostr?", "Label asking if the user is new to Nostr. Underneath this label is a button to create an account.")) 87 .color(ui.style().visuals.noninteractive().fg_stroke.color) 88 .text_style(NotedeckTextStyle::Body.text_style()), 89 ); 90 91 if ui 92 .add(Button::new(RichText::new(tr!(self.i18n,"Create Account", "Button to create a new account"))).frame(false)) 93 .clicked() 94 { 95 self.manager.should_create_new(); 96 } 97 }); 98 }); 99 100 if self.manager.check_for_create_new() { 101 return Some(AccountLoginResponse::CreatingNew); 102 } 103 104 if let Some(keypair) = self.manager.get_login_keypair() { 105 return Some(AccountLoginResponse::LoginWith(keypair.clone())); 106 } 107 None 108 } 109 } 110 111 fn login_title_text(i18n: &mut Localization) -> RichText { 112 RichText::new(tr!(i18n, "Login", "Login page title")) 113 .text_style(NotedeckTextStyle::Heading2.text_style()) 114 .strong() 115 } 116 117 fn login_textedit_info_text(i18n: &mut Localization) -> RichText { 118 RichText::new(tr!(i18n, "Enter your key", "Label for key input field. Key can be public key (npub), private key (nsec), or Nostr address (NIP-05).")) 119 .strong() 120 .text_style(NotedeckTextStyle::Body.text_style()) 121 } 122 123 fn login_button(i18n: &mut Localization) -> Button<'static> { 124 Button::new( 125 RichText::new(tr!(i18n, "Login now — let's do this!", "Login button text")) 126 .text_style(NotedeckTextStyle::Body.text_style()) 127 .strong(), 128 ) 129 .fill(Color32::from_rgb(0xF8, 0x69, 0xB6)) // TODO: gradient 130 .min_size(Vec2::new(0.0, 40.0)) 131 } 132 133 fn login_textedit<'a>( 134 manager: &'a mut AcquireKeyState, 135 i18n: &'a mut Localization, 136 ) -> TextEdit<'a> { 137 let create_textedit = |text| { 138 egui::TextEdit::singleline(text) 139 .hint_text( 140 RichText::new(tr!( 141 i18n, 142 "Your key here...", 143 "Placeholder text for key input field" 144 )) 145 .text_style(NotedeckTextStyle::Body.text_style()), 146 ) 147 .vertical_align(Align::Center) 148 .min_size(Vec2::new(0.0, 40.0)) 149 .margin(Margin::same(12)) 150 }; 151 152 let is_visible = manager.password_visible(); 153 let mut text_edit = manager.get_acquire_textedit(create_textedit); 154 if !is_visible { 155 text_edit = text_edit.password(true); 156 } 157 text_edit 158 } 159 160 pub fn eye_button(ui: &mut egui::Ui, is_visible: bool) -> egui::Response { 161 let is_dark_mode = ui.visuals().dark_mode; 162 let icon = if is_visible && is_dark_mode { 163 app_images::eye_dark_image() 164 } else if is_visible { 165 app_images::eye_light_image() 166 } else if is_dark_mode { 167 app_images::eye_slash_dark_image() 168 } else { 169 app_images::eye_slash_light_image() 170 }; 171 ui.add(Button::image(icon).frame(false)) 172 } 173 174 mod preview { 175 use super::*; 176 use notedeck::{App, AppContext, AppResponse}; 177 178 pub struct AccountLoginPreview { 179 manager: AcquireKeyState, 180 } 181 182 impl App for AccountLoginPreview { 183 fn update(&mut self, ctx: &mut AppContext<'_>, ui: &mut egui::Ui) -> AppResponse { 184 AccountLoginView::new(&mut self.manager, ctx.clipboard, ctx.i18n).ui(ui); 185 186 AppResponse::none() 187 } 188 } 189 190 impl Preview for AccountLoginView<'_> { 191 type Prev = AccountLoginPreview; 192 193 fn preview(cfg: PreviewConfig) -> Self::Prev { 194 let _ = cfg; 195 let manager = AcquireKeyState::new(); 196 AccountLoginPreview { manager } 197 } 198 } 199 }