use crate::{ account_manager::UserAccount, colors::PINK, profile::DisplayName, route::Route, ui::profile_preview_controller, Damus, Result, }; use nostrdb::Ndb; use egui::{ Align, Button, Color32, Frame, Id, Image, Layout, Margin, RichText, Rounding, ScrollArea, Sense, Vec2, }; use super::profile::preview::SimpleProfilePreview; pub struct AccountSelectionWidget {} enum AccountSelectAction { RemoveAccount { _index: usize }, SelectAccount { _index: usize }, OpenAccountManagement, } #[derive(Default)] struct AccountSelectResponse { action: Option, } 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 { account_switcher_window(&mut app.show_account_switcher.clone()).show( ui.ctx(), |ui: &mut egui::Ui| { let (account_selection_response, response) = Self::show(app, ui); if let Some(action) = account_selection_response.action { Self::perform_action(app, action); } response }, ); } } fn perform_action(app: &mut Damus, action: AccountSelectAction) { match action { AccountSelectAction::RemoveAccount { _index } => { app.account_manager.remove_account(_index) } AccountSelectAction::SelectAccount { _index } => { app.show_account_switcher = false; app.account_manager.select_account(_index); } AccountSelectAction::OpenAccountManagement => { app.show_account_switcher = false; app.global_nav.push(Route::ManageAccount); app.show_global_popup = true; } } } fn show(app: &mut Damus, ui: &mut egui::Ui) -> (AccountSelectResponse, egui::Response) { let mut res = AccountSelectResponse::default(); let mut selected_index = app.account_manager.get_selected_account_index(); let response = Frame::none() .outer_margin(8.0) .show(ui, |ui| { res = top_section_widget(ui); scroll_area().show(ui, |ui| { if let Some(_index) = Self::show_accounts(app, ui) { selected_index = Some(_index); res.action = Some(AccountSelectAction::SelectAccount { _index }); } }); ui.add_space(8.0); ui.add(add_account_button()); if let Some(_index) = selected_index { if let Some(account) = app.account_manager.get_account(_index) { ui.add_space(8.0); if Self::handle_sign_out(&app.ndb, ui, account) { res.action = Some(AccountSelectAction::RemoveAccount { _index }) } } } ui.add_space(8.0); }) .response; (res, response) } fn handle_sign_out(ndb: &Ndb, ui: &mut egui::Ui, account: &UserAccount) -> bool { if let Ok(response) = Self::sign_out_button(ndb, ui, account) { response.clicked() } else { false } } fn show_mobile(ui: &mut egui::Ui) -> egui::Response { let _ = ui; todo!() } fn show_accounts(app: &mut Damus, ui: &mut egui::Ui) -> Option { profile_preview_controller::view_profile_previews(app, ui, account_switcher_card_ui) } fn sign_out_button( ndb: &Ndb, ui: &mut egui::Ui, account: &UserAccount, ) -> Result { profile_preview_controller::show_with_nickname( ndb, ui, account.pubkey.bytes(), |ui: &mut egui::Ui, username: &DisplayName| { let img_data = egui::include_image!("../../assets/icons/signout_icon_4x.png"); let img = Image::new(img_data).fit_to_exact_size(Vec2::new(16.0, 16.0)); let button = egui::Button::image_and_text( img, RichText::new(format!(" Sign out @{}", username.username())) .color(PINK) .size(16.0), ) .frame(false); ui.add(button) }, ) } } fn account_switcher_card_ui( ui: &mut egui::Ui, preview: SimpleProfilePreview, width: f32, is_selected: bool, index: usize, ) -> bool { let resp = ui.add_sized(Vec2::new(width, 50.0), |ui: &mut egui::Ui| { Frame::none() .show(ui, |ui| { ui.add_space(8.0); ui.horizontal(|ui| { if is_selected { Frame::none() .rounding(Rounding::same(8.0)) .inner_margin(Margin::same(8.0)) .fill(Color32::from_rgb(0x45, 0x1B, 0x59)) .show(ui, |ui| { ui.add(preview); ui.with_layout(Layout::right_to_left(Align::Center), |ui| { ui.add(selection_widget()); }); }); } else { ui.add_space(8.0); ui.add(preview); } }); }) .response }); ui.interact(resp.rect, Id::new(index), Sense::click()) .clicked() } fn account_switcher_window(open: &'_ mut bool) -> egui::Window<'_> { egui::Window::new("account switcher") .title_bar(false) .collapsible(false) .anchor(egui::Align2::LEFT_BOTTOM, Vec2::new(4.0, -44.0)) .fixed_size(Vec2::new(360.0, 406.0)) .open(open) .movable(false) } fn selection_widget() -> impl egui::Widget { |ui: &mut egui::Ui| { let img_data: egui::ImageSource = egui::include_image!("../../assets/icons/select_icon_3x.png"); let img = Image::new(img_data).max_size(Vec2::new(16.0, 16.0)); ui.add(img) } } fn top_section_widget(ui: &mut egui::Ui) -> AccountSelectResponse { ui.horizontal(|ui| { let mut resp = AccountSelectResponse::default(); ui.allocate_ui_with_layout( Vec2::new(ui.available_size_before_wrap().x, 32.0), Layout::left_to_right(egui::Align::Center), |ui| ui.add(account_switcher_title()), ); ui.allocate_ui_with_layout( Vec2::new(ui.available_size_before_wrap().x, 32.0), Layout::right_to_left(egui::Align::Center), |ui| { if ui.add(manage_accounts_button()).clicked() { resp.action = Some(AccountSelectAction::OpenAccountManagement); } }, ); resp }) .inner } fn manage_accounts_button() -> egui::Button<'static> { Button::new(RichText::new("Manage").color(PINK).size(16.0)).frame(false) } fn account_switcher_title() -> impl egui::Widget { |ui: &mut egui::Ui| ui.label(RichText::new("Account switcher").size(20.0).strong()) } fn scroll_area() -> ScrollArea { egui::ScrollArea::vertical() .scroll_bar_visibility(egui::scroll_area::ScrollBarVisibility::AlwaysHidden) .auto_shrink([false; 2]) } fn add_account_button() -> egui::Button<'static> { let img_data = egui::include_image!("../../assets/icons/plus_icon_4x.png"); let img = Image::new(img_data).fit_to_exact_size(Vec2::new(16.0, 16.0)); Button::image_and_text(img, RichText::new(" Add account").size(16.0).color(PINK)).frame(false) } mod previews { use crate::{ test_data, ui::{Preview, PreviewConfig, View}, Damus, }; use super::AccountSelectionWidget; pub struct AccountSelectionPreview { app: Damus, } impl AccountSelectionPreview { fn new(is_mobile: bool) -> Self { let app = test_data::test_app(is_mobile); AccountSelectionPreview { app } } } impl View for AccountSelectionPreview { fn ui(&mut self, ui: &mut egui::Ui) { AccountSelectionWidget::ui(&mut self.app, ui); } } impl Preview for AccountSelectionWidget { type Prev = AccountSelectionPreview; fn preview(cfg: PreviewConfig) -> Self::Prev { AccountSelectionPreview::new(cfg.is_mobile) } } }