commit a927c56870fa898944561b8b75bdee25d038ae81
parent 343d3dc569f6ca9ac7c97ab01027b84d403b71d0
Author: kernelkind <kernelkind@gmail.com>
Date: Mon, 1 Apr 2024 11:04:48 -0400
Create account login panel
Signed-off-by: kernelkind <kernelkind@gmail.com>
Diffstat:
5 files changed, 297 insertions(+), 0 deletions(-)
diff --git a/assets/Logo-Gradient-2x.png b/assets/Logo-Gradient-2x.png
Binary files differ.
diff --git a/assets/Welcome to Nostrdeck 2x.png b/assets/Welcome to Nostrdeck 2x.png
Binary files differ.
diff --git a/src/account_login_view.rs b/src/account_login_view.rs
@@ -0,0 +1,286 @@
+use crate::colors::{
+ ALMOST_WHITE, DARKER_BG, DARK_BG_1, DARK_ISH_BG, GRAY_SECONDARY, RED_700, SEMI_DARKER_BG, WHITE,
+};
+use crate::key_parsing::{perform_key_retrieval, LoginError};
+use crate::login_manager::LoginManager;
+use egui::{
+ epaint::Shadow, Align, Align2, Button, Color32, Frame, Id, LayerId, Margin, Pos2, Rect,
+ RichText, Rounding, Ui, Vec2, Window,
+};
+
+pub struct AccountLoginView<'a> {
+ ctx: &'a egui::Context,
+ manager: &'a mut LoginManager,
+ generate_y_intercept: Option<f32>,
+}
+
+impl<'a> AccountLoginView<'a> {
+ pub fn new(ctx: &'a egui::Context, manager: &'a mut LoginManager) -> Self {
+ AccountLoginView {
+ ctx,
+ manager,
+ generate_y_intercept: None,
+ }
+ }
+ pub fn panel(&mut self) {
+ let frame = egui::CentralPanel::default().frame(Frame {
+ inner_margin: Margin::same(0.0),
+ fill: DARKER_BG,
+ ..Default::default()
+ });
+
+ let screen_width = self.ctx.screen_rect().max.x;
+ let screen_height = self.ctx.screen_rect().max.y;
+
+ frame.show(self.ctx, |ui| {
+ let title_layer = LayerId::new(egui::Order::Background, Id::new("Title layer"));
+
+ let mut top_panel_height: Option<f32> = None;
+ ui.with_layer_id(title_layer, |ui| {
+ egui::TopBottomPanel::top("Top")
+ .resizable(false)
+ .default_height(340.0)
+ .frame(Frame::none())
+ .show_separator_line(false)
+ .show_inside(ui, |ui| {
+ top_panel_height = Some(ui.available_rect_before_wrap().bottom());
+ self.top_title_area(ui);
+ });
+ });
+
+ egui::TopBottomPanel::bottom("Bottom")
+ .resizable(false)
+ .frame(Frame::none())
+ .show_separator_line(false)
+ .show_inside(ui, |ui| {
+ self.window(ui, top_panel_height.unwrap_or(0.0));
+ });
+
+ let top_rect = Rect {
+ min: Pos2::ZERO,
+ max: Pos2::new(
+ screen_width,
+ self.generate_y_intercept.unwrap_or(screen_height * 0.5),
+ ),
+ };
+
+ ui.painter_at(top_rect)
+ .with_layer_id(LayerId::background())
+ .rect_filled(top_rect, Rounding::ZERO, DARK_ISH_BG);
+ });
+ }
+
+ fn window(&mut self, ui: &mut Ui, top_panel_height: f32) {
+ let needed_height_over_top = (self.ctx.screen_rect().bottom() / 2.0) - 230.0;
+ let y_offset = if top_panel_height > needed_height_over_top {
+ top_panel_height - needed_height_over_top
+ } else {
+ 0.0
+ };
+ Window::new("Account login")
+ .movable(false)
+ .constrain(true)
+ .collapsible(false)
+ .drag_to_scroll(false)
+ .title_bar(false)
+ .resizable(false)
+ .anchor(Align2::CENTER_CENTER, [0f32, y_offset])
+ .max_width(538.0)
+ .frame(
+ egui::Frame::default()
+ .fill(DARK_ISH_BG)
+ .rounding(egui::Rounding::from(32f32))
+ .stroke(egui::Stroke::new(1.0, DARK_BG_1))
+ .shadow(Shadow {
+ offset: [0.0, 8.0].into(),
+ blur: 24.0,
+ spread: 0.0,
+ color: egui::Color32::from_rgba_unmultiplied(0x6D, 0x6D, 0x6D, 0x14),
+ }),
+ )
+ .show(ui.ctx(), |ui| {
+ ui.vertical_centered(|ui| {
+ ui.add_space(40.0);
+ ui.label(
+ RichText::new("Login")
+ .size(24f32)
+ .color(WHITE)
+ .strong()
+ .line_height(Some(36f32)),
+ );
+
+ ui.add_space(16f32);
+
+ ui.label(
+ RichText::new("Enter your private key to start using Notedeck")
+ .size(13f32)
+ .color(GRAY_SECONDARY)
+ .line_height(Some(19.5)),
+ );
+
+ ui.add_space(24.0);
+
+ Frame::none()
+ .outer_margin(Margin::symmetric(48.0, 0.0))
+ .show(ui, |ui| {
+ self.login_form(ui);
+ });
+
+ ui.add_space(32.0);
+
+ let y_margin: f32 = 24.0;
+ let generate_frame = egui::Frame::default()
+ .fill(Color32::from_rgb(0x26, 0x26, 0x26)) // TODO: gradient
+ .rounding(egui::Rounding::from(32f32))
+ .stroke(egui::Stroke::new(1.0, DARK_BG_1))
+ .inner_margin(Margin::symmetric(48.0, y_margin));
+
+ generate_frame.show(ui, |ui| {
+ self.generate_y_intercept =
+ Some(ui.available_rect_before_wrap().top() - y_margin);
+ self.generate_group(ui);
+ });
+ });
+ });
+ }
+
+ fn top_title_area(&mut self, ui: &mut egui::Ui) {
+ ui.vertical_centered(|ui| {
+ let logo_gradient_data = egui::include_image!("../assets/Logo-Gradient-2x.png");
+ ui.add(egui::Image::new(logo_gradient_data).max_width(232.0));
+
+ ui.add_space(48.0);
+
+ let welcome_data = egui::include_image!("../assets/Welcome to Nostrdeck 2x.png");
+ ui.add(egui::Image::new(welcome_data).max_width(528.0));
+
+ ui.add_space(12.0);
+
+ // ui.label(
+ // RichText::new("Welcome to Nostrdeck")
+ // .size(48.0)
+ // .strong()
+ // .line_height(Some(72.0)),
+ // );
+ ui.label(
+ RichText::new("The best alternative to tweetDeck built in nostr protocol")
+ .size(24.0)
+ .line_height(Some(36.0))
+ .color(ALMOST_WHITE),
+ );
+ });
+ }
+
+ fn login_form(&mut self, ui: &mut egui::Ui) {
+ ui.vertical_centered_justified(|ui| {
+ ui.horizontal(|ui| {
+ ui.label(
+ RichText::new("Enter your key")
+ .color(WHITE)
+ .strong()
+ .line_height(Some(19.5f32))
+ .size(13f32),
+ );
+ });
+
+ ui.add_space(8f32);
+
+ ui.add(
+ egui::TextEdit::singleline(&mut self.manager.login_key)
+ .hint_text(
+ RichText::new("Your key here...")
+ .size(13f32)
+ .line_height(Some(19.5f32))
+ .color(GRAY_SECONDARY),
+ )
+ .margin(Margin::symmetric(12.0, 12.0))
+ .min_size(Vec2::new(440.0, 40.0))
+ .vertical_align(Align::Center),
+ );
+
+ ui.add_space(8.0);
+
+ ui.vertical_centered(|ui| {
+ if self.manager.promise.is_some() {
+ 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 {
+ ui.horizontal(|ui| {
+ let error_label = match err {
+ LoginError::InvalidKey => {
+ egui::Label::new(RichText::new("Invalid key.").color(RED_700))
+ }
+ LoginError::Nip05Failed(e) => {
+ egui::Label::new(RichText::new(e).color(RED_700))
+ }
+ };
+ ui.add(error_label.truncate(true));
+ });
+ }
+
+ ui.add_space(8.0);
+
+ let login_button = Button::new(
+ RichText::new("Login now — let's do this!")
+ .line_height(Some(16.25f32))
+ .strong()
+ .size(13f32)
+ .color(WHITE),
+ )
+ .rounding(Rounding::same(8f32))
+ .min_size(Vec2::new(442.0, 40.0))
+ .fill(Color32::from_rgb(0xF8, 0x69, 0xB6)); // TODO: gradient
+
+ if ui.add(login_button).clicked() {
+ self.manager.promise = Some(perform_key_retrieval(&self.manager.login_key));
+ }
+ });
+ }
+
+ fn generate_group(&mut self, ui: &mut egui::Ui) {
+ ui.horizontal(|ui| {
+ ui.label(
+ RichText::new("New in nostr?")
+ .size(20f32)
+ .line_height(Some(30f32))
+ .color(ALMOST_WHITE),
+ );
+
+ ui.label(
+ RichText::new(" — we got you!")
+ .size(20f32)
+ .line_height(Some(30f32))
+ .color(GRAY_SECONDARY),
+ );
+ });
+
+ ui.add_space(6.0);
+
+ ui.horizontal(|ui| {
+ ui.label(
+ RichText::new("Quickly generate your keys. Make sure you save them safely.")
+ .color(GRAY_SECONDARY)
+ .size(13f32)
+ .line_height(Some(19.5f32)),
+ );
+ });
+
+ ui.add_space(16.0);
+
+ let generate_button = Button::new(RichText::new("Generate keys").color(WHITE))
+ .fill(SEMI_DARKER_BG)
+ .min_size(Vec2::new(442.0, 40.0))
+ .rounding(Rounding::same(8.0));
+ if ui.add(generate_button).clicked() {
+ // TODO: keygen
+ }
+ }
+}
diff --git a/src/colors.rs b/src/colors.rs
@@ -2,3 +2,13 @@ use egui::Color32;
pub const PURPLE: Color32 = Color32::from_rgb(0xCC, 0x43, 0xC5);
pub const DARK_BG: Color32 = egui::Color32::from_rgb(40, 44, 52);
+pub const GRAY_SECONDARY: Color32 = Color32::from_rgb(0x8A, 0x8A, 0x8A);
+pub const WHITE: Color32 = Color32::from_rgb(0xFF, 0xFF, 0xFF);
+pub const ALMOST_WHITE: Color32 = Color32::from_rgb(0xFA, 0xFA, 0xFA);
+pub const RED_700: Color32 = Color32::from_rgb(0xC7, 0x37, 0x5A);
+
+// BACKGROUNDS
+pub const SEMI_DARKER_BG: Color32 = Color32::from_rgb(0x39, 0x39, 0x39);
+pub const DARKER_BG: Color32 = Color32::from_rgb(0x1E, 0x1E, 0x1E);
+pub const DARK_BG_1: Color32 = Color32::from_rgb(0x2C, 0x2C, 0x2C);
+pub const DARK_ISH_BG: Color32 = egui::Color32::from_rgb(0x22, 0x22, 0x22);
diff --git a/src/lib.rs b/src/lib.rs
@@ -20,6 +20,7 @@ mod colors;
mod profile;
mod key_parsing;
mod login_manager;
+mod account_login_view;
#[cfg(test)]
#[macro_use]