allows mentions/quotes to be made (adding "mention" "e" tags along with a reference to them in .content).

does it just like the npub-parsing thing, now with note1 parsing.

also refactors some of the previous PR that added npub parsing and fixes a bug that caused mentions to not be
recognized some times.
This commit is contained in:
fiatjaf 2023-01-17 21:33:42 -03:00
parent 275fd17b8d
commit 5cf9f21729
No known key found for this signature in database
GPG Key ID: BAD43C4BE5C1A3A1
3 changed files with 133 additions and 83 deletions

View File

@ -6,7 +6,9 @@ use crate::db::{DbEvent, DbEventSeen, DbPersonRelay, DbRelay};
use crate::error::Error;
use crate::globals::GLOBALS;
use crate::people::People;
use crate::tags::{add_pubkey_to_tags, add_tag, keys_from_text};
use crate::tags::{
add_event_to_tags, add_pubkey_hex_to_tags, add_pubkey_to_tags, keys_from_text, notes_from_text,
};
use minion::Minion;
use nostr_types::{
Event, EventKind, Id, IdHex, PreEvent, PrivateKey, PublicKey, PublicKeyHex, Tag, Unixtime, Url,
@ -626,7 +628,13 @@ impl Overlord {
// Add tags for keys that are in the post body as npub1...
for (npub, pubkey) in keys_from_text(&content) {
let idx = add_pubkey_to_tags(&mut tags, pubkey);
let idx = add_pubkey_to_tags(&mut tags, pubkey).await;
content = content.replace(&npub, &format!("#[{}]", idx));
}
// Do the same as above, but now with note1...
for (npub, pubkey) in notes_from_text(&content) {
let idx = add_event_to_tags(&mut tags, pubkey, "mention").await;
content = content.replace(&npub, &format!("#[{}]", idx));
}
@ -707,46 +715,43 @@ impl Overlord {
// Add a 'p' tag for the author we are replying to (except if it is our own key)
if let Some(pubkey) = GLOBALS.signer.read().await.public_key() {
if pubkey != event.pubkey {
add_pubkey_to_tags(&mut tags, pubkey);
add_pubkey_to_tags(&mut tags, pubkey).await;
}
}
// Add all the 'p' tags from the note we are replying to (except our own)
for tag in &event.tags {
if tag.tagname() == "p" {
add_tag(&mut tags, tag);
match tag {
Tag::Pubkey { pubkey, .. } => {
add_pubkey_hex_to_tags(&mut tags, pubkey).await;
}
_ => {}
}
}
// Add tags for keys that are in the post body as npub1...
for (npub, pubkey) in keys_from_text(&content) {
let idx = add_pubkey_to_tags(&mut tags, pubkey);
let idx = add_pubkey_to_tags(&mut tags, pubkey).await;
content = content.replace(&npub, &format!("#[{}]", idx));
}
// Do the same as above, but now with note1...
for (npub, pubkey) in notes_from_text(&content) {
// NIP-10: "Those marked with "mention" denote a quoted or reposted event id."
let idx = add_event_to_tags(&mut tags, pubkey, "mention").await;
content = content.replace(&npub, &format!("#[{}]", idx));
}
if let Some((root, _maybeurl)) = event.replies_to_root() {
// Add an 'e' tag for the root
tags.push(Tag::Event {
id: root,
recommended_relay_url: DbRelay::recommended_relay_for_reply(root).await?,
marker: Some("root".to_string()),
});
add_event_to_tags(&mut tags, root, "root").await;
// Add an 'e' tag for the note we are replying to
tags.push(Tag::Event {
id: reply_to,
recommended_relay_url: DbRelay::recommended_relay_for_reply(reply_to).await?,
marker: Some("reply".to_string()),
});
add_event_to_tags(&mut tags, reply_to, "reply").await;
} else {
// We are replying to the root.
// NIP-10: "A direct reply to the root of a thread should have a single marked "e" tag of type "root"."
tags.push(Tag::Event {
id: reply_to,
recommended_relay_url: DbRelay::recommended_relay_for_reply(reply_to).await?,
marker: Some("root".to_string()),
});
add_event_to_tags(&mut tags, reply_to, "root").await;
}
if GLOBALS.settings.read().await.set_client_tag {

View File

@ -1,8 +1,9 @@
use nostr_types::{PublicKey, Tag};
use crate::db::DbRelay;
use nostr_types::{Id, PublicKey, PublicKeyHex, Tag};
pub fn keys_from_text(text: &str) -> Vec<(String, PublicKey)> {
let mut pubkeys: Vec<(String, PublicKey)> = text
.split(&[' ', ',', '.', ':', ';', '?', '!', '/'][..])
.split(|c: char| !c.is_alphanumeric())
.filter_map(|npub| {
if !npub.starts_with("npub1") {
None
@ -18,67 +19,95 @@ pub fn keys_from_text(text: &str) -> Vec<(String, PublicKey)> {
pubkeys
}
pub fn add_pubkey_to_tags(existing_tags: &mut Vec<Tag>, added: PublicKey) -> usize {
add_tag(
existing_tags,
&Tag::Pubkey {
pubkey: added.as_hex_string().into(),
recommended_relay_url: None,
petname: None,
},
)
pub fn notes_from_text(text: &str) -> Vec<(String, Id)> {
let mut noteids: Vec<(String, Id)> = text
.split(|c: char| !c.is_alphanumeric())
.filter_map(|note| {
if !note.starts_with("note1") {
None
} else {
Id::try_from_bech32_string(&note)
.ok()
.map(|id| (note.to_string(), id))
}
})
.collect();
noteids.sort_unstable_by_key(|ni| ni.1);
noteids.dedup();
noteids
}
pub fn add_tag(existing_tags: &mut Vec<Tag>, added: &Tag) -> usize {
match added {
Tag::Pubkey { pubkey, .. } => {
pub async fn add_pubkey_hex_to_tags(existing_tags: &mut Vec<Tag>, hex: &PublicKeyHex) -> usize {
let newtag = Tag::Pubkey {
pubkey: hex.to_owned(),
recommended_relay_url: None,
petname: None,
};
match existing_tags.iter().position(|existing_tag| {
matches!(
existing_tag,
Tag::Pubkey { pubkey: existing_p, .. } if existing_p.0 == pubkey.0
Tag::Pubkey { pubkey: existing_p, .. } if existing_p.0 == hex.0
)
}) {
None => {
// add (FIXME: include relay hint it not exists)
existing_tags.push(added.to_owned());
// FIXME: include relay hint
existing_tags.push(newtag);
existing_tags.len() - 1
}
Some(idx) => idx,
}
}
Tag::Event { id, .. } => {
match existing_tags.iter().position(|existing_tag| {
matches!(
existing_tag,
Tag::Event { id: existing_e, .. } if existing_e.0 == id.0
)
}) {
None => {
// add (FIXME: include relay hint it not exists)
existing_tags.push(added.to_owned());
existing_tags.len() - 1
}
Some(idx) => idx,
}
}
Tag::Hashtag(hashtag) => {
match existing_tags.iter().position(|existing_tag| {
matches!(
existing_tag,
Tag::Hashtag(existing_hashtag) if existing_hashtag == hashtag
)
}) {
None => {
// add (FIXME: include relay hint it not exists)
existing_tags.push(added.to_owned());
existing_tags.len() - 1
}
Some(idx) => idx,
}
}
_ => {
existing_tags.push(added.to_owned());
existing_tags.len() - 1
}
}
}
pub async fn add_pubkey_to_tags(existing_tags: &mut Vec<Tag>, added: PublicKey) -> usize {
add_pubkey_hex_to_tags(existing_tags, &added.as_hex_string().into()).await
}
pub async fn add_event_to_tags(existing_tags: &mut Vec<Tag>, added: Id, marker: &str) -> usize {
let newtag = Tag::Event {
id: added,
recommended_relay_url: DbRelay::recommended_relay_for_reply(added)
.await
.ok()
.flatten(),
marker: Some(marker.to_string()),
};
match existing_tags.iter().position(|existing_tag| {
matches!(
existing_tag,
Tag::Event { id: existing_e, .. } if existing_e.0 == added.0
)
}) {
None => {
existing_tags.push(newtag);
existing_tags.len() - 1
}
Some(idx) => idx,
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_parse_pubkeys() {
let pubkeys = keys_from_text("hello npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6 and npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6... actually npub1melv683fw6n2mvhl5h6dhqd8mqfv3wmxnz4qph83ua4dk4006ezsrt5c24");
assert_eq!(pubkeys.len(), 2);
assert_eq!(
pubkeys[0].1.as_hex_string(),
"3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"
);
}
#[test]
fn test_parse_notes() {
let ids = notes_from_text(
"note1pm88wxjcqfh886gf5tvzjwe6k0crmxzdwtfnmn7ww93dh8dcrkhq82j67f
Another naïve person falling for the scam of deletes.",
);
assert_eq!(ids.len(), 1);
}
}

View File

@ -172,7 +172,7 @@ fn real_posting_area(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Fram
ui.menu_button("@", |ui| {
for pair in pairs {
if ui.button(pair.0).clicked() {
if !app.draft.ends_with(' ') {
if !app.draft.ends_with(' ') && app.draft != "" {
app.draft.push(' ');
}
app.draft.push_str(&pair.1.try_as_bech32_string().unwrap());
@ -513,6 +513,21 @@ fn render_post_actual(
ui.add_space(24.0);
// Button to quote note
if ui
.add(Label::new(RichText::new("»").size(18.0)).sense(Sense::click()))
.clicked()
{
if !app.draft.ends_with(" ") && app.draft != "" {
app.draft.push_str(" ");
}
app.draft
.push_str(&event.id.try_as_bech32_string().unwrap());
}
ui.add_space(24.0);
// Button to reply
if ui
.add(Label::new(RichText::new("💬").size(18.0)).sense(Sense::click()))
.clicked()
@ -522,6 +537,7 @@ fn render_post_actual(
ui.add_space(24.0);
// Buttons to react and reaction counts
if app.settings.reactions {
if ui
.add(