mentions_picker.rs (4621B)
1 use egui::{vec2, Layout, ScrollArea, UiBuilder, Vec2b}; 2 use nostrdb::{Ndb, Transaction}; 3 use notedeck::{DragResponse, Images, Localization, MediaJobSender}; 4 use notedeck_ui::{profile_row, widgets::x_button, ProfileSearchResult}; 5 use tracing::error; 6 7 /// Displays user profiles for the user to pick from. 8 /// Useful for manually typing a username and selecting the profile desired 9 pub struct MentionPickerView<'a> { 10 ndb: &'a Ndb, 11 txn: &'a Transaction, 12 img_cache: &'a mut Images, 13 results: &'a [ProfileSearchResult], 14 jobs: &'a MediaJobSender, 15 i18n: &'a mut Localization, 16 } 17 18 pub enum MentionPickerResponse { 19 SelectResult(Option<usize>), 20 DeleteMention, 21 } 22 23 impl<'a> MentionPickerView<'a> { 24 pub fn new( 25 img_cache: &'a mut Images, 26 ndb: &'a Ndb, 27 txn: &'a Transaction, 28 results: &'a [ProfileSearchResult], 29 jobs: &'a MediaJobSender, 30 i18n: &'a mut Localization, 31 ) -> Self { 32 Self { 33 ndb, 34 txn, 35 img_cache, 36 results, 37 jobs, 38 i18n, 39 } 40 } 41 42 fn show(&mut self, ui: &mut egui::Ui) -> MentionPickerResponse { 43 let mut selection = None; 44 ui.vertical(|ui| { 45 for (i, res) in self.results.iter().enumerate() { 46 let profile = match self.ndb.get_profile_by_pubkey(self.txn, &res.pk) { 47 Ok(rec) => rec, 48 Err(e) => { 49 error!("Error fetching profile for pubkey {:?}: {e}", res.pk); 50 return; 51 } 52 }; 53 54 if profile_row( 55 ui, 56 Some(&profile), 57 res.is_contact, 58 self.img_cache, 59 self.jobs, 60 self.i18n, 61 ) { 62 selection = Some(i) 63 } 64 } 65 }); 66 67 MentionPickerResponse::SelectResult(selection) 68 } 69 70 pub fn show_in_rect( 71 &mut self, 72 rect: egui::Rect, 73 ui: &mut egui::Ui, 74 ) -> DragResponse<MentionPickerResponse> { 75 let widget_id = ui.id().with("mention_results"); 76 let area_resp = egui::Area::new(widget_id) 77 .order(egui::Order::Foreground) 78 .fixed_pos(rect.left_top()) 79 .constrain_to(rect) 80 .show(ui.ctx(), |ui| { 81 let inner_margin_size = 8.0; 82 egui::Frame::NONE 83 .fill(ui.visuals().panel_fill) 84 .show(ui, |ui| { 85 ui.allocate_space(vec2(ui.available_width(), inner_margin_size)); 86 let close_button_resp = { 87 let close_button_size = 16.0; 88 let width = rect.width() - (2.0 * inner_margin_size); 89 let (close_section_rect, _) = ui.allocate_exact_size( 90 vec2(width, close_button_size), 91 egui::Sense::hover(), 92 ); 93 let (_, button_rect) = close_section_rect.split_left_right_at_x( 94 close_section_rect.right() - close_button_size, 95 ); 96 let button_resp = ui.allocate_rect(button_rect, egui::Sense::click()); 97 ui.allocate_new_ui( 98 UiBuilder::new() 99 .max_rect(close_section_rect) 100 .layout(Layout::right_to_left(egui::Align::Center)), 101 |ui| ui.add(x_button(button_resp.rect)).clicked(), 102 ) 103 .inner 104 }; 105 106 ui.allocate_space(vec2(ui.available_width(), inner_margin_size)); 107 108 let scroll_resp = ScrollArea::vertical() 109 .max_width(rect.width()) 110 .auto_shrink(Vec2b::FALSE) 111 .show(ui, |ui| Some(self.show(ui))); 112 ui.advance_cursor_after_rect(rect); 113 114 DragResponse::scroll(scroll_resp).map_output(|o| { 115 if close_button_resp { 116 MentionPickerResponse::DeleteMention 117 } else { 118 o 119 } 120 }) 121 }) 122 .inner 123 }); 124 125 area_resp.inner 126 } 127 }