refactor: move note into its own widget
Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
253
src/app.rs
253
src/app.rs
@@ -2,18 +2,14 @@ use crate::app_creation::setup_cc;
|
|||||||
use crate::colors;
|
use crate::colors;
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::frame_history::FrameHistory;
|
use crate::frame_history::FrameHistory;
|
||||||
use crate::images::fetch_img;
|
|
||||||
use crate::imgcache::ImageCache;
|
use crate::imgcache::ImageCache;
|
||||||
use crate::notecache::NoteCache;
|
use crate::notecache::NoteCache;
|
||||||
use crate::timeline;
|
use crate::timeline;
|
||||||
use crate::ui::padding;
|
use crate::widgets;
|
||||||
use crate::widgets::note::NoteContents;
|
|
||||||
use crate::widgets::username::Username;
|
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
use egui::containers::scroll_area::ScrollBarVisibility;
|
use egui::containers::scroll_area::ScrollBarVisibility;
|
||||||
|
|
||||||
use egui::widgets::Spinner;
|
use egui::{Context, Frame, Margin, Style};
|
||||||
use egui::{Color32, Context, Frame, Label, Margin, RichText, Sense, Style, TextureHandle, Vec2};
|
|
||||||
|
|
||||||
use enostr::{ClientMessage, Filter, Pubkey, RelayEvent, RelayMessage};
|
use enostr::{ClientMessage, Filter, Pubkey, RelayEvent, RelayMessage};
|
||||||
use nostrdb::{BlockType, Config, Mention, Ndb, Note, NoteKey, Subscription, Transaction};
|
use nostrdb::{BlockType, Config, Mention, Ndb, Note, NoteKey, Subscription, Transaction};
|
||||||
@@ -81,11 +77,11 @@ pub struct Damus {
|
|||||||
//compose: String,
|
//compose: String,
|
||||||
note_cache: HashMap<NoteKey, NoteCache>,
|
note_cache: HashMap<NoteKey, NoteCache>,
|
||||||
pool: RelayPool,
|
pool: RelayPool,
|
||||||
irc_mode: bool,
|
pub textmode: bool,
|
||||||
|
|
||||||
timelines: Vec<Timeline>,
|
timelines: Vec<Timeline>,
|
||||||
|
|
||||||
img_cache: ImageCache,
|
pub img_cache: ImageCache,
|
||||||
pub ndb: Ndb,
|
pub ndb: Ndb,
|
||||||
|
|
||||||
frame_history: crate::frame_history::FrameHistory,
|
frame_history: crate::frame_history::FrameHistory,
|
||||||
@@ -448,7 +444,7 @@ impl Damus {
|
|||||||
img_cache: ImageCache::new(imgcache_dir),
|
img_cache: ImageCache::new(imgcache_dir),
|
||||||
note_cache: HashMap::new(),
|
note_cache: HashMap::new(),
|
||||||
timelines,
|
timelines,
|
||||||
irc_mode: false,
|
textmode: false,
|
||||||
ndb: Ndb::new(data_path.as_ref().to_str().expect("db path ok"), &config).expect("ndb"),
|
ndb: Ndb::new(data_path.as_ref().to_str().expect("db path ok"), &config).expect("ndb"),
|
||||||
//compose: "".to_string(),
|
//compose: "".to_string(),
|
||||||
frame_history: FrameHistory::default(),
|
frame_history: FrameHistory::default(),
|
||||||
@@ -462,72 +458,6 @@ impl Damus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint_circle(ui: &mut egui::Ui, size: f32) {
|
|
||||||
let (rect, _response) = ui.allocate_at_least(Vec2::new(size, size), Sense::hover());
|
|
||||||
ui.painter()
|
|
||||||
.circle_filled(rect.center(), size / 2.0, ui.visuals().weak_text_color());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_pfp(ui: &mut egui::Ui, damus: &mut Damus, url: &str) {
|
|
||||||
#[cfg(feature = "profiling")]
|
|
||||||
puffin::profile_function!();
|
|
||||||
|
|
||||||
let ui_size = 30.0;
|
|
||||||
|
|
||||||
// We will want to downsample these so it's not blurry on hi res displays
|
|
||||||
let img_size = (ui_size * 2.0) as u32;
|
|
||||||
|
|
||||||
let m_cached_promise = damus.img_cache.map().get(url);
|
|
||||||
if m_cached_promise.is_none() {
|
|
||||||
let res = fetch_img(&damus.img_cache, ui.ctx(), url, img_size);
|
|
||||||
damus.img_cache.map_mut().insert(url.to_owned(), res);
|
|
||||||
}
|
|
||||||
|
|
||||||
match damus.img_cache.map()[url].ready() {
|
|
||||||
None => {
|
|
||||||
ui.add(Spinner::new().size(ui_size));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Failed to fetch profile!
|
|
||||||
Some(Err(_err)) => {
|
|
||||||
let m_failed_promise = damus.img_cache.map().get(url);
|
|
||||||
if m_failed_promise.is_none() {
|
|
||||||
let no_pfp = fetch_img(&damus.img_cache, ui.ctx(), no_pfp_url(), img_size);
|
|
||||||
damus.img_cache.map_mut().insert(url.to_owned(), no_pfp);
|
|
||||||
}
|
|
||||||
|
|
||||||
match damus.img_cache.map().get(url).unwrap().ready() {
|
|
||||||
None => {
|
|
||||||
paint_circle(ui, ui_size);
|
|
||||||
}
|
|
||||||
Some(Err(_e)) => {
|
|
||||||
//error!("Image load error: {:?}", e);
|
|
||||||
paint_circle(ui, ui_size);
|
|
||||||
}
|
|
||||||
Some(Ok(img)) => {
|
|
||||||
pfp_image(ui, img, ui_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(Ok(img)) => {
|
|
||||||
pfp_image(ui, img, ui_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pfp_image<'a>(ui: &mut egui::Ui, img: &TextureHandle, size: f32) -> egui::Response {
|
|
||||||
#[cfg(feature = "profiling")]
|
|
||||||
puffin::profile_function!();
|
|
||||||
|
|
||||||
//img.show_max_size(ui, egui::vec2(size, size))
|
|
||||||
ui.add(egui::Image::new(img).max_width(size))
|
|
||||||
//.with_options()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn no_pfp_url() -> &'static str {
|
|
||||||
"https://damus.io/img/no-profile.svg"
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
fn render_notes_in_viewport(
|
fn render_notes_in_viewport(
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
@@ -570,30 +500,6 @@ fn render_notes_in_viewport(
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
fn render_reltime(
|
|
||||||
ui: &mut egui::Ui,
|
|
||||||
note_cache: &mut NoteCache,
|
|
||||||
before: bool,
|
|
||||||
) -> egui::InnerResponse<()> {
|
|
||||||
#[cfg(feature = "profiling")]
|
|
||||||
puffin::profile_function!();
|
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
let color = Color32::from_rgb(0x8A, 0x8A, 0x8A);
|
|
||||||
if before {
|
|
||||||
ui.add(Label::new(RichText::new("⋅").size(10.0).color(color)));
|
|
||||||
}
|
|
||||||
ui.add(Label::new(
|
|
||||||
RichText::new(note_cache.reltime_str())
|
|
||||||
.size(10.0)
|
|
||||||
.color(color),
|
|
||||||
));
|
|
||||||
if !before {
|
|
||||||
ui.add(Label::new(RichText::new("⋅").size(10.0).color(color)));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
fn circle_icon(ui: &mut egui::Ui, openness: f32, response: &egui::Response) {
|
fn circle_icon(ui: &mut egui::Ui, openness: f32, response: &egui::Response) {
|
||||||
let stroke = ui.style().interact(&response).fg_stroke;
|
let stroke = ui.style().interact(&response).fg_stroke;
|
||||||
@@ -609,156 +515,15 @@ struct NoteTimelineKey {
|
|||||||
note_key: NoteKey,
|
note_key: NoteKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_irc_note(
|
|
||||||
ui: &mut egui::Ui,
|
|
||||||
damus: &mut Damus,
|
|
||||||
note_key: NoteKey,
|
|
||||||
_timeline: usize,
|
|
||||||
) -> Result<()> {
|
|
||||||
let txn = Transaction::new(&damus.ndb)?;
|
|
||||||
let note = damus.ndb.get_note_by_key(&txn, note_key)?;
|
|
||||||
|
|
||||||
ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {
|
|
||||||
let profile = damus.ndb.get_profile_by_pubkey(&txn, note.pubkey());
|
|
||||||
|
|
||||||
//ui.with_layout(egui::Layout::top_down(egui::Align::LEFT), |ui| {
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.spacing_mut().item_spacing.x = 2.0;
|
|
||||||
|
|
||||||
let note_cache = damus.get_note_cache_mut(note_key, note.created_at());
|
|
||||||
let (_id, rect) = ui.allocate_space(egui::vec2(50.0, 20.0));
|
|
||||||
ui.allocate_rect(rect, Sense::hover());
|
|
||||||
ui.put(rect, |ui: &mut egui::Ui| {
|
|
||||||
render_reltime(ui, note_cache, false).response
|
|
||||||
});
|
|
||||||
let (_id, rect) = ui.allocate_space(egui::vec2(150.0, 20.0));
|
|
||||||
ui.allocate_rect(rect, Sense::hover());
|
|
||||||
ui.put(rect, |ui: &mut egui::Ui| {
|
|
||||||
ui.add(
|
|
||||||
Username::new(profile.as_ref().ok(), note.pubkey())
|
|
||||||
.abbreviated(8)
|
|
||||||
.pk_colored(true),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
ui.add(NoteContents::new(damus, &txn, ¬e, note_key));
|
|
||||||
});
|
|
||||||
|
|
||||||
//render_note_actionbar(ui);
|
|
||||||
|
|
||||||
//let header_res = ui.horizontal(|ui| {});
|
|
||||||
//});
|
|
||||||
|
|
||||||
//let resp = ui.interact(inner_resp.response.rect, id, Sense::hover());
|
|
||||||
|
|
||||||
//if resp.hovered() ^ collapse_state.is_open() {
|
|
||||||
//info!("clicked {:?}, {}", note_key, collapse_state.is_open());
|
|
||||||
//collapse_state.toggle(ui);
|
|
||||||
//collapse_state.store(ui.ctx());
|
|
||||||
//}
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_note(
|
|
||||||
ui: &mut egui::Ui,
|
|
||||||
damus: &mut Damus,
|
|
||||||
note_key: NoteKey,
|
|
||||||
timeline: usize,
|
|
||||||
) -> Result<()> {
|
|
||||||
#[cfg(feature = "profiling")]
|
|
||||||
puffin::profile_function!();
|
|
||||||
|
|
||||||
let txn = Transaction::new(&damus.ndb)?;
|
|
||||||
let note = damus.ndb.get_note_by_key(&txn, note_key)?;
|
|
||||||
let id = egui::Id::new(NoteTimelineKey { note_key, timeline });
|
|
||||||
|
|
||||||
ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {
|
|
||||||
let profile = damus.ndb.get_profile_by_pubkey(&txn, note.pubkey());
|
|
||||||
|
|
||||||
let mut collapse_state =
|
|
||||||
egui::collapsing_header::CollapsingState::load_with_default_open(ui.ctx(), id, false);
|
|
||||||
|
|
||||||
let inner_resp = padding(6.0, ui, |ui| {
|
|
||||||
match profile
|
|
||||||
.as_ref()
|
|
||||||
.ok()
|
|
||||||
.and_then(|p| p.record.profile()?.picture())
|
|
||||||
{
|
|
||||||
// these have different lifetimes and types,
|
|
||||||
// so the calls must be separate
|
|
||||||
Some(pic) => render_pfp(ui, damus, pic),
|
|
||||||
None => render_pfp(ui, damus, no_pfp_url()),
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.with_layout(egui::Layout::top_down(egui::Align::LEFT), |ui| {
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.spacing_mut().item_spacing.x = 2.0;
|
|
||||||
ui.add(Username::new(profile.as_ref().ok(), note.pubkey()).abbreviated(20));
|
|
||||||
|
|
||||||
let note_cache = damus.get_note_cache_mut(note_key, note.created_at());
|
|
||||||
render_reltime(ui, note_cache, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
ui.add(NoteContents::new(damus, &txn, ¬e, note_key));
|
|
||||||
|
|
||||||
render_note_actionbar(ui);
|
|
||||||
|
|
||||||
//let header_res = ui.horizontal(|ui| {});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
let resp = ui.interact(inner_resp.response.rect, id, Sense::hover());
|
|
||||||
|
|
||||||
if resp.hovered() ^ collapse_state.is_open() {
|
|
||||||
//info!("clicked {:?}, {}", note_key, collapse_state.is_open());
|
|
||||||
collapse_state.toggle(ui);
|
|
||||||
collapse_state.store(ui.ctx());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_note_actionbar(ui: &mut egui::Ui) -> egui::InnerResponse<()> {
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
let img_data = if ui.style().visuals.dark_mode {
|
|
||||||
egui::include_image!("../assets/icons/reply.png")
|
|
||||||
} else {
|
|
||||||
egui::include_image!("../assets/icons/reply-dark.png")
|
|
||||||
};
|
|
||||||
|
|
||||||
ui.spacing_mut().button_padding = egui::vec2(0.0, 0.0);
|
|
||||||
if ui
|
|
||||||
.add(
|
|
||||||
egui::Button::image(egui::Image::new(img_data).max_width(10.0))
|
|
||||||
//.stroke(egui::Stroke::NONE)
|
|
||||||
.frame(false)
|
|
||||||
.fill(ui.style().visuals.panel_fill),
|
|
||||||
)
|
|
||||||
.clicked()
|
|
||||||
{}
|
|
||||||
|
|
||||||
//if ui.add(egui::Button::new("like")).clicked() {}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_notes(ui: &mut egui::Ui, damus: &mut Damus, timeline: usize) {
|
fn render_notes(ui: &mut egui::Ui, damus: &mut Damus, timeline: usize) {
|
||||||
#[cfg(feature = "profiling")]
|
#[cfg(feature = "profiling")]
|
||||||
puffin::profile_function!();
|
puffin::profile_function!();
|
||||||
|
|
||||||
let num_notes = damus.timelines[timeline].notes.len();
|
let num_notes = damus.timelines[timeline].notes.len();
|
||||||
|
|
||||||
let renderer = if damus.irc_mode {
|
|
||||||
render_irc_note
|
|
||||||
} else {
|
|
||||||
render_note
|
|
||||||
};
|
|
||||||
|
|
||||||
for i in 0..num_notes {
|
for i in 0..num_notes {
|
||||||
let _ = renderer(ui, damus, damus.timelines[timeline].notes[i].key, timeline);
|
let note = widgets::Note::new(damus, damus.timelines[timeline].notes[i].key, timeline);
|
||||||
|
ui.add(note);
|
||||||
ui.add(egui::Separator::default().spacing(0.0));
|
ui.add(egui::Separator::default().spacing(0.0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -809,10 +574,10 @@ fn render_panel<'a>(ctx: &egui::Context, app: &'a mut Damus, timeline_ind: usize
|
|||||||
egui::widgets::global_dark_light_mode_switch(ui);
|
egui::widgets::global_dark_light_mode_switch(ui);
|
||||||
if ui
|
if ui
|
||||||
.add(egui::Button::new("A").frame(false))
|
.add(egui::Button::new("A").frame(false))
|
||||||
.on_hover_text("IRC mode")
|
.on_hover_text("Text mode")
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
app.irc_mode = !app.irc_mode;
|
app.textmode = !app.textmode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -1,2 +1,5 @@
|
|||||||
pub mod note;
|
pub mod note;
|
||||||
pub mod username;
|
pub mod username;
|
||||||
|
|
||||||
|
pub use note::Note;
|
||||||
|
pub use username::Username;
|
||||||
|
|||||||
@@ -1,3 +1,260 @@
|
|||||||
pub mod contents;
|
pub mod contents;
|
||||||
|
|
||||||
pub use contents::NoteContents;
|
pub use contents::NoteContents;
|
||||||
|
|
||||||
|
use crate::{widgets, Damus};
|
||||||
|
use egui::{Color32, Label, RichText, Sense, TextureHandle, Vec2};
|
||||||
|
use nostrdb::{NoteKey, Transaction};
|
||||||
|
|
||||||
|
pub struct Note<'a> {
|
||||||
|
app: &'a mut Damus,
|
||||||
|
note_key: NoteKey,
|
||||||
|
timeline: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Hash, Clone, Copy)]
|
||||||
|
struct NoteTimelineKey {
|
||||||
|
timeline: usize,
|
||||||
|
note_key: NoteKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> egui::Widget for Note<'a> {
|
||||||
|
fn ui(self, ui: &mut egui::Ui) -> egui::Response {
|
||||||
|
let res = if self.app.textmode {
|
||||||
|
self.textmode_ui(ui)
|
||||||
|
} else {
|
||||||
|
self.standard_ui(ui)
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Ok(resp) = res {
|
||||||
|
resp
|
||||||
|
} else {
|
||||||
|
ui.label("Could not render note")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Note<'a> {
|
||||||
|
pub fn new(app: &'a mut Damus, note_key: NoteKey, timeline: usize) -> Self {
|
||||||
|
Note {
|
||||||
|
app,
|
||||||
|
note_key,
|
||||||
|
timeline,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn textmode_ui(self, ui: &mut egui::Ui) -> Result<egui::Response, nostrdb::Error> {
|
||||||
|
let txn = Transaction::new(&self.app.ndb)?;
|
||||||
|
let note = self.app.ndb.get_note_by_key(&txn, self.note_key)?;
|
||||||
|
|
||||||
|
Ok(ui
|
||||||
|
.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {
|
||||||
|
let profile = self.app.ndb.get_profile_by_pubkey(&txn, note.pubkey());
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.spacing_mut().item_spacing.x = 2.0;
|
||||||
|
|
||||||
|
let note_cache = self
|
||||||
|
.app
|
||||||
|
.get_note_cache_mut(self.note_key, note.created_at());
|
||||||
|
let (_id, rect) = ui.allocate_space(egui::vec2(50.0, 20.0));
|
||||||
|
ui.allocate_rect(rect, Sense::hover());
|
||||||
|
ui.put(rect, |ui: &mut egui::Ui| {
|
||||||
|
render_reltime(ui, note_cache, false).response
|
||||||
|
});
|
||||||
|
let (_id, rect) = ui.allocate_space(egui::vec2(150.0, 20.0));
|
||||||
|
ui.allocate_rect(rect, Sense::hover());
|
||||||
|
ui.put(rect, |ui: &mut egui::Ui| {
|
||||||
|
ui.add(
|
||||||
|
widgets::Username::new(profile.as_ref().ok(), note.pubkey())
|
||||||
|
.abbreviated(8)
|
||||||
|
.pk_colored(true),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.add(NoteContents::new(self.app, &txn, ¬e, self.note_key));
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.response)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn standard_ui(self, ui: &mut egui::Ui) -> Result<egui::Response, nostrdb::Error> {
|
||||||
|
#[cfg(feature = "profiling")]
|
||||||
|
puffin::profile_function!();
|
||||||
|
|
||||||
|
let txn = Transaction::new(&self.app.ndb)?;
|
||||||
|
let note = self.app.ndb.get_note_by_key(&txn, self.note_key)?;
|
||||||
|
let note_key = self.note_key;
|
||||||
|
let timeline = self.timeline;
|
||||||
|
let id = egui::Id::new(NoteTimelineKey { note_key, timeline });
|
||||||
|
|
||||||
|
Ok(ui
|
||||||
|
.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {
|
||||||
|
let profile = self.app.ndb.get_profile_by_pubkey(&txn, note.pubkey());
|
||||||
|
|
||||||
|
let mut collapse_state =
|
||||||
|
egui::collapsing_header::CollapsingState::load_with_default_open(
|
||||||
|
ui.ctx(),
|
||||||
|
id,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
let inner_resp = crate::ui::padding(6.0, ui, |ui| {
|
||||||
|
match profile
|
||||||
|
.as_ref()
|
||||||
|
.ok()
|
||||||
|
.and_then(|p| p.record.profile()?.picture())
|
||||||
|
{
|
||||||
|
// these have different lifetimes and types,
|
||||||
|
// so the calls must be separate
|
||||||
|
Some(pic) => render_pfp(ui, self.app, pic),
|
||||||
|
None => render_pfp(ui, self.app, no_pfp_url()),
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.with_layout(egui::Layout::top_down(egui::Align::LEFT), |ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.spacing_mut().item_spacing.x = 2.0;
|
||||||
|
ui.add(
|
||||||
|
widgets::Username::new(profile.as_ref().ok(), note.pubkey())
|
||||||
|
.abbreviated(20),
|
||||||
|
);
|
||||||
|
|
||||||
|
let note_cache = self
|
||||||
|
.app
|
||||||
|
.get_note_cache_mut(self.note_key, note.created_at());
|
||||||
|
render_reltime(ui, note_cache, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.add(NoteContents::new(self.app, &txn, ¬e, self.note_key));
|
||||||
|
|
||||||
|
render_note_actionbar(ui);
|
||||||
|
|
||||||
|
//let header_res = ui.horizontal(|ui| {});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let resp = ui.interact(inner_resp.response.rect, id, Sense::hover());
|
||||||
|
|
||||||
|
if resp.hovered() ^ collapse_state.is_open() {
|
||||||
|
//info!("clicked {:?}, {}", self.note_key, collapse_state.is_open());
|
||||||
|
collapse_state.toggle(ui);
|
||||||
|
collapse_state.store(ui.ctx());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_note_actionbar(ui: &mut egui::Ui) -> egui::InnerResponse<()> {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
let img_data = if ui.style().visuals.dark_mode {
|
||||||
|
egui::include_image!("../../../assets/icons/reply.png")
|
||||||
|
} else {
|
||||||
|
egui::include_image!("../../../assets/icons/reply-dark.png")
|
||||||
|
};
|
||||||
|
|
||||||
|
ui.spacing_mut().button_padding = egui::vec2(0.0, 0.0);
|
||||||
|
if ui
|
||||||
|
.add(
|
||||||
|
egui::Button::image(egui::Image::new(img_data).max_width(10.0))
|
||||||
|
//.stroke(egui::Stroke::NONE)
|
||||||
|
.frame(false)
|
||||||
|
.fill(ui.style().visuals.panel_fill),
|
||||||
|
)
|
||||||
|
.clicked()
|
||||||
|
{}
|
||||||
|
|
||||||
|
//if ui.add(egui::Button::new("like")).clicked() {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: move to widget
|
||||||
|
fn render_pfp(ui: &mut egui::Ui, damus: &mut Damus, url: &str) {
|
||||||
|
#[cfg(feature = "profiling")]
|
||||||
|
puffin::profile_function!();
|
||||||
|
|
||||||
|
let ui_size = 30.0;
|
||||||
|
|
||||||
|
// We will want to downsample these so it's not blurry on hi res displays
|
||||||
|
let img_size = (ui_size * 2.0) as u32;
|
||||||
|
|
||||||
|
let m_cached_promise = damus.img_cache.map().get(url);
|
||||||
|
if m_cached_promise.is_none() {
|
||||||
|
let res = crate::images::fetch_img(&damus.img_cache, ui.ctx(), url, img_size);
|
||||||
|
damus.img_cache.map_mut().insert(url.to_owned(), res);
|
||||||
|
}
|
||||||
|
|
||||||
|
match damus.img_cache.map()[url].ready() {
|
||||||
|
None => {
|
||||||
|
ui.add(egui::Spinner::new().size(ui_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Failed to fetch profile!
|
||||||
|
Some(Err(_err)) => {
|
||||||
|
let m_failed_promise = damus.img_cache.map().get(url);
|
||||||
|
if m_failed_promise.is_none() {
|
||||||
|
let no_pfp =
|
||||||
|
crate::images::fetch_img(&damus.img_cache, ui.ctx(), no_pfp_url(), img_size);
|
||||||
|
damus.img_cache.map_mut().insert(url.to_owned(), no_pfp);
|
||||||
|
}
|
||||||
|
|
||||||
|
match damus.img_cache.map().get(url).unwrap().ready() {
|
||||||
|
None => {
|
||||||
|
paint_circle(ui, ui_size);
|
||||||
|
}
|
||||||
|
Some(Err(_e)) => {
|
||||||
|
//error!("Image load error: {:?}", e);
|
||||||
|
paint_circle(ui, ui_size);
|
||||||
|
}
|
||||||
|
Some(Ok(img)) => {
|
||||||
|
pfp_image(ui, img, ui_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(Ok(img)) => {
|
||||||
|
pfp_image(ui, img, ui_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pfp_image<'a>(ui: &mut egui::Ui, img: &TextureHandle, size: f32) -> egui::Response {
|
||||||
|
#[cfg(feature = "profiling")]
|
||||||
|
puffin::profile_function!();
|
||||||
|
|
||||||
|
//img.show_max_size(ui, egui::vec2(size, size))
|
||||||
|
ui.add(egui::Image::new(img).max_width(size))
|
||||||
|
//.with_options()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn no_pfp_url() -> &'static str {
|
||||||
|
"https://damus.io/img/no-profile.svg"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paint_circle(ui: &mut egui::Ui, size: f32) {
|
||||||
|
let (rect, _response) = ui.allocate_at_least(Vec2::new(size, size), Sense::hover());
|
||||||
|
ui.painter()
|
||||||
|
.circle_filled(rect.center(), size / 2.0, ui.visuals().weak_text_color());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_reltime(
|
||||||
|
ui: &mut egui::Ui,
|
||||||
|
note_cache: &mut crate::notecache::NoteCache,
|
||||||
|
before: bool,
|
||||||
|
) -> egui::InnerResponse<()> {
|
||||||
|
#[cfg(feature = "profiling")]
|
||||||
|
puffin::profile_function!();
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
let color = Color32::from_rgb(0x8A, 0x8A, 0x8A);
|
||||||
|
if before {
|
||||||
|
ui.add(Label::new(RichText::new("⋅").size(10.0).color(color)));
|
||||||
|
}
|
||||||
|
ui.add(Label::new(
|
||||||
|
RichText::new(note_cache.reltime_str())
|
||||||
|
.size(10.0)
|
||||||
|
.color(color),
|
||||||
|
));
|
||||||
|
if !before {
|
||||||
|
ui.add(Label::new(RichText::new("⋅").size(10.0).color(color)));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user