edit.rs (6284B)
1 use core::f32; 2 3 use egui::{vec2, Button, Layout, Margin, RichText, Rounding, ScrollArea, TextEdit}; 4 use notedeck::{ImageCache, 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 ImageCache, 13 } 14 15 impl<'a> EditProfileView<'a> { 16 pub fn new(state: &'a mut ProfileState, img_cache: &'a mut ImageCache) -> 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).size(size), 66 ); 67 68 in_frame(ui, |ui| { 69 ui.add(label("Display name")); 70 ui.add(singleline_textedit(&mut self.state.display_name)); 71 }); 72 73 in_frame(ui, |ui| { 74 ui.add(label("Username")); 75 ui.add(singleline_textedit(&mut self.state.name)); 76 }); 77 78 in_frame(ui, |ui| { 79 ui.add(label("Profile picture")); 80 ui.add(multiline_textedit(&mut self.state.picture)); 81 }); 82 83 in_frame(ui, |ui| { 84 ui.add(label("Banner")); 85 ui.add(multiline_textedit(&mut self.state.banner)); 86 }); 87 88 in_frame(ui, |ui| { 89 ui.add(label("About")); 90 ui.add(multiline_textedit(&mut self.state.about)); 91 }); 92 93 in_frame(ui, |ui| { 94 ui.add(label("Website")); 95 ui.add(singleline_textedit(&mut self.state.website)); 96 }); 97 98 in_frame(ui, |ui| { 99 ui.add(label("Lightning network address (lud16)")); 100 ui.add(multiline_textedit(&mut self.state.lud16)); 101 }); 102 103 in_frame(ui, |ui| { 104 ui.add(label("NIP-05 verification")); 105 ui.add(singleline_textedit(&mut self.state.nip05)); 106 let split = &mut self.state.nip05.split('@'); 107 let prefix = split.next(); 108 let suffix = split.next(); 109 if let Some(prefix) = prefix { 110 if let Some(suffix) = suffix { 111 let use_domain = if let Some(f) = prefix.chars().next() { 112 f == '_' 113 } else { 114 false 115 }; 116 ui.colored_label( 117 ui.visuals().noninteractive().fg_stroke.color, 118 RichText::new(if use_domain { 119 format!("\"{}\" will be used for verification", suffix) 120 } else { 121 format!( 122 "\"{}\" at \"{}\" will be used for verification", 123 prefix, suffix 124 ) 125 }), 126 ); 127 } 128 } 129 }); 130 } 131 } 132 133 fn label(text: &str) -> impl egui::Widget + '_ { 134 move |ui: &mut egui::Ui| -> egui::Response { 135 ui.label(RichText::new(text).font(NotedeckTextStyle::Body.get_bolded_font(ui.ctx()))) 136 } 137 } 138 139 fn singleline_textedit(data: &mut String) -> impl egui::Widget + '_ { 140 TextEdit::singleline(data) 141 .min_size(vec2(0.0, 40.0)) 142 .vertical_align(egui::Align::Center) 143 .margin(Margin::symmetric(12.0, 10.0)) 144 .desired_width(f32::INFINITY) 145 } 146 147 fn multiline_textedit(data: &mut String) -> impl egui::Widget + '_ { 148 TextEdit::multiline(data) 149 // .min_size(vec2(0.0, 40.0)) 150 .vertical_align(egui::Align::TOP) 151 .margin(Margin::symmetric(12.0, 10.0)) 152 .desired_width(f32::INFINITY) 153 .desired_rows(1) 154 } 155 156 fn in_frame(ui: &mut egui::Ui, contents: impl FnOnce(&mut egui::Ui)) { 157 egui::Frame::none().show(ui, |ui| { 158 ui.spacing_mut().item_spacing = egui::vec2(0.0, 8.0); 159 contents(ui); 160 }); 161 } 162 163 fn button(text: &str, width: f32) -> egui::Button<'static> { 164 Button::new(text) 165 .rounding(Rounding::same(8.0)) 166 .min_size(vec2(width, 40.0)) 167 } 168 169 mod preview { 170 use notedeck::App; 171 172 use crate::{ 173 profile_state::ProfileState, 174 test_data, 175 ui::{Preview, PreviewConfig}, 176 }; 177 178 use super::EditProfileView; 179 180 pub struct EditProfilePreivew { 181 state: ProfileState, 182 } 183 184 impl Default for EditProfilePreivew { 185 fn default() -> Self { 186 Self { 187 state: ProfileState::from_profile(&test_data::test_profile_record()), 188 } 189 } 190 } 191 192 impl App for EditProfilePreivew { 193 fn update(&mut self, ctx: &mut notedeck::AppContext<'_>, ui: &mut egui::Ui) { 194 EditProfileView::new(&mut self.state, ctx.img_cache).ui(ui); 195 } 196 } 197 198 impl Preview for EditProfileView<'_> { 199 type Prev = EditProfilePreivew; 200 201 fn preview(_cfg: PreviewConfig) -> Self::Prev { 202 EditProfilePreivew::default() 203 } 204 } 205 }