notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

commit 54c0fdb563a2d9bd4e633b8249597a4895f44863
parent 86d2a9e2e78b4d20503c67cea8b6362ff4341f05
Author: kernelkind <kernelkind@gmail.com>
Date:   Wed,  7 May 2025 14:36:56 -0400

don't show zap button if no wallet

Signed-off-by: kernelkind <kernelkind@gmail.com>

Diffstat:
Mcrates/enostr/src/keypair.rs | 4++--
Mcrates/notedeck/src/note/mod.rs | 1+
Mcrates/notedeck_columns/src/nav.rs | 3++-
Mcrates/notedeck_columns/src/ui/note/post.rs | 11+++++++++--
Mcrates/notedeck_columns/src/ui/note/reply.rs | 9+++++++--
Mcrates/notedeck_columns/src/ui/timeline.rs | 7++++++-
Mcrates/notedeck_dave/src/ui/dave.rs | 3++-
Mcrates/notedeck_ui/src/note/contents.rs | 8++++----
Mcrates/notedeck_ui/src/note/mod.rs | 94++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Mcrates/notedeck_ui/src/note/reply_description.rs | 2+-
10 files changed, 84 insertions(+), 58 deletions(-)

diff --git a/crates/enostr/src/keypair.rs b/crates/enostr/src/keypair.rs @@ -84,8 +84,8 @@ impl<'a> FilledKeypair<'a> { } } -impl<'a> From<FilledKeypair<'a>> for KeypairUnowned<'a> { - fn from(value: FilledKeypair<'a>) -> Self { +impl<'a> From<&'a FilledKeypair<'a>> for KeypairUnowned<'a> { + fn from(value: &'a FilledKeypair<'a>) -> Self { Self { pubkey: value.pubkey, secret_key: Some(value.secret_key), diff --git a/crates/notedeck/src/note/mod.rs b/crates/notedeck/src/note/mod.rs @@ -21,6 +21,7 @@ pub struct NoteContext<'d> { pub zaps: &'d mut Zaps, pub pool: &'d mut RelayPool, pub job_pool: &'d mut JobPool, + pub current_account_has_wallet: bool, } #[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)] diff --git a/crates/notedeck_columns/src/nav.rs b/crates/notedeck_columns/src/nav.rs @@ -27,7 +27,7 @@ use crate::{ use egui_nav::{Nav, NavAction, NavResponse, NavUiType}; use nostrdb::Transaction; -use notedeck::{AccountsAction, AppContext, NoteAction, NoteContext}; +use notedeck::{get_current_wallet, AccountsAction, AppContext, NoteAction, NoteContext}; use notedeck_ui::View; use tracing::error; @@ -277,6 +277,7 @@ fn render_nav_body( zaps: ctx.zaps, pool: ctx.pool, job_pool: ctx.job_pool, + current_account_has_wallet: get_current_wallet(ctx.accounts, ctx.global_wallet).is_some(), }; match top { Route::Timeline(kind) => render_timeline_route( diff --git a/crates/notedeck_columns/src/ui/note/post.rs b/crates/notedeck_columns/src/ui/note/post.rs @@ -11,7 +11,7 @@ use egui::{ widgets::text_edit::TextEdit, Frame, Layout, Margin, Pos2, ScrollArea, Sense, TextBuffer, }; -use enostr::{FilledKeypair, FullKeypair, NoteId, Pubkey, RelayPool}; +use enostr::{FilledKeypair, FullKeypair, KeypairUnowned, NoteId, Pubkey, RelayPool}; use nostrdb::{Ndb, Transaction}; use notedeck_ui::blur::PixelDimensions; use notedeck_ui::images::{get_render_state, RenderState}; @@ -341,10 +341,16 @@ impl<'a, 'd> PostView<'a, 'd> { .show(ui, |ui| { ui.vertical(|ui| { ui.set_max_width(avail_size.x * 0.8); + + let zapping_acc = self + .note_context + .current_account_has_wallet + .then(|| KeypairUnowned::from(&self.poster)); + render_note_preview( ui, self.note_context, - &Some(self.poster.into()), + zapping_acc.as_ref(), txn, id.bytes(), nostrdb::NoteKey::new(0), @@ -778,6 +784,7 @@ mod preview { zaps: app.zaps, pool: app.pool, job_pool: app.job_pool, + current_account_has_wallet: false, }; PostView::new( diff --git a/crates/notedeck_columns/src/ui/note/reply.rs b/crates/notedeck_columns/src/ui/note/reply.rs @@ -4,7 +4,7 @@ use crate::ui::{ note::{PostAction, PostResponse, PostType}, }; -use enostr::{FilledKeypair, NoteId}; +use enostr::{FilledKeypair, KeypairUnowned, NoteId}; use notedeck::NoteContext; use notedeck_ui::jobs::JobsCache; use notedeck_ui::{NoteOptions, NoteView, ProfilePic}; @@ -67,12 +67,17 @@ impl<'a, 'd> PostReplyView<'a, 'd> { let note_offset: i8 = pfp_offset - ProfilePic::medium_size() / 2 - NoteView::expand_size() / 2; + let zapping_acc = self + .note_context + .current_account_has_wallet + .then(|| KeypairUnowned::from(&self.poster)); + let quoted_note = egui::Frame::NONE .outer_margin(egui::Margin::same(note_offset)) .show(ui, |ui| { NoteView::new( self.note_context, - &Some(self.poster.into()), + zapping_acc.as_ref(), self.note, self.note_options, self.jobs, diff --git a/crates/notedeck_columns/src/ui/timeline.rs b/crates/notedeck_columns/src/ui/timeline.rs @@ -399,10 +399,15 @@ impl<'a, 'd> TimelineTabView<'a, 'd> { }; if !muted { + let zapping_acc = self + .cur_acc + .as_ref() + .filter(|_| self.note_context.current_account_has_wallet); + notedeck_ui::padding(8.0, ui, |ui| { let resp = NoteView::new( self.note_context, - self.cur_acc, + zapping_acc, &note, self.note_options, self.jobs, diff --git a/crates/notedeck_dave/src/ui/dave.rs b/crates/notedeck_dave/src/ui/dave.rs @@ -229,6 +229,7 @@ impl<'a> DaveUi<'a> { zaps: ctx.zaps, pool: ctx.pool, job_pool: ctx.job_pool, + current_account_has_wallet: false, }; let txn = Transaction::new(note_context.ndb).unwrap(); @@ -253,7 +254,7 @@ impl<'a> DaveUi<'a> { |ui| { notedeck_ui::NoteView::new( &mut note_context, - &None, + None, &note, NoteOptions::default(), jobs, diff --git a/crates/notedeck_ui/src/note/contents.rs b/crates/notedeck_ui/src/note/contents.rs @@ -18,7 +18,7 @@ use super::media::{find_renderable_media, image_carousel, RenderableMedia}; pub struct NoteContents<'a, 'd> { note_context: &'a mut NoteContext<'d>, - cur_acc: &'a Option<KeypairUnowned<'a>>, + cur_acc: Option<&'a KeypairUnowned<'a>>, txn: &'a Transaction, note: &'a Note<'a>, options: NoteOptions, @@ -30,7 +30,7 @@ impl<'a, 'd> NoteContents<'a, 'd> { #[allow(clippy::too_many_arguments)] pub fn new( note_context: &'a mut NoteContext<'d>, - cur_acc: &'a Option<KeypairUnowned<'a>>, + cur_acc: Option<&'a KeypairUnowned<'a>>, txn: &'a Transaction, note: &'a Note, options: NoteOptions, @@ -71,7 +71,7 @@ impl egui::Widget for &mut NoteContents<'_, '_> { pub fn render_note_preview( ui: &mut egui::Ui, note_context: &mut NoteContext, - cur_acc: &Option<KeypairUnowned>, + cur_acc: Option<&KeypairUnowned>, txn: &Transaction, id: &[u8; 32], parent: NoteKey, @@ -112,7 +112,7 @@ pub fn render_note_preview( pub fn render_note_contents( ui: &mut egui::Ui, note_context: &mut NoteContext, - cur_acc: &Option<KeypairUnowned>, + cur_acc: Option<&KeypairUnowned>, txn: &Transaction, note: &Note, options: NoteOptions, diff --git a/crates/notedeck_ui/src/note/mod.rs b/crates/notedeck_ui/src/note/mod.rs @@ -30,7 +30,7 @@ use notedeck::{ pub struct NoteView<'a, 'd> { note_context: &'a mut NoteContext<'d>, - cur_acc: &'a Option<KeypairUnowned<'a>>, + zapping_acc: Option<&'a KeypairUnowned<'a>>, parent: Option<NoteKey>, note: &'a nostrdb::Note<'a>, framed: bool, @@ -74,7 +74,7 @@ impl egui::Widget for &mut NoteView<'_, '_> { impl<'a, 'd> NoteView<'a, 'd> { pub fn new( note_context: &'a mut NoteContext<'d>, - cur_acc: &'a Option<KeypairUnowned<'a>>, + zapping_acc: Option<&'a KeypairUnowned<'a>>, note: &'a nostrdb::Note<'a>, mut flags: NoteOptions, jobs: &'a mut JobsCache, @@ -87,7 +87,7 @@ impl<'a, 'd> NoteView<'a, 'd> { Self { note_context, - cur_acc, + zapping_acc, parent, note, flags, @@ -214,7 +214,7 @@ impl<'a, 'd> NoteView<'a, 'd> { ui.add(&mut NoteContents::new( self.note_context, - self.cur_acc, + self.zapping_acc, txn, self.note, self.flags, @@ -338,7 +338,7 @@ impl<'a, 'd> NoteView<'a, 'd> { }); NoteView::new( self.note_context, - self.cur_acc, + self.zapping_acc, &note_to_repost, self.flags, self.jobs, @@ -441,7 +441,7 @@ impl<'a, 'd> NoteView<'a, 'd> { .horizontal(|ui| { reply_desc( ui, - self.cur_acc, + self.zapping_acc, txn, &note_reply, self.note_context, @@ -460,7 +460,7 @@ impl<'a, 'd> NoteView<'a, 'd> { let mut contents = NoteContents::new( self.note_context, - self.cur_acc, + self.zapping_acc, txn, self.note, self.flags, @@ -476,8 +476,10 @@ impl<'a, 'd> NoteView<'a, 'd> { if self.options().has_actionbar() { if let Some(action) = render_note_actionbar( ui, - self.note_context.zaps, - self.cur_acc.as_ref(), + self.zapping_acc.as_ref().map(|c| Zapper { + zaps: self.note_context.zaps, + cur_acc: c, + }), self.note.id(), self.note.pubkey(), note_key, @@ -514,7 +516,7 @@ impl<'a, 'd> NoteView<'a, 'd> { if note_reply.reply().is_some() { let action = reply_desc( ui, - self.cur_acc, + self.zapping_acc, txn, &note_reply, self.note_context, @@ -530,7 +532,7 @@ impl<'a, 'd> NoteView<'a, 'd> { let mut contents = NoteContents::new( self.note_context, - self.cur_acc, + self.zapping_acc, txn, self.note, self.flags, @@ -545,8 +547,10 @@ impl<'a, 'd> NoteView<'a, 'd> { if self.options().has_actionbar() { if let Some(action) = render_note_actionbar( ui, - self.note_context.zaps, - self.cur_acc.as_ref(), + self.zapping_acc.as_ref().map(|c| Zapper { + zaps: self.note_context.zaps, + cur_acc: c, + }), self.note.id(), self.note.pubkey(), note_key, @@ -657,11 +661,15 @@ fn note_hitbox_clicked( } } +struct Zapper<'a> { + zaps: &'a Zaps, + cur_acc: &'a KeypairUnowned<'a>, +} + #[profiling::function] fn render_note_actionbar( ui: &mut egui::Ui, - zaps: &Zaps, - cur_acc: Option<&KeypairUnowned>, + zapper: Option<Zapper>, note_id: &[u8; 32], note_pubkey: &[u8; 32], note_key: NoteKey, @@ -670,29 +678,7 @@ fn render_note_actionbar( let reply_resp = reply_button(ui, note_key); let quote_resp = quote_repost_button(ui, note_key); - let zap_target = ZapTarget::Note(NoteZapTarget { - note_id, - zap_recipient: note_pubkey, - }); - - let zap_state = cur_acc.map_or_else( - || Ok(AnyZapState::None), - |kp| zaps.any_zap_state_for(kp.pubkey.bytes(), zap_target), - ); - let zap_resp = cur_acc - .filter(|k| k.secret_key.is_some()) - .map(|_| match &zap_state { - Ok(any_zap_state) => ui.add(zap_button(any_zap_state.clone(), note_id)), - Err(zapping_error) => { - let (rect, _) = - ui.allocate_at_least(egui::vec2(10.0, 10.0), egui::Sense::click()); - ui.add(x_button(rect)) - .on_hover_text(format!("{zapping_error}")) - } - }); - let to_noteid = |id: &[u8; 32]| NoteId::new(*id); - if reply_resp.clicked() { break 's Some(NoteAction::Reply(to_noteid(note_id))); } else if reply_resp.hovered() { @@ -705,17 +691,16 @@ fn render_note_actionbar( crate::show_pointer(ui); } - let Some(zap_resp) = zap_resp else { + let Some(Zapper { zaps, cur_acc }) = zapper else { break 's None; }; - if zap_resp.hovered() { - crate::show_pointer(ui); - } + let zap_target = ZapTarget::Note(NoteZapTarget { + note_id, + zap_recipient: note_pubkey, + }); - if !zap_resp.clicked() { - break 's None; - } + let zap_state = zaps.any_zap_state_for(cur_acc.pubkey.bytes(), zap_target); let target = NoteZapTargetOwned { note_id: to_noteid(note_id), @@ -726,6 +711,27 @@ fn render_note_actionbar( break 's Some(NoteAction::Zap(ZapAction::ClearError(target))); } + let zap_resp = { + cur_acc.secret_key.as_ref()?; + + match zap_state { + Ok(any_zap_state) => ui.add(zap_button(any_zap_state, note_id)), + Err(err) => { + let (rect, _) = + ui.allocate_at_least(egui::vec2(10.0, 10.0), egui::Sense::click()); + ui.add(x_button(rect)).on_hover_text(err.to_string()) + } + } + }; + + if zap_resp.hovered() { + crate::show_pointer(ui); + } + + if !zap_resp.clicked() { + break 's None; + } + Some(NoteAction::Zap(ZapAction::Send(ZapTargetAmount { target, specified_msats: None, diff --git a/crates/notedeck_ui/src/note/reply_description.rs b/crates/notedeck_ui/src/note/reply_description.rs @@ -10,7 +10,7 @@ use notedeck::{NoteAction, NoteContext}; #[profiling::function] pub fn reply_desc( ui: &mut egui::Ui, - cur_acc: &Option<KeypairUnowned>, + cur_acc: Option<&KeypairUnowned>, txn: &Transaction, note_reply: &NoteReply, note_context: &mut NoteContext,