create_convo.rs (4791B)
1 use egui::{Label, RichText}; 2 use enostr::Pubkey; 3 use nostrdb::{Ndb, Transaction}; 4 use notedeck::{tr, ContactState, Images, Localization, MediaJobSender, NotedeckTextStyle}; 5 use notedeck_ui::{ 6 profile_row, search_input_box, search_profiles, ContactsListView, ProfileSearchResult, 7 }; 8 9 use crate::cache::CreateConvoState; 10 11 pub struct CreateConvoUi<'a> { 12 ndb: &'a Ndb, 13 jobs: &'a MediaJobSender, 14 img_cache: &'a mut Images, 15 contacts: &'a ContactState, 16 i18n: &'a mut Localization, 17 state: &'a mut CreateConvoState, 18 } 19 20 pub struct CreateConvoResponse { 21 pub recipient: Pubkey, 22 } 23 24 impl<'a> CreateConvoUi<'a> { 25 pub fn new( 26 ndb: &'a Ndb, 27 jobs: &'a MediaJobSender, 28 img_cache: &'a mut Images, 29 contacts: &'a ContactState, 30 i18n: &'a mut Localization, 31 state: &'a mut CreateConvoState, 32 ) -> Self { 33 Self { 34 ndb, 35 jobs, 36 img_cache, 37 contacts, 38 i18n, 39 state, 40 } 41 } 42 43 pub fn ui(&mut self, ui: &mut egui::Ui) -> Option<CreateConvoResponse> { 44 let txn = Transaction::new(self.ndb).expect("txn"); 45 46 // Search input 47 ui.add_space(8.0); 48 let hint = tr!( 49 self.i18n, 50 "Search profiles...", 51 "Placeholder for profile search input" 52 ); 53 ui.add(search_input_box(&mut self.state.query, &hint)); 54 ui.add_space(12.0); 55 56 let query = self.state.query.trim(); 57 58 if query.is_empty() { 59 // Show contacts list when not searching 60 ui.add(Label::new( 61 RichText::new(tr!( 62 self.i18n, 63 "Contacts", 64 "Heading shown when choosing a contact to start a new chat" 65 )) 66 .text_style(NotedeckTextStyle::Heading.text_style()), 67 )); 68 69 if let ContactState::Received { contacts, .. } = self.contacts { 70 let resp = ContactsListView::new( 71 contacts, 72 self.jobs, 73 self.ndb, 74 self.img_cache, 75 &txn, 76 self.i18n, 77 ) 78 .ui(ui); 79 80 resp.output.map(|a| match a { 81 notedeck_ui::ContactsListAction::Select(pubkey) => { 82 CreateConvoResponse { recipient: pubkey } 83 } 84 }) 85 } else { 86 // No contacts yet 87 ui.label(tr!( 88 self.i18n, 89 "No contacts yet", 90 "Shown when user has no contacts to display" 91 )); 92 None 93 } 94 } else { 95 // Show search results 96 ui.add(Label::new( 97 RichText::new(tr!( 98 self.i18n, 99 "Results", 100 "Heading shown above search results" 101 )) 102 .text_style(NotedeckTextStyle::Heading.text_style()), 103 )); 104 105 let results = search_profiles(self.ndb, &txn, query, self.contacts, 128); 106 107 if results.is_empty() { 108 ui.add_space(20.0); 109 ui.label( 110 RichText::new(tr!( 111 self.i18n, 112 "No profiles found", 113 "Shown when profile search returns no results" 114 )) 115 .weak(), 116 ); 117 None 118 } else { 119 search_results_list( 120 ui, 121 &results, 122 self.ndb, 123 &txn, 124 self.img_cache, 125 self.jobs, 126 self.i18n, 127 ) 128 } 129 } 130 } 131 } 132 133 /// Renders a scrollable list of search results. Returns `Some(CreateConvoResponse)` 134 /// if the user selects a profile. 135 fn search_results_list( 136 ui: &mut egui::Ui, 137 results: &[ProfileSearchResult], 138 ndb: &Ndb, 139 txn: &Transaction, 140 img_cache: &mut Images, 141 jobs: &MediaJobSender, 142 i18n: &mut Localization, 143 ) -> Option<CreateConvoResponse> { 144 let mut action = None; 145 146 egui::ScrollArea::vertical().show(ui, |ui| { 147 for result in results { 148 let profile = ndb.get_profile_by_pubkey(txn, &result.pk).ok(); 149 150 if profile_row( 151 ui, 152 profile.as_ref(), 153 result.is_contact, 154 img_cache, 155 jobs, 156 i18n, 157 ) { 158 action = Some(CreateConvoResponse { 159 recipient: Pubkey::new(result.pk), 160 }); 161 } 162 } 163 }); 164 165 action 166 }