commit 559086bc10e55ad5f875f5a81b0c56cbba16820b
parent b41f4c33591da82a89650dd324029d083cef4d0b
Author: William Casarin <jb55@jb55.com>
Date: Tue, 8 Jul 2025 08:41:01 -0700
Merge remote-tracking branches 'pr/9{29,30}' into master
Diffstat:
12 files changed, 140 insertions(+), 94 deletions(-)
diff --git a/assets/icons/reply-dark.png b/assets/icons/reply-dark.png
Binary files differ.
diff --git a/assets/icons/reply.png b/assets/icons/reply.png
Binary files differ.
diff --git a/assets/icons/reply.svg b/assets/icons/reply.svg
@@ -1 +1,6 @@
-<svg width="14" height="15" viewBox="0 0 14 15" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M1 4.2C1 3.07989 1 2.51984 1.21799 2.09202C1.40973 1.71569 1.71569 1.40973 2.09202 1.21799C2.51984 1 3.07989 1 4.2 1H9.8C10.9201 1 11.4801 1 11.908 1.21799C12.2843 1.40973 12.5903 1.71569 12.782 2.09202C13 2.51984 13 3.07989 13 4.2V7.8C13 8.92013 13 9.48013 12.782 9.908C12.5903 10.2843 12.2843 10.5903 11.908 10.782C11.4801 11 10.9201 11 9.8 11H8.12247C7.70647 11 7.49847 11 7.29947 11.0409C7.12293 11.0771 6.95213 11.137 6.79167 11.219C6.6108 11.3114 6.44833 11.4413 6.12347 11.7012L4.53317 12.9735C4.25578 13.1954 4.11709 13.3063 4.00036 13.3065C3.89885 13.3066 3.80281 13.2604 3.73949 13.1811C3.66667 13.0899 3.66667 12.9123 3.66667 12.557V11C3.04669 11 2.73669 11 2.48236 10.9319C1.79218 10.7469 1.25308 10.2078 1.06815 9.51767C1 9.26333 1 8.95333 1 8.33333V4.2V4.2Z" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> </svg>
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="64px" height="64px" viewBox="0 0 24 24">
+ <g fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
+ </g>
+</svg>
+\ No newline at end of file
diff --git a/assets/icons/repost.svg b/assets/icons/repost.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64px" height="64px" viewBox="0 0 24 24"><g fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="m2 9l3-3l3 3"/><path d="M13 18H7a2 2 0 0 1-2-2V6m17 9l-3 3l-3-3"/><path d="M11 6h6a2 2 0 0 1 2 2v10"/></g></svg>
+\ No newline at end of file
diff --git a/assets/icons/repost_icon_4x.png b/assets/icons/repost_icon_4x.png
Binary files differ.
diff --git a/assets/icons/repost_light.png b/assets/icons/repost_light.png
Binary files differ.
diff --git a/assets/icons/repost_light_4x.png b/assets/icons/repost_light_4x.png
Binary files differ.
diff --git a/assets/icons/zap.svg b/assets/icons/zap.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64px" height="64px" viewBox="0 0 24 24"><path fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"/></svg>
+\ No newline at end of file
diff --git a/assets/icons/zap_4x.png b/assets/icons/zap_4x.png
Binary files differ.
diff --git a/crates/notedeck_columns/src/ui/add_column.rs b/crates/notedeck_columns/src/ui/add_column.rs
@@ -2,8 +2,8 @@ use core::f32;
use std::collections::HashMap;
use egui::{
- pos2, vec2, Align, Color32, FontId, Id, Image, Margin, Pos2, Rect, RichText, Separator, Ui,
- Vec2, Widget,
+ pos2, vec2, Align, Color32, FontId, Id, Image, Margin, Pos2, Rect, RichText, ScrollArea,
+ Separator, Ui, Vec2, Widget,
};
use enostr::Pubkey;
use nostrdb::{Ndb, Transaction};
@@ -184,17 +184,21 @@ impl<'a> AddColumnView<'a> {
}
pub fn ui(&mut self, ui: &mut Ui) -> Option<AddColumnResponse> {
- let mut selected_option: Option<AddColumnResponse> = None;
- for column_option_data in self.get_base_options() {
- let option = column_option_data.option.clone();
- if self.column_option_ui(ui, column_option_data).clicked() {
- selected_option = Some(option.take_as_response(self.cur_account));
- }
+ ScrollArea::vertical()
+ .show(ui, |ui| {
+ let mut selected_option: Option<AddColumnResponse> = None;
+ for column_option_data in self.get_base_options() {
+ let option = column_option_data.option.clone();
+ if self.column_option_ui(ui, column_option_data).clicked() {
+ selected_option = Some(option.take_as_response(self.cur_account));
+ }
- ui.add(Separator::default().spacing(0.0));
- }
+ ui.add(Separator::default().spacing(0.0));
+ }
- selected_option
+ selected_option
+ })
+ .inner
}
fn notifications_ui(&mut self, ui: &mut Ui) -> Option<AddColumnResponse> {
diff --git a/crates/notedeck_ui/src/app_images.rs b/crates/notedeck_ui/src/app_images.rs
@@ -1,5 +1,5 @@
use eframe::icon_data::from_png_bytes;
-use egui::{include_image, IconData, Image};
+use egui::{include_image, Color32, IconData, Image};
pub fn app_icon() -> IconData {
from_png_bytes(include_bytes!("../../../assets/damus-app-icon.png")).expect("icon")
@@ -153,19 +153,19 @@ pub fn notifications_image() -> Image<'static> {
))
}
pub fn repost_dark_image() -> Image<'static> {
- Image::new(include_image!("../../../assets/icons/repost_icon_4x.png"))
+ Image::new(include_image!("../../../assets/icons/repost.svg"))
}
pub fn repost_light_image() -> Image<'static> {
- Image::new(include_image!("../../../assets/icons/repost_light_4x.png"))
+ Image::new(include_image!("../../../assets/icons/repost_light.png"))
}
pub fn reply_dark_image() -> Image<'static> {
- Image::new(include_image!("../../../assets/icons/reply.png"))
+ Image::new(include_image!("../../../assets/icons/reply.svg"))
}
pub fn reply_light_image() -> Image<'static> {
- Image::new(include_image!("../../../assets/icons/reply-dark.png"))
+ Image::new(include_image!("../../../assets/icons/reply.svg")).tint(Color32::BLACK)
}
pub fn profile_image() -> Image<'static> {
@@ -203,5 +203,5 @@ pub fn wallet_image() -> Image<'static> {
}
pub fn zap_image() -> Image<'static> {
- Image::new(include_image!("../../../assets/icons/zap_4x.png"))
+ Image::new(include_image!("../../../assets/icons/zap.svg"))
}
diff --git a/crates/notedeck_ui/src/note/mod.rs b/crates/notedeck_ui/src/note/mod.rs
@@ -30,6 +30,10 @@ use notedeck::{
NotedeckTextStyle, ZapTarget, Zaps,
};
+const ACTION_BAR_ICON_SIZE: f32 = 14.0;
+const ACTION_BAR_EXPAND_SIZE: f32 = 5.0;
+const ACTION_BAR_ANIM_SPEED: f32 = 0.05;
+
pub struct NoteView<'a, 'd> {
note_context: &'a mut NoteContext<'d>,
zapping_acc: Option<&'a KeypairUnowned<'a>>,
@@ -67,14 +71,6 @@ impl NoteResponse {
}
}
-/*
-impl View for NoteView<'_, '_> {
- fn ui(&mut self, ui: &mut egui::Ui) {
- self.show(ui);
- }
-}
-*/
-
impl egui::Widget for &mut NoteView<'_, '_> {
fn ui(self, ui: &mut egui::Ui) -> egui::Response {
self.show(ui).response
@@ -784,73 +780,91 @@ fn render_note_actionbar(
note_pubkey: &[u8; 32],
note_key: NoteKey,
) -> egui::InnerResponse<Option<NoteAction>> {
- ui.horizontal(|ui| 's: {
- let reply_resp = reply_button(ui, note_key);
- let quote_resp = quote_repost_button(ui, note_key);
-
- 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() {
- crate::show_pointer(ui);
- }
+ ui.add_space(10.0);
- if quote_resp.clicked() {
- break 's Some(NoteAction::Quote(to_noteid(note_id)));
- } else if quote_resp.hovered() {
- crate::show_pointer(ui);
- }
+ let icon_space = ACTION_BAR_ICON_SIZE * 2.0;
+ let width = ui.available_width();
+ let height = 22.0;
- let Some(Zapper { zaps, cur_acc }) = zapper else {
- break 's None;
- };
+ let (rect, _response) = ui.allocate_exact_size(egui::vec2(width, height), egui::Sense::click());
- let zap_target = ZapTarget::Note(NoteZapTarget {
- note_id,
- zap_recipient: note_pubkey,
- });
+ #[allow(deprecated)]
+ ui.allocate_ui_at_rect(rect, |ui| {
+ ui.horizontal(|ui| 's: {
+ let reply_resp = reply_button(ui, note_key);
- let zap_state = zaps.any_zap_state_for(cur_acc.pubkey.bytes(), zap_target);
+ ui.add_space(icon_space);
- let target = NoteZapTargetOwned {
- note_id: to_noteid(note_id),
- zap_recipient: Pubkey::new(*note_pubkey),
- };
+ let quote_resp = quote_repost_button(ui, note_key);
- if zap_state.is_err() {
- break 's Some(NoteAction::Zap(ZapAction::ClearError(target)));
- }
+ 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() {
+ crate::show_pointer(ui);
+ }
+
+ if quote_resp.clicked() {
+ break 's Some(NoteAction::Quote(to_noteid(note_id)));
+ } else if quote_resp.hovered() {
+ crate::show_pointer(ui);
+ }
- let zap_resp = {
- cur_acc.secret_key.as_ref()?;
+ let Some(Zapper { zaps, cur_acc }) = zapper else {
+ break 's None;
+ };
- 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())
- }
+ let zap_target = ZapTarget::Note(NoteZapTarget {
+ note_id,
+ zap_recipient: note_pubkey,
+ });
+
+ let zap_state = zaps.any_zap_state_for(cur_acc.pubkey.bytes(), zap_target);
+
+ let target = NoteZapTargetOwned {
+ note_id: to_noteid(note_id),
+ zap_recipient: Pubkey::new(*note_pubkey),
+ };
+
+ if zap_state.is_err() {
+ break 's Some(NoteAction::Zap(ZapAction::ClearError(target)));
}
- };
- if zap_resp.hovered() {
- crate::show_pointer(ui);
- }
+ let zap_resp = {
+ cur_acc.secret_key.as_ref()?;
- if zap_resp.secondary_clicked() {
- break 's Some(NoteAction::Zap(ZapAction::CustomizeAmount(target)));
- }
+ match zap_state {
+ Ok(any_zap_state) => {
+ ui.add_space(icon_space);
+ 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.clicked() {
- break 's None;
- }
+ if zap_resp.hovered() {
+ crate::show_pointer(ui);
+ }
+
+ if zap_resp.secondary_clicked() {
+ break 's Some(NoteAction::Zap(ZapAction::CustomizeAmount(target)));
+ }
+
+ if !zap_resp.clicked() {
+ break 's None;
+ }
- Some(NoteAction::Zap(ZapAction::Send(ZapTargetAmount {
- target,
- specified_msats: None,
- })))
+ Some(NoteAction::Zap(ZapAction::Send(ZapTargetAmount {
+ target,
+ specified_msats: None,
+ })))
+ })
})
+ .inner
}
fn secondary_label(ui: &mut egui::Ui, s: impl Into<String>) {
@@ -878,19 +892,24 @@ fn render_reltime(
}
fn reply_button(ui: &mut egui::Ui, note_key: NoteKey) -> egui::Response {
+ let id = ui.id().with(("reply_anim", note_key));
+
+ let (rect, size, resp) = crate::anim::hover_expand(
+ ui,
+ id,
+ ACTION_BAR_ICON_SIZE,
+ ACTION_BAR_EXPAND_SIZE,
+ ACTION_BAR_ANIM_SPEED,
+ );
+
+ let rect = rect.translate(egui::vec2(-(ACTION_BAR_EXPAND_SIZE / 2.0), -1.0));
+
let img = if ui.style().visuals.dark_mode {
app_images::reply_dark_image()
} else {
app_images::reply_light_image()
};
- let (rect, size, resp) =
- crate::anim::hover_expand_small(ui, ui.id().with(("reply_anim", note_key)));
-
- // align rect to note contents
- let expand_size = 5.0; // from hover_expand_small
- let rect = rect.translate(egui::vec2(-(expand_size / 2.0), 0.0));
-
let put_resp = ui
.put(rect, img.max_width(size))
.on_hover_text("Reply to this note");
@@ -907,14 +926,17 @@ fn repost_icon(dark_mode: bool) -> egui::Image<'static> {
}
fn quote_repost_button(ui: &mut egui::Ui, note_key: NoteKey) -> egui::Response {
- let size = 14.0;
- let expand_size = 5.0;
- let anim_speed = 0.05;
let id = ui.id().with(("repost_anim", note_key));
- let (rect, size, resp) = crate::anim::hover_expand(ui, id, size, expand_size, anim_speed);
+ let (rect, size, resp) = crate::anim::hover_expand(
+ ui,
+ id,
+ ACTION_BAR_ICON_SIZE,
+ ACTION_BAR_EXPAND_SIZE,
+ ACTION_BAR_ANIM_SPEED,
+ );
- let rect = rect.translate(egui::vec2(-(expand_size / 2.0), -1.0));
+ let rect = rect.translate(egui::vec2(-(ACTION_BAR_EXPAND_SIZE / 2.0), -1.0));
let put_resp = ui
.put(rect, repost_icon(ui.visuals().dark_mode).max_width(size))
@@ -925,9 +947,19 @@ fn quote_repost_button(ui: &mut egui::Ui, note_key: NoteKey) -> egui::Response {
fn zap_button(state: AnyZapState, noteid: &[u8; 32]) -> impl egui::Widget + use<'_> {
move |ui: &mut egui::Ui| -> egui::Response {
- let (rect, size, resp) = crate::anim::hover_expand_small(ui, ui.id().with("zap"));
-
- let mut img = app_images::zap_image().max_width(size);
+ let id = ui.id().with(("zap", noteid));
+
+ let (rect, size, resp) = crate::anim::hover_expand(
+ ui,
+ id,
+ ACTION_BAR_ICON_SIZE,
+ ACTION_BAR_EXPAND_SIZE,
+ ACTION_BAR_ANIM_SPEED,
+ );
+
+ let mut img = app_images::zap_image()
+ .max_width(size)
+ .tint(egui::Color32::WHITE);
let id = ui.id().with(("pulse", noteid));
let ctx = ui.ctx().clone();