ui: move note and profile rendering to notedeck_ui
We want to render notes in other apps like dave, so lets move our note rendering to notedeck_ui. We rework NoteAction so it doesn't have anything specific to notedeck_columns Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
@@ -1,13 +1,9 @@
|
||||
use core::f32;
|
||||
|
||||
use egui::{vec2, Button, CornerRadius, Layout, Margin, RichText, ScrollArea, TextEdit};
|
||||
use notedeck::{Images, NotedeckTextStyle};
|
||||
|
||||
use crate::profile_state::ProfileState;
|
||||
|
||||
use super::banner;
|
||||
|
||||
use notedeck_ui::{profile::unwrap_profile_url, ProfilePic};
|
||||
use egui::{vec2, Button, CornerRadius, Layout, Margin, RichText, ScrollArea, TextEdit};
|
||||
use notedeck::{profile::unwrap_profile_url, Images, NotedeckTextStyle};
|
||||
use notedeck_ui::{profile::banner, ProfilePic};
|
||||
|
||||
pub struct EditProfileView<'a> {
|
||||
state: &'a mut ProfileState,
|
||||
@@ -26,14 +22,14 @@ impl<'a> EditProfileView<'a> {
|
||||
banner(ui, Some(&self.state.banner), 188.0);
|
||||
|
||||
let padding = 24.0;
|
||||
crate::ui::padding(padding, ui, |ui| {
|
||||
notedeck_ui::padding(padding, ui, |ui| {
|
||||
self.inner(ui, padding);
|
||||
});
|
||||
|
||||
ui.separator();
|
||||
|
||||
let mut save = false;
|
||||
crate::ui::padding(padding, ui, |ui| {
|
||||
notedeck_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(notedeck_ui::colors::PINK))
|
||||
|
||||
@@ -1,27 +1,23 @@
|
||||
pub mod edit;
|
||||
pub mod preview;
|
||||
|
||||
pub use edit::EditProfileView;
|
||||
use egui::load::TexturePoll;
|
||||
use egui::{vec2, Color32, CornerRadius, Label, Layout, Rect, RichText, ScrollArea, Sense, Stroke};
|
||||
use egui::{vec2, Color32, CornerRadius, Layout, Rect, RichText, ScrollArea, Sense, Stroke};
|
||||
use enostr::Pubkey;
|
||||
use nostrdb::{ProfileRecord, Transaction};
|
||||
pub use preview::ProfilePreview;
|
||||
use tracing::error;
|
||||
|
||||
use crate::{
|
||||
actionbar::NoteAction,
|
||||
profile::get_display_name,
|
||||
timeline::{TimelineCache, TimelineKind},
|
||||
ui::timeline::{tabs_ui, TimelineTabView},
|
||||
NostrName,
|
||||
};
|
||||
|
||||
use notedeck::{Accounts, MuteFun, NotedeckTextStyle, UnknownIds};
|
||||
use notedeck_ui::{images, profile::get_profile_url, ProfilePic};
|
||||
|
||||
use super::note::contents::NoteContext;
|
||||
use super::note::NoteOptions;
|
||||
use notedeck::{
|
||||
name::get_display_name, profile::get_profile_url, Accounts, MuteFun, NoteAction, NoteContext,
|
||||
NotedeckTextStyle, UnknownIds,
|
||||
};
|
||||
use notedeck_ui::{
|
||||
profile::{about_section_widget, banner, display_name_widget},
|
||||
NoteOptions, ProfilePic,
|
||||
};
|
||||
|
||||
pub struct ProfileView<'a, 'd> {
|
||||
pubkey: &'a Pubkey,
|
||||
@@ -137,7 +133,7 @@ impl<'a, 'd> ProfileView<'a, 'd> {
|
||||
);
|
||||
|
||||
let padding = 12.0;
|
||||
crate::ui::padding(padding, ui, |ui| {
|
||||
notedeck_ui::padding(padding, ui, |ui| {
|
||||
let mut pfp_rect = ui.available_rect_before_wrap();
|
||||
let size = 80.0;
|
||||
pfp_rect.set_width(size);
|
||||
@@ -342,110 +338,3 @@ fn edit_profile_button() -> impl egui::Widget + 'static {
|
||||
resp
|
||||
}
|
||||
}
|
||||
|
||||
fn display_name_widget<'a>(
|
||||
name: &'a NostrName<'a>,
|
||||
add_placeholder_space: bool,
|
||||
) -> impl egui::Widget + 'a {
|
||||
move |ui: &mut egui::Ui| -> egui::Response {
|
||||
let disp_resp = name.display_name.map(|disp_name| {
|
||||
ui.add(
|
||||
Label::new(
|
||||
RichText::new(disp_name).text_style(NotedeckTextStyle::Heading3.text_style()),
|
||||
)
|
||||
.selectable(false),
|
||||
)
|
||||
});
|
||||
|
||||
let (username_resp, nip05_resp) = ui
|
||||
.horizontal(|ui| {
|
||||
let username_resp = name.username.map(|username| {
|
||||
ui.add(
|
||||
Label::new(
|
||||
RichText::new(format!("@{}", username))
|
||||
.size(16.0)
|
||||
.color(notedeck_ui::colors::MID_GRAY),
|
||||
)
|
||||
.selectable(false),
|
||||
)
|
||||
});
|
||||
|
||||
let nip05_resp = name.nip05.map(|nip05| {
|
||||
ui.image(egui::include_image!(
|
||||
"../../../../../assets/icons/verified_4x.png"
|
||||
));
|
||||
ui.add(Label::new(
|
||||
RichText::new(nip05)
|
||||
.size(16.0)
|
||||
.color(notedeck_ui::colors::TEAL),
|
||||
))
|
||||
});
|
||||
|
||||
(username_resp, nip05_resp)
|
||||
})
|
||||
.inner;
|
||||
|
||||
let resp = match (disp_resp, username_resp, nip05_resp) {
|
||||
(Some(disp), Some(username), Some(nip05)) => disp.union(username).union(nip05),
|
||||
(Some(disp), Some(username), None) => disp.union(username),
|
||||
(Some(disp), None, None) => disp,
|
||||
(None, Some(username), Some(nip05)) => username.union(nip05),
|
||||
(None, Some(username), None) => username,
|
||||
_ => ui.add(Label::new(RichText::new(name.name()))),
|
||||
};
|
||||
|
||||
if add_placeholder_space {
|
||||
ui.add_space(16.0);
|
||||
}
|
||||
|
||||
resp
|
||||
}
|
||||
}
|
||||
|
||||
fn about_section_widget<'a, 'b>(profile: &'b ProfileRecord<'a>) -> impl egui::Widget + 'b
|
||||
where
|
||||
'b: 'a,
|
||||
{
|
||||
move |ui: &mut egui::Ui| {
|
||||
if let Some(about) = profile.record().profile().and_then(|p| p.about()) {
|
||||
let resp = ui.label(about);
|
||||
ui.add_space(8.0);
|
||||
resp
|
||||
} else {
|
||||
// need any Response so we dont need an Option
|
||||
ui.allocate_response(egui::Vec2::ZERO, egui::Sense::hover())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn banner_texture(ui: &mut egui::Ui, banner_url: &str) -> Option<egui::load::SizedTexture> {
|
||||
// TODO: cache banner
|
||||
if !banner_url.is_empty() {
|
||||
let texture_load_res =
|
||||
egui::Image::new(banner_url).load_for_size(ui.ctx(), ui.available_size());
|
||||
if let Ok(texture_poll) = texture_load_res {
|
||||
match texture_poll {
|
||||
TexturePoll::Pending { .. } => {}
|
||||
TexturePoll::Ready { texture, .. } => return Some(texture),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn banner(ui: &mut egui::Ui, banner_url: Option<&str>, height: f32) -> egui::Response {
|
||||
ui.add_sized([ui.available_size().x, height], |ui: &mut egui::Ui| {
|
||||
banner_url
|
||||
.and_then(|url| banner_texture(ui, url))
|
||||
.map(|texture| {
|
||||
images::aspect_fill(
|
||||
ui,
|
||||
Sense::hover(),
|
||||
texture.id,
|
||||
texture.size.x / texture.size.y,
|
||||
)
|
||||
})
|
||||
.unwrap_or_else(|| ui.label(""))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,171 +0,0 @@
|
||||
use crate::ui::ProfilePic;
|
||||
use crate::NostrName;
|
||||
use egui::{Frame, Label, RichText, Widget};
|
||||
use egui_extras::Size;
|
||||
use nostrdb::ProfileRecord;
|
||||
|
||||
use notedeck::{Images, NotedeckTextStyle};
|
||||
|
||||
use super::{about_section_widget, banner, display_name_widget, get_display_name};
|
||||
use notedeck_ui::profile::get_profile_url;
|
||||
|
||||
pub struct ProfilePreview<'a, 'cache> {
|
||||
profile: &'a ProfileRecord<'a>,
|
||||
cache: &'cache mut Images,
|
||||
banner_height: Size,
|
||||
}
|
||||
|
||||
impl<'a, 'cache> ProfilePreview<'a, 'cache> {
|
||||
pub fn new(profile: &'a ProfileRecord<'a>, cache: &'cache mut Images) -> Self {
|
||||
let banner_height = Size::exact(80.0);
|
||||
ProfilePreview {
|
||||
profile,
|
||||
cache,
|
||||
banner_height,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn banner_height(&mut self, size: Size) {
|
||||
self.banner_height = size;
|
||||
}
|
||||
|
||||
fn body(self, ui: &mut egui::Ui) {
|
||||
let padding = 12.0;
|
||||
crate::ui::padding(padding, ui, |ui| {
|
||||
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))));
|
||||
|
||||
ui.put(
|
||||
pfp_rect,
|
||||
ProfilePic::new(self.cache, get_profile_url(Some(self.profile)))
|
||||
.size(size)
|
||||
.border(ProfilePic::border_stroke(ui)),
|
||||
);
|
||||
ui.add(display_name_widget(
|
||||
&get_display_name(Some(self.profile)),
|
||||
false,
|
||||
));
|
||||
ui.add(about_section_widget(self.profile));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl egui::Widget for ProfilePreview<'_, '_> {
|
||||
fn ui(self, ui: &mut egui::Ui) -> egui::Response {
|
||||
ui.vertical(|ui| {
|
||||
banner(
|
||||
ui,
|
||||
self.profile.record().profile().and_then(|p| p.banner()),
|
||||
80.0,
|
||||
);
|
||||
|
||||
self.body(ui);
|
||||
})
|
||||
.response
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SimpleProfilePreview<'a, 'cache> {
|
||||
profile: Option<&'a ProfileRecord<'a>>,
|
||||
cache: &'cache mut Images,
|
||||
is_nsec: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'cache> SimpleProfilePreview<'a, 'cache> {
|
||||
pub fn new(
|
||||
profile: Option<&'a ProfileRecord<'a>>,
|
||||
cache: &'cache mut Images,
|
||||
is_nsec: bool,
|
||||
) -> Self {
|
||||
SimpleProfilePreview {
|
||||
profile,
|
||||
cache,
|
||||
is_nsec,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl egui::Widget for SimpleProfilePreview<'_, '_> {
|
||||
fn ui(self, ui: &mut egui::Ui) -> egui::Response {
|
||||
Frame::new()
|
||||
.show(ui, |ui| {
|
||||
ui.add(ProfilePic::new(self.cache, get_profile_url(self.profile)).size(48.0));
|
||||
ui.vertical(|ui| {
|
||||
ui.add(display_name_widget(&get_display_name(self.profile), true));
|
||||
if !self.is_nsec {
|
||||
ui.add(
|
||||
Label::new(
|
||||
RichText::new("Read only")
|
||||
.size(notedeck::fonts::get_font_size(
|
||||
ui.ctx(),
|
||||
&NotedeckTextStyle::Tiny,
|
||||
))
|
||||
.color(ui.visuals().warn_fg_color),
|
||||
)
|
||||
.selectable(false),
|
||||
);
|
||||
}
|
||||
});
|
||||
})
|
||||
.response
|
||||
}
|
||||
}
|
||||
|
||||
mod previews {
|
||||
use super::*;
|
||||
use crate::test_data::test_profile_record;
|
||||
use crate::ui::{Preview, PreviewConfig};
|
||||
use notedeck::{App, AppContext};
|
||||
|
||||
pub struct ProfilePreviewPreview<'a> {
|
||||
profile: ProfileRecord<'a>,
|
||||
}
|
||||
|
||||
impl ProfilePreviewPreview<'_> {
|
||||
pub fn new() -> Self {
|
||||
let profile = test_profile_record();
|
||||
ProfilePreviewPreview { profile }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ProfilePreviewPreview<'_> {
|
||||
fn default() -> Self {
|
||||
ProfilePreviewPreview::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl App for ProfilePreviewPreview<'_> {
|
||||
fn update(&mut self, app: &mut AppContext<'_>, ui: &mut egui::Ui) {
|
||||
ProfilePreview::new(&self.profile, app.img_cache).ui(ui);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Preview for ProfilePreview<'a, '_> {
|
||||
/// A preview of the profile preview :D
|
||||
type Prev = ProfilePreviewPreview<'a>;
|
||||
|
||||
fn preview(_cfg: PreviewConfig) -> Self::Prev {
|
||||
ProfilePreviewPreview::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn one_line_display_name_widget<'a>(
|
||||
visuals: &egui::Visuals,
|
||||
display_name: NostrName<'a>,
|
||||
style: NotedeckTextStyle,
|
||||
) -> impl egui::Widget + 'a {
|
||||
let text_style = style.text_style();
|
||||
let color = visuals.noninteractive().fg_stroke.color;
|
||||
|
||||
move |ui: &mut egui::Ui| -> egui::Response {
|
||||
ui.label(
|
||||
RichText::new(display_name.name())
|
||||
.text_style(text_style)
|
||||
.color(color),
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user