top_buttons.rs (4052B)
1 /// Top buttons UI for the Dave chat interface. 2 /// 3 /// Contains the profile picture button, settings button, and session list toggle 4 /// that appear at the top of the chat view. 5 use super::DaveAction; 6 use nostrdb::{Ndb, Transaction}; 7 use notedeck::{Accounts, AppContext, Images, MediaJobSender}; 8 use notedeck_ui::{app_images, ProfilePic}; 9 10 /// Result from rendering top buttons, includes action and layout info 11 pub struct TopButtonsResult { 12 pub action: Option<DaveAction>, 13 /// X position after the last button (for placing inline content) 14 pub right_edge_x: f32, 15 /// Y position of the button row 16 pub y: f32, 17 } 18 19 /// Render the top buttons UI (profile pic, settings, session list toggle). 20 /// `status_dot` optionally paints a colored dot on the hamburger icon to 21 /// indicate what kind of attention the session needs (blue = working, 22 /// yellow = needs input, red = error). 23 pub fn top_buttons_ui( 24 app_ctx: &mut AppContext, 25 ui: &mut egui::Ui, 26 status_dot: Option<egui::Color32>, 27 ) -> TopButtonsResult { 28 let mut action: Option<DaveAction> = None; 29 let mut rect = ui.available_rect_before_wrap(); 30 rect = rect.translate(egui::vec2(20.0, 20.0)); 31 rect.set_height(32.0); 32 rect.set_width(32.0); 33 34 // Show session list button on mobile/narrow screens 35 if notedeck::ui::is_narrow(ui.ctx()) { 36 let r = ui 37 .put(rect, egui::Button::new("\u{2630}").frame(false)) 38 .on_hover_text("Show chats") 39 .on_hover_cursor(egui::CursorIcon::PointingHand); 40 41 // Draw notification dot when something needs attention 42 if let Some(color) = status_dot { 43 let dot_center = rect.right_top() + egui::vec2(-2.0, 6.0); 44 ui.painter().circle_filled(dot_center, 4.0, color); 45 } 46 47 if r.clicked() { 48 action = Some(DaveAction::ShowSessionList); 49 } 50 51 rect = rect.translate(egui::vec2(30.0, 0.0)); 52 } 53 54 let txn = Transaction::new(app_ctx.ndb).unwrap(); 55 let r = ui 56 .put( 57 rect, 58 &mut pfp_button( 59 &txn, 60 app_ctx.accounts, 61 app_ctx.img_cache, 62 app_ctx.ndb, 63 app_ctx.media_jobs.sender(), 64 ), 65 ) 66 .on_hover_cursor(egui::CursorIcon::PointingHand); 67 68 if r.clicked() { 69 action = Some(DaveAction::ToggleChrome); 70 } 71 72 // Settings button 73 rect = rect.translate(egui::vec2(30.0, 0.0)); 74 let dark_mode = ui.visuals().dark_mode; 75 let r = ui 76 .put(rect, settings_button(dark_mode)) 77 .on_hover_cursor(egui::CursorIcon::PointingHand); 78 79 if r.clicked() { 80 action = Some(DaveAction::OpenSettings); 81 } 82 83 TopButtonsResult { 84 action, 85 right_edge_x: rect.right() + 8.0, 86 y: rect.min.y, 87 } 88 } 89 90 fn settings_button(dark_mode: bool) -> impl egui::Widget { 91 move |ui: &mut egui::Ui| { 92 let img_size = 24.0; 93 let max_size = 32.0; 94 95 let img = if dark_mode { 96 app_images::settings_dark_image() 97 } else { 98 app_images::settings_light_image() 99 } 100 .max_width(img_size); 101 102 let helper = notedeck_ui::anim::AnimationHelper::new( 103 ui, 104 "settings-button", 105 egui::vec2(max_size, max_size), 106 ); 107 108 let cur_img_size = helper.scale_1d_pos(img_size); 109 img.paint_at( 110 ui, 111 helper 112 .get_animation_rect() 113 .shrink((max_size - cur_img_size) / 2.0), 114 ); 115 116 helper.take_animation_response() 117 } 118 } 119 120 fn pfp_button<'me, 'a>( 121 txn: &'a Transaction, 122 accounts: &Accounts, 123 img_cache: &'me mut Images, 124 ndb: &Ndb, 125 jobs: &'me MediaJobSender, 126 ) -> ProfilePic<'me, 'a> { 127 let account = accounts.get_selected_account(); 128 let profile = ndb 129 .get_profile_by_pubkey(txn, account.key.pubkey.bytes()) 130 .ok(); 131 132 ProfilePic::from_profile_or_default(img_cache, jobs, profile.as_ref()) 133 .size(24.0) 134 .sense(egui::Sense::click()) 135 }