mirror of
https://github.com/mikedilger/gossip.git
synced 2024-09-19 11:43:43 +00:00
Merge pull request #155 from fiatjaf/tag-stuff
Allow mentions/quotes to be made
This commit is contained in:
commit
08c47a4060
@ -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 {
|
||||
|
135
src/tags.rs
135
src/tags.rs
@ -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(¬e)
|
||||
.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);
|
||||
}
|
||||
}
|
||||
|
@ -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(
|
||||
|
Loading…
Reference in New Issue
Block a user