note: refactor to use action composition & reduce nesting

Signed-off-by: kernelkind <kernelkind@gmail.com>
This commit is contained in:
kernelkind
2025-06-20 16:54:46 -04:00
parent c3b8823f72
commit b7bab1d29f

View File

@@ -14,6 +14,7 @@ pub use contents::{render_note_contents, render_note_preview, NoteContents};
pub use context::NoteContextButton; pub use context::NoteContextButton;
use notedeck::note::MediaAction; use notedeck::note::MediaAction;
use notedeck::note::ZapTargetAmount; use notedeck::note::ZapTargetAmount;
use notedeck::Images;
pub use options::NoteOptions; pub use options::NoteOptions;
pub use reply_description::reply_desc; pub use reply_description::reply_desc;
@@ -234,8 +235,7 @@ impl<'a, 'd> NoteView<'a, 'd> {
note_key: NoteKey, note_key: NoteKey,
profile: &Result<nostrdb::ProfileRecord<'_>, nostrdb::Error>, profile: &Result<nostrdb::ProfileRecord<'_>, nostrdb::Error>,
ui: &mut egui::Ui, ui: &mut egui::Ui,
) -> (egui::Response, Option<MediaAction>) { ) -> PfpResponse {
let mut action = None;
if !self.options().has_wide() { if !self.options().has_wide() {
ui.spacing_mut().item_spacing.x = 16.0; ui.spacing_mut().item_spacing.x = 16.0;
} else { } else {
@@ -244,68 +244,32 @@ impl<'a, 'd> NoteView<'a, 'd> {
let pfp_size = self.options().pfp_size(); let pfp_size = self.options().pfp_size();
let sense = Sense::click(); match profile
let resp = match profile
.as_ref() .as_ref()
.ok() .ok()
.and_then(|p| p.record().profile()?.picture()) .and_then(|p| p.record().profile()?.picture())
{ {
// these have different lifetimes and types, // these have different lifetimes and types,
// so the calls must be separate // so the calls must be separate
Some(pic) => { Some(pic) => show_actual_pfp(
let anim_speed = 0.05;
let profile_key = profile.as_ref().unwrap().record().note_key();
let note_key = note_key.as_u64();
let (rect, size, resp) = crate::anim::hover_expand(
ui, ui,
egui::Id::new((profile_key, note_key)),
pfp_size as f32,
NoteView::expand_size() as f32,
anim_speed,
);
let mut pfp = ProfilePic::new(self.note_context.img_cache, pic).size(size);
let pfp_resp = ui.put(rect, &mut pfp);
action = action.or(pfp.action);
if resp.hovered() || resp.clicked() {
crate::show_pointer(ui);
}
pfp_resp.on_hover_ui_at_pointer(|ui| {
ui.set_max_width(300.0);
ui.add(ProfilePreview::new(
profile.as_ref().unwrap(),
self.note_context.img_cache, self.note_context.img_cache,
)); pic,
}); pfp_size,
note_key,
profile,
),
resp None => show_fallback_pfp(ui, self.note_context.img_cache, pfp_size),
}
} }
None => { fn show_repost(
// This has to match the expand size from the above case to &mut self,
// prevent bounciness ui: &mut egui::Ui,
let size = (pfp_size + NoteView::expand_size()) as f32; txn: &Transaction,
let (rect, _response) = ui.allocate_exact_size(egui::vec2(size, size), sense); note_to_repost: Note<'_>,
) -> NoteResponse {
let mut pfp =
ProfilePic::new(self.note_context.img_cache, notedeck::profile::no_pfp_url())
.size(pfp_size as f32);
let resp = ui.put(rect, &mut pfp).interact(sense);
action = action.or(pfp.action);
resp
}
};
(resp, action)
}
pub fn show_impl(&mut self, ui: &mut egui::Ui) -> NoteResponse {
let txn = self.note.txn().expect("txn");
if let Some(note_to_repost) = get_reposted_note(self.note_context.ndb, txn, self.note) {
let profile = self let profile = self
.note_context .note_context
.ndb .ndb
@@ -345,6 +309,12 @@ impl<'a, 'd> NoteView<'a, 'd> {
self.jobs, self.jobs,
) )
.show(ui) .show(ui)
}
pub fn show_impl(&mut self, ui: &mut egui::Ui) -> NoteResponse {
let txn = self.note.txn().expect("txn");
if let Some(note_to_repost) = get_reposted_note(self.note_context.ndb, txn, self.note) {
self.show_repost(ui, txn, note_to_repost)
} else { } else {
self.show_standard(ui) self.show_standard(ui)
} }
@@ -395,19 +365,16 @@ impl<'a, 'd> NoteView<'a, 'd> {
note_key: NoteKey, note_key: NoteKey,
profile: &Result<ProfileRecord, nostrdb::Error>, profile: &Result<ProfileRecord, nostrdb::Error>,
) -> egui::InnerResponse<Option<NoteAction>> { ) -> egui::InnerResponse<Option<NoteAction>> {
let mut note_action: Option<NoteAction> = None;
ui.with_layout(egui::Layout::top_down(egui::Align::LEFT), |ui| { ui.with_layout(egui::Layout::top_down(egui::Align::LEFT), |ui| {
let mut note_action: Option<NoteAction> = None;
ui.horizontal(|ui| { ui.horizontal(|ui| {
let (pfp_resp, action) = self.pfp(note_key, profile, ui); note_action = self
if pfp_resp.clicked() { .pfp(note_key, profile, ui)
note_action = Some(NoteAction::Profile(Pubkey::new(*self.note.pubkey()))); .into_action(self.note.pubkey())
} else if let Some(action) = action { .or(note_action.take());
note_action = Some(NoteAction::Media(action));
};
let size = ui.available_size(); let size = ui.available_size();
ui.vertical(|ui| { ui.vertical(|ui| 's: {
ui.add_sized( ui.add_sized(
[size.x, self.options().pfp_size() as f32], [size.x, self.options().pfp_size() as f32],
|ui: &mut egui::Ui| { |ui: &mut egui::Ui| {
@@ -430,10 +397,12 @@ impl<'a, 'd> NoteView<'a, 'd> {
.reply .reply
.borrow(self.note.tags()); .borrow(self.note.tags());
if note_reply.reply().is_some() { if note_reply.reply().is_none() {
let action = ui break 's;
.horizontal(|ui| { }
reply_desc(
ui.horizontal(|ui| {
note_action = reply_desc(
ui, ui,
self.zapping_acc, self.zapping_acc,
txn, txn,
@@ -442,13 +411,8 @@ impl<'a, 'd> NoteView<'a, 'd> {
self.flags, self.flags,
self.jobs, self.jobs,
) )
}) .or(note_action.take());
.inner; });
if action.is_some() {
note_action = action;
}
}
}); });
}); });
@@ -463,12 +427,10 @@ impl<'a, 'd> NoteView<'a, 'd> {
ui.add(&mut contents); ui.add(&mut contents);
if let Some(action) = contents.action { note_action = contents.action.or(note_action);
note_action = Some(action);
}
if self.options().has_actionbar() { if self.options().has_actionbar() {
if let Some(action) = render_note_actionbar( note_action = render_note_actionbar(
ui, ui,
self.zapping_acc.as_ref().map(|c| Zapper { self.zapping_acc.as_ref().map(|c| Zapper {
zaps: self.note_context.zaps, zaps: self.note_context.zaps,
@@ -479,9 +441,7 @@ impl<'a, 'd> NoteView<'a, 'd> {
note_key, note_key,
) )
.inner .inner
{ .or(note_action)
note_action = Some(action);
}
} }
note_action note_action
@@ -495,19 +455,15 @@ impl<'a, 'd> NoteView<'a, 'd> {
note_key: NoteKey, note_key: NoteKey,
profile: &Result<ProfileRecord, nostrdb::Error>, profile: &Result<ProfileRecord, nostrdb::Error>,
) -> egui::InnerResponse<Option<NoteAction>> { ) -> egui::InnerResponse<Option<NoteAction>> {
let mut note_action: Option<NoteAction> = None;
// main design // main design
ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| { ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {
let (pfp_resp, action) = self.pfp(note_key, profile, ui); let mut note_action: Option<NoteAction> = self
if pfp_resp.clicked() { .pfp(note_key, profile, ui)
note_action = Some(NoteAction::Profile(Pubkey::new(*self.note.pubkey()))); .into_action(self.note.pubkey());
} else if let Some(action) = action {
note_action = Some(NoteAction::Media(action));
};
ui.with_layout(egui::Layout::top_down(egui::Align::LEFT), |ui| { ui.with_layout(egui::Layout::top_down(egui::Align::LEFT), |ui| {
NoteView::note_header(ui, self.note_context.note_cache, self.note, profile); NoteView::note_header(ui, self.note_context.note_cache, self.note, profile);
ui.horizontal(|ui| { ui.horizontal(|ui| 's: {
ui.spacing_mut().item_spacing.x = 2.0; ui.spacing_mut().item_spacing.x = 2.0;
let note_reply = self let note_reply = self
@@ -517,8 +473,11 @@ impl<'a, 'd> NoteView<'a, 'd> {
.reply .reply
.borrow(self.note.tags()); .borrow(self.note.tags());
if note_reply.reply().is_some() { if note_reply.reply().is_none() {
let action = reply_desc( break 's;
}
note_action = reply_desc(
ui, ui,
self.zapping_acc, self.zapping_acc,
txn, txn,
@@ -526,12 +485,8 @@ impl<'a, 'd> NoteView<'a, 'd> {
self.note_context, self.note_context,
self.flags, self.flags,
self.jobs, self.jobs,
); )
.or(note_action.take());
if action.is_some() {
note_action = action;
}
}
}); });
let mut contents = NoteContents::new( let mut contents = NoteContents::new(
@@ -544,12 +499,10 @@ impl<'a, 'd> NoteView<'a, 'd> {
); );
ui.add(&mut contents); ui.add(&mut contents);
if let Some(action) = contents.action { note_action = contents.action.or(note_action);
note_action = Some(action);
}
if self.options().has_actionbar() { if self.options().has_actionbar() {
if let Some(action) = render_note_actionbar( note_action = render_note_actionbar(
ui, ui,
self.zapping_acc.as_ref().map(|c| Zapper { self.zapping_acc.as_ref().map(|c| Zapper {
zaps: self.note_context.zaps, zaps: self.note_context.zaps,
@@ -560,11 +513,8 @@ impl<'a, 'd> NoteView<'a, 'd> {
note_key, note_key,
) )
.inner .inner
{ .or(note_action)
note_action = Some(action);
} }
}
note_action note_action
}) })
.inner .inner
@@ -607,19 +557,20 @@ impl<'a, 'd> NoteView<'a, 'd> {
} }
} }
let note_action = note_action = note_hitbox_clicked(ui, hitbox_id, &response.response.rect, maybe_hitbox)
if note_hitbox_clicked(ui, hitbox_id, &response.response.rect, maybe_hitbox) { .then_some(NoteAction::Note(NoteId::new(*self.note.id())))
Some(NoteAction::Note(NoteId::new(*self.note.id()))) .or(note_action);
} else {
note_action
};
NoteResponse::new(response.response).with_action(note_action) NoteResponse::new(response.response).with_action(note_action)
} }
} }
fn get_reposted_note<'a>(ndb: &Ndb, txn: &'a Transaction, note: &Note) -> Option<Note<'a>> { fn get_reposted_note<'a>(ndb: &Ndb, txn: &'a Transaction, note: &Note) -> Option<Note<'a>> {
let new_note_id: &[u8; 32] = if note.kind() == 6 { if note.kind() != 6 {
return None;
}
let new_note_id: &[u8; 32] = {
let mut res = None; let mut res = None;
for tag in note.tags().iter() { for tag in note.tags().iter() {
if tag.count() == 0 { if tag.count() == 0 {
@@ -634,14 +585,82 @@ fn get_reposted_note<'a>(ndb: &Ndb, txn: &'a Transaction, note: &Note) -> Option
} }
} }
res? res?
} else {
return None;
}; };
let note = ndb.get_note_by_id(txn, new_note_id).ok(); let note = ndb.get_note_by_id(txn, new_note_id).ok();
note.filter(|note| note.kind() == 1) note.filter(|note| note.kind() == 1)
} }
struct PfpResponse {
action: Option<MediaAction>,
response: egui::Response,
}
impl PfpResponse {
fn into_action(self, note_pk: &[u8; 32]) -> Option<NoteAction> {
if self.response.clicked() {
return Some(NoteAction::Profile(Pubkey::new(*note_pk)));
}
self.action.map(NoteAction::Media)
}
}
fn show_actual_pfp(
ui: &mut egui::Ui,
images: &mut Images,
pic: &str,
pfp_size: i8,
note_key: NoteKey,
profile: &Result<nostrdb::ProfileRecord<'_>, nostrdb::Error>,
) -> PfpResponse {
let anim_speed = 0.05;
let profile_key = profile.as_ref().unwrap().record().note_key();
let note_key = note_key.as_u64();
let (rect, size, resp) = crate::anim::hover_expand(
ui,
egui::Id::new((profile_key, note_key)),
pfp_size as f32,
NoteView::expand_size() as f32,
anim_speed,
);
let mut pfp = ProfilePic::new(images, pic).size(size);
let pfp_resp = ui.put(rect, &mut pfp);
let action = pfp.action;
if resp.hovered() || resp.clicked() {
crate::show_pointer(ui);
}
pfp_resp.on_hover_ui_at_pointer(|ui| {
ui.set_max_width(300.0);
ui.add(ProfilePreview::new(profile.as_ref().unwrap(), images));
});
PfpResponse {
response: resp,
action,
}
}
fn show_fallback_pfp(ui: &mut egui::Ui, images: &mut Images, pfp_size: i8) -> PfpResponse {
let sense = Sense::click();
// This has to match the expand size from the above case to
// prevent bounciness
let size = (pfp_size + NoteView::expand_size()) as f32;
let (rect, _response) = ui.allocate_exact_size(egui::vec2(size, size), sense);
let mut pfp = ProfilePic::new(images, notedeck::profile::no_pfp_url()).size(pfp_size as f32);
let response = ui.put(rect, &mut pfp).interact(sense);
PfpResponse {
action: pfp.action,
response,
}
}
fn note_hitbox_id( fn note_hitbox_id(
note_key: NoteKey, note_key: NoteKey,
note_options: NoteOptions, note_options: NoteOptions,