commit 26ece3bc053be5b46bdbb82661dee5f58f00c862
parent a64ff3b6304ad52201f2467d36d69e6b5db0b986
Author: Fernando López Guevara <fernando.lguevara@gmail.com>
Date: Thu, 31 Jul 2025 18:09:55 -0300
feat(note): show full created date format on selected notes
Diffstat:
5 files changed, 51 insertions(+), 16 deletions(-)
diff --git a/crates/notedeck/src/time.rs b/crates/notedeck/src/time.rs
@@ -84,9 +84,9 @@ fn time_ago_between(i18n: &mut Localization, timestamp: u64, now: u64) -> String
}
}
-pub fn time_format(_i18n: &mut Localization, timestamp: i64) -> String {
+pub fn time_format(_i18n: &mut Localization, timestamp: u64) -> String {
// TODO: format this using the selected locale
- DateTime::from_timestamp(timestamp, 0)
+ DateTime::from_timestamp(timestamp as i64, 0)
.unwrap()
.format("%l:%M %p %b %d, %Y")
.to_string()
diff --git a/crates/notedeck_columns/src/ui/thread.rs b/crates/notedeck_columns/src/ui/thread.rs
@@ -292,6 +292,7 @@ struct ThreadNote<'a> {
impl<'a> ThreadNote<'a> {
fn options(&self, mut cur_options: NoteOptions) -> NoteOptions {
+ cur_options.set(NoteOptions::ShowCreatedAtBottom, true);
match self.note_type {
ThreadNoteType::Chain { root: _ } => cur_options,
ThreadNoteType::Selected { root: _ } => {
diff --git a/crates/notedeck_ui/src/note/contents.rs b/crates/notedeck_ui/src/note/contents.rs
@@ -1,16 +1,16 @@
+use super::media::image_carousel;
use crate::{
note::{NoteAction, NoteOptions, NoteResponse, NoteView},
secondary_label,
};
-use notedeck::{JobsCache, RenderableMedia};
-
use egui::{Color32, Hyperlink, Label, RichText};
use nostrdb::{BlockType, Mention, Note, NoteKey, Transaction};
+use notedeck::{
+ time_format, update_imeta_blurhashes, IsFollowing, NoteCache, NoteContext, NotedeckTextStyle,
+};
+use notedeck::{JobsCache, RenderableMedia};
use tracing::warn;
-use super::media::image_carousel;
-use notedeck::{update_imeta_blurhashes, IsFollowing, NoteCache, NoteContext, NotedeckTextStyle};
-
pub struct NoteContents<'a, 'd> {
note_context: &'a mut NoteContext<'d>,
txn: &'a Transaction,
@@ -42,8 +42,13 @@ impl<'a, 'd> NoteContents<'a, 'd> {
impl egui::Widget for &mut NoteContents<'_, '_> {
fn ui(self, ui: &mut egui::Ui) -> egui::Response {
+ let create_at_bottom = self.options.contains(NoteOptions::ShowCreatedAtBottom);
if self.options.contains(NoteOptions::ShowNoteClientTop) {
- render_client(ui, self.note_context.note_cache, self.note);
+ render_client(ui, self.note_context.note_cache, self.note, false);
+ }
+ // bottom created at only on selected note
+ if create_at_bottom {
+ self.options.set(NoteOptions::ShowCreatedAtBottom, false);
}
let result = render_note_contents(
ui,
@@ -53,21 +58,39 @@ impl egui::Widget for &mut NoteContents<'_, '_> {
self.options,
self.jobs,
);
- if self.options.contains(NoteOptions::ShowNoteClientBottom) {
- render_client(ui, self.note_context.note_cache, self.note);
- }
+ ui.horizontal(|ui| {
+ if create_at_bottom {
+ secondary_label(
+ ui,
+ time_format(self.note_context.i18n, self.note.created_at()),
+ );
+ }
+
+ if self.options.contains(NoteOptions::ShowNoteClientBottom) {
+ render_client(
+ ui,
+ self.note_context.note_cache,
+ self.note,
+ create_at_bottom,
+ );
+ }
+ });
+
self.action = result.action;
result.response
}
}
#[profiling::function]
-fn render_client(ui: &mut egui::Ui, note_cache: &mut NoteCache, note: &Note) {
+fn render_client(ui: &mut egui::Ui, note_cache: &mut NoteCache, note: &Note, before: bool) {
let cached_note = note_cache.cached_note_or_insert_mut(note.key().unwrap(), note);
match cached_note.client.as_deref() {
Some(client) if !client.is_empty() => {
ui.horizontal(|ui| {
+ if before {
+ secondary_label(ui, "⋅");
+ }
secondary_label(ui, format!("via {client}"));
});
}
diff --git a/crates/notedeck_ui/src/note/mod.rs b/crates/notedeck_ui/src/note/mod.rs
@@ -363,13 +363,17 @@ impl<'a, 'd> NoteView<'a, 'd> {
note: &Note,
profile: &Result<nostrdb::ProfileRecord<'_>, nostrdb::Error>,
show_unread_indicator: bool,
+ flags: NoteOptions,
) {
let horiz_resp = ui
.horizontal(|ui| {
ui.spacing_mut().item_spacing.x = if is_narrow(ui.ctx()) { 1.0 } else { 2.0 };
- ui.add(Username::new(i18n, profile.as_ref().ok(), note.pubkey()).abbreviated(20));
-
- render_notetime(ui, i18n, note.created_at(), true)
+ let response = ui
+ .add(Username::new(i18n, profile.as_ref().ok(), note.pubkey()).abbreviated(20));
+ if !flags.contains(NoteOptions::ShowCreatedAtBottom) {
+ return render_notetime(ui, i18n, note.created_at(), true).response;
+ }
+ response
})
.response;
@@ -417,6 +421,7 @@ impl<'a, 'd> NoteView<'a, 'd> {
self.note,
profile,
self.show_unread_indicator,
+ self.flags,
);
})
.response
@@ -503,6 +508,8 @@ impl<'a, 'd> NoteView<'a, 'd> {
let pfp_rect = pfp_resp.bounding_rect;
let mut note_action: Option<NoteAction> = pfp_resp.into_action(self.note.pubkey());
+ self.flags.set(NoteOptions::ShowCreatedAtBottom, false);
+
ui.with_layout(egui::Layout::top_down(egui::Align::LEFT), |ui| {
NoteView::note_header(
ui,
@@ -510,6 +517,7 @@ impl<'a, 'd> NoteView<'a, 'd> {
self.note,
profile,
self.show_unread_indicator,
+ self.flags,
);
ui.horizontal_wrapped(|ui| 's: {
diff --git a/crates/notedeck_ui/src/note/options.rs b/crates/notedeck_ui/src/note/options.rs
@@ -22,11 +22,14 @@ bitflags! {
/// Is the content truncated? If the length is over a certain size it
/// will end with a ... and a "Show more" button.
const Truncate = 1 << 11;
- /// Show note's client in the note header
+ /// Show note's client in the note content
const ShowNoteClientTop = 1 << 12;
const ShowNoteClientBottom = 1 << 13;
const RepliesNewestFirst = 1 << 14;
+
+ // Show note's created at note bottom
+ const ShowCreatedAtBottom = 1 << 15;
}
}