feat: add basic web of trust
This commit is contained in:
@@ -258,82 +258,105 @@ pub async fn login(
|
||||
// Connect to user's relay (NIP-65)
|
||||
init_nip65(client).await;
|
||||
|
||||
// Get user's contact list
|
||||
if let Ok(contacts) = client.get_contact_list(Some(Duration::from_secs(5))).await {
|
||||
let mut contacts_state = state.contact_list.lock().await;
|
||||
*contacts_state = contacts;
|
||||
};
|
||||
|
||||
// Get user's settings
|
||||
if let Ok(settings) = get_user_settings(client).await {
|
||||
let mut settings_state = state.settings.lock().await;
|
||||
*settings_state = settings;
|
||||
};
|
||||
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let state = handle.state::<Nostr>();
|
||||
let client = &state.client;
|
||||
let contact_list = state.contact_list.lock().await;
|
||||
|
||||
let signer = client.signer().await.unwrap();
|
||||
let public_key = signer.public_key().await.unwrap();
|
||||
|
||||
let notification_id = SubscriptionId::new(NOTIFICATION_SUB_ID);
|
||||
|
||||
if !contact_list.is_empty() {
|
||||
let authors: Vec<PublicKey> = contact_list.iter().map(|f| f.public_key).collect();
|
||||
let sync = Filter::new()
|
||||
.authors(authors.clone())
|
||||
.kinds(vec![Kind::TextNote, Kind::Repost])
|
||||
.limit(NEWSFEED_NEG_LIMIT);
|
||||
|
||||
if client
|
||||
.reconcile(sync, NegentropyOptions::default())
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
handle.emit("newsfeed_synchronized", ()).unwrap();
|
||||
}
|
||||
};
|
||||
|
||||
drop(contact_list);
|
||||
|
||||
let sync = Filter::new()
|
||||
.pubkey(public_key)
|
||||
.kinds(vec![
|
||||
Kind::TextNote,
|
||||
Kind::Repost,
|
||||
Kind::Reaction,
|
||||
Kind::ZapReceipt,
|
||||
])
|
||||
.limit(NOTIFICATION_NEG_LIMIT);
|
||||
let notification = Filter::new().pubkey(public_key).kinds(vec![
|
||||
Kind::TextNote,
|
||||
Kind::Repost,
|
||||
Kind::Reaction,
|
||||
Kind::ZapReceipt,
|
||||
]);
|
||||
|
||||
// Sync notification with negentropy
|
||||
if client
|
||||
.reconcile(sync, NegentropyOptions::default())
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
handle.emit("notification_synchronized", ()).unwrap();
|
||||
}
|
||||
|
||||
let notification = Filter::new()
|
||||
.pubkey(public_key)
|
||||
.kinds(vec![
|
||||
Kind::TextNote,
|
||||
Kind::Repost,
|
||||
Kind::Reaction,
|
||||
Kind::ZapReceipt,
|
||||
])
|
||||
.since(Timestamp::now());
|
||||
let _ = client
|
||||
.reconcile(
|
||||
notification.clone().limit(NOTIFICATION_NEG_LIMIT),
|
||||
NegentropyOptions::default(),
|
||||
)
|
||||
.await;
|
||||
|
||||
// Subscribing for new notification...
|
||||
if let Err(e) = client
|
||||
.subscribe_with_id(notification_id, vec![notification], None)
|
||||
.subscribe_with_id(
|
||||
notification_id,
|
||||
vec![notification.since(Timestamp::now())],
|
||||
None,
|
||||
)
|
||||
.await
|
||||
{
|
||||
println!("Error: {}", e)
|
||||
}
|
||||
|
||||
// Get user's settings
|
||||
if let Ok(settings) = get_user_settings(client).await {
|
||||
state.settings.lock().await.clone_from(&settings);
|
||||
};
|
||||
|
||||
// Get user's contact list
|
||||
if let Ok(contacts) = client.get_contact_list(Some(Duration::from_secs(5))).await {
|
||||
state.contact_list.lock().await.clone_from(&contacts);
|
||||
|
||||
if !contacts.is_empty() {
|
||||
let pubkeys: Vec<PublicKey> = contacts.iter().map(|f| f.public_key).collect();
|
||||
|
||||
let newsfeed = Filter::new()
|
||||
.authors(pubkeys.clone())
|
||||
.kinds(vec![Kind::TextNote, Kind::Repost])
|
||||
.limit(NEWSFEED_NEG_LIMIT);
|
||||
|
||||
if client
|
||||
.reconcile(newsfeed, NegentropyOptions::default())
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
handle.emit("synchronized", ()).unwrap();
|
||||
}
|
||||
|
||||
let filter = Filter::new()
|
||||
.authors(pubkeys.clone())
|
||||
.kind(Kind::ContactList)
|
||||
.limit(4000);
|
||||
|
||||
if client
|
||||
.reconcile(filter, NegentropyOptions::default())
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
for pubkey in pubkeys.into_iter() {
|
||||
let mut list: Vec<PublicKey> = Vec::new();
|
||||
let f = Filter::new()
|
||||
.author(pubkey)
|
||||
.kind(Kind::ContactList)
|
||||
.limit(1);
|
||||
|
||||
if let Ok(events) = client.database().query(vec![f]).await {
|
||||
if let Some(event) = events.into_iter().next() {
|
||||
for tag in event.tags.into_iter() {
|
||||
if let Some(TagStandard::PublicKey {
|
||||
public_key,
|
||||
uppercase: false,
|
||||
..
|
||||
}) = tag.to_standardized()
|
||||
{
|
||||
list.push(public_key)
|
||||
}
|
||||
}
|
||||
|
||||
if !list.is_empty() {
|
||||
state.circles.lock().await.insert(pubkey, list);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
Ok(public_key)
|
||||
|
||||
@@ -483,3 +483,13 @@ pub async fn verify_nip05(id: String, nip05: &str) -> Result<bool, String> {
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn is_trusted_user(id: String, state: State<'_, Nostr>) -> Result<bool, String> {
|
||||
let circles = &state.circles.lock().await;
|
||||
let public_key = PublicKey::from_str(&id).map_err(|e| e.to_string())?;
|
||||
let trusted = circles.values().any(|v| v.contains(&public_key));
|
||||
|
||||
Ok(trusted)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
use specta_typescript::Typescript;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs,
|
||||
io::{self, BufRead},
|
||||
str::FromStr,
|
||||
@@ -30,6 +31,7 @@ pub struct Nostr {
|
||||
client: Client,
|
||||
contact_list: Mutex<Vec<Contact>>,
|
||||
settings: Mutex<Settings>,
|
||||
circles: Mutex<HashMap<PublicKey, Vec<PublicKey>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Type)]
|
||||
@@ -38,6 +40,7 @@ pub struct Settings {
|
||||
image_resize_service: Option<String>,
|
||||
use_relay_hint: bool,
|
||||
content_warning: bool,
|
||||
trusted_only: bool,
|
||||
display_avatar: bool,
|
||||
display_zap_button: bool,
|
||||
display_repost_button: bool,
|
||||
@@ -52,6 +55,7 @@ impl Default for Settings {
|
||||
image_resize_service: Some("https://wsrv.nl".to_string()),
|
||||
use_relay_hint: true,
|
||||
content_warning: true,
|
||||
trusted_only: true,
|
||||
display_avatar: true,
|
||||
display_zap_button: true,
|
||||
display_repost_button: true,
|
||||
@@ -121,6 +125,7 @@ fn main() {
|
||||
get_settings,
|
||||
set_settings,
|
||||
verify_nip05,
|
||||
is_trusted_user,
|
||||
get_event_meta,
|
||||
get_event,
|
||||
get_event_from,
|
||||
@@ -265,6 +270,7 @@ fn main() {
|
||||
client,
|
||||
contact_list: Mutex::new(vec![]),
|
||||
settings: Mutex::new(Settings::default()),
|
||||
circles: Mutex::new(HashMap::new()),
|
||||
});
|
||||
|
||||
Subscription::listen_any(app, move |event| {
|
||||
|
||||
Reference in New Issue
Block a user