Big rewrite of DbPerson to match database

This commit is contained in:
Mike Dilger 2023-01-20 10:29:02 +13:00
parent 0a5814163d
commit 9b267f6c99
9 changed files with 235 additions and 172 deletions

View File

@ -16,9 +16,6 @@ pub use event_relationship::DbEventRelationship;
mod relay; mod relay;
pub use relay::DbRelay; pub use relay::DbRelay;
mod person;
pub use person::DbPerson;
mod contact; mod contact;
pub use contact::DbContact; pub use contact::DbContact;

View File

@ -1,16 +0,0 @@
use nostr_types::PublicKeyHex;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DbPerson {
pub pubkey: PublicKeyHex,
pub name: Option<String>,
pub about: Option<String>,
pub picture: Option<String>,
pub dns_id: Option<String>,
pub dns_id_valid: u8,
pub dns_id_last_checked: Option<u64>,
pub metadata_at: Option<i64>,
pub followed: u8,
pub followed_last_updated: i64,
}

View File

@ -1,7 +1,8 @@
use crate::db::{DbPerson, DbPersonRelay, DbRelay}; use crate::db::{DbPersonRelay, DbRelay};
use crate::error::Error; use crate::error::Error;
use crate::globals::GLOBALS; use crate::globals::GLOBALS;
use nostr_types::{Nip05, Unixtime, Url}; use crate::people::DbPerson;
use nostr_types::{Metadata, Nip05, Unixtime, Url};
// This updates the people map and the database with the result // This updates the people map and the database with the result
#[allow(dead_code)] #[allow(dead_code)]
@ -9,29 +10,31 @@ pub async fn validate_nip05(person: DbPerson) -> Result<(), Error> {
let now = Unixtime::now().unwrap(); let now = Unixtime::now().unwrap();
// invalid if their nip-05 is not set // invalid if their nip-05 is not set
if person.dns_id.is_none() { if person.metadata.is_none() || matches!(person.metadata, Some(Metadata { nip05: None, .. })) {
GLOBALS GLOBALS
.people .people
.upsert_nip05_validity(&person.pubkey, person.dns_id, false, now.0 as u64) .upsert_nip05_validity(&person.pubkey, None, false, now.0 as u64)
.await?; .await?;
return Ok(()); return Ok(());
} }
let metadata = person.metadata.as_ref().unwrap().to_owned();
let nip05 = metadata.nip05.as_ref().unwrap().to_owned();
// Split their DNS ID // Split their DNS ID
let dns_id = person.dns_id.clone().unwrap(); let (user, domain) = match parse_nip05(&nip05) {
let (user, domain) = match parse_dns_id(&dns_id) {
Ok(pair) => pair, Ok(pair) => pair,
Err(_) => { Err(_) => {
GLOBALS GLOBALS
.people .people
.upsert_nip05_validity(&person.pubkey, person.dns_id, false, now.0 as u64) .upsert_nip05_validity(&person.pubkey, Some(nip05), false, now.0 as u64)
.await?; .await?;
return Ok(()); return Ok(());
} }
}; };
// Fetch NIP-05 // Fetch NIP-05
let nip05 = match fetch_nip05(&user, &domain).await { let nip05file = match fetch_nip05(&user, &domain).await {
Ok(content) => content, Ok(content) => content,
Err(e) => { Err(e) => {
tracing::error!("NIP-05 fetch issue with {}@{}", user, domain); tracing::error!("NIP-05 fetch issue with {}@{}", user, domain);
@ -40,13 +43,13 @@ pub async fn validate_nip05(person: DbPerson) -> Result<(), Error> {
}; };
// Check if the response matches their public key // Check if the response matches their public key
match nip05.names.get(&user) { match nip05file.names.get(&user) {
Some(pk) => { Some(pk) => {
if *pk == person.pubkey { if *pk == person.pubkey {
// Validated // Validated
GLOBALS GLOBALS
.people .people
.upsert_nip05_validity(&person.pubkey, person.dns_id, true, now.0 as u64) .upsert_nip05_validity(&person.pubkey, Some(nip05), true, now.0 as u64)
.await?; .await?;
} }
} }
@ -54,7 +57,7 @@ pub async fn validate_nip05(person: DbPerson) -> Result<(), Error> {
// Failed // Failed
GLOBALS GLOBALS
.people .people
.upsert_nip05_validity(&person.pubkey, person.dns_id, false, now.0 as u64) .upsert_nip05_validity(&person.pubkey, Some(nip05), false, now.0 as u64)
.await?; .await?;
} }
} }
@ -62,15 +65,15 @@ pub async fn validate_nip05(person: DbPerson) -> Result<(), Error> {
Ok(()) Ok(())
} }
pub async fn get_and_follow_nip05(dns_id: String) -> Result<(), Error> { pub async fn get_and_follow_nip05(nip05: String) -> Result<(), Error> {
// Split their DNS ID // Split their DNS ID
let (user, domain) = parse_dns_id(&dns_id)?; let (user, domain) = parse_nip05(&nip05)?;
// Fetch NIP-05 // Fetch NIP-05
let nip05 = fetch_nip05(&user, &domain).await?; let nip05file = fetch_nip05(&user, &domain).await?;
// Get their pubkey // Get their pubkey
let pubkey = match nip05.names.get(&user) { let pubkey = match nip05file.names.get(&user) {
Some(pk) => pk, Some(pk) => pk,
None => return Err(Error::Nip05KeyNotFound), None => return Err(Error::Nip05KeyNotFound),
}; };
@ -80,7 +83,7 @@ pub async fn get_and_follow_nip05(dns_id: String) -> Result<(), Error> {
.people .people
.upsert_nip05_validity( .upsert_nip05_validity(
pubkey, pubkey,
Some(dns_id.clone()), Some(nip05.clone()),
true, true,
Unixtime::now().unwrap().0 as u64, Unixtime::now().unwrap().0 as u64,
) )
@ -89,10 +92,10 @@ pub async fn get_and_follow_nip05(dns_id: String) -> Result<(), Error> {
// Mark as followed // Mark as followed
GLOBALS.people.async_follow(pubkey, true).await?; GLOBALS.people.async_follow(pubkey, true).await?;
tracing::info!("Followed {}", &dns_id); tracing::info!("Followed {}", &nip05);
// Set their relays // Set their relays
let relays = match nip05.relays.get(pubkey) { let relays = match nip05file.relays.get(pubkey) {
Some(relays) => relays, Some(relays) => relays,
None => return Err(Error::Nip05RelaysNotFound), None => return Err(Error::Nip05RelaysNotFound),
}; };
@ -113,14 +116,14 @@ pub async fn get_and_follow_nip05(dns_id: String) -> Result<(), Error> {
} }
} }
tracing::info!("Setup {} relays for {}", relays.len(), &dns_id); tracing::info!("Setup {} relays for {}", relays.len(), &nip05);
Ok(()) Ok(())
} }
// returns user and domain // returns user and domain
fn parse_dns_id(dns_id: &str) -> Result<(String, String), Error> { fn parse_nip05(nip05: &str) -> Result<(String, String), Error> {
let mut parts: Vec<&str> = dns_id.split('@').collect(); let mut parts: Vec<&str> = nip05.split('@').collect();
// Add the underscore as a username if they just specified a domain name. // Add the underscore as a username if they just specified a domain name.
if parts.len() == 1 { if parts.len() == 1 {

View File

@ -393,9 +393,9 @@ impl Overlord {
ToOverlordMessage::FollowHex(hex, relay) => { ToOverlordMessage::FollowHex(hex, relay) => {
Overlord::follow_hexkey(hex, relay).await?; Overlord::follow_hexkey(hex, relay).await?;
} }
ToOverlordMessage::FollowNip05(dns_id) => { ToOverlordMessage::FollowNip05(nip05) => {
let _ = tokio::spawn(async move { let _ = tokio::spawn(async move {
if let Err(e) = crate::nip05::get_and_follow_nip05(dns_id).await { if let Err(e) = crate::nip05::get_and_follow_nip05(nip05).await {
tracing::error!("{}", e); tracing::error!("{}", e);
} }
}); });

View File

@ -1,4 +1,4 @@
use crate::db::{DbPerson, DbPersonRelay}; use crate::db::DbPersonRelay;
use crate::error::Error; use crate::error::Error;
use crate::globals::GLOBALS; use crate::globals::GLOBALS;
use crate::AVATAR_SIZE; use crate::AVATAR_SIZE;
@ -9,10 +9,56 @@ use image::imageops::FilterType;
use nostr_types::{ use nostr_types::{
Event, EventKind, Metadata, PreEvent, PublicKey, PublicKeyHex, Tag, Unixtime, Url, Event, EventKind, Metadata, PreEvent, PublicKey, PublicKeyHex, Tag, Unixtime, Url,
}; };
use serde::{Deserialize, Serialize};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::time::Duration; use std::time::Duration;
use tokio::task; use tokio::task;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DbPerson {
pub pubkey: PublicKeyHex,
pub metadata: Option<Metadata>,
pub metadata_at: Option<i64>,
pub nip05_valid: u8,
pub nip05_last_checked: Option<u64>,
pub followed: u8,
pub followed_last_updated: i64,
}
impl DbPerson {
pub fn name(&self) -> Option<&str> {
if let Some(md) = &self.metadata {
md.name.as_deref()
} else {
None
}
}
pub fn about(&self) -> Option<&str> {
if let Some(md) = &self.metadata {
md.about.as_deref()
} else {
None
}
}
pub fn picture(&self) -> Option<&str> {
if let Some(md) = &self.metadata {
md.picture.as_deref()
} else {
None
}
}
pub fn nip05(&self) -> Option<&str> {
if let Some(md) = &self.metadata {
md.nip05.as_deref()
} else {
None
}
}
}
pub struct People { pub struct People {
people: DashMap<PublicKeyHex, DbPerson>, people: DashMap<PublicKeyHex, DbPerson>,
@ -110,17 +156,17 @@ impl People {
} }
} }
if doit { if doit {
// Process fresh metadata let nip05_changed = if let Some(md) = &person.metadata {
metadata.nip05 != md.nip05.clone()
person.name = metadata.name; } else {
person.about = metadata.about; metadata.nip05.is_some()
person.picture = metadata.picture; };
if person.dns_id != metadata.nip05 { person.metadata = Some(metadata);
person.dns_id = metadata.nip05;
person.dns_id_valid = 0; // changed, so reset to invalid
person.dns_id_last_checked = None; // we haven't checked this one yet
}
person.metadata_at = Some(asof.0); person.metadata_at = Some(asof.0);
if nip05_changed {
person.nip05_valid = 0; // changed, so reset to invalid
person.nip05_last_checked = None; // we haven't checked this one yet
}
// Update the database // Update the database
let person = person.clone(); let person = person.clone();
@ -129,15 +175,20 @@ impl People {
let maybe_db = GLOBALS.db.blocking_lock(); let maybe_db = GLOBALS.db.blocking_lock();
let db = maybe_db.as_ref().unwrap(); let db = maybe_db.as_ref().unwrap();
let metadata_json: Option<String> = if let Some(md) = &person.metadata {
Some(serde_json::to_string(md)?)
} else {
None
};
let mut stmt = db.prepare( let mut stmt = db.prepare(
"UPDATE person SET name=?, about=?, picture=?, dns_id=?, metadata_at=? WHERE pubkey=?" "UPDATE person SET metadata=?, metadata_at=?, nip05_valid=?, nip05_last_checked=? WHERE pubkey=?"
)?; )?;
stmt.execute(( stmt.execute((
&person.name, &metadata_json,
&person.about,
&person.picture,
&person.dns_id,
&person.metadata_at, &person.metadata_at,
&person.nip05_valid,
&person.nip05_last_checked,
&pubkeyhex2.0, &pubkeyhex2.0,
))?; ))?;
Ok::<(), Error>(()) Ok::<(), Error>(())
@ -151,20 +202,26 @@ impl People {
let person = person.to_owned(); let person = person.to_owned();
// Only if they have a nip05 dns id set // Only if they have a nip05 dns id set
if person.dns_id.is_some() { if matches!(
person,
DbPerson {
metadata: Some(Metadata { nip05: Some(_), .. }),
..
}
) {
// Recheck nip05 every day if invalid, and every two weeks if valid // Recheck nip05 every day if invalid, and every two weeks if valid
// FIXME make these settings // FIXME make these settings
let recheck_duration = if person.dns_id_valid > 0 { let recheck_duration = if person.nip05_valid > 0 {
Duration::from_secs(60 * 60 * 24 * 14) Duration::from_secs(60 * 60 * 24 * 14)
} else { } else {
Duration::from_secs(60 * 60 * 24) Duration::from_secs(60 * 60 * 24)
}; };
// Maybe validate nip05 // Maybe validate nip05
if let Some(last) = person.dns_id_last_checked { if let Some(last) = person.nip05_last_checked {
if Unixtime::now().unwrap() - Unixtime(last as i64) > recheck_duration { if Unixtime::now().unwrap() - Unixtime(last as i64) > recheck_duration {
// recheck // recheck
self.update_dns_id_last_checked(person.pubkey.clone()) self.update_nip05_last_checked(person.pubkey.clone())
.await?; .await?;
task::spawn(async move { task::spawn(async move {
if let Err(e) = crate::nip05::validate_nip05(person).await { if let Err(e) = crate::nip05::validate_nip05(person).await {
@ -173,7 +230,7 @@ impl People {
}); });
} }
} else { } else {
self.update_dns_id_last_checked(person.pubkey.clone()) self.update_nip05_last_checked(person.pubkey.clone())
.await?; .await?;
task::spawn(async move { task::spawn(async move {
if let Err(e) = crate::nip05::validate_nip05(person).await { if let Err(e) = crate::nip05::validate_nip05(person).await {
@ -194,9 +251,8 @@ impl People {
)); ));
} }
let sql = let sql = "SELECT pubkey, metadata, metadata_at, nip05_valid, nip05_last_checked, \
"SELECT pubkey, name, about, picture, dns_id, dns_id_valid, dns_id_last_checked, \ followed, followed_last_updated FROM person WHERE followed=1"
metadata_at, followed, followed_last_updated FROM person WHERE followed=1"
.to_owned(); .to_owned();
let output: Result<Vec<DbPerson>, Error> = task::spawn_blocking(move || { let output: Result<Vec<DbPerson>, Error> = task::spawn_blocking(move || {
@ -204,23 +260,23 @@ impl People {
let db = maybe_db.as_ref().unwrap(); let db = maybe_db.as_ref().unwrap();
let mut stmt = db.prepare(&sql)?; let mut stmt = db.prepare(&sql)?;
let rows = stmt.query_map([], |row| { let mut rows = stmt.query([])?;
Ok(DbPerson {
pubkey: PublicKeyHex(row.get(0)?),
name: row.get(1)?,
about: row.get(2)?,
picture: row.get(3)?,
dns_id: row.get(4)?,
dns_id_valid: row.get(5)?,
dns_id_last_checked: row.get(6)?,
metadata_at: row.get(7)?,
followed: row.get(8)?,
followed_last_updated: row.get(9)?,
})
})?;
let mut output: Vec<DbPerson> = Vec::new(); let mut output: Vec<DbPerson> = Vec::new();
for row in rows { while let Some(row) = rows.next()? {
output.push(row?); let metadata_json: Option<String> = row.get(1)?;
let metadata = match metadata_json {
Some(s) => serde_json::from_str(&s)?,
None => None,
};
output.push(DbPerson {
pubkey: PublicKeyHex(row.get(0)?),
metadata,
metadata_at: row.get(2)?,
nip05_valid: row.get(3)?,
nip05_last_checked: row.get(4)?,
followed: row.get(5)?,
followed_last_updated: row.get(6)?,
});
} }
Ok(output) Ok(output)
}) })
@ -258,7 +314,7 @@ impl People {
pub fn get_all(&self) -> Vec<DbPerson> { pub fn get_all(&self) -> Vec<DbPerson> {
let mut v: Vec<DbPerson> = self.people.iter().map(|e| e.value().to_owned()).collect(); let mut v: Vec<DbPerson> = self.people.iter().map(|e| e.value().to_owned()).collect();
v.sort_by(|a, b| { v.sort_by(|a, b| {
let c = a.name.cmp(&b.name); let c = a.name().cmp(&b.name());
if c == Ordering::Equal { if c == Ordering::Equal {
a.pubkey.cmp(&b.pubkey) a.pubkey.cmp(&b.pubkey)
} else { } else {
@ -296,13 +352,13 @@ impl People {
// Fail if they don't have a picture url // Fail if they don't have a picture url
// FIXME: we could get metadata that sets this while we are running, so just failing for // FIXME: we could get metadata that sets this while we are running, so just failing for
// the duration of the client isn't quite right. But for now, retrying is taxing. // the duration of the client isn't quite right. But for now, retrying is taxing.
if person.picture.is_none() { if person.picture().is_none() {
return Err(()); return Err(());
} }
// FIXME: we could get metadata that sets this while we are running, so just failing for // FIXME: we could get metadata that sets this while we are running, so just failing for
// the duration of the client isn't quite right. But for now, retrying is taxing. // the duration of the client isn't quite right. But for now, retrying is taxing.
let url = Url::new(person.picture.as_ref().unwrap()); let url = Url::new(person.picture().unwrap());
if !url.is_valid() { if !url.is_valid() {
return Err(()); return Err(());
} }
@ -370,7 +426,7 @@ impl People {
let mut result_name = String::from(""); let mut result_name = String::from("");
// search for users by name // search for users by name
if let Some(name) = &person.name.as_ref() { if let Some(name) = &person.name() {
let matchable = name.to_lowercase(); let matchable = name.to_lowercase();
if matchable.starts_with(&search) { if matchable.starts_with(&search) {
score = 300; score = 300;
@ -383,15 +439,15 @@ impl People {
} }
// search for users by nip05 id // search for users by nip05 id
if score == 0 && person.dns_id_valid > 0 { if score == 0 && person.nip05_valid > 0 {
if let Some(dns_id) = &person.dns_id.as_ref().map(|n| n.to_lowercase()) { if let Some(nip05) = &person.nip05().map(|n| n.to_lowercase()) {
if dns_id.starts_with(&search) { if nip05.starts_with(&search) {
score = 400; score = 400;
result_name = dns_id.to_string(); result_name = nip05.to_string();
} }
if dns_id.contains(&search) { if nip05.contains(&search) {
score = 100; score = 100;
result_name = dns_id.to_string(); result_name = nip05.to_string();
} }
} }
} }
@ -602,10 +658,10 @@ impl People {
Ok(()) Ok(())
} }
pub async fn update_dns_id_last_checked(&self, pubkeyhex: PublicKeyHex) -> Result<(), Error> { pub async fn update_nip05_last_checked(&self, pubkeyhex: PublicKeyHex) -> Result<(), Error> {
let maybe_db = GLOBALS.db.lock().await; let maybe_db = GLOBALS.db.lock().await;
let db = maybe_db.as_ref().unwrap(); let db = maybe_db.as_ref().unwrap();
let mut stmt = db.prepare("UPDATE person SET dns_id_last_checked=? WHERE pubkey=?")?; let mut stmt = db.prepare("UPDATE person SET nip05_last_checked=? WHERE pubkey=?")?;
let now = Unixtime::now().unwrap().0; let now = Unixtime::now().unwrap().0;
stmt.execute((&now, &pubkeyhex.0))?; stmt.execute((&now, &pubkeyhex.0))?;
Ok(()) Ok(())
@ -614,36 +670,54 @@ impl People {
pub async fn upsert_nip05_validity( pub async fn upsert_nip05_validity(
&self, &self,
pubkeyhex: &PublicKeyHex, pubkeyhex: &PublicKeyHex,
dns_id: Option<String>, nip05: Option<String>,
dns_id_valid: bool, nip05_valid: bool,
dns_id_last_checked: u64, nip05_last_checked: u64,
) -> Result<(), Error> { ) -> Result<(), Error> {
// Update memory // Update memory
if let Some(mut dbperson) = self.people.get_mut(pubkeyhex) { if let Some(mut dbperson) = self.people.get_mut(pubkeyhex) {
dbperson.dns_id = dns_id.clone(); if let Some(metadata) = &mut dbperson.metadata {
dbperson.dns_id_valid = u8::from(dns_id_valid); metadata.nip05 = nip05.clone()
dbperson.dns_id_last_checked = Some(dns_id_last_checked); } else {
let mut metadata = Metadata::new();
metadata.nip05 = nip05.clone();
dbperson.metadata = Some(metadata);
}
dbperson.nip05_valid = u8::from(nip05_valid);
dbperson.nip05_last_checked = Some(nip05_last_checked);
} }
// Update in database // Update in database
let sql = "INSERT INTO person (pubkey, dns_id, dns_id_valid, dns_id_last_checked) \ let sql = "INSERT INTO person (pubkey, metadata, nip05_valid, nip05_last_checked) \
values (?, ?, ?, ?) \ values (?, ?, ?, ?) \
ON CONFLICT(pubkey) DO UPDATE SET dns_id=?, dns_id_valid=?, dns_id_last_checked=?"; ON CONFLICT(pubkey) DO \
UPDATE SET metadata=json_patch(metadata, ?), nip05_valid=?, nip05_last_checked=?";
let pubkeyhex2 = pubkeyhex.to_owned(); let pubkeyhex2 = pubkeyhex.to_owned();
task::spawn_blocking(move || { task::spawn_blocking(move || {
let maybe_db = GLOBALS.db.blocking_lock(); let maybe_db = GLOBALS.db.blocking_lock();
let db = maybe_db.as_ref().unwrap(); let db = maybe_db.as_ref().unwrap();
let mut metadata = Metadata::new();
metadata.nip05 = nip05.clone();
let metadata_json: Option<String> = Some(serde_json::to_string(&metadata)?);
let metadata_patch = format!(
"{{\"nip05\": {}}}",
match nip05 {
Some(s) => s,
None => "null".to_owned(),
}
);
let mut stmt = db.prepare(sql)?; let mut stmt = db.prepare(sql)?;
stmt.execute(( stmt.execute((
&pubkeyhex2.0, &pubkeyhex2.0,
&dns_id, &metadata_json,
&dns_id_valid, &nip05_valid,
&dns_id_last_checked, &nip05_last_checked,
&dns_id, &metadata_patch,
&dns_id_valid, &nip05_valid,
&dns_id_last_checked, &nip05_last_checked,
))?; ))?;
Ok::<(), Error>(()) Ok::<(), Error>(())
}) })
@ -653,9 +727,9 @@ impl People {
} }
async fn fetch(criteria: Option<&str>) -> Result<Vec<DbPerson>, Error> { async fn fetch(criteria: Option<&str>) -> Result<Vec<DbPerson>, Error> {
let sql = let sql = "SELECT pubkey, metadata, metadata_at, \
"SELECT pubkey, name, about, picture, dns_id, dns_id_valid, dns_id_last_checked, \ nip05_valid, nip05_last_checked, \
metadata_at, followed, followed_last_updated FROM person" followed, followed_last_updated FROM person"
.to_owned(); .to_owned();
let sql = match criteria { let sql = match criteria {
None => sql, None => sql,
@ -667,24 +741,23 @@ impl People {
let db = maybe_db.as_ref().unwrap(); let db = maybe_db.as_ref().unwrap();
let mut stmt = db.prepare(&sql)?; let mut stmt = db.prepare(&sql)?;
let rows = stmt.query_map([], |row| { let mut rows = stmt.query([])?;
Ok(DbPerson {
pubkey: PublicKeyHex(row.get(0)?),
name: row.get(1)?,
about: row.get(2)?,
picture: row.get(3)?,
dns_id: row.get(4)?,
dns_id_valid: row.get(5)?,
dns_id_last_checked: row.get(6)?,
metadata_at: row.get(7)?,
followed: row.get(8)?,
followed_last_updated: row.get(9)?,
})
})?;
let mut output: Vec<DbPerson> = Vec::new(); let mut output: Vec<DbPerson> = Vec::new();
for row in rows { while let Some(row) = rows.next()? {
output.push(row?); let metadata_json: Option<String> = row.get(1)?;
let metadata = match metadata_json {
Some(s) => serde_json::from_str(&s)?,
None => None,
};
output.push(DbPerson {
pubkey: PublicKeyHex(row.get(0)?),
metadata,
metadata_at: row.get(2)?,
nip05_valid: row.get(3)?,
nip05_last_checked: row.get(4)?,
followed: row.get(5)?,
followed_last_updated: row.get(6)?,
});
} }
Ok(output) Ok(output)
}) })
@ -705,8 +778,10 @@ impl People {
async fn fetch_many(pubkeys: &[&PublicKeyHex]) -> Result<Vec<DbPerson>, Error> { async fn fetch_many(pubkeys: &[&PublicKeyHex]) -> Result<Vec<DbPerson>, Error> {
let sql = format!( let sql = format!(
"SELECT pubkey, name, about, picture, dns_id, dns_id_valid, dns_id_last_checked, \ "SELECT pubkey, metadata, metadata_at, \
metadata_at, followed, followed_last_updated FROM person WHERE pubkey IN ({})", nip05_valid, nip05_last_checked, \
followed, followed_last_updated \
FROM person WHERE pubkey IN ({})",
repeat_vars(pubkeys.len()) repeat_vars(pubkeys.len())
); );
@ -727,17 +802,19 @@ impl People {
let mut rows = stmt.raw_query(); let mut rows = stmt.raw_query();
let mut people: Vec<DbPerson> = Vec::new(); let mut people: Vec<DbPerson> = Vec::new();
while let Some(row) = rows.next()? { while let Some(row) = rows.next()? {
let metadata_json: Option<String> = row.get(1)?;
let metadata = match metadata_json {
Some(s) => serde_json::from_str(&s)?,
None => None,
};
people.push(DbPerson { people.push(DbPerson {
pubkey: PublicKeyHex(row.get(0)?), pubkey: PublicKeyHex(row.get(0)?),
name: row.get(1)?, metadata,
about: row.get(2)?, metadata_at: row.get(2)?,
picture: row.get(3)?, nip05_valid: row.get(3)?,
dns_id: row.get(4)?, nip05_last_checked: row.get(4)?,
dns_id_valid: row.get(5)?, followed: row.get(5)?,
dns_id_last_checked: row.get(6)?, followed_last_updated: row.get(6)?,
metadata_at: row.get(7)?,
followed: row.get(8)?,
followed_last_updated: row.get(9)?,
}); });
} }
@ -750,25 +827,27 @@ impl People {
#[allow(dead_code)] #[allow(dead_code)]
async fn insert(person: DbPerson) -> Result<(), Error> { async fn insert(person: DbPerson) -> Result<(), Error> {
let sql = let sql = "INSERT OR IGNORE INTO person (pubkey, metadata, metadata_at, \
"INSERT OR IGNORE INTO person (pubkey, name, about, picture, dns_id, dns_id_valid, \ nip05_valid, nip05_last_checked, followed, followed_last_updated) \
dns_id_last_checked, metadata_at, followed, followed_last_updated) \ VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)";
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)";
task::spawn_blocking(move || { task::spawn_blocking(move || {
let maybe_db = GLOBALS.db.blocking_lock(); let maybe_db = GLOBALS.db.blocking_lock();
let db = maybe_db.as_ref().unwrap(); let db = maybe_db.as_ref().unwrap();
let metadata_json: Option<String> = if let Some(md) = &person.metadata {
Some(serde_json::to_string(md)?)
} else {
None
};
let mut stmt = db.prepare(sql)?; let mut stmt = db.prepare(sql)?;
stmt.execute(( stmt.execute((
&person.pubkey.0, &person.pubkey.0,
&person.name, &metadata_json,
&person.about,
&person.picture,
&person.dns_id,
&person.dns_id_valid,
&person.dns_id_last_checked,
&person.metadata_at, &person.metadata_at,
&person.nip05_valid,
&person.nip05_last_checked,
&person.followed, &person.followed,
&person.followed_last_updated, &person.followed_last_updated,
))?; ))?;

View File

@ -203,12 +203,12 @@ fn real_posting_area(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Fram
// List tags that will be applied (FIXME: list tags from parent event too in case of reply) // List tags that will be applied (FIXME: list tags from parent event too in case of reply)
for (i, (npub, pubkey)) in keys_from_text(&app.draft).iter().enumerate() { for (i, (npub, pubkey)) in keys_from_text(&app.draft).iter().enumerate() {
let rendered = if let Some(person) = GLOBALS.people.get(&pubkey.as_hex_string().into()) { let rendered = if let Some(person) = GLOBALS.people.get(&pubkey.as_hex_string().into()) {
match person.name { match person.name() {
Some(name) => name, Some(name) => name.to_owned(),
None => npub.to_string(), None => npub.to_owned(),
} }
} else { } else {
npub.to_string() npub.to_owned()
}; };
ui.label(format!("{}: {}", i, rendered)); ui.label(format!("{}: {}", i, rendered));
@ -357,9 +357,9 @@ fn render_post_actual(
// name // name
// about // about
// picture // picture
// dns_id // nip05
// dns_id_valid // nip05_valid
// dns_id_last_checked // nip05_last_checked
// metadata_at // metadata_at
// followed // followed
@ -626,7 +626,7 @@ fn render_content(app: &mut GossipUi, ui: &mut Ui, tag_re: &regex::Regex, event:
match tag { match tag {
Tag::Pubkey { pubkey, .. } => { Tag::Pubkey { pubkey, .. } => {
let nam = match GLOBALS.people.get(pubkey) { let nam = match GLOBALS.people.get(pubkey) {
Some(p) => match p.name { Some(p) => match p.name() {
Some(n) => format!("@{}", n), Some(n) => format!("@{}", n),
None => format!("@{}", GossipUi::hex_pubkey_short(pubkey)), None => format!("@{}", GossipUi::hex_pubkey_short(pubkey)),
}, },

View File

@ -8,10 +8,10 @@ mod widgets;
mod you; mod you;
use crate::about::About; use crate::about::About;
use crate::db::DbPerson;
use crate::error::Error; use crate::error::Error;
use crate::feed::FeedKind; use crate::feed::FeedKind;
use crate::globals::GLOBALS; use crate::globals::GLOBALS;
use crate::people::DbPerson;
use crate::settings::Settings; use crate::settings::Settings;
use crate::ui::widgets::CopyButton; use crate::ui::widgets::CopyButton;
use eframe::{egui, IconData, Theme}; use eframe::{egui, IconData, Theme};
@ -377,7 +377,7 @@ impl GossipUi {
pub fn render_person_name_line(ui: &mut Ui, maybe_person: Option<&DbPerson>) { pub fn render_person_name_line(ui: &mut Ui, maybe_person: Option<&DbPerson>) {
ui.horizontal(|ui| { ui.horizontal(|ui| {
if let Some(person) = maybe_person { if let Some(person) = maybe_person {
if let Some(name) = &person.name { if let Some(name) = person.name() {
ui.label(RichText::new(name).strong()); ui.label(RichText::new(name).strong());
} else { } else {
ui.label(RichText::new(GossipUi::hex_pubkey_short(&person.pubkey)).weak()); ui.label(RichText::new(GossipUi::hex_pubkey_short(&person.pubkey)).weak());
@ -387,15 +387,15 @@ impl GossipUi {
ui.label("🚶"); ui.label("🚶");
} }
if let Some(mut dns_id) = person.dns_id.clone() { if let Some(mut nip05) = person.nip05().map(|s| s.to_owned()) {
if dns_id.starts_with("_@") { if nip05.starts_with("_@") {
dns_id = dns_id.get(2..).unwrap().to_string(); nip05 = nip05.get(2..).unwrap().to_string();
} }
if person.dns_id_valid > 0 { if person.nip05_valid > 0 {
ui.label(RichText::new(dns_id).monospace().small()); ui.label(RichText::new(nip05).monospace().small());
} else { } else {
ui.label(RichText::new(dns_id).monospace().small().strikethrough()); ui.label(RichText::new(nip05).monospace().small().strikethrough());
} }
} }

View File

@ -1,7 +1,7 @@
use super::{GossipUi, Page}; use super::{GossipUi, Page};
use crate::comms::ToOverlordMessage; use crate::comms::ToOverlordMessage;
use crate::db::DbPerson;
use crate::globals::GLOBALS; use crate::globals::GLOBALS;
use crate::people::DbPerson;
use crate::AVATAR_SIZE_F32; use crate::AVATAR_SIZE_F32;
use eframe::egui; use eframe::egui;
use egui::{Context, Image, RichText, ScrollArea, Sense, Ui, Vec2}; use egui::{Context, Image, RichText, ScrollArea, Sense, Ui, Vec2};
@ -107,7 +107,7 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, _frame: &mut eframe::Fra
} }
fn get_name(person: &DbPerson) -> String { fn get_name(person: &DbPerson) -> String {
if let Some(name) = &person.name { if let Some(name) = person.name() {
name.to_owned() name.to_owned()
} else { } else {
GossipUi::hex_pubkey_short(&person.pubkey) GossipUi::hex_pubkey_short(&person.pubkey)

View File

@ -1,8 +1,8 @@
use super::{GossipUi, Page}; use super::{GossipUi, Page};
use crate::comms::ToOverlordMessage; use crate::comms::ToOverlordMessage;
use crate::db::DbPerson;
use crate::feed::FeedKind; use crate::feed::FeedKind;
use crate::globals::GLOBALS; use crate::globals::GLOBALS;
use crate::people::DbPerson;
use crate::AVATAR_SIZE_F32; use crate::AVATAR_SIZE_F32;
use eframe::egui; use eframe::egui;
use egui::{Context, RichText, Ui, Vec2}; use egui::{Context, RichText, Ui, Vec2};
@ -50,7 +50,7 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, _frame: &mut eframe::Fra
ui.add_space(12.0); ui.add_space(12.0);
if let Some(person) = &maybe_person { if let Some(person) = &maybe_person {
if let Some(about) = person.about.as_deref() { if let Some(about) = person.about() {
ui.label(about); ui.label(about);
} }
} }
@ -82,7 +82,7 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, _frame: &mut eframe::Fra
} }
fn get_name(person: &DbPerson) -> String { fn get_name(person: &DbPerson) -> String {
if let Some(name) = &person.name { if let Some(name) = person.name() {
name.to_owned() name.to_owned()
} else { } else {
GossipUi::hex_pubkey_short(&person.pubkey) GossipUi::hex_pubkey_short(&person.pubkey)