diff --git a/apps/desktop2/src/routes/settings/relay.tsx b/apps/desktop2/src/routes/settings/relay.tsx
new file mode 100644
index 00000000..3781fb58
--- /dev/null
+++ b/apps/desktop2/src/routes/settings/relay.tsx
@@ -0,0 +1,135 @@
+import { CancelIcon, PlusIcon } from "@lume/icons";
+import { createFileRoute } from "@tanstack/react-router";
+import { useState } from "react";
+import { useForm } from "react-hook-form";
+import { toast } from "sonner";
+
+export const Route = createFileRoute("/settings/relay")({
+ loader: async ({ context }) => {
+ const ark = context.ark;
+ const relays = await ark.get_relays();
+
+ return relays;
+ },
+ component: Screen,
+});
+
+function Screen() {
+ const relayList = Route.useLoaderData();
+ const [relays, setRelays] = useState(relayList.connected);
+
+ const { ark } = Route.useRouteContext();
+ const { register, reset, handleSubmit } = useForm();
+
+ const onSubmit = async (data: { url: string }) => {
+ try {
+ const add = await ark.add_relay(data.url);
+ if (add) {
+ setRelays((prev) => [...prev, data.url]);
+ reset();
+ }
+ } catch (e) {
+ toast.error(String(e));
+ }
+ };
+
+ return (
+
+ );
+}
diff --git a/apps/desktop2/src/routes/settings/user.tsx b/apps/desktop2/src/routes/settings/user.tsx
index 7cd57324..4f626c39 100644
--- a/apps/desktop2/src/routes/settings/user.tsx
+++ b/apps/desktop2/src/routes/settings/user.tsx
@@ -28,7 +28,7 @@ function Screen() {
try {
setLoading(true);
- const profile = { ...data };
+ const profile = { ...data, picture };
await ark.create_profile(profile);
setLoading(false);
@@ -44,7 +44,7 @@ function Screen() {
{profile.picture ? (
+
);
diff --git a/packages/types/index.d.ts b/packages/types/index.d.ts
index c0707797..53822653 100644
--- a/packages/types/index.d.ts
+++ b/packages/types/index.d.ts
@@ -165,3 +165,10 @@ export interface NIP05 {
};
};
}
+
+export interface Relays {
+ connected: string[];
+ read: string[];
+ write: string[];
+ both: string[];
+}
diff --git a/packages/ui/src/note/activity.tsx b/packages/ui/src/note/activity.tsx
index 3f6ec922..d6cb45e5 100644
--- a/packages/ui/src/note/activity.tsx
+++ b/packages/ui/src/note/activity.tsx
@@ -15,7 +15,7 @@ export function NoteActivity({ className }: { className?: string }) {
{mentions.splice(0, 4).map((mention) => (
-
+
))}
diff --git a/packages/ui/src/user/name.tsx b/packages/ui/src/user/name.tsx
index 28b08167..ea792d10 100644
--- a/packages/ui/src/user/name.tsx
+++ b/packages/ui/src/user/name.tsx
@@ -3,19 +3,19 @@ import { useUserContext } from "./provider";
export function UserName({
className,
- suffix,
+ prefix,
}: {
className?: string;
- suffix?: string;
+ prefix?: string;
}) {
const user = useUserContext();
return (
+ {prefix}
{user.profile?.display_name ||
user.profile?.name ||
displayNpub(user.pubkey, 16)}
- {suffix}
);
}
diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs
index d0d9e0b8..d916f70f 100644
--- a/src-tauri/src/main.rs
+++ b/src-tauri/src/main.rs
@@ -60,6 +60,10 @@ fn main() {
.add_relay("wss://bostr.nokotaro.work/")
.await
.expect("Cannot connect to bostr.nokotaro.work, please try again later.");
+ client
+ .add_relay("wss://purplepag.es/")
+ .await
+ .expect("Cannot connect to purplepag.es, please try again later.");
// Connect
client.connect().await;
@@ -92,6 +96,10 @@ fn main() {
Some(vec![]),
))
.invoke_handler(tauri::generate_handler![
+ nostr::relay::get_relays,
+ nostr::relay::list_connected_relays,
+ nostr::relay::connect_relay,
+ nostr::relay::remove_relay,
nostr::keys::create_keys,
nostr::keys::save_key,
nostr::keys::get_encrypted_key,
@@ -108,7 +116,6 @@ fn main() {
nostr::metadata::get_current_user_profile,
nostr::metadata::get_profile,
nostr::metadata::get_contact_list,
- nostr::metadata::get_contact_metadata,
nostr::metadata::create_profile,
nostr::metadata::follow,
nostr::metadata::unfollow,
diff --git a/src-tauri/src/nostr/keys.rs b/src-tauri/src/nostr/keys.rs
index 65981418..fceb89bf 100644
--- a/src-tauri/src/nostr/keys.rs
+++ b/src-tauri/src/nostr/keys.rs
@@ -118,7 +118,7 @@ pub async fn verify_signer(state: State<'_, Nostr>) -> Result
{
}
}
-#[tauri::command]
+#[tauri::command(async)]
pub fn get_encrypted_key(npub: &str, password: &str) -> Result {
let keyring = Entry::new("Lume Secret Storage", npub).unwrap();
@@ -190,12 +190,26 @@ pub async fn load_selected_account(npub: &str, state: State<'_, Nostr>) -> Resul
if let Some(event) = events.first() {
let relay_list = nip65::extract_relay_list(event);
for item in relay_list.into_iter() {
- println!("connecting to relay: {}", item.0);
- // Add relay to pool
+ println!("connecting to relay: {} - {:?}", item.0, item.1);
+
+ let relay_url = item.0.to_string();
+ let opts = match item.1 {
+ Some(val) => {
+ if val == RelayMetadata::Read {
+ RelayOptions::new().read(true).write(false)
+ } else {
+ RelayOptions::new().write(true).read(false)
+ }
+ }
+ None => RelayOptions::new(),
+ };
+
+ // Add relay to relay pool
let _ = client
- .add_relay(item.0.to_string())
+ .add_relay_with_opts(relay_url, opts)
.await
.unwrap_or_default();
+
// Connect relay
client
.connect_relay(item.0.to_string())
diff --git a/src-tauri/src/nostr/metadata.rs b/src-tauri/src/nostr/metadata.rs
index eefeaf16..fa8c339b 100644
--- a/src-tauri/src/nostr/metadata.rs
+++ b/src-tauri/src/nostr/metadata.rs
@@ -5,12 +5,6 @@ use std::{str::FromStr, time::Duration};
use tauri::{Manager, State};
use url::Url;
-#[derive(serde::Serialize)]
-pub struct CacheContact {
- pubkey: String,
- profile: Metadata,
-}
-
#[tauri::command]
pub fn run_notification(accounts: Vec, app: tauri::AppHandle) -> Result<(), ()> {
tauri::async_runtime::spawn(async move {
@@ -206,28 +200,6 @@ pub async fn get_contact_list(state: State<'_, Nostr>) -> Result, St
}
}
-#[tauri::command]
-pub async fn get_contact_metadata(state: State<'_, Nostr>) -> Result, String> {
- let client = &state.client;
-
- if let Ok(contact_list) = client
- .get_contact_list_metadata(Some(Duration::from_secs(10)))
- .await
- {
- let list: Vec = contact_list
- .into_iter()
- .map(|(id, metadata)| CacheContact {
- pubkey: id.to_hex(),
- profile: metadata,
- })
- .collect();
-
- Ok(list)
- } else {
- Err("Contact list not found".into())
- }
-}
-
#[tauri::command]
pub async fn create_profile(
name: &str,
diff --git a/src-tauri/src/nostr/relay.rs b/src-tauri/src/nostr/relay.rs
index 434feaaf..4cb6aed3 100644
--- a/src-tauri/src/nostr/relay.rs
+++ b/src-tauri/src/nostr/relay.rs
@@ -2,11 +2,81 @@ use crate::Nostr;
use nostr_sdk::prelude::*;
use tauri::State;
+#[derive(serde::Serialize)]
+pub struct Relays {
+ connected: Vec,
+ read: Option>,
+ write: Option>,
+ both: Option>,
+}
+
+#[tauri::command]
+pub async fn get_relays(state: State<'_, Nostr>) -> Result {
+ let client = &state.client;
+
+ // Get connected relays
+ let list = client.relays().await;
+ let connected_relays: Vec = list.into_iter().map(|(url, _)| url.to_string()).collect();
+
+ // Get NIP-65 relay list
+ let signer = client.signer().await.unwrap();
+ let public_key = signer.public_key().await.unwrap();
+ let filter = Filter::new()
+ .author(public_key)
+ .kind(Kind::RelayList)
+ .limit(1);
+
+ match client.get_events_of(vec![filter], None).await {
+ Ok(events) => {
+ if let Some(event) = events.first() {
+ let nip65_list = nip65::extract_relay_list(event);
+ let read: Vec = nip65_list
+ .clone()
+ .into_iter()
+ .filter(|i| matches!(&i.1, Some(y) if *y == RelayMetadata::Read))
+ .map(|(url, _)| url.to_string())
+ .collect();
+ let write: Vec = nip65_list
+ .clone()
+ .into_iter()
+ .filter(|i| matches!(&i.1, Some(y) if *y == RelayMetadata::Write))
+ .map(|(url, _)| url.to_string())
+ .collect();
+ let both: Vec = nip65_list
+ .into_iter()
+ .filter(|i| i.1.is_none())
+ .map(|(url, _)| url.to_string())
+ .collect();
+
+ Ok(Relays {
+ connected: connected_relays,
+ read: Some(read),
+ write: Some(write),
+ both: Some(both),
+ })
+ } else {
+ Ok(Relays {
+ connected: connected_relays,
+ read: None,
+ write: None,
+ both: None,
+ })
+ }
+ }
+ Err(_) => Ok(Relays {
+ connected: connected_relays,
+ read: None,
+ write: None,
+ both: None,
+ }),
+ }
+}
+
#[tauri::command]
pub async fn list_connected_relays(state: State<'_, Nostr>) -> Result, ()> {
let client = &state.client;
- let relays = client.relays().await;
- let list: Vec = relays.into_keys().collect();
+ let connected_relays = client.relays().await;
+ let list = connected_relays.into_keys().collect();
Ok(list)
}
@@ -15,6 +85,7 @@ pub async fn list_connected_relays(state: State<'_, Nostr>) -> Result,
pub async fn connect_relay(relay: &str, state: State<'_, Nostr>) -> Result {
let client = &state.client;
if let Ok(_) = client.add_relay(relay).await {
+ let _ = client.connect_relay(relay);
Ok(true)
} else {
Ok(false)
@@ -25,6 +96,7 @@ pub async fn connect_relay(relay: &str, state: State<'_, Nostr>) -> Result) -> Result {
let client = &state.client;
if let Ok(_) = client.remove_relay(relay).await {
+ let _ = client.disconnect_relay(relay);
Ok(true)
} else {
Ok(false)