diff --git a/Cargo.lock b/Cargo.lock index 5ddca15d..642af157 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1699,6 +1699,7 @@ dependencies = [ "nostr-types", "parking_lot", "rand 0.8.5", + "regex", "reqwest", "rusqlite", "serde", diff --git a/Cargo.toml b/Cargo.toml index 5d17cc77..a52ec025 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ linkify = "0.9" nostr-types = { git = "https://github.com/mikedilger/nostr-types" } parking_lot = "0.12" rand = "0.8" +regex = "1.7" reqwest = { version = "0.11", features = ["json"] } rusqlite = { version = "0.28", features = ["bundled", "chrono", "serde_json"] } serde = { version = "1.0", features = ["derive"] } diff --git a/src/ui/feed.rs b/src/ui/feed.rs index 90ba1f8b..0ba55df0 100644 --- a/src/ui/feed.rs +++ b/src/ui/feed.rs @@ -9,7 +9,7 @@ use egui::{ TextEdit, Ui, Vec2, }; use linkify::{LinkFinder, LinkKind}; -use nostr_types::{EventKind, Id, PublicKeyHex}; +use nostr_types::{Event, EventKind, Id, IdHex, PublicKeyHex, Tag}; struct FeedPostParams { id: Id, @@ -344,6 +344,8 @@ fn render_post_actual( let reactions = Globals::get_reactions_sync(event.id); + let tag_re = app.tag_re.clone(); + // Person Things we can render: // pubkey // name @@ -476,7 +478,9 @@ fn render_post_actual( }); }); - render_content(ui, &event.content); + ui.horizontal_wrapped(|ui| { + render_content(app, ui, &tag_re, &event); + }); // Under row if !as_reply_to { @@ -540,12 +544,58 @@ fn render_post_actual( } } -fn render_content(ui: &mut Ui, content: &str) { - for span in LinkFinder::new().kinds(&[LinkKind::Url]).spans(content) { +fn render_content(app: &mut GossipUi, ui: &mut Ui, tag_re: ®ex::Regex, event: &Event) { + for span in LinkFinder::new() + .kinds(&[LinkKind::Url]) + .spans(&event.content) + { if span.kind().is_some() { ui.hyperlink_to(span.as_str(), span.as_str()); } else { - ui.label(span.as_str()); + let s = span.as_str(); + let mut pos = 0; + for mat in tag_re.find_iter(s) { + ui.label(&s[pos..mat.start()]); + let num: usize = s[mat.start() + 2..mat.end() - 1].parse::().unwrap(); + if let Some(tag) = event.tags.get(num) { + match tag { + Tag::Pubkey { pubkey, .. } => { + let pkhex: PublicKeyHex = (*pubkey).into(); + let nam = match GLOBALS.people.blocking_write().get(&pkhex) { + Some(p) => match p.name { + Some(n) => format!("@{}", n), + None => format!("@{}", GossipUi::hex_pubkey_short(&pkhex)), + }, + None => format!("@{}", GossipUi::hex_pubkey_short(&pkhex)), + }; + if ui.link(&nam).clicked() { + set_person_view(app, &pkhex); + }; + } + Tag::Event { id, .. } => { + let idhex: IdHex = (*id).into(); + let nam = format!("#{}", GossipUi::hex_id_short(&idhex)); + if ui.link(&nam).clicked() { + GLOBALS.feed.set_feed_to_thread(event.id); + app.page = Page::FeedThread; + }; + } + Tag::Hashtag(s) => { + if ui.link(format!("#{}", s)).clicked() { + app.status = "Gossip doesn't have a hashtag feed yet.".to_owned(); + } + } + _ => { + if ui.link(format!("#[{}]", num)).clicked() { + app.status = + "Gossip can't handle this kind of tag link yet.".to_owned(); + } + } + } + } + pos = mat.end(); + } + ui.label(&s[pos..]); } } } diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 6b7c0b74..22c53c02 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -18,7 +18,7 @@ use egui::{ ColorImage, Context, ImageData, Label, RichText, SelectableLabel, Sense, TextStyle, TextureHandle, TextureOptions, Ui, }; -use nostr_types::{Id, PublicKey, PublicKeyHex}; +use nostr_types::{Id, IdHex, PublicKey, PublicKeyHex}; use std::collections::HashMap; use std::time::{Duration, Instant}; use zeroize::Zeroize; @@ -89,6 +89,7 @@ struct GossipUi { person_view_pubkey: Option, avatars: HashMap, new_relay_url: String, + tag_re: regex::Regex, } impl Drop for GossipUi { @@ -161,6 +162,7 @@ impl GossipUi { person_view_pubkey: None, avatars: HashMap::new(), new_relay_url: "".to_owned(), + tag_re: regex::Regex::new(r"(\#\[\d+\])").unwrap(), } } } @@ -291,6 +293,10 @@ impl GossipUi { ) } + pub fn hex_id_short(idhex: &IdHex) -> String { + idhex.0[0..8].to_string() + } + #[allow(dead_code)] pub fn pubkey_long(pubkey: &PublicKey) -> String { let hex: PublicKeyHex = (*pubkey).into();