notedeck

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

commit f9d61615001a626643f9cdf0354eda1eeac2328c
parent 5b7b47aaf5e2af09d85db8e6657835d5d912c551
Author: William Casarin <jb55@jb55.com>
Date:   Fri,  3 May 2024 07:56:08 -0500

move account_login_view to ui submodule

trying to keep all views and widgets in here

Diffstat:
Dsrc/account_login_view.rs | 378-------------------------------------------------------------------------------
Msrc/lib.rs | 1-
Asrc/ui/account_login_view.rs | 380+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/ui/mod.rs | 1+
Msrc/ui_preview/main.rs | 2+-
5 files changed, 382 insertions(+), 380 deletions(-)

diff --git a/src/account_login_view.rs b/src/account_login_view.rs @@ -1,378 +0,0 @@ -use crate::app_style::NotedeckTextStyle; -use crate::key_parsing::LoginError; -use crate::login_manager::LoginManager; -use crate::ui; -use crate::ui::{Preview, View}; -use egui::{ - Align, Align2, Button, Color32, Frame, Id, LayerId, Margin, Pos2, Rect, RichText, Rounding, Ui, - Vec2, Window, -}; -use egui::{Image, TextEdit}; - -pub struct AccountLoginView<'a> { - manager: &'a mut LoginManager, - generate_y_intercept: Option<f32>, -} - -impl<'a> View for AccountLoginView<'a> { - fn ui(&mut self, ui: &mut egui::Ui) { - let is_mobile = ui::is_mobile(ui.ctx()); - if let Some(key) = self.manager.check_for_successful_login() { - // TODO: route to "home" - println!("successful login with key: {:?}", key); - return if is_mobile { - // route to "home" on mobile - } else { - // route to "home" on desktop - }; - } - if is_mobile { - self.show_mobile(ui); - } else { - self.show(ui); - } - } -} - -impl<'a> AccountLoginView<'a> { - pub fn new(manager: &'a mut LoginManager) -> Self { - AccountLoginView { - manager, - generate_y_intercept: None, - } - } - - fn show(&mut self, ui: &mut egui::Ui) -> egui::Response { - let screen_width = ui.ctx().screen_rect().max.x; - let screen_height = ui.ctx().screen_rect().max.y; - - 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), - ), - }; - - let top_background_color = ui.visuals().noninteractive().bg_fill; - ui.painter_at(top_rect) - .with_layer_id(LayerId::background()) - .rect_filled(top_rect, Rounding::ZERO, top_background_color); - - egui::CentralPanel::default() - .show(ui.ctx(), |_ui: &mut egui::Ui| {}) - .response - } - - fn mobile_ui(&mut self, ui: &mut egui::Ui) -> egui::Response { - ui.vertical(|ui| { - ui.vertical_centered(|ui| { - ui.add(logo_unformatted().max_width(256.0)); - ui.add_space(64.0); - ui.label(login_info_text()); - ui.add_space(32.0); - ui.label(login_title_text()); - }); - - ui.horizontal(|ui| { - ui.label(login_textedit_info_text()); - }); - - ui.vertical_centered_justified(|ui| { - ui.add(login_textedit(self.manager)); - - self.loading_and_error(ui); - - if ui.add(login_button()).clicked() { - self.manager.apply_login(); - } - }); - - ui.horizontal(|ui| { - ui.label( - RichText::new("New to Nostr?") - .color(ui.style().visuals.noninteractive().fg_stroke.color) - .text_style(NotedeckTextStyle::Body.text_style()), - ); - - if ui - .add(Button::new(RichText::new("Create Account")).frame(false)) - .clicked() - { - // TODO: navigate to 'create account' screen - } - }); - }) - .response - } - - pub fn show_mobile(&mut self, ui: &mut egui::Ui) -> egui::Response { - egui::CentralPanel::default() - .show(ui.ctx(), |_| { - Window::new("Login") - .movable(true) - .constrain(true) - .collapsible(false) - .drag_to_scroll(false) - .title_bar(false) - .resizable(false) - .anchor(Align2::CENTER_CENTER, [0.0, 0.0]) - .frame(Frame::central_panel(&ui.ctx().style())) - .max_width(ui.ctx().screen_rect().width() - 32.0) // margin - .show(ui.ctx(), |ui| self.mobile_ui(ui)); - }) - .response - } - - fn window(&mut self, ui: &mut Ui, top_panel_height: f32) { - let needed_height_over_top = (ui.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::window(ui.style()).inner_margin(Margin::ZERO)) - .show(ui.ctx(), |ui| { - ui.vertical_centered(|ui| { - ui.add_space(40.0); - - ui.label(login_title_text()); - - ui.add_space(16f32); - - ui.label(login_window_info_text(ui)); - - 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(ui.style().noninteractive().bg_fill) // TODO: gradient - .rounding(ui.style().visuals.window_rounding) - .stroke(ui.style().noninteractive().bg_stroke) - .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| { - ui.add(logo_unformatted().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(login_info_text()); - }); - } - - fn login_form(&mut self, ui: &mut egui::Ui) { - ui.vertical_centered_justified(|ui| { - ui.horizontal(|ui| { - ui.label(login_textedit_info_text()); - }); - - ui.add_space(8f32); - - ui.add(login_textedit(self.manager).min_size(Vec2::new(440.0, 40.0))); - - self.loading_and_error(ui); - - let login_button = login_button().min_size(Vec2::new(442.0, 40.0)); - - if ui.add(login_button).clicked() { - self.manager.apply_login() - } - }); - } - - fn loading_and_error(&mut self, ui: &mut egui::Ui) { - ui.add_space(8.0); - - ui.vertical_centered(|ui| { - if self.manager.is_awaiting_network() { - ui.add(egui::Spinner::new()); - } - }); - - if let Some(err) = self.manager.check_for_error() { - show_error(ui, err); - } - - ui.add_space(8.0); - } - - fn generate_group(&mut self, ui: &mut egui::Ui) { - ui.horizontal(|ui| { - ui.label( - RichText::new("New in nostr?").text_style(NotedeckTextStyle::Heading3.text_style()), - ); - - ui.label( - RichText::new(" — we got you!") - .text_style(NotedeckTextStyle::Heading3.text_style()) - .color(ui.visuals().noninteractive().fg_stroke.color), - ); - }); - - ui.add_space(6.0); - - ui.horizontal(|ui| { - ui.label(generate_info_text().color(ui.visuals().noninteractive().fg_stroke.color)); - }); - - ui.add_space(16.0); - - let generate_button = generate_keys_button().min_size(Vec2::new(442.0, 40.0)); - if ui.add(generate_button).clicked() { - // TODO: keygen - } - } -} - -fn show_error(ui: &mut egui::Ui, err: &LoginError) { - ui.horizontal(|ui| { - let error_label = match err { - LoginError::InvalidKey => { - egui::Label::new(RichText::new("Invalid key.").color(ui.visuals().error_fg_color)) - } - LoginError::Nip05Failed(e) => { - egui::Label::new(RichText::new(e).color(ui.visuals().error_fg_color)) - } - }; - ui.add(error_label.truncate(true)); - }); -} - -fn login_title_text() -> RichText { - RichText::new("Login") - .text_style(NotedeckTextStyle::Heading2.text_style()) - .strong() -} - -fn login_info_text() -> RichText { - RichText::new("The best alternative to tweetDeck built in nostr protocol") - .text_style(NotedeckTextStyle::Heading3.text_style()) -} - -fn login_window_info_text(ui: &Ui) -> RichText { - RichText::new("Enter your private key to start using Notedeck") - .text_style(NotedeckTextStyle::Body.text_style()) - .color(ui.visuals().noninteractive().fg_stroke.color) -} - -fn login_textedit_info_text() -> RichText { - RichText::new("Enter your key") - .strong() - .text_style(NotedeckTextStyle::Body.text_style()) -} - -fn logo_unformatted() -> Image<'static> { - let logo_gradient_data = egui::include_image!("../assets/Logo-Gradient-2x.png"); - return egui::Image::new(logo_gradient_data); -} - -fn generate_info_text() -> RichText { - RichText::new("Quickly generate your keys. Make sure you save them safely.") - .text_style(NotedeckTextStyle::Body.text_style()) -} - -fn generate_keys_button() -> Button<'static> { - Button::new(RichText::new("Generate keys").text_style(NotedeckTextStyle::Body.text_style())) -} - -fn login_button() -> Button<'static> { - Button::new( - RichText::new("Login now — let's do this!") - .text_style(NotedeckTextStyle::Body.text_style()) - .strong(), - ) - .fill(Color32::from_rgb(0xF8, 0x69, 0xB6)) // TODO: gradient - .min_size(Vec2::new(0.0, 40.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 { - manager: LoginManager, -} - -impl View for AccountLoginPreview { - fn ui(&mut self, ui: &mut egui::Ui) { - AccountLoginView::new(&mut self.manager).ui(ui); - } -} - -impl<'a> Preview for AccountLoginView<'a> { - type Prev = AccountLoginPreview; - - fn preview() -> Self::Prev { - let manager = LoginManager::new(); - AccountLoginPreview { manager } - } -} diff --git a/src/lib.rs b/src/lib.rs @@ -4,7 +4,6 @@ mod error; //mod note; //mod block; mod abbrev; -pub mod account_login_view; pub mod app_creation; mod app_style; mod colors; diff --git a/src/ui/account_login_view.rs b/src/ui/account_login_view.rs @@ -0,0 +1,380 @@ +use crate::app_style::NotedeckTextStyle; +use crate::key_parsing::LoginError; +use crate::login_manager::LoginManager; +use crate::ui; +use crate::ui::{Preview, View}; +use egui::{ + Align, Align2, Button, Color32, Frame, Id, LayerId, Margin, Pos2, Rect, RichText, Rounding, Ui, + Vec2, Window, +}; +use egui::{Image, TextEdit}; + +pub struct AccountLoginView<'a> { + manager: &'a mut LoginManager, + generate_y_intercept: Option<f32>, +} + +impl<'a> View for AccountLoginView<'a> { + fn ui(&mut self, ui: &mut egui::Ui) { + let is_mobile = ui::is_mobile(ui.ctx()); + if let Some(key) = self.manager.check_for_successful_login() { + // TODO: route to "home" + println!("successful login with key: {:?}", key); + /* + return if is_mobile { + // route to "home" on mobile + } else { + // route to "home" on desktop + }; + */ + } + if is_mobile { + self.show_mobile(ui); + } else { + self.show(ui); + } + } +} + +impl<'a> AccountLoginView<'a> { + pub fn new(manager: &'a mut LoginManager) -> Self { + AccountLoginView { + manager, + generate_y_intercept: None, + } + } + + fn show(&mut self, ui: &mut egui::Ui) -> egui::Response { + let screen_width = ui.ctx().screen_rect().max.x; + let screen_height = ui.ctx().screen_rect().max.y; + + 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), + ), + }; + + let top_background_color = ui.visuals().noninteractive().bg_fill; + ui.painter_at(top_rect) + .with_layer_id(LayerId::background()) + .rect_filled(top_rect, Rounding::ZERO, top_background_color); + + egui::CentralPanel::default() + .show(ui.ctx(), |_ui: &mut egui::Ui| {}) + .response + } + + fn mobile_ui(&mut self, ui: &mut egui::Ui) -> egui::Response { + ui.vertical(|ui| { + ui.vertical_centered(|ui| { + ui.add(logo_unformatted().max_width(256.0)); + ui.add_space(64.0); + ui.label(login_info_text()); + ui.add_space(32.0); + ui.label(login_title_text()); + }); + + ui.horizontal(|ui| { + ui.label(login_textedit_info_text()); + }); + + ui.vertical_centered_justified(|ui| { + ui.add(login_textedit(self.manager)); + + self.loading_and_error(ui); + + if ui.add(login_button()).clicked() { + self.manager.apply_login(); + } + }); + + ui.horizontal(|ui| { + ui.label( + RichText::new("New to Nostr?") + .color(ui.style().visuals.noninteractive().fg_stroke.color) + .text_style(NotedeckTextStyle::Body.text_style()), + ); + + if ui + .add(Button::new(RichText::new("Create Account")).frame(false)) + .clicked() + { + // TODO: navigate to 'create account' screen + } + }); + }) + .response + } + + pub fn show_mobile(&mut self, ui: &mut egui::Ui) -> egui::Response { + egui::CentralPanel::default() + .show(ui.ctx(), |_| { + Window::new("Login") + .movable(true) + .constrain(true) + .collapsible(false) + .drag_to_scroll(false) + .title_bar(false) + .resizable(false) + .anchor(Align2::CENTER_CENTER, [0.0, 0.0]) + .frame(Frame::central_panel(&ui.ctx().style())) + .max_width(ui.ctx().screen_rect().width() - 32.0) // margin + .show(ui.ctx(), |ui| self.mobile_ui(ui)); + }) + .response + } + + fn window(&mut self, ui: &mut Ui, top_panel_height: f32) { + let needed_height_over_top = (ui.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::window(ui.style()).inner_margin(Margin::ZERO)) + .show(ui.ctx(), |ui| { + ui.vertical_centered(|ui| { + ui.add_space(40.0); + + ui.label(login_title_text()); + + ui.add_space(16f32); + + ui.label(login_window_info_text(ui)); + + 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(ui.style().noninteractive().bg_fill) // TODO: gradient + .rounding(ui.style().visuals.window_rounding) + .stroke(ui.style().noninteractive().bg_stroke) + .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| { + ui.add(logo_unformatted().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(login_info_text()); + }); + } + + fn login_form(&mut self, ui: &mut egui::Ui) { + ui.vertical_centered_justified(|ui| { + ui.horizontal(|ui| { + ui.label(login_textedit_info_text()); + }); + + ui.add_space(8f32); + + ui.add(login_textedit(self.manager).min_size(Vec2::new(440.0, 40.0))); + + self.loading_and_error(ui); + + let login_button = login_button().min_size(Vec2::new(442.0, 40.0)); + + if ui.add(login_button).clicked() { + self.manager.apply_login() + } + }); + } + + fn loading_and_error(&mut self, ui: &mut egui::Ui) { + ui.add_space(8.0); + + ui.vertical_centered(|ui| { + if self.manager.is_awaiting_network() { + ui.add(egui::Spinner::new()); + } + }); + + if let Some(err) = self.manager.check_for_error() { + show_error(ui, err); + } + + ui.add_space(8.0); + } + + fn generate_group(&mut self, ui: &mut egui::Ui) { + ui.horizontal(|ui| { + ui.label( + RichText::new("New in nostr?").text_style(NotedeckTextStyle::Heading3.text_style()), + ); + + ui.label( + RichText::new(" — we got you!") + .text_style(NotedeckTextStyle::Heading3.text_style()) + .color(ui.visuals().noninteractive().fg_stroke.color), + ); + }); + + ui.add_space(6.0); + + ui.horizontal(|ui| { + ui.label(generate_info_text().color(ui.visuals().noninteractive().fg_stroke.color)); + }); + + ui.add_space(16.0); + + let generate_button = generate_keys_button().min_size(Vec2::new(442.0, 40.0)); + if ui.add(generate_button).clicked() { + // TODO: keygen + } + } +} + +fn show_error(ui: &mut egui::Ui, err: &LoginError) { + ui.horizontal(|ui| { + let error_label = match err { + LoginError::InvalidKey => { + egui::Label::new(RichText::new("Invalid key.").color(ui.visuals().error_fg_color)) + } + LoginError::Nip05Failed(e) => { + egui::Label::new(RichText::new(e).color(ui.visuals().error_fg_color)) + } + }; + ui.add(error_label.truncate(true)); + }); +} + +fn login_title_text() -> RichText { + RichText::new("Login") + .text_style(NotedeckTextStyle::Heading2.text_style()) + .strong() +} + +fn login_info_text() -> RichText { + RichText::new("The best alternative to tweetDeck built in nostr protocol") + .text_style(NotedeckTextStyle::Heading3.text_style()) +} + +fn login_window_info_text(ui: &Ui) -> RichText { + RichText::new("Enter your private key to start using Notedeck") + .text_style(NotedeckTextStyle::Body.text_style()) + .color(ui.visuals().noninteractive().fg_stroke.color) +} + +fn login_textedit_info_text() -> RichText { + RichText::new("Enter your key") + .strong() + .text_style(NotedeckTextStyle::Body.text_style()) +} + +fn logo_unformatted() -> Image<'static> { + let logo_gradient_data = egui::include_image!("../../assets/Logo-Gradient-2x.png"); + return egui::Image::new(logo_gradient_data); +} + +fn generate_info_text() -> RichText { + RichText::new("Quickly generate your keys. Make sure you save them safely.") + .text_style(NotedeckTextStyle::Body.text_style()) +} + +fn generate_keys_button() -> Button<'static> { + Button::new(RichText::new("Generate keys").text_style(NotedeckTextStyle::Body.text_style())) +} + +fn login_button() -> Button<'static> { + Button::new( + RichText::new("Login now — let's do this!") + .text_style(NotedeckTextStyle::Body.text_style()) + .strong(), + ) + .fill(Color32::from_rgb(0xF8, 0x69, 0xB6)) // TODO: gradient + .min_size(Vec2::new(0.0, 40.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 { + manager: LoginManager, +} + +impl View for AccountLoginPreview { + fn ui(&mut self, ui: &mut egui::Ui) { + AccountLoginView::new(&mut self.manager).ui(ui); + } +} + +impl<'a> Preview for AccountLoginView<'a> { + type Prev = AccountLoginPreview; + + fn preview() -> Self::Prev { + let manager = LoginManager::new(); + AccountLoginPreview { manager } + } +} diff --git a/src/ui/mod.rs b/src/ui/mod.rs @@ -1,3 +1,4 @@ +pub mod account_login_view; pub mod anim; pub mod mention; pub mod note; diff --git a/src/ui_preview/main.rs b/src/ui_preview/main.rs @@ -1,7 +1,7 @@ -use notedeck::account_login_view::AccountLoginView; use notedeck::app_creation::{ generate_mobile_emulator_native_options, generate_native_options, setup_cc, }; +use notedeck::ui::account_login_view::AccountLoginView; use notedeck::ui::{Preview, PreviewApp, ProfilePic, ProfilePreview, RelayView}; use std::env;