notedeck

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

account_login_view.rs (6868B)


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