mod.rs (4695B)
1 use nostrdb::ProfileRecord; 2 3 pub mod context; 4 pub mod name; 5 pub mod picture; 6 pub mod preview; 7 8 pub use picture::ProfilePic; 9 pub use preview::ProfilePreview; 10 11 use egui::{load::TexturePoll, Label, RichText}; 12 use notedeck::{IsFollowing, NostrName, NotedeckTextStyle}; 13 14 use crate::{app_images, colors, widgets::styled_button_toggleable}; 15 16 pub fn display_name_widget<'a>( 17 name: &'a NostrName<'a>, 18 add_placeholder_space: bool, 19 ) -> impl egui::Widget + 'a { 20 move |ui: &mut egui::Ui| -> egui::Response { 21 let disp_resp = name.display_name.map(|disp_name| { 22 ui.add( 23 Label::new( 24 RichText::new(disp_name).text_style(NotedeckTextStyle::Heading3.text_style()), 25 ) 26 .selectable(false), 27 ) 28 }); 29 30 let (username_resp, nip05_resp) = ui 31 .horizontal_wrapped(|ui| { 32 let username_resp = name.username.map(|username| { 33 ui.add( 34 Label::new( 35 RichText::new(format!("@{username}")) 36 .size(16.0) 37 .color(crate::colors::MID_GRAY), 38 ) 39 .selectable(false), 40 ) 41 }); 42 43 if name.username.is_some() && name.nip05.is_some() { 44 ui.end_row(); 45 } 46 47 let nip05_resp = name.nip05.map(|nip05| { 48 ui.horizontal_wrapped(|ui| { 49 ui.spacing_mut().item_spacing.x = 2.0; 50 51 ui.add(app_images::verified_image()); 52 53 ui.label(RichText::new(nip05).size(16.0).color(crate::colors::TEAL)) 54 .on_hover_text(nip05) 55 }) 56 .inner 57 }); 58 59 (username_resp, nip05_resp) 60 }) 61 .inner; 62 63 let resp = match (disp_resp, username_resp, nip05_resp) { 64 (Some(disp), Some(username), Some(nip05)) => disp.union(username).union(nip05), 65 (Some(disp), Some(username), None) => disp.union(username), 66 (Some(disp), None, None) => disp, 67 (None, Some(username), Some(nip05)) => username.union(nip05), 68 (None, Some(username), None) => username, 69 _ => ui.add(Label::new(RichText::new(name.name()))), 70 }; 71 72 if add_placeholder_space { 73 ui.add_space(16.0); 74 } 75 76 resp 77 } 78 } 79 80 pub fn about_section_widget<'a>(profile: Option<&'a ProfileRecord<'a>>) -> impl egui::Widget + 'a { 81 move |ui: &mut egui::Ui| { 82 if let Some(about) = profile 83 .map(|p| p.record().profile()) 84 .and_then(|p| p.and_then(|p| p.about())) 85 { 86 let resp = ui.label(about); 87 ui.add_space(8.0); 88 resp 89 } else { 90 // need any Response so we dont need an Option 91 ui.allocate_response(egui::Vec2::ZERO, egui::Sense::hover()) 92 } 93 } 94 } 95 96 pub fn banner_texture(ui: &mut egui::Ui, banner_url: &str) -> Option<egui::load::SizedTexture> { 97 // TODO: cache banner 98 if !banner_url.is_empty() { 99 let texture_load_res = 100 egui::Image::new(banner_url).load_for_size(ui.ctx(), ui.available_size()); 101 if let Ok(texture_poll) = texture_load_res { 102 match texture_poll { 103 TexturePoll::Pending { .. } => {} 104 TexturePoll::Ready { texture, .. } => return Some(texture), 105 } 106 } 107 } 108 109 None 110 } 111 112 pub fn banner(ui: &mut egui::Ui, banner_url: Option<&str>, height: f32) -> egui::Response { 113 ui.add_sized([ui.available_size().x, height], |ui: &mut egui::Ui| { 114 banner_url 115 .and_then(|url| banner_texture(ui, url)) 116 .map(|texture| { 117 notedeck::media::images::aspect_fill( 118 ui, 119 egui::Sense::hover(), 120 texture.id, 121 texture.size.x / texture.size.y, 122 ) 123 }) 124 .unwrap_or_else(|| ui.label("")) 125 }) 126 } 127 128 pub fn follow_button(following: IsFollowing) -> impl egui::Widget + 'static { 129 move |ui: &mut egui::Ui| -> egui::Response { 130 let (bg_color, text) = match following { 131 IsFollowing::Unknown => (ui.visuals().noninteractive().bg_fill, "Unknown"), 132 IsFollowing::Yes => (ui.visuals().widgets.inactive.bg_fill, "Unfollow"), 133 IsFollowing::No => (colors::PINK, "Follow"), 134 }; 135 136 let enabled = following != IsFollowing::Unknown; 137 ui.add(styled_button_toggleable(text, bg_color, enabled)) 138 } 139 }