notedeck

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

edit.rs (6360B)


      1 use core::f32;
      2 
      3 use egui::{vec2, Button, Layout, Margin, RichText, Rounding, ScrollArea, TextEdit};
      4 use notedeck::{Images, NotedeckTextStyle};
      5 
      6 use crate::{colors, profile_state::ProfileState};
      7 
      8 use super::{banner, unwrap_profile_url, ProfilePic};
      9 
     10 pub struct EditProfileView<'a> {
     11     state: &'a mut ProfileState,
     12     img_cache: &'a mut Images,
     13 }
     14 
     15 impl<'a> EditProfileView<'a> {
     16     pub fn new(state: &'a mut ProfileState, img_cache: &'a mut Images) -> Self {
     17         Self { state, img_cache }
     18     }
     19 
     20     // return true to save
     21     pub fn ui(&mut self, ui: &mut egui::Ui) -> bool {
     22         ScrollArea::vertical()
     23             .show(ui, |ui| {
     24                 banner(ui, Some(&self.state.banner), 188.0);
     25 
     26                 let padding = 24.0;
     27                 crate::ui::padding(padding, ui, |ui| {
     28                     self.inner(ui, padding);
     29                 });
     30 
     31                 ui.separator();
     32 
     33                 let mut save = false;
     34                 crate::ui::padding(padding, ui, |ui| {
     35                     ui.with_layout(Layout::right_to_left(egui::Align::Center), |ui| {
     36                         if ui
     37                             .add(button("Save changes", 119.0).fill(colors::PINK))
     38                             .clicked()
     39                         {
     40                             save = true;
     41                         }
     42                     });
     43                 });
     44 
     45                 save
     46             })
     47             .inner
     48     }
     49 
     50     fn inner(&mut self, ui: &mut egui::Ui, padding: f32) {
     51         ui.spacing_mut().item_spacing = egui::vec2(0.0, 16.0);
     52         let mut pfp_rect = ui.available_rect_before_wrap();
     53         let size = 80.0;
     54         pfp_rect.set_width(size);
     55         pfp_rect.set_height(size);
     56         let pfp_rect = pfp_rect.translate(egui::vec2(0.0, -(padding + 2.0 + (size / 2.0))));
     57 
     58         let pfp_url = unwrap_profile_url(if self.state.picture.is_empty() {
     59             None
     60         } else {
     61             Some(&self.state.picture)
     62         });
     63         ui.put(
     64             pfp_rect,
     65             ProfilePic::new(self.img_cache, pfp_url)
     66                 .size(size)
     67                 .border(ProfilePic::border_stroke(ui)),
     68         );
     69 
     70         in_frame(ui, |ui| {
     71             ui.add(label("Display name"));
     72             ui.add(singleline_textedit(&mut self.state.display_name));
     73         });
     74 
     75         in_frame(ui, |ui| {
     76             ui.add(label("Username"));
     77             ui.add(singleline_textedit(&mut self.state.name));
     78         });
     79 
     80         in_frame(ui, |ui| {
     81             ui.add(label("Profile picture"));
     82             ui.add(multiline_textedit(&mut self.state.picture));
     83         });
     84 
     85         in_frame(ui, |ui| {
     86             ui.add(label("Banner"));
     87             ui.add(multiline_textedit(&mut self.state.banner));
     88         });
     89 
     90         in_frame(ui, |ui| {
     91             ui.add(label("About"));
     92             ui.add(multiline_textedit(&mut self.state.about));
     93         });
     94 
     95         in_frame(ui, |ui| {
     96             ui.add(label("Website"));
     97             ui.add(singleline_textedit(&mut self.state.website));
     98         });
     99 
    100         in_frame(ui, |ui| {
    101             ui.add(label("Lightning network address (lud16)"));
    102             ui.add(multiline_textedit(&mut self.state.lud16));
    103         });
    104 
    105         in_frame(ui, |ui| {
    106             ui.add(label("Nostr address (NIP-05 identity)"));
    107             ui.add(singleline_textedit(&mut self.state.nip05));
    108             let split = &mut self.state.nip05.split('@');
    109             let prefix = split.next();
    110             let suffix = split.next();
    111             if let Some(prefix) = prefix {
    112                 if let Some(suffix) = suffix {
    113                     let use_domain = if let Some(f) = prefix.chars().next() {
    114                         f == '_'
    115                     } else {
    116                         false
    117                     };
    118                     ui.colored_label(
    119                         ui.visuals().noninteractive().fg_stroke.color,
    120                         RichText::new(if use_domain {
    121                             format!("\"{}\" will be used for identification", suffix)
    122                         } else {
    123                             format!(
    124                                 "\"{}\" at \"{}\" will be used for identification",
    125                                 prefix, suffix
    126                             )
    127                         }),
    128                     );
    129                 }
    130             }
    131         });
    132     }
    133 }
    134 
    135 fn label(text: &str) -> impl egui::Widget + '_ {
    136     move |ui: &mut egui::Ui| -> egui::Response {
    137         ui.label(RichText::new(text).font(NotedeckTextStyle::Body.get_bolded_font(ui.ctx())))
    138     }
    139 }
    140 
    141 fn singleline_textedit(data: &mut String) -> impl egui::Widget + '_ {
    142     TextEdit::singleline(data)
    143         .min_size(vec2(0.0, 40.0))
    144         .vertical_align(egui::Align::Center)
    145         .margin(Margin::symmetric(12.0, 10.0))
    146         .desired_width(f32::INFINITY)
    147 }
    148 
    149 fn multiline_textedit(data: &mut String) -> impl egui::Widget + '_ {
    150     TextEdit::multiline(data)
    151         // .min_size(vec2(0.0, 40.0))
    152         .vertical_align(egui::Align::TOP)
    153         .margin(Margin::symmetric(12.0, 10.0))
    154         .desired_width(f32::INFINITY)
    155         .desired_rows(1)
    156 }
    157 
    158 fn in_frame(ui: &mut egui::Ui, contents: impl FnOnce(&mut egui::Ui)) {
    159     egui::Frame::none().show(ui, |ui| {
    160         ui.spacing_mut().item_spacing = egui::vec2(0.0, 8.0);
    161         contents(ui);
    162     });
    163 }
    164 
    165 fn button(text: &str, width: f32) -> egui::Button<'static> {
    166     Button::new(text)
    167         .rounding(Rounding::same(8.0))
    168         .min_size(vec2(width, 40.0))
    169 }
    170 
    171 mod preview {
    172     use notedeck::App;
    173 
    174     use crate::{
    175         profile_state::ProfileState,
    176         test_data,
    177         ui::{Preview, PreviewConfig},
    178     };
    179 
    180     use super::EditProfileView;
    181 
    182     pub struct EditProfilePreivew {
    183         state: ProfileState,
    184     }
    185 
    186     impl Default for EditProfilePreivew {
    187         fn default() -> Self {
    188             Self {
    189                 state: ProfileState::from_profile(&test_data::test_profile_record()),
    190             }
    191         }
    192     }
    193 
    194     impl App for EditProfilePreivew {
    195         fn update(&mut self, ctx: &mut notedeck::AppContext<'_>, ui: &mut egui::Ui) {
    196             EditProfileView::new(&mut self.state, ctx.img_cache).ui(ui);
    197         }
    198     }
    199 
    200     impl Preview for EditProfileView<'_> {
    201         type Prev = EditProfilePreivew;
    202 
    203         fn preview(_cfg: PreviewConfig) -> Self::Prev {
    204             EditProfilePreivew::default()
    205         }
    206     }
    207 }