feat: improve negentropy sync
This commit is contained in:
@@ -10,12 +10,8 @@ use std::{
|
|||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
use tauri::{Emitter, Manager, State};
|
use tauri::{Emitter, Manager, State};
|
||||||
use tokio::time::sleep;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{Nostr, NOTIFICATION_SUB_ID};
|
||||||
common::{get_latest_event, init_nip65},
|
|
||||||
Nostr, NOTIFICATION_SUB_ID,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Type)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Type)]
|
||||||
struct Account {
|
struct Account {
|
||||||
@@ -284,7 +280,7 @@ pub async fn login(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// NIP-65: Connect to user's relay list
|
// NIP-65: Connect to user's relay list
|
||||||
init_nip65(client, &public_key).await;
|
// init_nip65(client, &public_key).await;
|
||||||
|
|
||||||
// NIP-03: Get user's contact list
|
// NIP-03: Get user's contact list
|
||||||
let contact_list = {
|
let contact_list = {
|
||||||
@@ -325,8 +321,8 @@ pub async fn login(
|
|||||||
.reconcile(
|
.reconcile(
|
||||||
Filter::new()
|
Filter::new()
|
||||||
.authors(authors.clone())
|
.authors(authors.clone())
|
||||||
.kinds(vec![Kind::Metadata, Kind::ContactList, Kind::EventDeletion])
|
.kinds(vec![Kind::Metadata, Kind::ContactList])
|
||||||
.limit(authors.len() * 20),
|
.limit(authors.len() * 10),
|
||||||
NegentropyOptions::default(),
|
NegentropyOptions::default(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@@ -339,8 +335,8 @@ pub async fn login(
|
|||||||
.reconcile(
|
.reconcile(
|
||||||
Filter::new()
|
Filter::new()
|
||||||
.authors(authors.clone())
|
.authors(authors.clone())
|
||||||
.kinds(vec![Kind::TextNote, Kind::Repost])
|
.kinds(vec![Kind::TextNote, Kind::Repost, Kind::EventDeletion])
|
||||||
.limit(authors.len() * 50),
|
.limit(authors.len() * 40),
|
||||||
NegentropyOptions::default(),
|
NegentropyOptions::default(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@@ -350,84 +346,94 @@ pub async fn login(
|
|||||||
|
|
||||||
// Create the trusted public key list from contact list
|
// Create the trusted public key list from contact list
|
||||||
// TODO: create a cached file
|
// TODO: create a cached file
|
||||||
let mut trusted_list: HashSet<PublicKey> = HashSet::new();
|
if let Ok(events) = client
|
||||||
|
.database()
|
||||||
|
.query(vec![Filter::new().kind(Kind::ContactList)])
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
let keys: Vec<&str> = events
|
||||||
|
.iter()
|
||||||
|
.flat_map(|event| event.get_tags_content(TagKind::p()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
for author in authors.into_iter() {
|
let trusted_list: HashSet<PublicKey> = keys
|
||||||
trusted_list.insert(author);
|
.into_iter()
|
||||||
|
.filter_map(|item| {
|
||||||
let filter = Filter::new()
|
if let Ok(pk) = PublicKey::from_str(item) {
|
||||||
.author(author)
|
Some(pk)
|
||||||
.kind(Kind::ContactList)
|
} else {
|
||||||
.limit(1);
|
None
|
||||||
|
|
||||||
if let Ok(events) = client.database().query(vec![filter]).await {
|
|
||||||
if let Some(event) = get_latest_event(&events) {
|
|
||||||
for tag in event.tags.iter() {
|
|
||||||
if let Some(TagStandard::PublicKey {
|
|
||||||
public_key,
|
|
||||||
uppercase: false,
|
|
||||||
..
|
|
||||||
}) = tag.to_owned().to_standardized()
|
|
||||||
{
|
|
||||||
trusted_list.insert(public_key);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Update app's state
|
||||||
|
state.trusted_list.lock().await.clone_from(&trusted_list);
|
||||||
|
|
||||||
|
let trusted_users: Vec<PublicKey> = trusted_list.into_iter().collect();
|
||||||
|
println!("Total trusted users: {}", trusted_users.len());
|
||||||
|
|
||||||
|
if let Ok(report) = client
|
||||||
|
.reconcile(
|
||||||
|
Filter::new()
|
||||||
|
.authors(trusted_users)
|
||||||
|
.kinds(vec![
|
||||||
|
Kind::Metadata,
|
||||||
|
Kind::TextNote,
|
||||||
|
Kind::Repost,
|
||||||
|
Kind::EventDeletion,
|
||||||
|
])
|
||||||
|
.limit(5000),
|
||||||
|
NegentropyOptions::default(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
println!("Received: {}", report.received.len())
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// Update app's state
|
// Syncing all user's events
|
||||||
state.trusted_list.lock().await.clone_from(&trusted_list);
|
if let Ok(report) = client
|
||||||
|
.reconcile(
|
||||||
// Syncing all user's events
|
Filter::new().author(author).kinds(vec![
|
||||||
if let Ok(report) = client
|
Kind::TextNote,
|
||||||
.reconcile(Filter::new().author(author), NegentropyOptions::default())
|
Kind::Repost,
|
||||||
.await
|
Kind::FollowSet,
|
||||||
{
|
Kind::InterestSet,
|
||||||
println!("Received: {}", report.received.len())
|
Kind::Interests,
|
||||||
}
|
Kind::EventDeletion,
|
||||||
|
Kind::MuteList,
|
||||||
// Syncing all tagged events for current user
|
Kind::BookmarkSet,
|
||||||
if let Ok(report) = client
|
Kind::BlockedRelays,
|
||||||
.reconcile(
|
Kind::EmojiSet,
|
||||||
Filter::new().pubkey(author).kinds(vec![
|
Kind::RelaySet,
|
||||||
Kind::TextNote,
|
Kind::RelayList,
|
||||||
Kind::Repost,
|
Kind::ApplicationSpecificData,
|
||||||
Kind::Reaction,
|
]),
|
||||||
Kind::ZapReceipt,
|
NegentropyOptions::default(),
|
||||||
]),
|
)
|
||||||
NegentropyOptions::default(),
|
.await
|
||||||
)
|
{
|
||||||
.await
|
println!("Received: {}", report.received.len())
|
||||||
{
|
|
||||||
println!("Received: {}", report.received.len())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Syncing all events for trusted list
|
|
||||||
let trusted: Vec<PublicKey> = trusted_list.into_iter().collect();
|
|
||||||
if let Ok(report) = client
|
|
||||||
.reconcile(
|
|
||||||
Filter::new()
|
|
||||||
.authors(trusted)
|
|
||||||
.kinds(vec![
|
|
||||||
Kind::Metadata,
|
|
||||||
Kind::TextNote,
|
|
||||||
Kind::Repost,
|
|
||||||
Kind::EventDeletion,
|
|
||||||
])
|
|
||||||
.limit(30000),
|
|
||||||
NegentropyOptions::default(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
println!("Received: {}", report.received.len())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait a little longer
|
|
||||||
// TODO: remove?
|
|
||||||
sleep(Duration::from_secs(5)).await;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Syncing all tagged events for current user
|
||||||
|
if let Ok(report) = client
|
||||||
|
.reconcile(
|
||||||
|
Filter::new().pubkey(author).kinds(vec![
|
||||||
|
Kind::TextNote,
|
||||||
|
Kind::Repost,
|
||||||
|
Kind::Reaction,
|
||||||
|
Kind::ZapReceipt,
|
||||||
|
]),
|
||||||
|
NegentropyOptions::default(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
println!("Received: {}", report.received.len())
|
||||||
|
};
|
||||||
|
|
||||||
handle
|
handle
|
||||||
.emit("neg_synchronized", ())
|
.emit("neg_synchronized", ())
|
||||||
.expect("Something wrong!");
|
.expect("Something wrong!");
|
||||||
|
|||||||
@@ -40,25 +40,26 @@ pub async fn get_event(id: String, state: State<'_, Nostr>) -> Result<RichEvent,
|
|||||||
|
|
||||||
Ok(RichEvent { raw, parsed })
|
Ok(RichEvent { raw, parsed })
|
||||||
} else {
|
} else {
|
||||||
println!("Not found, getting event from relays...");
|
|
||||||
match client
|
match client
|
||||||
.stream_events_of(vec![filter], Some(Duration::from_secs(10)))
|
.get_events_of(
|
||||||
|
vec![filter],
|
||||||
|
EventSource::relays(Some(Duration::from_secs(10))),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(mut rx) => {
|
Ok(events) => {
|
||||||
let mut raw: String = String::new();
|
if let Some(event) = get_latest_event(&events) {
|
||||||
let mut parsed: Option<Meta> = None;
|
let raw = event.as_json();
|
||||||
|
let parsed = if event.kind == Kind::TextNote {
|
||||||
while let Some(event) = rx.next().await {
|
|
||||||
raw = event.as_json();
|
|
||||||
parsed = if event.kind == Kind::TextNote {
|
|
||||||
Some(parse_event(&event.content).await)
|
Some(parse_event(&event.content).await)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
Ok(RichEvent { raw, parsed })
|
Ok(RichEvent { raw, parsed })
|
||||||
|
} else {
|
||||||
|
Err("Not found.".into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(err) => Err(err.to_string()),
|
Err(err) => Err(err.to_string()),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,23 +58,23 @@ pub async fn get_profile(id: Option<String>, state: State<'_, Nostr>) -> Result<
|
|||||||
Err("Parse metadata failed".into())
|
Err("Parse metadata failed".into())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
println!("Not found, getting event from relays...");
|
|
||||||
match client
|
match client
|
||||||
.stream_events_of(vec![filter], Some(Duration::from_secs(10)))
|
.get_events_of(
|
||||||
|
vec![filter],
|
||||||
|
EventSource::relays(Some(Duration::from_secs(10))),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(mut rx) => {
|
Ok(events) => {
|
||||||
let mut metadata: String = Metadata::new().as_json();
|
if let Some(event) = get_latest_event(&events) {
|
||||||
|
if let Ok(metadata) = Metadata::from_json(&event.content) {
|
||||||
while let Some(event) = rx.next().await {
|
Ok(metadata.as_json())
|
||||||
println!("Event: {}", event.as_json());
|
} else {
|
||||||
if let Ok(m) = Metadata::from_json(&event.content) {
|
Err("Metadata is not valid.".into())
|
||||||
metadata = m.as_json();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Err("Not found.".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(metadata)
|
|
||||||
}
|
}
|
||||||
Err(e) => Err(e.to_string()),
|
Err(e) => Err(e.to_string()),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -273,8 +273,12 @@ fn main() {
|
|||||||
println!("Add discovery relay failed: {}", e)
|
println!("Add discovery relay failed: {}", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Err(e) = client.add_discovery_relay("wss://user.kindpag.es/").await {
|
||||||
|
println!("Add discovery relay failed: {}", e)
|
||||||
|
}
|
||||||
|
|
||||||
// Connect
|
// Connect
|
||||||
client.connect_with_timeout(Duration::from_secs(20)).await;
|
client.connect_with_timeout(Duration::from_secs(10)).await;
|
||||||
|
|
||||||
client
|
client
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -113,10 +113,12 @@ const ContentWarning = memo(function ContentWarning({
|
|||||||
}: { warning: string }) {
|
}: { warning: string }) {
|
||||||
const [blurred, setBlurred] = useState(() => warning?.length > 0);
|
const [blurred, setBlurred] = useState(() => warning?.length > 0);
|
||||||
|
|
||||||
if (!blurred) return null;
|
if (!blurred) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="absolute inset-0 z-10 flex items-center justify-center w-full h-full bg-black/80 backdrop-blur-lg">
|
<div className="absolute inset-0 z-10 flex items-center justify-center w-full bg-black/80 backdrop-blur-lg">
|
||||||
<div className="flex flex-col items-center justify-center gap-2 text-center">
|
<div className="flex flex-col items-center justify-center gap-2 text-center">
|
||||||
<p className="text-sm text-white/60">
|
<p className="text-sm text-white/60">
|
||||||
The content is hidden because the author
|
The content is hidden because the author
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ function Screen() {
|
|||||||
<div className="flex flex-col gap-1 text-center">
|
<div className="flex flex-col gap-1 text-center">
|
||||||
<h1 className="leading-tight text-xl font-semibold">Manage Relays</h1>
|
<h1 className="leading-tight text-xl font-semibold">Manage Relays</h1>
|
||||||
<p className="text-sm text-neutral-700 dark:text-neutral-300">
|
<p className="text-sm text-neutral-700 dark:text-neutral-300">
|
||||||
This relays will be only use to get user's metadata.
|
The default relays that Lume will connected.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3">
|
||||||
@@ -127,6 +127,13 @@ function Screen() {
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="text-xs text-neutral-600 dark:text-neutral-400">
|
||||||
|
<p>
|
||||||
|
Lume is heavily depend on Negentropy for syncing data. You need
|
||||||
|
to use at least 1 relay that support Negentropy. If you not
|
||||||
|
sure, you can keep using the default relay list.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</Frame>
|
</Frame>
|
||||||
<div className="flex flex-col items-center gap-1">
|
<div className="flex flex-col items-center gap-1">
|
||||||
<button
|
<button
|
||||||
|
|||||||
Reference in New Issue
Block a user