Crude 'tagging' ability, will improve over time

This commit is contained in:
Mike Dilger 2023-01-08 16:31:52 +13:00
parent aea9c1f1a2
commit 5c1fc6a510
5 changed files with 96 additions and 33 deletions

View File

@ -1,4 +1,4 @@
use nostr_types::{Event, Id, IdHex, PublicKey, PublicKeyHex};
use nostr_types::{Event, Id, IdHex, PublicKey, PublicKeyHex, Tag};
/// This is a message sent to the Overlord
#[derive(Debug, Clone)]
@ -15,8 +15,8 @@ pub enum ToOverlordMessage {
Like(Id, PublicKey),
MinionIsReady,
ProcessIncomingEvents,
PostReply(String, Id),
PostTextNote(String),
PostReply(String, Vec<Tag>, Id),
PostTextNote(String, Vec<Tag>),
SaveRelays,
SaveSettings,
Shutdown,

View File

@ -436,11 +436,11 @@ impl Overlord {
}
});
}
ToOverlordMessage::PostReply(content, reply_to) => {
self.post_reply(content, reply_to).await?;
ToOverlordMessage::PostReply(content, tags, reply_to) => {
self.post_reply(content, tags, reply_to).await?;
}
ToOverlordMessage::PostTextNote(content) => {
self.post_textnote(content).await?;
ToOverlordMessage::PostTextNote(content, tags) => {
self.post_textnote(content, tags).await?;
}
ToOverlordMessage::SaveRelays => {
let dirty_relays: Vec<DbRelay> = GLOBALS
@ -619,7 +619,7 @@ impl Overlord {
Ok(())
}
async fn post_textnote(&mut self, content: String) -> Result<(), Error> {
async fn post_textnote(&mut self, content: String, tags: Vec<Tag>) -> Result<(), Error> {
let event = {
let public_key = match GLOBALS.signer.read().await.public_key() {
Some(pk) => pk,
@ -633,7 +633,7 @@ impl Overlord {
pubkey: public_key,
created_at: Unixtime::now().unwrap(),
kind: EventKind::TextNote,
tags: vec![],
tags,
content,
ots: None,
};
@ -672,9 +672,12 @@ impl Overlord {
Ok(())
}
async fn post_reply(&mut self, content: String, reply_to: Id) -> Result<(), Error> {
let mut tags: Vec<Tag> = Vec::new();
async fn post_reply(
&mut self,
content: String,
mut tags: Vec<Tag>,
reply_to: Id,
) -> Result<(), Error> {
let event = {
let public_key = match GLOBALS.signer.read().await.public_key() {
Some(pk) => pk,
@ -720,6 +723,8 @@ impl Overlord {
.collect();
tags.extend(parent_p_tags);
// FIXME deduplicate 'p' tags
let pre_event = PreEvent {
pubkey: public_key,
created_at: Unixtime::now().unwrap(),

View File

@ -2,7 +2,7 @@ use crate::db::DbPerson;
use crate::error::Error;
use crate::globals::GLOBALS;
use image::RgbaImage;
use nostr_types::{Metadata, PublicKeyHex, Unixtime, Url};
use nostr_types::{Metadata, PublicKey, PublicKeyHex, Unixtime, Url};
use std::cmp::Ordering;
use std::collections::{HashMap, HashSet};
use std::time::Duration;
@ -333,6 +333,28 @@ impl People {
}
}
/// This lets you start typing a name, and autocomplete the results for tagging
/// someone in a post. It returns maximum 10 results.
pub fn get_ids_from_prefix(&self, mut prefix: &str) -> Vec<(String, PublicKey)> {
// work with or without the @ symbol:
if prefix.starts_with('@') {
prefix = &prefix[1..]
}
self.people
.iter()
.filter_map(|(_, person)| {
if let Some(name) = &person.name {
if name.starts_with(prefix) {
let pubkey = PublicKey::try_from_hex_string(&person.pubkey).unwrap(); // FIXME
return Some((name.clone(), pubkey));
}
}
None
})
.take(10)
.collect()
}
/// This is a 'just in case' the main code isn't keeping them in sync.
pub async fn populate_new_people() -> Result<(), Error> {
let sql = "INSERT or IGNORE INTO person (pubkey) SELECT DISTINCT pubkey FROM EVENT";

View File

@ -145,28 +145,60 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Fram
}
ui.with_layout(Layout::right_to_left(Align::TOP), |ui| {
if ui.button("Send").clicked() && !app.draft.is_empty() {
match app.replying_to {
Some(replying_to_id) => {
let _ = GLOBALS.to_overlord.send(ToOverlordMessage::PostReply(
app.draft.clone(),
replying_to_id,
));
ui.with_layout(Layout::top_down(Align::RIGHT), |ui| {
if ui.button("Send").clicked() && !app.draft.is_empty() {
match app.replying_to {
Some(replying_to_id) => {
let _ = GLOBALS.to_overlord.send(ToOverlordMessage::PostReply(
app.draft.clone(),
app.draft_tags.clone(),
replying_to_id,
));
}
None => {
let _ = GLOBALS.to_overlord.send(ToOverlordMessage::PostTextNote(
app.draft.clone(),
app.draft_tags.clone(),
));
}
}
None => {
let _ = GLOBALS
.to_overlord
.send(ToOverlordMessage::PostTextNote(app.draft.clone()));
app.draft = "".to_owned();
app.replying_to = None;
}
if ui.button("Cancel").clicked() {
app.draft = "".to_owned();
app.replying_to = None;
}
ui.add(
TextEdit::singleline(&mut app.tag_someone)
.desired_width(100.0)
.hint_text("@username"),
);
if !app.tag_someone.is_empty() {
let pairs = GLOBALS
.people
.blocking_read()
.get_ids_from_prefix(&app.tag_someone);
if !pairs.is_empty() {
ui.menu_button("@", |ui| {
for pair in pairs {
if ui.button(pair.0).clicked() {
app.draft_tags.push(Tag::Pubkey {
pubkey: pair.1,
recommended_relay_url: None, // FIXME
petname: None,
});
app.draft
.push_str(&format!("#[{}]", app.draft_tags.len() - 1));
app.tag_someone = "".to_owned();
}
}
});
}
}
app.draft = "".to_owned();
app.replying_to = None;
}
if ui.button("Cancel").clicked() {
app.draft = "".to_owned();
app.replying_to = None;
}
});
ui.add(
TextEdit::multiline(&mut app.draft)
.hint_text("Type your message here")

View File

@ -18,7 +18,7 @@ use egui::{
ColorImage, Context, ImageData, Label, RichText, SelectableLabel, Sense, TextStyle,
TextureHandle, TextureOptions, Ui,
};
use nostr_types::{Id, IdHex, PublicKey, PublicKeyHex};
use nostr_types::{Id, IdHex, PublicKey, PublicKeyHex, Tag};
use std::collections::HashMap;
use std::time::{Duration, Instant};
use zeroize::Zeroize;
@ -76,6 +76,8 @@ struct GossipUi {
icon: TextureHandle,
placeholder_avatar: TextureHandle,
draft: String,
draft_tags: Vec<Tag>,
tag_someone: String,
settings: Settings,
nip05follow: String,
follow_bech32_pubkey: String,
@ -148,6 +150,8 @@ impl GossipUi {
icon: icon_texture_handle,
placeholder_avatar: placeholder_avatar_texture_handle,
draft: "".to_owned(),
draft_tags: Vec::new(),
tag_someone: "".to_owned(),
settings,
nip05follow: "".to_owned(),
follow_bech32_pubkey: "".to_owned(),