Render tag links, e.g. @user and #event

This commit is contained in:
Mike Dilger 2023-01-04 19:12:19 +13:00
parent c216a0b5c1
commit 39e64af917
4 changed files with 64 additions and 6 deletions

1
Cargo.lock generated
View File

@ -1699,6 +1699,7 @@ dependencies = [
"nostr-types", "nostr-types",
"parking_lot", "parking_lot",
"rand 0.8.5", "rand 0.8.5",
"regex",
"reqwest", "reqwest",
"rusqlite", "rusqlite",
"serde", "serde",

View File

@ -26,6 +26,7 @@ linkify = "0.9"
nostr-types = { git = "https://github.com/mikedilger/nostr-types" } nostr-types = { git = "https://github.com/mikedilger/nostr-types" }
parking_lot = "0.12" parking_lot = "0.12"
rand = "0.8" rand = "0.8"
regex = "1.7"
reqwest = { version = "0.11", features = ["json"] } reqwest = { version = "0.11", features = ["json"] }
rusqlite = { version = "0.28", features = ["bundled", "chrono", "serde_json"] } rusqlite = { version = "0.28", features = ["bundled", "chrono", "serde_json"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }

View File

@ -9,7 +9,7 @@ use egui::{
TextEdit, Ui, Vec2, TextEdit, Ui, Vec2,
}; };
use linkify::{LinkFinder, LinkKind}; use linkify::{LinkFinder, LinkKind};
use nostr_types::{EventKind, Id, PublicKeyHex}; use nostr_types::{Event, EventKind, Id, IdHex, PublicKeyHex, Tag};
struct FeedPostParams { struct FeedPostParams {
id: Id, id: Id,
@ -344,6 +344,8 @@ fn render_post_actual(
let reactions = Globals::get_reactions_sync(event.id); let reactions = Globals::get_reactions_sync(event.id);
let tag_re = app.tag_re.clone();
// Person Things we can render: // Person Things we can render:
// pubkey // pubkey
// name // 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 // Under row
if !as_reply_to { if !as_reply_to {
@ -540,12 +544,58 @@ fn render_post_actual(
} }
} }
fn render_content(ui: &mut Ui, content: &str) { fn render_content(app: &mut GossipUi, ui: &mut Ui, tag_re: &regex::Regex, event: &Event) {
for span in LinkFinder::new().kinds(&[LinkKind::Url]).spans(content) { for span in LinkFinder::new()
.kinds(&[LinkKind::Url])
.spans(&event.content)
{
if span.kind().is_some() { if span.kind().is_some() {
ui.hyperlink_to(span.as_str(), span.as_str()); ui.hyperlink_to(span.as_str(), span.as_str());
} else { } 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::<usize>().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..]);
} }
} }
} }

View File

@ -18,7 +18,7 @@ use egui::{
ColorImage, Context, ImageData, Label, RichText, SelectableLabel, Sense, TextStyle, ColorImage, Context, ImageData, Label, RichText, SelectableLabel, Sense, TextStyle,
TextureHandle, TextureOptions, Ui, TextureHandle, TextureOptions, Ui,
}; };
use nostr_types::{Id, PublicKey, PublicKeyHex}; use nostr_types::{Id, IdHex, PublicKey, PublicKeyHex};
use std::collections::HashMap; use std::collections::HashMap;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use zeroize::Zeroize; use zeroize::Zeroize;
@ -89,6 +89,7 @@ struct GossipUi {
person_view_pubkey: Option<PublicKeyHex>, person_view_pubkey: Option<PublicKeyHex>,
avatars: HashMap<PublicKeyHex, TextureHandle>, avatars: HashMap<PublicKeyHex, TextureHandle>,
new_relay_url: String, new_relay_url: String,
tag_re: regex::Regex,
} }
impl Drop for GossipUi { impl Drop for GossipUi {
@ -161,6 +162,7 @@ impl GossipUi {
person_view_pubkey: None, person_view_pubkey: None,
avatars: HashMap::new(), avatars: HashMap::new(),
new_relay_url: "".to_owned(), 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)] #[allow(dead_code)]
pub fn pubkey_long(pubkey: &PublicKey) -> String { pub fn pubkey_long(pubkey: &PublicKey) -> String {
let hex: PublicKeyHex = (*pubkey).into(); let hex: PublicKeyHex = (*pubkey).into();