notedeck

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

account_switcher.rs (8826B)


      1 use crate::{
      2     account_manager::UserAccount, colors::PINK, profile::DisplayName, route::Route,
      3     ui::profile_preview_controller, Damus, Result,
      4 };
      5 
      6 use nostrdb::Ndb;
      7 
      8 use egui::{
      9     Align, Button, Color32, Frame, Id, Image, Layout, Margin, RichText, Rounding, ScrollArea,
     10     Sense, Vec2,
     11 };
     12 
     13 use super::profile::preview::SimpleProfilePreview;
     14 
     15 pub struct AccountSelectionWidget {}
     16 
     17 enum AccountSelectAction {
     18     RemoveAccount { _index: usize },
     19     SelectAccount { _index: usize },
     20     OpenAccountManagement,
     21 }
     22 
     23 #[derive(Default)]
     24 struct AccountSelectResponse {
     25     action: Option<AccountSelectAction>,
     26 }
     27 
     28 impl AccountSelectionWidget {
     29     pub fn ui(app: &mut Damus, ui: &mut egui::Ui) {
     30         if !app.show_account_switcher {
     31             return;
     32         }
     33 
     34         if app.is_mobile() {
     35             Self::show_mobile(ui);
     36         } else {
     37             account_switcher_window(&mut app.show_account_switcher.clone()).show(
     38                 ui.ctx(),
     39                 |ui: &mut egui::Ui| {
     40                     let (account_selection_response, response) = Self::show(app, ui);
     41                     if let Some(action) = account_selection_response.action {
     42                         Self::perform_action(app, action);
     43                     }
     44                     response
     45                 },
     46             );
     47         }
     48     }
     49 
     50     fn perform_action(app: &mut Damus, action: AccountSelectAction) {
     51         match action {
     52             AccountSelectAction::RemoveAccount { _index } => {
     53                 app.account_manager.remove_account(_index)
     54             }
     55             AccountSelectAction::SelectAccount { _index } => {
     56                 app.show_account_switcher = false;
     57                 app.account_manager.select_account(_index);
     58             }
     59             AccountSelectAction::OpenAccountManagement => {
     60                 app.show_account_switcher = false;
     61                 app.global_nav.push(Route::ManageAccount);
     62                 app.show_global_popup = true;
     63             }
     64         }
     65     }
     66 
     67     fn show(app: &mut Damus, ui: &mut egui::Ui) -> (AccountSelectResponse, egui::Response) {
     68         let mut res = AccountSelectResponse::default();
     69         let mut selected_index = app.account_manager.get_selected_account_index();
     70 
     71         let response = Frame::none()
     72             .outer_margin(8.0)
     73             .show(ui, |ui| {
     74                 res = top_section_widget(ui);
     75 
     76                 scroll_area().show(ui, |ui| {
     77                     if let Some(_index) = Self::show_accounts(app, ui) {
     78                         selected_index = Some(_index);
     79                         res.action = Some(AccountSelectAction::SelectAccount { _index });
     80                     }
     81                 });
     82                 ui.add_space(8.0);
     83                 ui.add(add_account_button());
     84 
     85                 if let Some(_index) = selected_index {
     86                     if let Some(account) = app.account_manager.get_account(_index) {
     87                         ui.add_space(8.0);
     88                         if Self::handle_sign_out(&app.ndb, ui, account) {
     89                             res.action = Some(AccountSelectAction::RemoveAccount { _index })
     90                         }
     91                     }
     92                 }
     93 
     94                 ui.add_space(8.0);
     95             })
     96             .response;
     97 
     98         (res, response)
     99     }
    100 
    101     fn handle_sign_out(ndb: &Ndb, ui: &mut egui::Ui, account: &UserAccount) -> bool {
    102         if let Ok(response) = Self::sign_out_button(ndb, ui, account) {
    103             response.clicked()
    104         } else {
    105             false
    106         }
    107     }
    108 
    109     fn show_mobile(ui: &mut egui::Ui) -> egui::Response {
    110         let _ = ui;
    111         todo!()
    112     }
    113 
    114     fn show_accounts(app: &mut Damus, ui: &mut egui::Ui) -> Option<usize> {
    115         profile_preview_controller::view_profile_previews(app, ui, account_switcher_card_ui)
    116     }
    117 
    118     fn sign_out_button(
    119         ndb: &Ndb,
    120         ui: &mut egui::Ui,
    121         account: &UserAccount,
    122     ) -> Result<egui::Response> {
    123         profile_preview_controller::show_with_nickname(
    124             ndb,
    125             ui,
    126             account.pubkey.bytes(),
    127             |ui: &mut egui::Ui, username: &DisplayName| {
    128                 let img_data = egui::include_image!("../../assets/icons/signout_icon_4x.png");
    129                 let img = Image::new(img_data).fit_to_exact_size(Vec2::new(16.0, 16.0));
    130                 let button = egui::Button::image_and_text(
    131                     img,
    132                     RichText::new(format!(" Sign out @{}", username.username()))
    133                         .color(PINK)
    134                         .size(16.0),
    135                 )
    136                 .frame(false);
    137 
    138                 ui.add(button)
    139             },
    140         )
    141     }
    142 }
    143 
    144 fn account_switcher_card_ui(
    145     ui: &mut egui::Ui,
    146     preview: SimpleProfilePreview,
    147     width: f32,
    148     is_selected: bool,
    149     index: usize,
    150 ) -> bool {
    151     let resp = ui.add_sized(Vec2::new(width, 50.0), |ui: &mut egui::Ui| {
    152         Frame::none()
    153             .show(ui, |ui| {
    154                 ui.add_space(8.0);
    155                 ui.horizontal(|ui| {
    156                     if is_selected {
    157                         Frame::none()
    158                             .rounding(Rounding::same(8.0))
    159                             .inner_margin(Margin::same(8.0))
    160                             .fill(Color32::from_rgb(0x45, 0x1B, 0x59))
    161                             .show(ui, |ui| {
    162                                 ui.add(preview);
    163                                 ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
    164                                     ui.add(selection_widget());
    165                                 });
    166                             });
    167                     } else {
    168                         ui.add_space(8.0);
    169                         ui.add(preview);
    170                     }
    171                 });
    172             })
    173             .response
    174     });
    175 
    176     ui.interact(resp.rect, Id::new(index), Sense::click())
    177         .clicked()
    178 }
    179 
    180 fn account_switcher_window(open: &'_ mut bool) -> egui::Window<'_> {
    181     egui::Window::new("account switcher")
    182         .title_bar(false)
    183         .collapsible(false)
    184         .anchor(egui::Align2::LEFT_BOTTOM, Vec2::new(4.0, -44.0))
    185         .fixed_size(Vec2::new(360.0, 406.0))
    186         .open(open)
    187         .movable(false)
    188 }
    189 
    190 fn selection_widget() -> impl egui::Widget {
    191     |ui: &mut egui::Ui| {
    192         let img_data: egui::ImageSource =
    193             egui::include_image!("../../assets/icons/select_icon_3x.png");
    194         let img = Image::new(img_data).max_size(Vec2::new(16.0, 16.0));
    195         ui.add(img)
    196     }
    197 }
    198 
    199 fn top_section_widget(ui: &mut egui::Ui) -> AccountSelectResponse {
    200     ui.horizontal(|ui| {
    201         let mut resp = AccountSelectResponse::default();
    202 
    203         ui.allocate_ui_with_layout(
    204             Vec2::new(ui.available_size_before_wrap().x, 32.0),
    205             Layout::left_to_right(egui::Align::Center),
    206             |ui| ui.add(account_switcher_title()),
    207         );
    208 
    209         ui.allocate_ui_with_layout(
    210             Vec2::new(ui.available_size_before_wrap().x, 32.0),
    211             Layout::right_to_left(egui::Align::Center),
    212             |ui| {
    213                 if ui.add(manage_accounts_button()).clicked() {
    214                     resp.action = Some(AccountSelectAction::OpenAccountManagement);
    215                 }
    216             },
    217         );
    218 
    219         resp
    220     })
    221     .inner
    222 }
    223 
    224 fn manage_accounts_button() -> egui::Button<'static> {
    225     Button::new(RichText::new("Manage").color(PINK).size(16.0)).frame(false)
    226 }
    227 
    228 fn account_switcher_title() -> impl egui::Widget {
    229     |ui: &mut egui::Ui| ui.label(RichText::new("Account switcher").size(20.0).strong())
    230 }
    231 
    232 fn scroll_area() -> ScrollArea {
    233     egui::ScrollArea::vertical()
    234         .scroll_bar_visibility(egui::scroll_area::ScrollBarVisibility::AlwaysHidden)
    235         .auto_shrink([false; 2])
    236 }
    237 
    238 fn add_account_button() -> egui::Button<'static> {
    239     let img_data = egui::include_image!("../../assets/icons/plus_icon_4x.png");
    240     let img = Image::new(img_data).fit_to_exact_size(Vec2::new(16.0, 16.0));
    241     Button::image_and_text(img, RichText::new(" Add account").size(16.0).color(PINK)).frame(false)
    242 }
    243 
    244 mod previews {
    245 
    246     use crate::{
    247         test_data,
    248         ui::{Preview, PreviewConfig, View},
    249         Damus,
    250     };
    251 
    252     use super::AccountSelectionWidget;
    253 
    254     pub struct AccountSelectionPreview {
    255         app: Damus,
    256     }
    257 
    258     impl AccountSelectionPreview {
    259         fn new(is_mobile: bool) -> Self {
    260             let app = test_data::test_app(is_mobile);
    261             AccountSelectionPreview { app }
    262         }
    263     }
    264 
    265     impl View for AccountSelectionPreview {
    266         fn ui(&mut self, ui: &mut egui::Ui) {
    267             AccountSelectionWidget::ui(&mut self.app, ui);
    268         }
    269     }
    270 
    271     impl Preview for AccountSelectionWidget {
    272         type Prev = AccountSelectionPreview;
    273 
    274         fn preview(cfg: PreviewConfig) -> Self::Prev {
    275             AccountSelectionPreview::new(cfg.is_mobile)
    276         }
    277     }
    278 }