contacts_list.rs (3688B)
1 use std::collections::HashSet; 2 3 use crate::ProfilePic; 4 use egui::{RichText, Sense}; 5 use enostr::Pubkey; 6 use nostrdb::{Ndb, Transaction}; 7 use notedeck::{ 8 name::get_display_name, profile::get_profile_url, DragResponse, Images, MediaJobSender, 9 }; 10 11 pub struct ContactsListView<'a, 'txn> { 12 contacts: ContactsCollection<'a>, 13 jobs: &'a MediaJobSender, 14 ndb: &'a Ndb, 15 img_cache: &'a mut Images, 16 txn: &'txn Transaction, 17 } 18 19 #[derive(Clone)] 20 pub enum ContactsListAction { 21 Select(Pubkey), 22 } 23 24 pub enum ContactsCollection<'a> { 25 Vec(&'a Vec<Pubkey>), 26 Set(&'a HashSet<Pubkey>), 27 } 28 29 pub enum ContactsIter<'a> { 30 Vec(std::slice::Iter<'a, Pubkey>), 31 Set(std::collections::hash_set::Iter<'a, Pubkey>), 32 } 33 34 impl<'a> ContactsCollection<'a> { 35 pub fn iter(&'a self) -> ContactsIter<'a> { 36 match self { 37 ContactsCollection::Vec(v) => ContactsIter::Vec(v.iter()), 38 ContactsCollection::Set(s) => ContactsIter::Set(s.iter()), 39 } 40 } 41 } 42 43 impl<'a> Iterator for ContactsIter<'a> { 44 type Item = &'a Pubkey; 45 46 fn next(&mut self) -> Option<Self::Item> { 47 match self { 48 ContactsIter::Vec(iter) => iter.next().as_ref().copied(), 49 ContactsIter::Set(iter) => iter.next().as_ref().copied(), 50 } 51 } 52 } 53 54 impl<'a, 'txn> ContactsListView<'a, 'txn> { 55 pub fn new( 56 contacts: ContactsCollection<'a>, 57 jobs: &'a MediaJobSender, 58 ndb: &'a Ndb, 59 img_cache: &'a mut Images, 60 txn: &'txn Transaction, 61 ) -> Self { 62 ContactsListView { 63 contacts, 64 ndb, 65 img_cache, 66 txn, 67 jobs, 68 } 69 } 70 71 pub fn ui(&mut self, ui: &mut egui::Ui) -> DragResponse<ContactsListAction> { 72 let mut action = None; 73 74 egui::ScrollArea::vertical().show(ui, |ui| { 75 let clip_rect = ui.clip_rect(); 76 77 for contact_pubkey in self.contacts.iter() { 78 let (rect, resp) = 79 ui.allocate_exact_size(egui::vec2(ui.available_width(), 56.0), Sense::click()); 80 81 if !clip_rect.intersects(rect) { 82 continue; 83 } 84 85 let profile = self 86 .ndb 87 .get_profile_by_pubkey(self.txn, contact_pubkey.bytes()) 88 .ok(); 89 90 let display_name = get_display_name(profile.as_ref()); 91 let name_str = display_name.display_name.unwrap_or("Anonymous"); 92 let profile_url = get_profile_url(profile.as_ref()); 93 94 let resp = resp.on_hover_cursor(egui::CursorIcon::PointingHand); 95 96 if resp.hovered() { 97 ui.painter() 98 .rect_filled(rect, 0.0, ui.visuals().widgets.hovered.weak_bg_fill); 99 } 100 101 let mut child_ui = ui.new_child(egui::UiBuilder::new().max_rect(rect)); 102 child_ui.horizontal(|ui| { 103 ui.add_space(16.0); 104 105 ui.add(&mut ProfilePic::new(self.img_cache, self.jobs, profile_url).size(48.0)); 106 107 ui.add_space(12.0); 108 109 ui.add( 110 egui::Label::new( 111 RichText::new(name_str) 112 .size(16.0) 113 .color(ui.visuals().text_color()), 114 ) 115 .selectable(false), 116 ); 117 }); 118 119 if resp.clicked() { 120 action = Some(ContactsListAction::Select(*contact_pubkey)); 121 } 122 } 123 }); 124 125 DragResponse::output(action) 126 } 127 }