notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

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 }