79
crates/notedeck_columns/src/profile_state.rs
Normal file
79
crates/notedeck_columns/src/profile_state.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
use nostrdb::{NdbProfile, ProfileRecord};
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct ProfileState {
|
||||
pub display_name: String,
|
||||
pub name: String,
|
||||
pub picture: String,
|
||||
pub banner: String,
|
||||
pub about: String,
|
||||
pub website: String,
|
||||
pub lud16: String,
|
||||
pub nip05: String,
|
||||
}
|
||||
|
||||
impl ProfileState {
|
||||
pub fn from_profile(record: &ProfileRecord<'_>) -> Self {
|
||||
let display_name = get_item(record, |p| p.display_name());
|
||||
let username = get_item(record, |p| p.name());
|
||||
let profile_picture = get_item(record, |p| p.picture());
|
||||
let cover_image = get_item(record, |p| p.banner());
|
||||
let about = get_item(record, |p| p.about());
|
||||
let website = get_item(record, |p| p.website());
|
||||
let lud16 = get_item(record, |p| p.lud16());
|
||||
let nip05 = get_item(record, |p| p.nip05());
|
||||
|
||||
Self {
|
||||
display_name,
|
||||
name: username,
|
||||
picture: profile_picture,
|
||||
banner: cover_image,
|
||||
about,
|
||||
website,
|
||||
lud16,
|
||||
nip05,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_json(&self) -> String {
|
||||
let mut fields = Vec::new();
|
||||
|
||||
if !self.display_name.is_empty() {
|
||||
fields.push(format!(r#""display_name":"{}""#, self.display_name));
|
||||
}
|
||||
if !self.name.is_empty() {
|
||||
fields.push(format!(r#""name":"{}""#, self.name));
|
||||
}
|
||||
if !self.picture.is_empty() {
|
||||
fields.push(format!(r#""picture":"{}""#, self.picture));
|
||||
}
|
||||
if !self.banner.is_empty() {
|
||||
fields.push(format!(r#""banner":"{}""#, self.banner));
|
||||
}
|
||||
if !self.about.is_empty() {
|
||||
fields.push(format!(r#""about":"{}""#, self.about));
|
||||
}
|
||||
if !self.website.is_empty() {
|
||||
fields.push(format!(r#""website":"{}""#, self.website));
|
||||
}
|
||||
if !self.lud16.is_empty() {
|
||||
fields.push(format!(r#""lud16":"{}""#, self.lud16));
|
||||
}
|
||||
if !self.nip05.is_empty() {
|
||||
fields.push(format!(r#""nip05":"{}""#, self.nip05));
|
||||
}
|
||||
|
||||
format!("{{{}}}", fields.join(","))
|
||||
}
|
||||
}
|
||||
|
||||
fn get_item<'a>(
|
||||
record: &ProfileRecord<'a>,
|
||||
item_retriever: fn(NdbProfile<'a>) -> Option<&'a str>,
|
||||
) -> String {
|
||||
record
|
||||
.record()
|
||||
.profile()
|
||||
.and_then(item_retriever)
|
||||
.map_or_else(String::new, ToString::to_string)
|
||||
}
|
||||
205
crates/notedeck_columns/src/ui/profile/edit.rs
Normal file
205
crates/notedeck_columns/src/ui/profile/edit.rs
Normal file
@@ -0,0 +1,205 @@
|
||||
use core::f32;
|
||||
|
||||
use egui::{vec2, Button, Layout, Margin, RichText, Rounding, ScrollArea, TextEdit};
|
||||
use notedeck::{ImageCache, NotedeckTextStyle};
|
||||
|
||||
use crate::{colors, profile_state::ProfileState};
|
||||
|
||||
use super::{banner, unwrap_profile_url, ProfilePic};
|
||||
|
||||
pub struct EditProfileView<'a> {
|
||||
state: &'a mut ProfileState,
|
||||
img_cache: &'a mut ImageCache,
|
||||
}
|
||||
|
||||
impl<'a> EditProfileView<'a> {
|
||||
pub fn new(state: &'a mut ProfileState, img_cache: &'a mut ImageCache) -> Self {
|
||||
Self { state, img_cache }
|
||||
}
|
||||
|
||||
// return true to save
|
||||
pub fn ui(&mut self, ui: &mut egui::Ui) -> bool {
|
||||
ScrollArea::vertical()
|
||||
.show(ui, |ui| {
|
||||
banner(ui, Some(&self.state.banner), 188.0);
|
||||
|
||||
let padding = 24.0;
|
||||
crate::ui::padding(padding, ui, |ui| {
|
||||
self.inner(ui, padding);
|
||||
});
|
||||
|
||||
ui.separator();
|
||||
|
||||
let mut save = false;
|
||||
crate::ui::padding(padding, ui, |ui| {
|
||||
ui.with_layout(Layout::right_to_left(egui::Align::Center), |ui| {
|
||||
if ui
|
||||
.add(button("Save changes", 119.0).fill(colors::PINK))
|
||||
.clicked()
|
||||
{
|
||||
save = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
save
|
||||
})
|
||||
.inner
|
||||
}
|
||||
|
||||
fn inner(&mut self, ui: &mut egui::Ui, padding: f32) {
|
||||
ui.spacing_mut().item_spacing = egui::vec2(0.0, 16.0);
|
||||
let mut pfp_rect = ui.available_rect_before_wrap();
|
||||
let size = 80.0;
|
||||
pfp_rect.set_width(size);
|
||||
pfp_rect.set_height(size);
|
||||
let pfp_rect = pfp_rect.translate(egui::vec2(0.0, -(padding + 2.0 + (size / 2.0))));
|
||||
|
||||
let pfp_url = unwrap_profile_url(if self.state.picture.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(&self.state.picture)
|
||||
});
|
||||
ui.put(
|
||||
pfp_rect,
|
||||
ProfilePic::new(self.img_cache, pfp_url).size(size),
|
||||
);
|
||||
|
||||
in_frame(ui, |ui| {
|
||||
ui.add(label("Display name"));
|
||||
ui.add(singleline_textedit(&mut self.state.display_name));
|
||||
});
|
||||
|
||||
in_frame(ui, |ui| {
|
||||
ui.add(label("Username"));
|
||||
ui.add(singleline_textedit(&mut self.state.name));
|
||||
});
|
||||
|
||||
in_frame(ui, |ui| {
|
||||
ui.add(label("Profile picture"));
|
||||
ui.add(multiline_textedit(&mut self.state.picture));
|
||||
});
|
||||
|
||||
in_frame(ui, |ui| {
|
||||
ui.add(label("Banner"));
|
||||
ui.add(multiline_textedit(&mut self.state.banner));
|
||||
});
|
||||
|
||||
in_frame(ui, |ui| {
|
||||
ui.add(label("About"));
|
||||
ui.add(multiline_textedit(&mut self.state.about));
|
||||
});
|
||||
|
||||
in_frame(ui, |ui| {
|
||||
ui.add(label("Website"));
|
||||
ui.add(singleline_textedit(&mut self.state.website));
|
||||
});
|
||||
|
||||
in_frame(ui, |ui| {
|
||||
ui.add(label("Lightning network address (lud16)"));
|
||||
ui.add(multiline_textedit(&mut self.state.lud16));
|
||||
});
|
||||
|
||||
in_frame(ui, |ui| {
|
||||
ui.add(label("NIP-05 verification"));
|
||||
ui.add(singleline_textedit(&mut self.state.nip05));
|
||||
let split = &mut self.state.nip05.split('@');
|
||||
let prefix = split.next();
|
||||
let suffix = split.next();
|
||||
if let Some(prefix) = prefix {
|
||||
if let Some(suffix) = suffix {
|
||||
let use_domain = if let Some(f) = prefix.chars().next() {
|
||||
f == '_'
|
||||
} else {
|
||||
false
|
||||
};
|
||||
ui.colored_label(
|
||||
ui.visuals().noninteractive().fg_stroke.color,
|
||||
RichText::new(if use_domain {
|
||||
format!("\"{}\" will be used for verification", suffix)
|
||||
} else {
|
||||
format!(
|
||||
"\"{}\" at \"{}\" will be used for verification",
|
||||
prefix, suffix
|
||||
)
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn label(text: &str) -> impl egui::Widget + '_ {
|
||||
move |ui: &mut egui::Ui| -> egui::Response {
|
||||
ui.label(RichText::new(text).font(NotedeckTextStyle::Body.get_bolded_font(ui.ctx())))
|
||||
}
|
||||
}
|
||||
|
||||
fn singleline_textedit(data: &mut String) -> impl egui::Widget + '_ {
|
||||
TextEdit::singleline(data)
|
||||
.min_size(vec2(0.0, 40.0))
|
||||
.vertical_align(egui::Align::Center)
|
||||
.margin(Margin::symmetric(12.0, 10.0))
|
||||
.desired_width(f32::INFINITY)
|
||||
}
|
||||
|
||||
fn multiline_textedit(data: &mut String) -> impl egui::Widget + '_ {
|
||||
TextEdit::multiline(data)
|
||||
// .min_size(vec2(0.0, 40.0))
|
||||
.vertical_align(egui::Align::TOP)
|
||||
.margin(Margin::symmetric(12.0, 10.0))
|
||||
.desired_width(f32::INFINITY)
|
||||
.desired_rows(1)
|
||||
}
|
||||
|
||||
fn in_frame(ui: &mut egui::Ui, contents: impl FnOnce(&mut egui::Ui)) {
|
||||
egui::Frame::none().show(ui, |ui| {
|
||||
ui.spacing_mut().item_spacing = egui::vec2(0.0, 8.0);
|
||||
contents(ui);
|
||||
});
|
||||
}
|
||||
|
||||
fn button(text: &str, width: f32) -> egui::Button<'static> {
|
||||
Button::new(text)
|
||||
.rounding(Rounding::same(8.0))
|
||||
.min_size(vec2(width, 40.0))
|
||||
}
|
||||
|
||||
mod preview {
|
||||
use notedeck::App;
|
||||
|
||||
use crate::{
|
||||
profile_state::ProfileState,
|
||||
test_data,
|
||||
ui::{Preview, PreviewConfig},
|
||||
};
|
||||
|
||||
use super::EditProfileView;
|
||||
|
||||
pub struct EditProfilePreivew {
|
||||
state: ProfileState,
|
||||
}
|
||||
|
||||
impl Default for EditProfilePreivew {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
state: ProfileState::from_profile(&test_data::test_profile_record()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl App for EditProfilePreivew {
|
||||
fn update(&mut self, ctx: &mut notedeck::AppContext<'_>, ui: &mut egui::Ui) {
|
||||
EditProfileView::new(&mut self.state, ctx.img_cache).ui(ui);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Preview for EditProfileView<'a> {
|
||||
type Prev = EditProfilePreivew;
|
||||
|
||||
fn preview(_cfg: PreviewConfig) -> Self::Prev {
|
||||
EditProfilePreivew::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user