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 }