chore: update deps

This commit is contained in:
reya 2024-08-18 15:51:34 +07:00
parent 4cb49d44c7
commit beac1a189e
12 changed files with 930 additions and 743 deletions

View File

@ -10,7 +10,7 @@
"tauri": "tauri" "tauri": "tauri"
}, },
"dependencies": { "dependencies": {
"@getalby/bitcoin-connect-react": "^3.6.1", "@getalby/bitcoin-connect-react": "^3.6.2",
"@phosphor-icons/react": "^2.1.7", "@phosphor-icons/react": "^2.1.7",
"@radix-ui/react-avatar": "^1.1.0", "@radix-ui/react-avatar": "^1.1.0",
"@radix-ui/react-checkbox": "^1.1.1", "@radix-ui/react-checkbox": "^1.1.1",
@ -21,12 +21,12 @@
"@radix-ui/react-tooltip": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.2",
"@tanstack/query-persist-client-core": "^5.51.21", "@tanstack/query-persist-client-core": "^5.51.21",
"@tanstack/react-query": "^5.51.23", "@tanstack/react-query": "^5.51.23",
"@tanstack/react-router": "^1.47.1", "@tanstack/react-router": "^1.48.1",
"@tauri-apps/api": "2.0.0-rc.0", "@tauri-apps/api": "2.0.0-rc.1",
"@tauri-apps/plugin-clipboard-manager": "2.0.0-rc.0", "@tauri-apps/plugin-clipboard-manager": "2.0.0-rc.0",
"@tauri-apps/plugin-dialog": "2.0.0-rc.0", "@tauri-apps/plugin-dialog": "2.0.0-rc.0",
"@tauri-apps/plugin-fs": "2.0.0-rc.0", "@tauri-apps/plugin-fs": "2.0.0-rc.1",
"@tauri-apps/plugin-http": "2.0.0-rc.0", "@tauri-apps/plugin-http": "2.0.0-rc.1",
"@tauri-apps/plugin-os": "2.0.0-rc.0", "@tauri-apps/plugin-os": "2.0.0-rc.0",
"@tauri-apps/plugin-process": "2.0.0-rc.0", "@tauri-apps/plugin-process": "2.0.0-rc.0",
"@tauri-apps/plugin-shell": "2.0.0-rc.0", "@tauri-apps/plugin-shell": "2.0.0-rc.0",
@ -36,7 +36,7 @@
"bitcoin-units": "^1.0.0", "bitcoin-units": "^1.0.0",
"dayjs": "^1.11.12", "dayjs": "^1.11.12",
"embla-carousel-react": "^8.1.8", "embla-carousel-react": "^8.1.8",
"i18next": "^23.12.2", "i18next": "^23.13.0",
"i18next-resources-to-backend": "^1.2.1", "i18next-resources-to-backend": "^1.2.1",
"light-bolt11-decoder": "^3.1.1", "light-bolt11-decoder": "^3.1.1",
"minidenticons": "^4.2.1", "minidenticons": "^4.2.1",
@ -50,17 +50,17 @@
"react-string-replace": "^1.1.1", "react-string-replace": "^1.1.1",
"slate": "^0.103.0", "slate": "^0.103.0",
"slate-react": "^0.107.1", "slate-react": "^0.107.1",
"use-debounce": "^10.0.2", "use-debounce": "^10.0.3",
"virtua": "^0.33.5" "virtua": "^0.33.7"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^1.8.3", "@biomejs/biome": "^1.8.3",
"@evilmartians/harmony": "^1.2.0", "@evilmartians/harmony": "^1.2.0",
"@tailwindcss/forms": "^0.5.7", "@tailwindcss/forms": "^0.5.7",
"@tailwindcss/typography": "^0.5.14", "@tailwindcss/typography": "^0.5.14",
"@tanstack/router-devtools": "^1.47.1", "@tanstack/router-devtools": "^1.48.1",
"@tanstack/router-plugin": "^1.47.0", "@tanstack/router-plugin": "^1.47.0",
"@tauri-apps/cli": "2.0.0-rc.1", "@tauri-apps/cli": "2.0.0-rc.4",
"@types/react": "npm:types-react@19.0.0-rc.1", "@types/react": "npm:types-react@19.0.0-rc.1",
"@types/react-dom": "npm:types-react-dom@19.0.0-rc.1", "@types/react-dom": "npm:types-react-dom@19.0.0-rc.1",
"@vitejs/plugin-react": "^4.3.1", "@vitejs/plugin-react": "^4.3.1",
@ -69,12 +69,12 @@
"clsx": "^2.1.1", "clsx": "^2.1.1",
"postcss": "^8.4.41", "postcss": "^8.4.41",
"tailwind-gradient-mask-image": "^1.2.0", "tailwind-gradient-mask-image": "^1.2.0",
"tailwind-merge": "^2.5.0", "tailwind-merge": "^2.5.2",
"tailwind-scrollbar": "^3.1.0", "tailwind-scrollbar": "^3.1.0",
"tailwindcss": "^3.4.9", "tailwindcss": "^3.4.10",
"tailwindcss-content-visibility": "^0.2.0", "tailwindcss-content-visibility": "^0.2.0",
"typescript": "^5.5.4", "typescript": "^5.5.4",
"vite": "^5.4.0", "vite": "^5.4.1",
"vite-tsconfig-paths": "^5.0.1" "vite-tsconfig-paths": "^5.0.1"
}, },
"overrides": { "overrides": {

474
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

782
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -3443,6 +3443,13 @@
"core:webview:allow-create-webview-window" "core:webview:allow-create-webview-window"
] ]
}, },
{
"description": "core:webview:allow-get-all-webviews -> Enables the get_all_webviews command without any pre-configured scope.",
"type": "string",
"enum": [
"core:webview:allow-get-all-webviews"
]
},
{ {
"description": "core:webview:allow-internal-toggle-devtools -> Enables the internal_toggle_devtools command without any pre-configured scope.", "description": "core:webview:allow-internal-toggle-devtools -> Enables the internal_toggle_devtools command without any pre-configured scope.",
"type": "string", "type": "string",
@ -3527,6 +3534,13 @@
"core:webview:deny-create-webview-window" "core:webview:deny-create-webview-window"
] ]
}, },
{
"description": "core:webview:deny-get-all-webviews -> Denies the get_all_webviews command without any pre-configured scope.",
"type": "string",
"enum": [
"core:webview:deny-get-all-webviews"
]
},
{ {
"description": "core:webview:deny-internal-toggle-devtools -> Denies the internal_toggle_devtools command without any pre-configured scope.", "description": "core:webview:deny-internal-toggle-devtools -> Denies the internal_toggle_devtools command without any pre-configured scope.",
"type": "string", "type": "string",
@ -3653,6 +3667,13 @@
"core:window:allow-destroy" "core:window:allow-destroy"
] ]
}, },
{
"description": "core:window:allow-get-all-windows -> Enables the get_all_windows command without any pre-configured scope.",
"type": "string",
"enum": [
"core:window:allow-get-all-windows"
]
},
{ {
"description": "core:window:allow-hide -> Enables the hide command without any pre-configured scope.", "description": "core:window:allow-hide -> Enables the hide command without any pre-configured scope.",
"type": "string", "type": "string",
@ -4108,6 +4129,13 @@
"core:window:deny-destroy" "core:window:deny-destroy"
] ]
}, },
{
"description": "core:window:deny-get-all-windows -> Denies the get_all_windows command without any pre-configured scope.",
"type": "string",
"enum": [
"core:window:deny-get-all-windows"
]
},
{ {
"description": "core:window:deny-hide -> Denies the hide command without any pre-configured scope.", "description": "core:window:deny-hide -> Denies the hide command without any pre-configured scope.",
"type": "string", "type": "string",

View File

@ -3443,6 +3443,13 @@
"core:webview:allow-create-webview-window" "core:webview:allow-create-webview-window"
] ]
}, },
{
"description": "core:webview:allow-get-all-webviews -> Enables the get_all_webviews command without any pre-configured scope.",
"type": "string",
"enum": [
"core:webview:allow-get-all-webviews"
]
},
{ {
"description": "core:webview:allow-internal-toggle-devtools -> Enables the internal_toggle_devtools command without any pre-configured scope.", "description": "core:webview:allow-internal-toggle-devtools -> Enables the internal_toggle_devtools command without any pre-configured scope.",
"type": "string", "type": "string",
@ -3527,6 +3534,13 @@
"core:webview:deny-create-webview-window" "core:webview:deny-create-webview-window"
] ]
}, },
{
"description": "core:webview:deny-get-all-webviews -> Denies the get_all_webviews command without any pre-configured scope.",
"type": "string",
"enum": [
"core:webview:deny-get-all-webviews"
]
},
{ {
"description": "core:webview:deny-internal-toggle-devtools -> Denies the internal_toggle_devtools command without any pre-configured scope.", "description": "core:webview:deny-internal-toggle-devtools -> Denies the internal_toggle_devtools command without any pre-configured scope.",
"type": "string", "type": "string",
@ -3653,6 +3667,13 @@
"core:window:allow-destroy" "core:window:allow-destroy"
] ]
}, },
{
"description": "core:window:allow-get-all-windows -> Enables the get_all_windows command without any pre-configured scope.",
"type": "string",
"enum": [
"core:window:allow-get-all-windows"
]
},
{ {
"description": "core:window:allow-hide -> Enables the hide command without any pre-configured scope.", "description": "core:window:allow-hide -> Enables the hide command without any pre-configured scope.",
"type": "string", "type": "string",
@ -4108,6 +4129,13 @@
"core:window:deny-destroy" "core:window:deny-destroy"
] ]
}, },
{
"description": "core:window:deny-get-all-windows -> Denies the get_all_windows command without any pre-configured scope.",
"type": "string",
"enum": [
"core:window:deny-get-all-windows"
]
},
{ {
"description": "core:window:deny-hide -> Denies the hide command without any pre-configured scope.", "description": "core:window:deny-hide -> Denies the hide command without any pre-configured scope.",
"type": "string", "type": "string",

View File

@ -39,7 +39,10 @@ pub async fn get_event(id: &str, state: State<'_, Nostr>) -> Result<RichEvent, S
}; };
match client match client
.get_events_of(vec![Filter::new().id(event_id)], None) .get_events_of(
vec![Filter::new().id(event_id)],
EventSource::both(Some(Duration::from_secs(5))),
)
.await .await
{ {
Ok(events) => { Ok(events) => {
@ -88,7 +91,10 @@ pub async fn get_event_from(
if !settings.use_relay_hint { if !settings.use_relay_hint {
match client match client
.get_events_of(vec![Filter::new().id(event_id)], None) .get_events_of(
vec![Filter::new().id(event_id)],
EventSource::both(Some(Duration::from_secs(5))),
)
.await .await
{ {
Ok(events) => { Ok(events) => {
@ -152,7 +158,13 @@ pub async fn get_replies(id: &str, state: State<'_, Nostr>) -> Result<Vec<RichEv
let filter = Filter::new().kinds(vec![Kind::TextNote]).event(event_id); let filter = Filter::new().kinds(vec![Kind::TextNote]).event(event_id);
match client.get_events_of(vec![filter], None).await { match client
.get_events_of(
vec![filter],
EventSource::both(Some(Duration::from_secs(5))),
)
.await
{
Ok(events) => { Ok(events) => {
let futures = events.into_iter().map(|ev| async move { let futures = events.into_iter().map(|ev| async move {
let raw = ev.as_json(); let raw = ev.as_json();
@ -217,7 +229,13 @@ pub async fn get_events_by(
.limit(FETCH_LIMIT) .limit(FETCH_LIMIT)
.until(until); .until(until);
match client.get_events_of(vec![filter], None).await { match client
.get_events_of(
vec![filter],
EventSource::both(Some(Duration::from_secs(5))),
)
.await
{
Ok(events) => { Ok(events) => {
let futures = events.into_iter().map(|ev| async move { let futures = events.into_iter().map(|ev| async move {
let raw = ev.as_json(); let raw = ev.as_json();
@ -344,7 +362,10 @@ pub async fn get_group_events(
.authors(authors); .authors(authors);
match client match client
.get_events_of(vec![filter], Some(Duration::from_secs(10))) .get_events_of(
vec![filter],
EventSource::both(Some(Duration::from_secs(5))),
)
.await .await
{ {
Ok(events) => { Ok(events) => {
@ -387,7 +408,10 @@ pub async fn get_global_events(
.until(as_of); .until(as_of);
match client match client
.get_events_of(vec![filter], Some(Duration::from_secs(8))) .get_events_of(
vec![filter],
EventSource::both(Some(Duration::from_secs(5))),
)
.await .await
{ {
Ok(events) => { Ok(events) => {
@ -428,7 +452,13 @@ pub async fn get_hashtag_events(
.until(as_of) .until(as_of)
.hashtags(hashtags); .hashtags(hashtags);
match client.get_events_of(vec![filter], None).await { match client
.get_events_of(
vec![filter],
EventSource::both(Some(Duration::from_secs(5))),
)
.await
{
Ok(events) => { Ok(events) => {
let dedup = dedup_event(&events); let dedup = dedup_event(&events);
let futures = dedup.into_iter().map(|ev| async move { let futures = dedup.into_iter().map(|ev| async move {
@ -644,7 +674,7 @@ pub async fn user_to_bech32(user: &str, state: State<'_, Nostr>) -> Result<Strin
.author(public_key) .author(public_key)
.kind(Kind::RelayList) .kind(Kind::RelayList)
.limit(1)], .limit(1)],
Some(Duration::from_secs(10)), EventSource::both(Some(Duration::from_secs(5))),
) )
.await .await
{ {

View File

@ -3,22 +3,18 @@ use nostr_sdk::prelude::*;
use std::{str::FromStr, time::Duration}; use std::{str::FromStr, time::Duration};
use tauri::State; use tauri::State;
use crate::common::get_latest_event;
use crate::{Nostr, Settings}; use crate::{Nostr, Settings};
#[tauri::command] #[tauri::command]
#[specta::specta] #[specta::specta]
pub async fn get_current_profile(state: State<'_, Nostr>) -> Result<String, String> { pub async fn get_profile(id: Option<String>, state: State<'_, Nostr>) -> Result<String, String> {
let client = &state.client; let client = &state.client;
let public_key: PublicKey = match id {
let signer = match client.signer().await { Some(user_id) => match PublicKey::from_str(&user_id) {
Ok(signer) => signer, Ok(val) => val,
Err(err) => return Err(format!("Failed to get signer: {}", err)), Err(_) => return Err("Public Key is not valid".into()),
}; },
None => client.signer().await.unwrap().public_key().await.unwrap(),
let public_key = match signer.public_key().await {
Ok(pk) => pk,
Err(err) => return Err(format!("Failed to get public key: {}", err)),
}; };
let filter = Filter::new() let filter = Filter::new()
@ -26,70 +22,25 @@ pub async fn get_current_profile(state: State<'_, Nostr>) -> Result<String, Stri
.kind(Kind::Metadata) .kind(Kind::Metadata)
.limit(1); .limit(1);
let events_result = client let query = client
.get_events_of(vec![filter], Some(Duration::from_secs(10))) .get_events_of(
vec![filter],
EventSource::both(Some(Duration::from_secs(3))),
)
.await; .await;
match events_result { if let Ok(events) = query {
Ok(events) => { if let Some(event) = events.first() {
if let Some(event) = get_latest_event(&events) { if let Ok(metadata) = Metadata::from_json(&event.content) {
match Metadata::from_json(&event.content) { Ok(metadata.as_json())
Ok(metadata) => Ok(metadata.as_json()),
Err(_) => Err("Failed to parse metadata.".into()),
}
} else { } else {
Err("No matching events found.".into()) Err("Parse metadata failed".into())
}
}
Err(err) => Err(format!("Failed to get events: {}", err)),
}
}
#[tauri::command]
#[specta::specta]
pub async fn get_profile(id: &str, state: State<'_, Nostr>) -> Result<String, String> {
let client = &state.client;
let public_key: Option<PublicKey> = match Nip19::from_bech32(id) {
Ok(val) => match val {
Nip19::Pubkey(key) => Some(key),
Nip19::Profile(profile) => {
let relays = profile.relays;
for relay in relays.into_iter() {
let _ = client.add_relay(&relay).await.unwrap_or_default();
client.connect_relay(&relay).await.unwrap_or_default();
}
Some(profile.public_key)
}
_ => None,
},
Err(_) => match PublicKey::from_str(id) {
Ok(val) => Some(val),
Err(_) => None,
},
};
if let Some(author) = public_key {
let filter = Filter::new().author(author).kind(Kind::Metadata).limit(1);
let query = client
.get_events_of(vec![filter], Some(Duration::from_secs(10)))
.await;
if let Ok(events) = query {
if let Some(event) = events.first() {
if let Ok(metadata) = Metadata::from_json(&event.content) {
Ok(metadata.as_json())
} else {
Err("Parse metadata failed".into())
}
} else {
let rand_metadata = Metadata::new();
Ok(rand_metadata.as_json())
} }
} else { } else {
Err("Get metadata failed".into()) Ok(Metadata::new().as_json())
} }
} else { } else {
Err("Public Key is not valid".into()) Err("Get metadata failed".into())
} }
} }
@ -185,10 +136,10 @@ pub async fn is_contact_list_empty(state: State<'_, Nostr>) -> Result<bool, ()>
#[tauri::command] #[tauri::command]
#[specta::specta] #[specta::specta]
pub async fn check_contact(hex: &str, state: State<'_, Nostr>) -> Result<bool, String> { pub async fn check_contact(hex: String, state: State<'_, Nostr>) -> Result<bool, String> {
let contact_list = state.contact_list.lock().unwrap(); let contact_list = state.contact_list.lock().unwrap();
match PublicKey::from_str(hex) { match PublicKey::from_str(&hex) {
Ok(public_key) => match contact_list.iter().position(|x| x.public_key == public_key) { Ok(public_key) => match contact_list.iter().position(|x| x.public_key == public_key) {
Some(_) => Ok(true), Some(_) => Ok(true),
None => Ok(false), None => Ok(false),
@ -244,27 +195,19 @@ pub async fn set_nstore(
state: State<'_, Nostr>, state: State<'_, Nostr>,
) -> Result<String, String> { ) -> Result<String, String> {
let client = &state.client; let client = &state.client;
let signer = client.signer().await.map_err(|e| e.to_string())?;
let public_key = signer.public_key().await.map_err(|e| e.to_string())?;
match client.signer().await { let encrypted = signer
Ok(signer) => { .nip44_encrypt(public_key, content)
let public_key = match signer.public_key().await { .await
Ok(pk) => pk, .map_err(|e| e.to_string())?;
Err(err) => return Err(format!("Failed to get public key: {}", err)),
};
let encrypted = match signer.nip44_encrypt(public_key, content).await { let tag = Tag::identifier(key);
Ok(enc) => enc, let builder = EventBuilder::new(Kind::ApplicationSpecificData, encrypted, vec![tag]);
Err(err) => return Err(format!("Encryption failed: {}", err)),
};
let tag = Tag::identifier(key); match client.send_event_builder(builder).await {
let builder = EventBuilder::new(Kind::ApplicationSpecificData, encrypted, vec![tag]); Ok(event_id) => Ok(event_id.to_string()),
match client.send_event_builder(builder).await {
Ok(event_id) => Ok(event_id.to_string()),
Err(err) => Err(err.to_string()),
}
}
Err(err) => Err(err.to_string()), Err(err) => Err(err.to_string()),
} }
} }
@ -273,38 +216,34 @@ pub async fn set_nstore(
#[specta::specta] #[specta::specta]
pub async fn get_nstore(key: &str, state: State<'_, Nostr>) -> Result<String, String> { pub async fn get_nstore(key: &str, state: State<'_, Nostr>) -> Result<String, String> {
let client = &state.client; let client = &state.client;
let signer = client.signer().await.map_err(|e| e.to_string())?;
let public_key = signer.public_key().await.map_err(|e| e.to_string())?;
if let Ok(signer) = client.signer().await { let filter = Filter::new()
let public_key = match signer.public_key().await { .author(public_key)
Ok(pk) => pk, .kind(Kind::ApplicationSpecificData)
Err(err) => return Err(format!("Failed to get public key: {}", err)), .identifier(key)
}; .limit(1);
let filter = Filter::new() match client
.author(public_key) .get_events_of(
.kind(Kind::ApplicationSpecificData) vec![filter],
.identifier(key) EventSource::both(Some(Duration::from_secs(5))),
.limit(1); )
.await
match client {
.get_events_of(vec![filter], Some(Duration::from_secs(5))) Ok(events) => {
.await if let Some(event) = events.first() {
{ let content = event.content();
Ok(events) => { match signer.nip44_decrypt(public_key, content).await {
if let Some(event) = events.first() { Ok(decrypted) => Ok(decrypted),
let content = event.content(); Err(_) => Err(event.content.to_string()),
match signer.nip44_decrypt(public_key, content).await {
Ok(decrypted) => Ok(decrypted),
Err(_) => Err(event.content.to_string()),
}
} else {
Err("Value not found".into())
} }
} else {
Err("Not found".into())
} }
Err(err) => Err(err.to_string()),
} }
} else { Err(err) => Err(err.to_string()),
Err("Signer is required".into())
} }
} }
@ -329,7 +268,8 @@ pub async fn set_wallet(uri: &str, state: State<'_, Nostr>) -> Result<bool, Stri
#[specta::specta] #[specta::specta]
pub async fn load_wallet(state: State<'_, Nostr>) -> Result<String, String> { pub async fn load_wallet(state: State<'_, Nostr>) -> Result<String, String> {
let client = &state.client; let client = &state.client;
let keyring = Entry::new("Lume Secret", "Bitcoin Connect").unwrap(); let keyring =
Entry::new("Lume Secret Storage", "Bitcoin Connect").map_err(|e| e.to_string())?;
match keyring.get_password() { match keyring.get_password() {
Ok(val) => { Ok(val) => {
@ -353,16 +293,17 @@ pub async fn load_wallet(state: State<'_, Nostr>) -> Result<String, String> {
#[tauri::command] #[tauri::command]
#[specta::specta] #[specta::specta]
pub async fn remove_wallet(state: State<'_, Nostr>) -> Result<(), ()> { pub async fn remove_wallet(state: State<'_, Nostr>) -> Result<(), String> {
let client = &state.client; let client = &state.client;
let keyring = Entry::new("Lume Secret", "Bitcoin Connect").unwrap(); let keyring =
Entry::new("Lume Secret Storage", "Bitcoin Connect").map_err(|e| e.to_string())?;
match keyring.delete_credential() { match keyring.delete_credential() {
Ok(_) => { Ok(_) => {
client.unset_zapper().await; client.unset_zapper().await;
Ok(()) Ok(())
} }
Err(_) => Err(()), Err(e) => Err(e.to_string()),
} }
} }
@ -375,23 +316,10 @@ pub async fn zap_profile(
state: State<'_, Nostr>, state: State<'_, Nostr>,
) -> Result<bool, String> { ) -> Result<bool, String> {
let client = &state.client; let client = &state.client;
let public_key = match Nip19::from_bech32(id) { let public_key: PublicKey = PublicKey::from_str(id).map_err(|e| e.to_string())?;
Ok(val) => match val {
Nip19::Pubkey(key) => key,
Nip19::Profile(profile) => profile.public_key,
_ => return Err("Public Key is not valid.".into()),
},
Err(_) => match PublicKey::from_str(id) {
Ok(val) => val,
Err(_) => return Err("Public Key is not valid.".into()),
},
};
let details = ZapDetails::new(ZapType::Private).message(message); let details = ZapDetails::new(ZapType::Private).message(message);
let num = match amount.parse::<u64>() { let num = amount.parse::<u64>().map_err(|e| e.to_string())?;
Ok(val) => val,
Err(_) => return Err("Invalid amount.".into()),
};
if client.zap(public_key, num, Some(details)).await.is_ok() { if client.zap(public_key, num, Some(details)).await.is_ok() {
Ok(true) Ok(true)
@ -422,10 +350,7 @@ pub async fn zap_event(
}; };
let details = ZapDetails::new(ZapType::Private).message(message); let details = ZapDetails::new(ZapType::Private).message(message);
let num = match amount.parse::<u64>() { let num = amount.parse::<u64>().map_err(|e| e.to_string())?;
Ok(val) => val,
Err(_) => return Err("Invalid amount.".into()),
};
if client.zap(event_id, num, Some(details)).await.is_ok() { if client.zap(event_id, num, Some(details)).await.is_ok() {
Ok(true) Ok(true)
@ -447,8 +372,12 @@ pub async fn friend_to_friend(npub: &str, state: State<'_, Nostr>) -> Result<boo
.kind(Kind::ContactList) .kind(Kind::ContactList)
.limit(1); .limit(1);
if let Ok(contact_list_events) = if let Ok(contact_list_events) = client
client.get_events_of(vec![contact_list_filter], None).await .get_events_of(
vec![contact_list_filter],
EventSource::both(Some(Duration::from_secs(5))),
)
.await
{ {
for event in contact_list_events.into_iter() { for event in contact_list_events.into_iter() {
for tag in event.into_iter_tags() { for tag in event.into_iter_tags() {
@ -477,19 +406,22 @@ pub async fn friend_to_friend(npub: &str, state: State<'_, Nostr>) -> Result<boo
pub async fn get_following( pub async fn get_following(
state: State<'_, Nostr>, state: State<'_, Nostr>,
public_key: &str, public_key: &str,
timeout: Option<u64>,
) -> Result<Vec<String>, String> { ) -> Result<Vec<String>, String> {
let client = &state.client; let client = &state.client;
let public_key = match PublicKey::from_str(public_key) { let public_key = PublicKey::from_str(public_key).map_err(|e| e.to_string())?;
Ok(val) => val,
Err(err) => return Err(err.to_string()),
};
let duration = timeout.map(Duration::from_secs);
let filter = Filter::new().kind(Kind::ContactList).author(public_key); let filter = Filter::new().kind(Kind::ContactList).author(public_key);
let events = match client.get_events_of(vec![filter], duration).await { let events = match client
.get_events_of(
vec![filter],
EventSource::both(Some(Duration::from_secs(5))),
)
.await
{
Ok(events) => events, Ok(events) => events,
Err(err) => return Err(err.to_string()), Err(err) => return Err(err.to_string()),
}; };
let mut ret: Vec<String> = vec![]; let mut ret: Vec<String> = vec![];
if let Some(latest_event) = events.iter().max_by_key(|event| event.created_at()) { if let Some(latest_event) = events.iter().max_by_key(|event| event.created_at()) {
ret.extend(latest_event.tags().iter().filter_map(|tag| { ret.extend(latest_event.tags().iter().filter_map(|tag| {
@ -503,33 +435,38 @@ pub async fn get_following(
} }
})); }));
} }
Ok(ret) Ok(ret)
} }
pub async fn get_followers( pub async fn get_followers(
state: State<'_, Nostr>, state: State<'_, Nostr>,
public_key: &str, public_key: &str,
timeout: Option<u64>,
) -> Result<Vec<String>, String> { ) -> Result<Vec<String>, String> {
let client = &state.client; let client = &state.client;
let public_key = match PublicKey::from_str(public_key) { let public_key = PublicKey::from_str(public_key).map_err(|e| e.to_string())?;
Ok(val) => val,
Err(err) => return Err(err.to_string()),
};
let duration = timeout.map(Duration::from_secs);
let filter = Filter::new().kind(Kind::ContactList).custom_tag( let filter = Filter::new().kind(Kind::ContactList).custom_tag(
SingleLetterTag::lowercase(Alphabet::P), SingleLetterTag::lowercase(Alphabet::P),
vec![public_key.to_hex()], vec![public_key.to_hex()],
); );
let events = match client.get_events_of(vec![filter], duration).await {
let events = match client
.get_events_of(
vec![filter],
EventSource::both(Some(Duration::from_secs(5))),
)
.await
{
Ok(events) => events, Ok(events) => events,
Err(err) => return Err(err.to_string()), Err(err) => return Err(err.to_string()),
}; };
let ret: Vec<String> = events let ret: Vec<String> = events
.into_iter() .into_iter()
.map(|event| event.author().to_hex()) .map(|event| event.author().to_hex())
.collect(); .collect();
Ok(ret) Ok(ret)
// TODO: get more than 500 events // TODO: get more than 500 events
} }

View File

@ -5,6 +5,7 @@ use specta::Type;
use std::{ use std::{
fs::OpenOptions, fs::OpenOptions,
io::{self, BufRead, Write}, io::{self, BufRead, Write},
time::Duration,
}; };
use tauri::{path::BaseDirectory, Manager, State}; use tauri::{path::BaseDirectory, Manager, State};
@ -36,7 +37,16 @@ pub async fn get_relays(state: State<'_, Nostr>) -> Result<Relays, String> {
.kind(Kind::RelayList) .kind(Kind::RelayList)
.limit(1); .limit(1);
match client.get_events_of(vec![filter], None).await { match client
.get_events_of(
vec![filter],
EventSource::Relays {
timeout: Some(Duration::from_secs(5)),
specific_relays: None,
},
)
.await
{
Ok(events) => { Ok(events) => {
if let Some(event) = events.first() { if let Some(event) = events.first() {
let nip65_list = nip65::extract_relay_list(event).collect::<Vec<_>>(); let nip65_list = nip65::extract_relay_list(event).collect::<Vec<_>>();

View File

@ -5,6 +5,7 @@ use serde::Serialize;
use specta::Type; use specta::Type;
use std::collections::HashSet; use std::collections::HashSet;
use std::str::FromStr; use std::str::FromStr;
use std::time::Duration;
use crate::Settings; use crate::Settings;
@ -66,7 +67,13 @@ pub async fn init_nip65(client: &Client) {
.kind(Kind::RelayList) .kind(Kind::RelayList)
.limit(1); .limit(1);
if let Ok(events) = client.get_events_of(vec![filter], None).await { if let Ok(events) = client
.get_events_of(
vec![filter],
EventSource::both(Some(Duration::from_secs(5))),
)
.await
{
if let Some(event) = events.first() { if let Some(event) = events.first() {
let relay_list = nip65::extract_relay_list(event); let relay_list = nip65::extract_relay_list(event);
for (url, metadata) in relay_list { for (url, metadata) in relay_list {
@ -107,7 +114,13 @@ pub async fn get_user_settings(client: &Client) -> Result<Settings, String> {
.identifier(ident) .identifier(ident)
.limit(1); .limit(1);
match client.get_events_of(vec![filter], None).await { match client
.get_events_of(
vec![filter],
EventSource::both(Some(Duration::from_secs(5))),
)
.await
{
Ok(events) => { Ok(events) => {
if let Some(event) = events.first() { if let Some(event) = events.first() {
let content = event.content(); let content = event.content();

View File

@ -89,7 +89,6 @@ fn main() {
connect_account, connect_account,
delete_account, delete_account,
login, login,
get_current_profile,
get_profile, get_profile,
get_contact_list, get_contact_list,
set_contact_list, set_contact_list,

View File

@ -88,15 +88,7 @@ async login(account: string, password: string) : Promise<Result<string, string>>
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async getCurrentProfile() : Promise<Result<string, string>> { async getProfile(id: string | null) : Promise<Result<string, string>> {
try {
return { status: "ok", data: await TAURI_INVOKE("get_current_profile") };
} catch (e) {
if(e instanceof Error) throw e;
else return { status: "error", error: e as any };
}
},
async getProfile(id: string) : Promise<Result<string, string>> {
try { try {
return { status: "ok", data: await TAURI_INVOKE("get_profile", { id }) }; return { status: "ok", data: await TAURI_INVOKE("get_profile", { id }) };
} catch (e) { } catch (e) {
@ -184,7 +176,7 @@ async loadWallet() : Promise<Result<string, string>> {
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async removeWallet() : Promise<Result<null, null>> { async removeWallet() : Promise<Result<null, string>> {
try { try {
return { status: "ok", data: await TAURI_INVOKE("remove_wallet") }; return { status: "ok", data: await TAURI_INVOKE("remove_wallet") };
} catch (e) { } catch (e) {