diff --git a/package.json b/package.json index bd50031..e9271fb 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "nostr-tools": "^2.7.1", "react": "19.0.0-rc-d025ddd3-20240722", "react-dom": "19.0.0-rc-d025ddd3-20240722", + "unique-names-generator": "^4.7.1", "virtua": "^0.33.3" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7d8fd64..1b97caf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -53,6 +53,9 @@ importers: react-dom: specifier: 19.0.0-rc-d025ddd3-20240722 version: 19.0.0-rc-d025ddd3-20240722(react@19.0.0-rc-d025ddd3-20240722) + unique-names-generator: + specifier: ^4.7.1 + version: 4.7.1 virtua: specifier: ^0.33.3 version: 0.33.3(react-dom@19.0.0-rc-d025ddd3-20240722(react@19.0.0-rc-d025ddd3-20240722))(react@19.0.0-rc-d025ddd3-20240722) @@ -1462,6 +1465,10 @@ packages: engines: {node: '>=14.17'} hasBin: true + unique-names-generator@4.7.1: + resolution: {integrity: sha512-lMx9dX+KRmG8sq6gulYYpKWZc9RlGsgBR6aoO8Qsm3qvkSJ+3rAymr+TnV8EDMrIrwuFJ4kruzMWM/OpYzPoow==} + engines: {node: '>=8'} + unplugin@1.11.0: resolution: {integrity: sha512-3r7VWZ/webh0SGgJScpWl2/MRCZK5d3ZYFcNaeci/GQ7Teop7zf0Nl2pUuz7G21BwPd9pcUPOC5KmJ2L3WgC5g==} engines: {node: '>=14.0.0'} @@ -2824,6 +2831,8 @@ snapshots: typescript@5.5.4: {} + unique-names-generator@4.7.1: {} + unplugin@1.11.0: dependencies: acorn: 8.12.1 diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index ed4d720..637095b 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -929,6 +929,7 @@ dependencies = [ "tauri-plugin-devtools", "tauri-plugin-dialog", "tauri-plugin-os", + "tauri-plugin-prevent-default", "tauri-plugin-shell", "tauri-specta", ] @@ -5092,6 +5093,16 @@ dependencies = [ "thiserror", ] +[[package]] +name = "tauri-plugin-prevent-default" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38be0ac8fcc5fa03422409fc506015b01dc29cc8a3a72572c68d41e6f10f4491" +dependencies = [ + "bitflags 2.6.0", + "tauri", +] + [[package]] name = "tauri-plugin-shell" version = "2.0.0-beta.9" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index d5561f8..d9998bc 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -12,16 +12,18 @@ tauri-build = { version = "2.0.0-beta", features = [] } [dependencies] nostr-sdk = { git = "https://github.com/rust-nostr/nostr", features = [ - "sqlite", + "sqlite", ] } tauri = { version = "2.0.0-beta", features = [ - "tray-icon", - "macos-private-api", - "protocol-asset", + "tray-icon", + "macos-private-api", + "protocol-asset", ] } tauri-specta = { git = "https://github.com/reyamir/tauri-specta", branch = "feat/tauri-v2", features = [ - "typescript", + "typescript", ] } +tauri-plugin-devtools = "2.0.0-beta" +tauri-plugin-prevent-default = "0.1" tauri-plugin-os = "2.0.0-beta" tauri-plugin-clipboard-manager = "2.0.0-beta" tauri-plugin-dialog = "2.0.0-beta" @@ -30,15 +32,14 @@ tauri-plugin-decorum = "0.1.5" serde = { version = "1", features = ["derive"] } serde_json = "1" keyring = { version = "3", features = [ - "apple-native", - "windows-native", - "linux-native", + "apple-native", + "windows-native", + "linux-native", ] } keyring-search = "1.2.0" itertools = "0.13.0" futures = "0.3.30" specta = "^2.0.0-rc.12" -tauri-plugin-devtools = "2.0.0-beta" [target.'cfg(target_os = "macos")'.dependencies] border = { git = "https://github.com/ahkohd/tauri-toolkit", branch = "v2" } diff --git a/src-tauri/src/commands/account.rs b/src-tauri/src/commands/account.rs index be894bd..9f5a995 100644 --- a/src-tauri/src/commands/account.rs +++ b/src-tauri/src/commands/account.rs @@ -155,55 +155,6 @@ pub async fn login( let hex = public_key.to_hex(); let keyring = Entry::new(&id, "nostr_secret").expect("Unexpected."); - tauri::async_runtime::spawn(async move { - let window = app.get_webview_window("main").expect("Window is terminated."); - let state = window.state::(); - let client = &state.client; - - client - .handle_notifications(|notification| async { - if let RelayPoolNotification::Message { message, relay_url } = notification { - if let RelayMessage::Event { event, .. } = message { - if event.kind == Kind::GiftWrap { - if let Ok(UnwrappedGift { rumor, sender }) = - client.unwrap_gift_wrap(&event).await - { - window - .emit( - "event", - Payload { event: rumor.as_json(), sender: sender.to_hex() }, - ) - .unwrap(); - } - } - } else if let RelayMessage::Auth { challenge } = message { - match client.auth(challenge, relay_url.clone()).await { - Ok(..) => { - println!("Authenticated to {} relay.", relay_url); - - if let Ok(relay) = client.relay(relay_url).await { - let opts = RelaySendOptions::new().skip_send_confirmation(true); - if let Err(e) = relay.resubscribe(opts).await { - println!( - "Impossible to resubscribe to '{}': {e}", - relay.url() - ); - } - } - } - Err(e) => { - println!("Can't authenticate to '{relay_url}' relay: {e}"); - } - } - } else { - println!("relay message: {}", message.as_json()); - } - } - Ok(false) - }) - .await - }); - let password = match keyring.get_password() { Ok(pw) => pw, Err(_) => return Err("Cancelled".into()), @@ -256,12 +207,41 @@ pub async fn login( if client.reconcile_with(relays.clone(), old, NegentropyOptions::default()).await.is_ok() { handle.emit("synchronized", ()).unwrap(); - println!("synchronized"); }; if client.subscribe_to(relays, vec![new], None).await.is_ok() { println!("Waiting for new message...") }; + tauri::async_runtime::spawn(async move { + let window = app.get_webview_window("main").expect("Window is terminated."); + let state = window.state::(); + let client = &state.client; + + // Workaround for https://github.com/rust-nostr/nostr/issues/509 + // TODO: remove + let _ = client.get_events_of(vec![Filter::new().kind(Kind::TextNote).limit(0)], None).await; + + client + .handle_notifications(|notification| async { + if let RelayPoolNotification::Event { event, .. } = notification { + if event.kind == Kind::GiftWrap { + if let Ok(UnwrappedGift { rumor, sender }) = + client.unwrap_gift_wrap(&event).await + { + window + .emit( + "event", + Payload { event: rumor.as_json(), sender: sender.to_hex() }, + ) + .unwrap(); + } + } + } + Ok(false) + }) + .await + }); + Ok(hex) } diff --git a/src-tauri/src/commands/chat.rs b/src-tauri/src/commands/chat.rs index 6212408..8ee1499 100644 --- a/src-tauri/src/commands/chat.rs +++ b/src-tauri/src/commands/chat.rs @@ -32,9 +32,9 @@ pub async fn get_chats(state: State<'_, Nostr>) -> Result, String> { let uniqs = rumors .into_iter() + .sorted_by_key(|ev| Reverse(ev.created_at)) .filter(|ev| ev.pubkey != public_key) .unique_by(|ev| ev.pubkey) - .sorted_by_key(|ev| Reverse(ev.created_at)) .map(|ev| ev.as_json()) .collect::>(); diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index b3d71c3..d77c23f 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -76,9 +76,8 @@ fn main() { // Config let opts = Options::new() .autoconnect(true) - .automatic_authentication(false) .timeout(Duration::from_secs(5)) - .send_timeout(Some(Duration::from_secs(50))) + .send_timeout(Some(Duration::from_secs(5))) .connection_timeout(Some(Duration::from_secs(20))); // Setup nostr client @@ -98,6 +97,7 @@ fn main() { Ok(()) }) .enable_macos_default_menu(false) + .plugin(tauri_plugin_prevent_default::init()) .plugin(tauri_plugin_os::init()) .plugin(tauri_plugin_clipboard_manager::init()) .plugin(tauri_plugin_dialog::init()) diff --git a/src/components/user/avatar.tsx b/src/components/user/avatar.tsx index a5904f7..868d0c5 100644 --- a/src/components/user/avatar.tsx +++ b/src/components/user/avatar.tsx @@ -17,26 +17,29 @@ export function UserAvatar({ className }: { className?: string }) { return ( - {user?.profile?.picture ? ( - + {!user.isLoading ? ( + <> + + + {user.pubkey} + + ) : null} - - {user.pubkey} - ); } diff --git a/src/components/user/name.tsx b/src/components/user/name.tsx index 1470929..760eaaa 100644 --- a/src/components/user/name.tsx +++ b/src/components/user/name.tsx @@ -1,12 +1,24 @@ import { cn } from "@/commons"; import { useUserContext } from "./provider"; +import { useMemo } from "react"; +import { uniqueNamesGenerator, names } from "unique-names-generator"; export function UserName({ className }: { className?: string }) { const user = useUserContext(); + const name = useMemo( + () => uniqueNamesGenerator({ dictionaries: [names] }), + [user.pubkey], + ); + + if (user.isLoading) { + return ( +
+ ); + } return (
- {user.profile?.display_name || user.profile?.name || "Anon"} + {user.profile?.display_name || user.profile?.name || name}
); } diff --git a/src/routes/$account.chats.lazy.tsx b/src/routes/$account.chats.lazy.tsx index 6fb3fd4..33870ca 100644 --- a/src/routes/$account.chats.lazy.tsx +++ b/src/routes/$account.chats.lazy.tsx @@ -25,13 +25,9 @@ function Screen() { data-tauri-drag-region className="shrink-0 w-[280px] h-full flex flex-col justify-between border-r border-black/5 dark:border-white/5" > -
-
- -
-
- -
+
+ +
@@ -44,7 +40,7 @@ function Header() { return (
{isLoading ? ( -

Loading...

+
+ {Array.from(Array(5)).map((index) => ( +
+
+
+
+ ))} +
) : isError ? ( -

Error

+
Error
) : ( data.map((item) => ( - +
@@ -182,11 +188,13 @@ function CurrentUser() { const { account } = Route.useParams(); return ( - - - - - - +
+ + + + + + +
); }