notedeck

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

commit 6afb618089635c01ee35df204b0c273724c8ed39
parent ac0821db7955947b421a8aaf6b21202410415091
Author: kernelkind <kernelkind@gmail.com>
Date:   Sun, 23 Jun 2024 19:46:58 -0400

reintroduce account management

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

Diffstat:
Msrc/app.rs | 14+++++++++-----
Asrc/fixed_window.rs | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/lib.rs | 1+
Msrc/route.rs | 24+++++++++++++++++++++++-
Msrc/ui/account_management.rs | 30+++++++++++++++++++++---------
Msrc/ui/account_switcher.rs | 9+++++++--
Asrc/ui/global_popup.rs | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/ui/mod.rs | 2++
Msrc/ui/side_panel.rs | 7+++----
9 files changed, 181 insertions(+), 21 deletions(-)

diff --git a/src/app.rs b/src/app.rs @@ -10,7 +10,7 @@ use crate::relay_pool_manager::RelayPoolManager; use crate::route::Route; use crate::timeline; use crate::timeline::{MergeKind, NoteRef, Timeline, ViewFilter}; -use crate::ui::{self, AccountSelectionWidget}; +use crate::ui::{self, AccountSelectionWidget, DesktopGlobalPopup}; use crate::ui::{DesktopSidePanel, RelayView, View}; use crate::Result; use egui_nav::{Nav, NavAction}; @@ -46,7 +46,7 @@ pub struct Damus { is_mobile: bool, /// global navigation for account management popups, etc. - //nav: Vec<Route>, + pub global_nav: Vec<Route>, pub textmode: bool, pub drafts: HashMap<enostr::NoteId, Draft>, @@ -59,6 +59,7 @@ pub struct Damus { frame_history: crate::frame_history::FrameHistory, pub show_account_switcher: bool, + pub show_global_popup: bool, } fn relay_setup(pool: &mut RelayPool, ctx: &egui::Context) { @@ -728,6 +729,8 @@ impl Damus { //compose: "".to_string(), frame_history: FrameHistory::default(), show_account_switcher: false, + show_global_popup: false, + global_nav: Vec::new(), } } @@ -755,6 +758,8 @@ impl Damus { account_manager: AccountManager::new(None, crate::key_storage::KeyStorage::None), frame_history: FrameHistory::default(), show_account_switcher: false, + show_global_popup: true, + global_nav: Vec::new(), } } @@ -990,9 +995,8 @@ fn render_damus_desktop(ctx: &egui::Context, app: &mut Damus) { main_panel(&ctx.style(), app.is_mobile()).show(ctx, |ui| { ui.spacing_mut().item_spacing.x = 0.0; - if app.show_account_switcher { - AccountSelectionWidget::ui(app, ui); - } + AccountSelectionWidget::ui(app, ui); + DesktopGlobalPopup::show(app.global_nav.clone(), app, ui); if need_scroll { egui::ScrollArea::horizontal().show(ui, |ui| { timelines_view(ui, panel_sizes, app, app.timelines.len()); diff --git a/src/fixed_window.rs b/src/fixed_window.rs @@ -0,0 +1,62 @@ +use egui::{Rect, Response, RichText, Sense, Window}; + +pub struct FixedWindow { + title: Option<RichText>, +} + +#[derive(PartialEq)] +pub enum FixedWindowResponse { + Opened, + Closed, +} + +impl FixedWindow { + #[allow(dead_code)] + pub fn new() -> Self { + Self { title: None } + } + + pub fn maybe_with_title(maybe_title: Option<RichText>) -> Self { + Self { title: maybe_title } + } + + #[allow(dead_code)] + pub fn with_title(mut self, title: RichText) -> Self { + self.title = Some(title); + self + } + + pub fn show( + self, + ui: &mut egui::Ui, + rect: Rect, + add_contents: impl FnOnce(&mut egui::Ui) -> Response, + ) -> FixedWindowResponse { + let mut is_open = true; + + let use_title_bar = self.title.is_some(); + let title = if let Some(title) = self.title { + title + } else { + RichText::new("") + }; + + Window::new(title) + .open(&mut is_open) + .fixed_rect(rect) + .collapsible(false) + .movable(false) + .resizable(false) + .title_bar(use_title_bar) + .show(ui.ctx(), |ui| { + let resp = add_contents(ui); + ui.allocate_rect(resp.rect, Sense::hover()) + }); + + if !is_open { + FixedWindowResponse::Closed + } else { + FixedWindowResponse::Opened + } + } +} diff --git a/src/lib.rs b/src/lib.rs @@ -10,6 +10,7 @@ mod app_style; mod colors; mod draft; mod filter; +mod fixed_window; mod fonts; mod frame_history; mod images; diff --git a/src/route.rs b/src/route.rs @@ -1,5 +1,8 @@ +use egui::{Response, RichText}; use enostr::NoteId; -use std::fmt; +use std::fmt::{self}; + +use crate::{ui::AccountManagementView, Damus}; /// App routing. These describe different places you can go inside Notedeck. #[derive(Clone, Debug)] @@ -22,3 +25,22 @@ impl fmt::Display for Route { } } } + +impl Route { + pub fn show_global_popup(&self, app: &mut Damus, ui: &mut egui::Ui) -> Option<Response> { + match self { + Route::ManageAccount => AccountManagementView::ui(app, ui), + _ => None, + } + } + + pub fn title(&self) -> RichText { + match self { + Route::ManageAccount => RichText::new("Manage Account").size(24.0), + Route::Thread(_) => RichText::new("Thread"), + Route::Reply(_) => RichText::new("Reply"), + Route::Relays => RichText::new("Relays"), + Route::Timeline(_) => RichText::new("Timeline"), + } + } +} diff --git a/src/ui/account_management.rs b/src/ui/account_management.rs @@ -5,7 +5,7 @@ use crate::{ ui::{profile_preview_controller, Preview, PreviewConfig, View}, Damus, }; -use egui::{Align, Button, Frame, Image, Layout, RichText, ScrollArea, Vec2}; +use egui::{Align, Button, Frame, Image, Layout, Response, RichText, ScrollArea, Vec2}; use super::profile::preview::SimpleProfilePreview; use super::profile::ProfilePreviewOp; @@ -13,14 +13,26 @@ use super::profile::ProfilePreviewOp; pub struct AccountManagementView {} impl AccountManagementView { - fn show(app: &mut Damus, ui: &mut egui::Ui) { - Frame::none().outer_margin(24.0).show(ui, |ui| { - Self::top_section_buttons_widget(ui); - ui.add_space(8.0); - scroll_area().show(ui, |ui| { - Self::show_accounts(app, ui); - }); - }); + pub fn ui(app: &mut Damus, ui: &mut egui::Ui) -> Option<Response> { + if app.is_mobile() { + AccountManagementView::show_mobile(app, ui); + None + } else { + Some(AccountManagementView::show(app, ui)) + } + } + + fn show(app: &mut Damus, ui: &mut egui::Ui) -> Response { + Frame::none() + .outer_margin(24.0) + .show(ui, |ui| { + Self::top_section_buttons_widget(ui); + ui.add_space(8.0); + scroll_area().show(ui, |ui| { + Self::show_accounts(app, ui); + }); + }) + .response } fn show_accounts(app: &mut Damus, ui: &mut egui::Ui) { diff --git a/src/ui/account_switcher.rs b/src/ui/account_switcher.rs @@ -1,5 +1,5 @@ use crate::{ - account_manager::UserAccount, colors::PINK, profile::DisplayName, + account_manager::UserAccount, colors::PINK, profile::DisplayName, route::Route, ui::profile_preview_controller, Damus, Result, }; @@ -27,6 +27,10 @@ struct AccountSelectResponse { impl AccountSelectionWidget { pub fn ui(app: &mut Damus, ui: &mut egui::Ui) { + if !app.show_account_switcher { + return; + } + if app.is_mobile() { Self::show_mobile(ui); } else { @@ -54,7 +58,8 @@ impl AccountSelectionWidget { } AccountSelectAction::OpenAccountManagement => { app.show_account_switcher = false; - // TODO: push account management to global popup router + app.global_nav.push(Route::ManageAccount); + app.show_global_popup = true; } } } diff --git a/src/ui/global_popup.rs b/src/ui/global_popup.rs @@ -0,0 +1,53 @@ +use std::{cell::RefCell, rc::Rc}; + +use egui::Sense; +use egui_nav::{Nav, NavAction}; + +use crate::{ + fixed_window::{FixedWindow, FixedWindowResponse}, + route::Route, + Damus, +}; + +static MARGIN: f32 = 100.0; + +pub struct DesktopGlobalPopup {} + +impl DesktopGlobalPopup { + pub fn show(routes: Vec<Route>, app: &mut Damus, ui: &mut egui::Ui) { + if routes.is_empty() || !app.show_global_popup { + return; + } + + let rect = ui.ctx().screen_rect().shrink(MARGIN); + let title = if let Some(first) = routes.first() { + // TODO(kernelkind): not a great way of getting the title of the routes 'grouping' + Some(first.title()) + } else { + None + }; + + let app_ctx = Rc::new(RefCell::new(app)); + + let resp = FixedWindow::maybe_with_title(title).show(ui, rect, |ui| { + let nav_response = Nav::new(routes).navigating(false).show(ui, |ui, nav| { + if let Some(resp) = nav.top().show_global_popup(&mut app_ctx.borrow_mut(), ui) { + ui.allocate_rect(resp.rect, Sense::hover()) + } else { + ui.label("") // TODO(kernelkind): not a great practice + } + }); + + if let Some(NavAction::Returned) = nav_response.action { + app_ctx.borrow_mut().global_nav.pop(); + } + + nav_response.inner + }); + + if resp == FixedWindowResponse::Closed { + app_ctx.borrow_mut().global_nav.pop(); + app_ctx.borrow_mut().show_global_popup = false; + } + } +} diff --git a/src/ui/mod.rs b/src/ui/mod.rs @@ -2,6 +2,7 @@ pub mod account_login_view; pub mod account_management; pub mod account_switcher; pub mod anim; +pub mod global_popup; pub mod mention; pub mod note; pub mod preview; @@ -12,6 +13,7 @@ pub mod username; pub use account_management::AccountManagementView; pub use account_switcher::AccountSelectionWidget; +pub use global_popup::DesktopGlobalPopup; pub use mention::Mention; pub use note::{BarAction, Note, NoteResponse, PostView}; pub use preview::{Preview, PreviewApp, PreviewConfig}; diff --git a/src/ui/side_panel.rs b/src/ui/side_panel.rs @@ -127,7 +127,7 @@ mod preview { use crate::{ test_data, - ui::{AccountSelectionWidget, Preview, PreviewConfig}, + ui::{AccountSelectionWidget, DesktopGlobalPopup, Preview, PreviewConfig}, }; use super::*; @@ -157,9 +157,8 @@ mod preview { }); }); - if self.app.show_account_switcher { - AccountSelectionWidget::ui(&mut self.app, ui); - } + AccountSelectionWidget::ui(&mut self.app, ui); + DesktopGlobalPopup::show(self.app.global_nav.clone(), &mut self.app, ui); } }