feat: negentropy progress

This commit is contained in:
2024-10-25 14:57:12 +07:00
parent 055d73c829
commit 5ab2b1ae31
27 changed files with 769 additions and 663 deletions

84
src-tauri/Cargo.lock generated
View File

@@ -44,6 +44,7 @@ dependencies = [
"tauri-plugin-window-state",
"tauri-specta",
"tokio",
"tracing-subscriber",
"url",
]
@@ -3479,7 +3480,7 @@ checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
[[package]]
name = "nostr"
version = "0.35.0"
source = "git+https://github.com/rust-nostr/nostr#a8114e090b333e6ca50fb80d8afc1ced4229ee33"
source = "git+https://github.com/rust-nostr/nostr#a398775dd1013482f785816e6b9fe99f7668416a"
dependencies = [
"aes",
"base64 0.22.1",
@@ -3509,7 +3510,7 @@ dependencies = [
[[package]]
name = "nostr-database"
version = "0.35.0"
source = "git+https://github.com/rust-nostr/nostr#a8114e090b333e6ca50fb80d8afc1ced4229ee33"
source = "git+https://github.com/rust-nostr/nostr#a398775dd1013482f785816e6b9fe99f7668416a"
dependencies = [
"async-trait",
"flatbuffers",
@@ -3523,7 +3524,7 @@ dependencies = [
[[package]]
name = "nostr-lmdb"
version = "0.35.0"
source = "git+https://github.com/rust-nostr/nostr#a8114e090b333e6ca50fb80d8afc1ced4229ee33"
source = "git+https://github.com/rust-nostr/nostr#a398775dd1013482f785816e6b9fe99f7668416a"
dependencies = [
"heed",
"nostr",
@@ -3536,7 +3537,7 @@ dependencies = [
[[package]]
name = "nostr-relay-pool"
version = "0.35.0"
source = "git+https://github.com/rust-nostr/nostr#a8114e090b333e6ca50fb80d8afc1ced4229ee33"
source = "git+https://github.com/rust-nostr/nostr#a398775dd1013482f785816e6b9fe99f7668416a"
dependencies = [
"async-utility",
"async-wsocket",
@@ -3554,7 +3555,7 @@ dependencies = [
[[package]]
name = "nostr-sdk"
version = "0.35.0"
source = "git+https://github.com/rust-nostr/nostr#a8114e090b333e6ca50fb80d8afc1ced4229ee33"
source = "git+https://github.com/rust-nostr/nostr#a398775dd1013482f785816e6b9fe99f7668416a"
dependencies = [
"async-utility",
"atomic-destructor",
@@ -3574,7 +3575,7 @@ dependencies = [
[[package]]
name = "nostr-signer"
version = "0.35.0"
source = "git+https://github.com/rust-nostr/nostr#a8114e090b333e6ca50fb80d8afc1ced4229ee33"
source = "git+https://github.com/rust-nostr/nostr#a398775dd1013482f785816e6b9fe99f7668416a"
dependencies = [
"async-utility",
"nostr",
@@ -3587,7 +3588,7 @@ dependencies = [
[[package]]
name = "nostr-zapper"
version = "0.35.0"
source = "git+https://github.com/rust-nostr/nostr#a8114e090b333e6ca50fb80d8afc1ced4229ee33"
source = "git+https://github.com/rust-nostr/nostr#a398775dd1013482f785816e6b9fe99f7668416a"
dependencies = [
"async-trait",
"nostr",
@@ -3607,6 +3608,16 @@ dependencies = [
"zbus 4.4.0",
]
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
dependencies = [
"overload",
"winapi",
]
[[package]]
name = "num"
version = "0.4.3"
@@ -3721,7 +3732,7 @@ dependencies = [
[[package]]
name = "nwc"
version = "0.35.0"
source = "git+https://github.com/rust-nostr/nostr#a8114e090b333e6ca50fb80d8afc1ced4229ee33"
source = "git+https://github.com/rust-nostr/nostr#a398775dd1013482f785816e6b9fe99f7668416a"
dependencies = [
"async-utility",
"nostr",
@@ -3993,6 +4004,12 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "page_size"
version = "0.6.0"
@@ -5316,6 +5333,15 @@ dependencies = [
"digest",
]
[[package]]
name = "sharded-slab"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
dependencies = [
"lazy_static",
]
[[package]]
name = "share-picker"
version = "0.1.0"
@@ -6331,6 +6357,16 @@ dependencies = [
"syn 2.0.79",
]
[[package]]
name = "thread_local"
version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
dependencies = [
"cfg-if",
"once_cell",
]
[[package]]
name = "tiff"
version = "0.9.1"
@@ -6606,6 +6642,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
dependencies = [
"once_cell",
"valuable",
]
[[package]]
name = "tracing-log"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
dependencies = [
"log",
"once_cell",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
dependencies = [
"nu-ansi-term",
"sharded-slab",
"smallvec",
"thread_local",
"tracing-core",
"tracing-log",
]
[[package]]
@@ -6813,6 +6875,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "valuable"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "vcpkg"
version = "0.2.15"

View File

@@ -48,6 +48,7 @@ linkify = "0.10.0"
regex = "1.10.4"
keyring = { version = "3", features = ["apple-native", "windows-native"] }
keyring-search = "1.2.0"
tracing-subscriber = { version = "0.3.18", features = ["fmt"] }
[target.'cfg(target_os = "macos")'.dependencies]
border = { git = "https://github.com/ahkohd/tauri-toolkit", branch = "v2" }

View File

@@ -1,4 +1,4 @@
wss://relay.damus.io,
wss://relay.nostr.net,
wss://relay.primal.net,
wss://nostr.fmt.wiz.biz,
wss://offchain.pub,

View File

@@ -1,15 +1,12 @@
use async_utility::thread::sleep;
use keyring::Entry;
use nostr_sdk::prelude::*;
use serde::{Deserialize, Serialize};
use specta::Type;
use std::{fs, str::FromStr, time::Duration};
use tauri::{Emitter, Manager, State};
use std::{str::FromStr, time::Duration};
use tauri::{Emitter, State};
use crate::{common::get_all_accounts, Nostr};
use super::sync::sync_account;
#[derive(Debug, Clone, Serialize, Deserialize, Type)]
struct Account {
secret_key: String,
@@ -24,11 +21,8 @@ pub fn get_accounts() -> Vec<String> {
#[tauri::command]
#[specta::specta]
pub async fn watch_account(
id: String,
state: State<'_, Nostr>,
app_handle: tauri::AppHandle,
) -> Result<String, String> {
pub async fn watch_account(id: String, state: State<'_, Nostr>) -> Result<String, String> {
let client = &state.client;
let public_key = PublicKey::from_str(&id).map_err(|e| e.to_string())?;
let npub = public_key.to_bech32().map_err(|e| e.to_string())?;
let keyring = Entry::new("Lume Safe Storage", &npub).map_err(|e| e.to_string())?;
@@ -36,14 +30,14 @@ pub async fn watch_account(
// Set empty password
keyring.set_password("").map_err(|e| e.to_string())?;
// Run sync for this account
sync_account(public_key, app_handle);
// Update state
state.accounts.lock().unwrap().push(npub.clone());
let mut accounts = state.accounts.lock().unwrap().clone();
accounts.push(npub.clone());
// Fake loading
sleep(Duration::from_secs(4)).await;
// Get user's profile
let _ = client
.fetch_metadata(public_key, Some(Duration::from_secs(4)))
.await;
Ok(npub)
}
@@ -54,7 +48,6 @@ pub async fn import_account(
key: String,
password: Option<String>,
state: State<'_, Nostr>,
app_handle: tauri::AppHandle,
) -> Result<String, String> {
let client = &state.client;
@@ -87,25 +80,21 @@ pub async fn import_account(
// Update signer
client.set_signer(Some(signer)).await;
// Run sync for this account
sync_account(public_key, app_handle);
// Fake loading
sleep(Duration::from_secs(4)).await;
// Update state
state.accounts.lock().unwrap().push(npub.clone());
let mut accounts = state.accounts.lock().unwrap().clone();
accounts.push(npub.clone());
// Get user's profile
let _ = client
.fetch_metadata(public_key, Some(Duration::from_secs(4)))
.await;
Ok(npub)
}
#[tauri::command]
#[specta::specta]
pub async fn connect_account(
uri: String,
state: State<'_, Nostr>,
app_handle: tauri::AppHandle,
) -> Result<String, String> {
pub async fn connect_account(uri: String, state: State<'_, Nostr>) -> Result<String, String> {
let client = &state.client;
match NostrConnectURI::parse(uri.clone()) {
@@ -118,9 +107,6 @@ pub async fn connect_account(
let remote_user = bunker_uri.signer_public_key().unwrap();
let remote_npub = remote_user.to_bech32().unwrap();
// Run sync for this account
sync_account(remote_user, app_handle);
match Nip46Signer::new(bunker_uri, app_keys, Duration::from_secs(120), None) {
Ok(signer) => {
let mut url = Url::parse(&uri).unwrap();
@@ -146,7 +132,13 @@ pub async fn connect_account(
let _ = client.set_signer(Some(signer.into())).await;
// Update state
state.accounts.lock().unwrap().push(remote_npub.clone());
let mut accounts = state.accounts.lock().unwrap().clone();
accounts.push(remote_npub.clone());
// Get user's profile
let _ = client
.fetch_metadata(remote_user, Some(Duration::from_secs(4)))
.await;
Ok(remote_npub)
}
@@ -198,24 +190,6 @@ pub fn delete_account(id: String) -> Result<(), String> {
Ok(())
}
#[tauri::command]
#[specta::specta]
pub async fn is_new_account(id: String, app_handle: tauri::AppHandle) -> Result<bool, String> {
let config_dir = app_handle.path().config_dir().map_err(|e| e.to_string())?;
let exist = fs::metadata(config_dir.join(id)).is_ok();
Ok(!exist)
}
#[tauri::command]
#[specta::specta]
pub async fn toggle_new_account(id: String, app_handle: tauri::AppHandle) -> Result<(), String> {
let config_dir = app_handle.path().config_dir().map_err(|e| e.to_string())?;
fs::File::create(config_dir.join(id)).unwrap();
Ok(())
}
#[tauri::command]
#[specta::specta]
pub async fn has_signer(id: String, state: State<'_, Nostr>) -> Result<bool, String> {

View File

@@ -1,11 +1,10 @@
use futures::future::join_all;
use nostr_sdk::prelude::*;
use serde::Serialize;
use specta::Type;
use std::{str::FromStr, time::Duration};
use tauri::State;
use crate::common::{create_tags, get_latest_event, parse_event, process_event, Meta};
use crate::common::{create_tags, parse_event, process_event, Meta};
use crate::{Nostr, DEFAULT_DIFFICULTY, FETCH_LIMIT};
#[derive(Debug, Clone, Serialize, Type)]
@@ -14,23 +13,15 @@ pub struct RichEvent {
pub parsed: Option<Meta>,
}
#[tauri::command]
#[specta::specta]
pub async fn get_event_meta(content: String) -> Result<Meta, ()> {
let meta = parse_event(&content).await;
Ok(meta)
}
#[tauri::command]
#[specta::specta]
pub async fn get_event(id: String, state: State<'_, Nostr>) -> Result<RichEvent, String> {
let client = &state.client;
let event_id = EventId::parse(&id).map_err(|err| err.to_string())?;
let filter = Filter::new().id(event_id);
let event_id = EventId::from_str(&id).map_err(|err| err.to_string())?;
match client.database().query(vec![filter.clone()]).await {
match client.database().event_by_id(&event_id).await {
Ok(events) => {
if let Some(event) = get_latest_event(&events) {
if let Some(event) = events {
let raw = event.as_json();
let parsed = if event.kind == Kind::TextNote {
Some(parse_event(&event.content).await)
@@ -40,26 +31,7 @@ pub async fn get_event(id: String, state: State<'_, Nostr>) -> Result<RichEvent,
Ok(RichEvent { raw, parsed })
} else {
match client
.fetch_events(vec![filter], Some(Duration::from_secs(10)))
.await
{
Ok(events) => {
if let Some(event) = get_latest_event(&events) {
let raw = event.as_json();
let parsed = if event.kind == Kind::TextNote {
Some(parse_event(&event.content).await)
} else {
None
};
Ok(RichEvent { raw, parsed })
} else {
Err("Not found.".into())
}
}
Err(err) => Err(err.to_string()),
}
Err("Event not found".to_string())
}
}
Err(err) => Err(err.to_string()),
@@ -68,35 +40,8 @@ pub async fn get_event(id: String, state: State<'_, Nostr>) -> Result<RichEvent,
#[tauri::command]
#[specta::specta]
pub async fn get_event_from(
id: String,
_relay_hint: String,
state: State<'_, Nostr>,
) -> Result<RichEvent, String> {
let client = &state.client;
let event_id = EventId::parse(&id).map_err(|err| err.to_string())?;
let filter = Filter::new().id(event_id);
match client
.fetch_events(vec![filter], Some(Duration::from_secs(5)))
.await
{
Ok(events) => {
if let Some(event) = events.first() {
let raw = event.as_json();
let parsed = if event.kind == Kind::TextNote {
Some(parse_event(&event.content).await)
} else {
None
};
Ok(RichEvent { raw, parsed })
} else {
Err("Cannot found this event with current relay list".into())
}
}
Err(err) => Err(err.to_string()),
}
pub async fn get_meta_from_event(content: String) -> Result<Meta, ()> {
Ok(parse_event(&content).await)
}
#[tauri::command]
@@ -110,21 +55,7 @@ pub async fn get_replies(id: String, state: State<'_, Nostr>) -> Result<Vec<Rich
.fetch_events(vec![filter], Some(Duration::from_secs(5)))
.await
{
Ok(events) => {
let futures = events.iter().map(|ev| async move {
let raw = ev.as_json();
let parsed = if ev.kind == Kind::TextNote {
Some(parse_event(&ev.content).await)
} else {
None
};
RichEvent { raw, parsed }
});
let rich_events = join_all(futures).await;
Ok(rich_events)
}
Ok(events) => Ok(process_event(client, events).await),
Err(err) => Err(err.to_string()),
}
}

View File

@@ -237,7 +237,7 @@ pub async fn set_group(
.authors(public_keys)
.limit(500);
if let Ok(report) = client.sync(filter, NegentropyOptions::default()).await {
if let Ok(report) = client.sync(filter, SyncOptions::default()).await {
println!("Received: {}", report.received.len());
handle.emit("synchronized", ()).unwrap();
};
@@ -331,7 +331,7 @@ pub async fn set_interest(
.hashtags(hashtags)
.limit(500);
if let Ok(report) = client.sync(filter, NegentropyOptions::default()).await {
if let Ok(report) = client.sync(filter, SyncOptions::default()).await {
println!("Received: {}", report.received.len());
handle.emit("synchronized", ()).unwrap();
};
@@ -560,15 +560,12 @@ pub async fn get_notifications(id: String, state: State<'_, Nostr>) -> Result<Ve
let client = &state.client;
let public_key = PublicKey::from_str(&id).map_err(|e| e.to_string())?;
let filter = Filter::new()
.pubkey(public_key)
.kinds(vec![
Kind::TextNote,
Kind::Repost,
Kind::Reaction,
Kind::ZapReceipt,
])
.limit(200);
let filter = Filter::new().pubkey(public_key).kinds(vec![
Kind::TextNote,
Kind::Repost,
Kind::Reaction,
Kind::ZapReceipt,
]);
match client.database().query(vec![filter]).await {
Ok(events) => Ok(events.into_iter().map(|ev| ev.as_json()).collect()),

View File

@@ -1,8 +1,11 @@
use nostr_sdk::prelude::*;
use serde::{Deserialize, Serialize};
use specta::Type;
use std::str::FromStr;
use tauri::{AppHandle, Manager};
use std::{
fs::{self, File},
str::FromStr,
};
use tauri::{ipc::Channel, AppHandle, Manager, State};
use tauri_specta::Event as TauriEvent;
use crate::Nostr;
@@ -45,70 +48,19 @@ pub fn sync_all(accounts: Vec<String>, app_handle: AppHandle) {
let client = &state.client;
let bootstrap_relays = state.bootstrap_relays.lock().unwrap().clone();
// NEG: Sync metadata
//
let metadata = Filter::new().authors(public_keys.clone()).kinds(vec![
Kind::Metadata,
Kind::ContactList,
Kind::Interests,
Kind::InterestSet,
Kind::FollowSet,
Kind::EventDeletion,
Kind::TextNote,
Kind::Repost,
Kind::Custom(30315),
]);
if let Ok(report) = client
.sync_with(&bootstrap_relays, metadata, NegentropyOptions::default())
.await
{
NegentropyEvent {
kind: NegentropyKind::Others,
total_event: report.received.len() as i32,
}
.emit(&app_handle)
.unwrap();
}
// NEG: Sync notification
//
let notification = Filter::new()
.pubkeys(public_keys)
.kinds(vec![
Kind::TextNote,
Kind::Repost,
Kind::Reaction,
Kind::ZapReceipt,
])
.limit(5000);
if let Ok(report) = client
.sync_with(
&bootstrap_relays,
notification,
NegentropyOptions::default(),
)
.await
{
NegentropyEvent {
kind: NegentropyKind::Notification,
total_event: report.received.len() as i32,
}
.emit(&app_handle)
.unwrap();
}
// NEG: Sync events for all pubkeys in local database
//
let pubkey_filter = Filter::new().kinds(vec![
Kind::ContactList,
Kind::Repost,
Kind::TextNote,
Kind::FollowSet,
]);
if let Ok(events) = client.database().query(vec![pubkey_filter]).await {
if let Ok(events) = client
.database()
.query(vec![Filter::new().kinds(vec![
Kind::ContactList,
Kind::FollowSet,
Kind::MuteList,
Kind::Repost,
Kind::TextNote,
])])
.await
{
let pubkeys: Vec<PublicKey> = events
.iter()
.flat_map(|ev| ev.tags.public_keys().copied())
@@ -126,15 +78,15 @@ pub fn sync_all(accounts: Vec<String>, app_handle: AppHandle) {
let events = Filter::new()
.authors(authors.clone())
.kinds(vec![Kind::TextNote, Kind::Repost])
.limit(5000);
.limit(1000);
if let Ok(report) = client
.sync_with(&bootstrap_relays, events, NegentropyOptions::default())
if let Ok(output) = client
.sync_with(&bootstrap_relays, events, SyncOptions::default())
.await
{
NegentropyEvent {
kind: NegentropyKind::Events,
total_event: report.received.len() as i32,
total_event: output.received.len() as i32,
}
.emit(&app_handle)
.unwrap();
@@ -144,124 +96,35 @@ pub fn sync_all(accounts: Vec<String>, app_handle: AppHandle) {
//
let metadata = Filter::new()
.authors(authors)
.kinds(vec![Kind::Metadata, Kind::ContactList]);
.kinds(vec![
Kind::Metadata,
Kind::ContactList,
Kind::Interests,
Kind::InterestSet,
Kind::FollowSet,
Kind::MuteList,
Kind::RelaySet,
])
.limit(1000);
if let Ok(report) = client
.sync_with(&bootstrap_relays, metadata, NegentropyOptions::default())
if let Ok(output) = client
.sync_with(&bootstrap_relays, metadata, SyncOptions::default())
.await
{
NegentropyEvent {
kind: NegentropyKind::Metadata,
total_event: report.received.len() as i32,
total_event: output.received.len() as i32,
}
.emit(&app_handle)
.unwrap();
}
}
}
});
}
pub fn sync_account(public_key: PublicKey, app_handle: AppHandle) {
tauri::async_runtime::spawn(async move {
let state = app_handle.state::<Nostr>();
let client = &state.client;
let bootstrap_relays = state.bootstrap_relays.lock().unwrap().clone();
// NEG: Sync all user's metadata
//
let metadata = Filter::new().author(public_key).kinds(vec![
Kind::Metadata,
Kind::ContactList,
Kind::Interests,
Kind::InterestSet,
Kind::FollowSet,
Kind::RelayList,
Kind::RelaySet,
Kind::EventDeletion,
Kind::Custom(30315),
]);
if let Ok(report) = client
.sync_with(&bootstrap_relays, metadata, NegentropyOptions::default())
.await
{
NegentropyEvent {
kind: NegentropyKind::Metadata,
total_event: report.received.len() as i32,
}
.emit(&app_handle)
.unwrap();
}
if let Ok(contact_list) = client.database().contacts_public_keys(public_key).await {
// NEG: Sync all contact's metadata
//
let metadata = Filter::new()
.authors(contact_list.clone())
.kinds(vec![Kind::Metadata, Kind::RelaySet, Kind::Custom(30315)])
.limit(1000);
if let Ok(report) = client
.sync_with(&bootstrap_relays, metadata, NegentropyOptions::default())
.await
{
NegentropyEvent {
kind: NegentropyKind::Metadata,
total_event: report.received.len() as i32,
}
.emit(&app_handle)
.unwrap();
}
// NEG: Sync all contact's events
//
let metadata = Filter::new()
.authors(contact_list.clone())
.kinds(vec![Kind::TextNote, Kind::Repost])
.limit(1000);
if let Ok(report) = client
.sync_with(&bootstrap_relays, metadata, NegentropyOptions::default())
.await
{
NegentropyEvent {
kind: NegentropyKind::Events,
total_event: report.received.len() as i32,
}
.emit(&app_handle)
.unwrap();
}
// NEG: Sync all contact's other metadata
//
let metadata = Filter::new()
.authors(contact_list)
.kinds(vec![
Kind::Interests,
Kind::InterestSet,
Kind::FollowSet,
Kind::EventDeletion,
])
.limit(1000);
if let Ok(report) = client
.sync_with(&bootstrap_relays, metadata, NegentropyOptions::default())
.await
{
NegentropyEvent {
kind: NegentropyKind::Metadata,
total_event: report.received.len() as i32,
}
.emit(&app_handle)
.unwrap();
}
}
// NEG: Sync all user's metadata
// NEG: Sync notification
//
let notification = Filter::new()
.pubkey(public_key)
.pubkeys(public_keys.clone())
.kinds(vec![
Kind::TextNote,
Kind::Repost,
@@ -270,20 +133,188 @@ pub fn sync_account(public_key: PublicKey, app_handle: AppHandle) {
])
.limit(500);
if let Ok(report) = client
.sync_with(
&bootstrap_relays,
notification,
NegentropyOptions::default(),
)
if let Ok(output) = client
.sync_with(&bootstrap_relays, notification, SyncOptions::default())
.await
{
NegentropyEvent {
kind: NegentropyKind::Notification,
total_event: report.received.len() as i32,
total_event: output.received.len() as i32,
}
.emit(&app_handle)
.unwrap();
}
// NEG: Sync metadata
//
let metadata = Filter::new().authors(public_keys.clone()).kinds(vec![
Kind::Metadata,
Kind::ContactList,
Kind::Interests,
Kind::InterestSet,
Kind::FollowSet,
Kind::RelayList,
Kind::MuteList,
Kind::EventDeletion,
Kind::Bookmarks,
Kind::BookmarkSet,
Kind::Emojis,
Kind::EmojiSet,
Kind::TextNote,
Kind::Repost,
Kind::Custom(30315),
]);
if let Ok(output) = client
.sync_with(&bootstrap_relays, metadata, SyncOptions::default())
.await
{
NegentropyEvent {
kind: NegentropyKind::Others,
total_event: output.received.len() as i32,
}
.emit(&app_handle)
.unwrap();
}
});
}
#[tauri::command]
#[specta::specta]
pub fn is_account_sync(id: String, app_handle: tauri::AppHandle) -> Result<bool, String> {
let config_dir = app_handle
.path()
.app_config_dir()
.map_err(|e| e.to_string())?;
let exist = fs::metadata(config_dir.join(id)).is_ok();
Ok(exist)
}
#[tauri::command]
#[specta::specta]
pub async fn sync_account(
id: String,
state: State<'_, Nostr>,
reader: Channel<f64>,
app_handle: tauri::AppHandle,
) -> Result<(), String> {
let client = &state.client;
let bootstrap_relays = state.bootstrap_relays.lock().unwrap().clone();
let public_key = PublicKey::from_bech32(&id).map_err(|e| e.to_string())?;
let filter = Filter::new().author(public_key).kinds(vec![
Kind::Metadata,
Kind::ContactList,
Kind::Interests,
Kind::InterestSet,
Kind::FollowSet,
Kind::RelayList,
Kind::MuteList,
Kind::EventDeletion,
Kind::Bookmarks,
Kind::BookmarkSet,
Kind::TextNote,
Kind::Repost,
Kind::Custom(30315),
]);
let (tx, mut rx) = SyncProgress::channel();
let opts = SyncOptions::default().progress(tx);
tauri::async_runtime::spawn(async move {
while (rx.changed().await).is_ok() {
let SyncProgress { total, current } = *rx.borrow_and_update();
if total > 0 {
reader
.send((current as f64 / total as f64) * 100.0)
.unwrap()
}
}
});
if let Ok(output) = client
.sync_with(&bootstrap_relays, filter, opts.clone())
.await
{
println!("Success: {:?}", output.success);
println!("Failed: {:?}", output.failed);
let event_pubkeys = client
.database()
.query(vec![Filter::new().kinds(vec![
Kind::ContactList,
Kind::FollowSet,
Kind::MuteList,
Kind::Repost,
Kind::TextNote,
])])
.await
.map_err(|e| e.to_string())?;
if !event_pubkeys.is_empty() {
let pubkeys: Vec<PublicKey> = event_pubkeys
.iter()
.flat_map(|ev| ev.tags.public_keys().copied())
.collect();
let filter = Filter::new()
.authors(pubkeys)
.kinds(vec![
Kind::Metadata,
Kind::TextNote,
Kind::Repost,
Kind::EventDeletion,
Kind::Interests,
Kind::InterestSet,
Kind::FollowSet,
Kind::RelayList,
Kind::MuteList,
Kind::EventDeletion,
Kind::Bookmarks,
Kind::BookmarkSet,
Kind::Custom(30315),
])
.limit(10000);
if let Ok(output) = client
.sync_with(&bootstrap_relays, filter, opts.clone())
.await
{
println!("Success: {:?}", output.success);
println!("Failed: {:?}", output.failed);
}
};
}
let event_ids = client
.database()
.query(vec![Filter::new().kinds(vec![
Kind::TextNote,
Kind::Repost,
Kind::Bookmarks,
Kind::BookmarkSet,
])])
.await
.map_err(|e| e.to_string())?;
if !event_ids.is_empty() {
let ids: Vec<EventId> = event_ids.iter().map(|ev| ev.id).collect();
let filter = Filter::new().events(ids);
if let Ok(output) = client.sync_with(&bootstrap_relays, filter, opts).await {
println!("Success: {:?}", output.success);
println!("Failed: {:?}", output.failed);
}
}
let config_dir = app_handle
.path()
.app_config_dir()
.map_err(|e| e.to_string())?;
let _ = File::create(config_dir.join(id));
Ok(())
}

View File

@@ -34,14 +34,12 @@ const NOSTR_EVENTS: [&str; 10] = [
"Nostr:nevent1",
];
const NOSTR_MENTIONS: [&str; 10] = [
const NOSTR_MENTIONS: [&str; 8] = [
"@npub1",
"nostr:npub1",
"nostr:nprofile1",
"nostr:naddr1",
"npub1",
"nprofile1",
"naddr1",
"Nostr:npub1",
"Nostr:nprofile1",
"Nostr:naddr1",

View File

@@ -5,14 +5,7 @@
#[cfg(target_os = "macos")]
use border::WebviewWindowExt as BorderWebviewWindowExt;
use commands::{
account::*,
event::*,
metadata::*,
relay::*,
sync::{sync_all, NegentropyEvent},
window::*,
};
use commands::{account::*, event::*, metadata::*, relay::*, sync::*, window::*};
use common::{get_all_accounts, parse_event};
use nostr_sdk::prelude::{Profile as DatabaseProfile, *};
use serde::{Deserialize, Serialize};
@@ -97,8 +90,12 @@ pub const FETCH_LIMIT: usize = 50;
pub const NOTIFICATION_SUB_ID: &str = "lume_notification";
fn main() {
tracing_subscriber::fmt::init();
let builder = Builder::<tauri::Wry>::new()
.commands(collect_commands![
sync_account,
is_account_sync,
get_relays,
connect_relay,
remove_relay,
@@ -111,8 +108,6 @@ fn main() {
get_private_key,
delete_account,
reset_password,
is_new_account,
toggle_new_account,
has_signer,
set_signer,
get_profile,
@@ -138,9 +133,8 @@ fn main() {
get_user_settings,
set_user_settings,
verify_nip05,
get_event_meta,
get_meta_from_event,
get_event,
get_event_from,
get_replies,
subscribe_to,
get_all_events_by_author,