ui/replydesc: quick TextSegment cleanup/optimize

most a micro-optimize + cleanup

Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
William Casarin
2025-07-24 09:03:47 -07:00
parent 4e27c1f491
commit 90975180f5

View File

@@ -7,16 +7,16 @@ use notedeck::{tr, NoteAction, NoteContext};
// Rich text segment types for internationalized rendering // Rich text segment types for internationalized rendering
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum TextSegment { pub enum TextSegment<'a> {
Plain(String), Plain(String),
UserMention([u8; 32]), // pubkey UserMention(Option<&'a [u8; 32]>), // pubkey
ThreadUserMention([u8; 32]), // pubkey ThreadUserMention(Option<&'a [u8; 32]>), // pubkey
NoteLink([u8; 32]), NoteLink(Option<&'a [u8; 32]>),
ThreadLink([u8; 32]), ThreadLink(Option<&'a [u8; 32]>),
} }
// Helper function to parse i18n template strings with placeholders // Helper function to parse i18n template strings with placeholders
fn parse_i18n_template(template: &str) -> Vec<TextSegment> { fn parse_i18n_template(template: &str) -> Vec<TextSegment<'_>> {
let mut segments = Vec::new(); let mut segments = Vec::new();
let mut current_text = String::new(); let mut current_text = String::new();
let mut chars = template.chars().peekable(); let mut chars = template.chars().peekable();
@@ -41,10 +41,10 @@ fn parse_i18n_template(template: &str) -> Vec<TextSegment> {
// Handle different placeholder types // Handle different placeholder types
match placeholder.as_str() { match placeholder.as_str() {
// Placeholder values will be filled later. // Placeholder values will be filled later.
"user" => segments.push(TextSegment::UserMention([0; 32])), "user" => segments.push(TextSegment::UserMention(None)),
"thread_user" => segments.push(TextSegment::ThreadUserMention([0; 32])), "thread_user" => segments.push(TextSegment::ThreadUserMention(None)),
"note" => segments.push(TextSegment::NoteLink([0; 32])), "note" => segments.push(TextSegment::NoteLink(None)),
"thread" => segments.push(TextSegment::ThreadLink([0; 32])), "thread" => segments.push(TextSegment::ThreadLink(None)),
_ => { _ => {
// Unknown placeholder, treat as plain text // Unknown placeholder, treat as plain text
current_text.push_str(&format!("{{{placeholder}}}")); current_text.push_str(&format!("{{{placeholder}}}"));
@@ -64,39 +64,45 @@ fn parse_i18n_template(template: &str) -> Vec<TextSegment> {
} }
// Helper function to fill in the actual data for placeholders // Helper function to fill in the actual data for placeholders
fn fill_template_data( fn fill_template_data<'a>(
mut segments: Vec<TextSegment>, segments: &mut [TextSegment<'a>],
reply_pubkey: &[u8; 32], reply_pubkey: &'a [u8; 32],
reply_note_id: &[u8; 32], reply_note_id: &'a [u8; 32],
root_pubkey: Option<&[u8; 32]>, root_pubkey: Option<&'a [u8; 32]>,
root_note_id: Option<&[u8; 32]>, root_note_id: Option<&'a [u8; 32]>,
) -> Vec<TextSegment> { ) {
for segment in &mut segments { for segment in segments {
match segment { match segment {
TextSegment::UserMention(pubkey) if *pubkey == [0; 32] => { TextSegment::UserMention(pubkey) => {
*pubkey = *reply_pubkey; if pubkey.is_none() {
*pubkey = Some(reply_pubkey);
}
} }
TextSegment::ThreadUserMention(pubkey) if *pubkey == [0; 32] => { TextSegment::ThreadUserMention(pubkey) => {
*pubkey = *root_pubkey.unwrap_or(reply_pubkey); if pubkey.is_none() {
*pubkey = Some(root_pubkey.unwrap_or(reply_pubkey));
}
} }
TextSegment::NoteLink(note_id) if *note_id == [0; 32] => { TextSegment::NoteLink(note_id) => {
*note_id = *reply_note_id; if note_id.is_none() {
*note_id = Some(reply_note_id);
}
} }
TextSegment::ThreadLink(note_id) if *note_id == [0; 32] => { TextSegment::ThreadLink(note_id) => {
*note_id = *root_note_id.unwrap_or(reply_note_id); if note_id.is_none() {
*note_id = Some(root_note_id.unwrap_or(reply_note_id));
}
} }
_ => {} TextSegment::Plain(_) => {}
} }
} }
segments
} }
// Main rendering function for text segments // Main rendering function for text segments
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn render_text_segments( fn render_text_segments(
ui: &mut egui::Ui, ui: &mut egui::Ui,
segments: &[TextSegment], segments: &[TextSegment<'_>],
txn: &Transaction, txn: &Transaction,
note_context: &mut NoteContext, note_context: &mut NoteContext,
note_options: NoteOptions, note_options: NoteOptions,
@@ -117,17 +123,25 @@ fn render_text_segments(
); );
} }
TextSegment::UserMention(pubkey) | TextSegment::ThreadUserMention(pubkey) => { TextSegment::UserMention(pubkey) | TextSegment::ThreadUserMention(pubkey) => {
let action = Mention::new(note_context.ndb, note_context.img_cache, txn, pubkey) let action = Mention::new(
.size(size) note_context.ndb,
.selectable(selectable) note_context.img_cache,
.show(ui); txn,
pubkey.expect("expected pubkey"),
)
.size(size)
.selectable(selectable)
.show(ui);
if action.is_some() { if action.is_some() {
note_action = action; note_action = action;
} }
} }
TextSegment::NoteLink(note_id) => { TextSegment::NoteLink(note_id) => {
if let Ok(note) = note_context.ndb.get_note_by_id(txn, note_id) { if let Ok(note) = note_context
.ndb
.get_note_by_id(txn, note_id.expect("expected text segment note_id"))
{
let r = ui.add( let r = ui.add(
Label::new( Label::new(
RichText::new(tr!( RichText::new(tr!(
@@ -158,7 +172,10 @@ fn render_text_segments(
} }
} }
TextSegment::ThreadLink(note_id) => { TextSegment::ThreadLink(note_id) => {
if let Ok(note) = note_context.ndb.get_note_by_id(txn, note_id) { if let Ok(note) = note_context
.ndb
.get_note_by_id(txn, note_id.expect("expected text segment threadlink"))
{
let r = ui.add( let r = ui.add(
Label::new( Label::new(
RichText::new(tr!( RichText::new(tr!(
@@ -231,7 +248,7 @@ pub fn reply_desc(
); );
}; };
let segments = if note_reply.is_reply_to_root() { if note_reply.is_reply_to_root() {
// Template: "replying to {user}'s {thread}" // Template: "replying to {user}'s {thread}"
let template = tr!( let template = tr!(
note_context.i18n, note_context.i18n,
@@ -240,13 +257,23 @@ pub fn reply_desc(
user = "{user}", user = "{user}",
thread = "{thread}" thread = "{thread}"
); );
let segments = parse_i18n_template(&template); let mut segments = parse_i18n_template(&template);
fill_template_data( fill_template_data(
segments, &mut segments,
reply_note.pubkey(), reply_note.pubkey(),
reply.id, reply.id,
None, None,
Some(reply.id), Some(reply.id),
);
render_text_segments(
ui,
&segments,
txn,
note_context,
note_options,
jobs,
size,
selectable,
) )
} else if let Some(root) = note_reply.root() { } else if let Some(root) = note_reply.root() {
if let Ok(root_note) = note_context.ndb.get_note_by_id(txn, root.id) { if let Ok(root_note) = note_context.ndb.get_note_by_id(txn, root.id) {
@@ -259,8 +286,18 @@ pub fn reply_desc(
user = "{user}", user = "{user}",
note = "{note}" note = "{note}"
); );
let segments = parse_i18n_template(&template); let mut segments = parse_i18n_template(&template);
fill_template_data(segments, reply_note.pubkey(), reply.id, None, None) fill_template_data(&mut segments, reply_note.pubkey(), reply.id, None, None);
render_text_segments(
ui,
&segments,
txn,
note_context,
note_options,
jobs,
size,
selectable,
)
} else { } else {
// Template: "replying to {reply_user}'s {note} in {thread_user}'s {thread}" // Template: "replying to {reply_user}'s {note} in {thread_user}'s {thread}"
// This would need more sophisticated placeholder handling // This would need more sophisticated placeholder handling
@@ -273,13 +310,23 @@ pub fn reply_desc(
thread_user = "{thread_user}", thread_user = "{thread_user}",
thread = "{thread}" thread = "{thread}"
); );
let segments = parse_i18n_template(&template); let mut segments = parse_i18n_template(&template);
fill_template_data( fill_template_data(
segments, &mut segments,
reply_note.pubkey(), reply_note.pubkey(),
reply.id, reply.id,
Some(root_note.pubkey()), Some(root_note.pubkey()),
Some(root.id), Some(root.id),
);
render_text_segments(
ui,
&segments,
txn,
note_context,
note_options,
jobs,
size,
selectable,
) )
} }
} else { } else {
@@ -290,8 +337,18 @@ pub fn reply_desc(
"Template for replying to user in unknown thread", "Template for replying to user in unknown thread",
user = "{user}" user = "{user}"
); );
let segments = parse_i18n_template(&template); let mut segments = parse_i18n_template(&template);
fill_template_data(segments, reply_note.pubkey(), reply.id, None, None) fill_template_data(&mut segments, reply_note.pubkey(), reply.id, None, None);
render_text_segments(
ui,
&segments,
txn,
note_context,
note_options,
jobs,
size,
selectable,
)
} }
} else { } else {
// Fallback // Fallback
@@ -301,18 +358,17 @@ pub fn reply_desc(
"Fallback template for replying to user", "Fallback template for replying to user",
user = "{user}" user = "{user}"
); );
let segments = parse_i18n_template(&template); let mut segments = parse_i18n_template(&template);
fill_template_data(segments, reply_note.pubkey(), reply.id, None, None) fill_template_data(&mut segments, reply_note.pubkey(), reply.id, None, None);
}; render_text_segments(
ui,
render_text_segments( &segments,
ui, txn,
&segments, note_context,
txn, note_options,
note_context, jobs,
note_options, size,
jobs, selectable,
size, )
selectable, }
)
} }