From d206f1d2aa6444d47f91063973dc81082b505546 Mon Sep 17 00:00:00 2001 From: reya <123083837+reyamir@users.noreply.github.com> Date: Thu, 25 Jul 2024 09:09:02 +0700 Subject: [PATCH] feat: add message form --- src-tauri/Cargo.lock | 68 +++++----- src-tauri/capabilities/default.json | 2 + src-tauri/src/commands/account.rs | 64 ++++++---- src-tauri/src/commands/chat.rs | 90 +++++++++++++ src-tauri/src/main.rs | 16 ++- src/commands.ts | 40 ++++++ src/routes/$account.chats.$id.tsx | 187 +++++++++++++++++++++------- src/routes/$account.chats.tsx | 10 +- src/routes/index.tsx | 21 +--- 9 files changed, 367 insertions(+), 131 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 5d206ac..af85d47 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -526,7 +526,7 @@ dependencies = [ [[package]] name = "border" version = "0.1.0" -source = "git+https://github.com/ahkohd/tauri-toolkit?branch=v2#c7ab4d735c227ea7f8b756b5565c7ae8ab9158eb" +source = "git+https://github.com/ahkohd/tauri-toolkit?branch=v2#bb267e4d34b34fc01a94a60a14579fb6cdd2a256" dependencies = [ "cocoa", "color", @@ -801,7 +801,7 @@ dependencies = [ [[package]] name = "color" version = "0.1.0" -source = "git+https://github.com/ahkohd/tauri-toolkit?branch=v2#c7ab4d735c227ea7f8b756b5565c7ae8ab9158eb" +source = "git+https://github.com/ahkohd/tauri-toolkit?branch=v2#bb267e4d34b34fc01a94a60a14579fb6cdd2a256" dependencies = [ "cocoa", "tauri", @@ -2271,9 +2271,9 @@ dependencies = [ [[package]] name = "keyring" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9961b98f55dc0b2737000132505bdafa249abab147ee9de43c50ae04a054aa6c" +checksum = "c118b1bc529b034aad851808f41f49a69a337d10e112039e7f342e5fd514635b" dependencies = [ "byteorder", "linux-keyutils", @@ -2521,13 +2521,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.11" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" dependencies = [ + "hermit-abi 0.3.9", "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2611,7 +2612,7 @@ checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" [[package]] name = "nostr" version = "0.33.0" -source = "git+https://github.com/rust-nostr/nostr#9e8fea62f3a8c4ab943291a3f265c0a42b457c77" +source = "git+https://github.com/rust-nostr/nostr#79b84f618d2e339f13c763f697c6fd56d8062aec" dependencies = [ "aes", "base64 0.21.7", @@ -2640,7 +2641,7 @@ dependencies = [ [[package]] name = "nostr-database" version = "0.33.0" -source = "git+https://github.com/rust-nostr/nostr#9e8fea62f3a8c4ab943291a3f265c0a42b457c77" +source = "git+https://github.com/rust-nostr/nostr#79b84f618d2e339f13c763f697c6fd56d8062aec" dependencies = [ "async-trait", "flatbuffers", @@ -2654,7 +2655,7 @@ dependencies = [ [[package]] name = "nostr-relay-pool" version = "0.33.0" -source = "git+https://github.com/rust-nostr/nostr#9e8fea62f3a8c4ab943291a3f265c0a42b457c77" +source = "git+https://github.com/rust-nostr/nostr#79b84f618d2e339f13c763f697c6fd56d8062aec" dependencies = [ "async-utility", "async-wsocket", @@ -2669,7 +2670,7 @@ dependencies = [ [[package]] name = "nostr-sdk" version = "0.33.0" -source = "git+https://github.com/rust-nostr/nostr#9e8fea62f3a8c4ab943291a3f265c0a42b457c77" +source = "git+https://github.com/rust-nostr/nostr#79b84f618d2e339f13c763f697c6fd56d8062aec" dependencies = [ "async-utility", "atomic-destructor", @@ -2689,7 +2690,7 @@ dependencies = [ [[package]] name = "nostr-signer" version = "0.33.0" -source = "git+https://github.com/rust-nostr/nostr#9e8fea62f3a8c4ab943291a3f265c0a42b457c77" +source = "git+https://github.com/rust-nostr/nostr#79b84f618d2e339f13c763f697c6fd56d8062aec" dependencies = [ "async-utility", "nostr", @@ -2702,7 +2703,7 @@ dependencies = [ [[package]] name = "nostr-sqlite" version = "0.33.0" -source = "git+https://github.com/rust-nostr/nostr#9e8fea62f3a8c4ab943291a3f265c0a42b457c77" +source = "git+https://github.com/rust-nostr/nostr#79b84f618d2e339f13c763f697c6fd56d8062aec" dependencies = [ "async-trait", "nostr", @@ -2716,7 +2717,7 @@ dependencies = [ [[package]] name = "nostr-zapper" version = "0.33.0" -source = "git+https://github.com/rust-nostr/nostr#9e8fea62f3a8c4ab943291a3f265c0a42b457c77" +source = "git+https://github.com/rust-nostr/nostr#79b84f618d2e339f13c763f697c6fd56d8062aec" dependencies = [ "async-trait", "nostr", @@ -2812,16 +2813,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi 0.3.9", - "libc", -] - [[package]] name = "num_enum" version = "0.5.11" @@ -2846,7 +2837,7 @@ dependencies = [ [[package]] name = "nwc" version = "0.33.0" -source = "git+https://github.com/rust-nostr/nostr#9e8fea62f3a8c4ab943291a3f265c0a42b457c77" +source = "git+https://github.com/rust-nostr/nostr#79b84f618d2e339f13c763f697c6fd56d8062aec" dependencies = [ "async-utility", "nostr", @@ -2997,9 +2988,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.1" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" +checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" dependencies = [ "memchr", ] @@ -3840,9 +3831,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.11" +version = "0.23.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4828ea528154ae444e5a642dbb7d5623354030dc9822b83fd9bb79683c7399d0" +checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" dependencies = [ "once_cell", "ring", @@ -4696,9 +4687,9 @@ dependencies = [ [[package]] name = "tauri-plugin-decorum" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "863250b9545d3823954e8d5d3b14d6d294ea7c66b71d2aa3d9f7269842a7c575" +checksum = "413d2c0123553c93dde37556fc86c96cbb10c1eeae5694b38d2f88d553b2fc93" dependencies = [ "anyhow", "cocoa", @@ -4832,7 +4823,7 @@ dependencies = [ [[package]] name = "tauri-specta" version = "2.0.0-rc.11" -source = "git+https://github.com/reyamir/tauri-specta?branch=feat/tauri-v2#5c09319b345814bfce3c4c02527e481d18339051" +source = "git+https://github.com/reyamir/tauri-specta?branch=feat/tauri-v2#c5373f178c1676582c3ed833e3589bc7d7872253" dependencies = [ "heck 0.5.0", "indoc", @@ -4847,7 +4838,7 @@ dependencies = [ [[package]] name = "tauri-specta-macros" version = "2.0.0-rc.5" -source = "git+https://github.com/reyamir/tauri-specta?branch=feat/tauri-v2#5c09319b345814bfce3c4c02527e481d18339051" +source = "git+https://github.com/reyamir/tauri-specta?branch=feat/tauri-v2#c5373f178c1676582c3ed833e3589bc7d7872253" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -5018,28 +5009,27 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.38.1" +version = "1.39.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df" +checksum = "d040ac2b29ab03b09d4129c2f5bbd012a3ac2f79d38ff506a4bf8dd34b0eac8a" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", "tracing", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index fde75a5..36bba74 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -15,6 +15,8 @@ "menu:default", "tray:default", "shell:allow-open", + "dialog:default", + "dialog:allow-open", "window:allow-close", "window:allow-center", "window:allow-minimize", diff --git a/src-tauri/src/commands/account.rs b/src-tauri/src/commands/account.rs index 17d2665..33fa51e 100644 --- a/src-tauri/src/commands/account.rs +++ b/src-tauri/src/commands/account.rs @@ -3,7 +3,7 @@ use keyring::Entry; use keyring_search::{Limit, List, Search}; use nostr_sdk::prelude::*; use serde::Serialize; -use std::{collections::HashSet, time::Duration}; +use std::collections::HashSet; use tauri::{Emitter, Manager, State}; use crate::Nostr; @@ -33,7 +33,7 @@ pub async fn get_profile(id: String, state: State<'_, Nostr>) -> Result { if let Some(event) = events.first() { Ok(Metadata::from_json(&event.content).unwrap_or(Metadata::new()).as_json()) @@ -53,6 +53,7 @@ pub async fn login( handle: tauri::AppHandle, ) -> Result { let client = &state.client; + let public_key = PublicKey::parse(&id).map_err(|e| e.to_string())?; let keyring = Entry::new(&id, "nostr_secret").expect("Unexpected."); let password = match keyring.get_password() { @@ -60,39 +61,36 @@ pub async fn login( Err(_) => return Err("Cancelled".into()), }; - let id_clone = id.clone(); let keys = Keys::parse(password).expect("Secret Key is modified, please check again."); let signer = NostrSigner::Keys(keys); // Set signer client.set_signer(Some(signer)).await; - tauri::async_runtime::spawn(async move { - let window = handle.get_webview_window("main").unwrap(); - let state = window.state::(); - let client = &state.client; + let inbox = Filter::new().kind(Kind::Custom(10050)).author(public_key).limit(1); - let public_key = PublicKey::parse(&id_clone).unwrap(); - let inbox = Filter::new().kind(Kind::Custom(10050)).author(public_key).limit(1); + if let Ok(events) = client.get_events_of(vec![inbox], None).await { + if let Some(event) = events.into_iter().next() { + for tag in &event.tags { + if let Some(TagStandard::Relay(url)) = tag.as_standardized() { + let url = url.to_string(); - if let Ok(events) = client.get_events_of(vec![inbox], None).await { - if let Some(event) = events.into_iter().next() { - for tag in &event.tags { - if let Some(TagStandard::Relay(url)) = tag.as_standardized() { - let opts = RelayOptions::new().retry_sec(5); - let url = url.to_string(); + if client.add_relay(&url).await.is_ok() { + println!("Adding relay {} ...", url); - if client.add_relay_with_opts(&url, opts).await.is_ok() { - println!("Adding relay {} ...", url); - - if client.connect_relay(&url).await.is_ok() { - println!("Connecting relay {} ...", url); - } + if client.connect_relay(&url).await.is_ok() { + println!("Connecting relay {} ...", url); } } } } } + } + + tauri::async_runtime::spawn(async move { + let window = handle.get_webview_window("main").expect("Window is terminated."); + let state = window.state::(); + let client = &state.client; let old = Filter::new().kind(Kind::GiftWrap).pubkey(public_key).until(Timestamp::now()); let new = Filter::new().kind(Kind::GiftWrap).pubkey(public_key).limit(0); @@ -129,7 +127,7 @@ pub async fn login( client .handle_notifications(|notification| async { - if let RelayPoolNotification::Message { message, .. } = notification { + 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 }) = @@ -143,6 +141,27 @@ pub async fn login( .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) @@ -150,7 +169,6 @@ pub async fn login( .await }); - let public_key = PublicKey::parse(&id).unwrap(); let hex = public_key.to_hex(); Ok(hex) diff --git a/src-tauri/src/commands/chat.rs b/src-tauri/src/commands/chat.rs index 420df90..b4971f5 100644 --- a/src-tauri/src/commands/chat.rs +++ b/src-tauri/src/commands/chat.rs @@ -1,3 +1,5 @@ +use std::{cmp::Reverse, time::Duration}; + use futures::stream::{self, StreamExt}; use itertools::Itertools; use nostr_sdk::prelude::*; @@ -30,7 +32,9 @@ pub async fn get_chats(state: State<'_, Nostr>) -> Result, String> { let uniqs = rumors .into_iter() + .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::>(); @@ -77,3 +81,89 @@ pub async fn get_chat_messages( Err(e) => Err(e.to_string()), } } + +#[tauri::command] +#[specta::specta] +pub async fn subscribe_to(id: String, state: State<'_, Nostr>) -> Result<(), String> { + let client = &state.client; + let public_key = PublicKey::parse(&id).map_err(|e| e.to_string())?; + + let filter = Filter::new().kind(Kind::GiftWrap).pubkey(public_key).limit(0); + let subscription_id = SubscriptionId::new(&id[..6]); + + if client.subscribe_with_id(subscription_id, vec![filter], None).await.is_ok() { + println!("Watching ... {}", id) + }; + + Ok(()) +} + +#[tauri::command] +#[specta::specta] +pub async fn unsubscribe(id: String, state: State<'_, Nostr>) -> Result<(), ()> { + let client = &state.client; + let subscription_id = SubscriptionId::new(&id[..6]); + + client.unsubscribe(subscription_id).await; + println!("Unwatching ... {}", id); + + Ok(()) +} + +#[tauri::command] +#[specta::specta] +pub async fn get_inboxes(id: String, state: State<'_, Nostr>) -> Result, String> { + let client = &state.client; + let public_key = PublicKey::parse(&id).map_err(|e| e.to_string())?; + let inbox = Filter::new().kind(Kind::Custom(10050)).author(public_key).limit(1); + + match client.get_events_of(vec![inbox], Some(Duration::from_secs(2))).await { + Ok(events) => { + let mut relays = Vec::new(); + + if let Some(event) = events.into_iter().next() { + for tag in &event.tags { + if let Some(TagStandard::Relay(url)) = tag.as_standardized() { + let relay = url.to_string(); + let _ = client.add_relay(&relay).await; + let _ = client.connect_relay(&relay).await; + + relays.push(relay); + } + } + } + + Ok(relays) + } + Err(e) => Err(e.to_string()), + } +} + +#[tauri::command] +#[specta::specta] +pub async fn drop_inbox(relays: Vec, state: State<'_, Nostr>) -> Result<(), ()> { + let client = &state.client; + + for relay in relays.iter() { + let _ = client.disconnect_relay(relay).await; + } + + Ok(()) +} + +#[tauri::command] +#[specta::specta] +pub async fn send_message( + to: String, + message: String, + relays: Vec, + state: State<'_, Nostr>, +) -> Result<(), String> { + let client = &state.client; + let receiver = PublicKey::parse(&to).map_err(|e| e.to_string())?; + + match client.send_private_msg_to(relays, receiver, message, None).await { + Ok(_) => Ok(()), + Err(e) => Err(e.to_string()), + } +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index ca3a911..0c7b139 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -10,7 +10,10 @@ use tauri_plugin_decorum::WebviewWindowExt; use commands::{ account::{get_accounts, get_profile, login}, - chat::{get_chat_messages, get_chats}, + chat::{ + drop_inbox, get_chat_messages, get_chats, get_inboxes, send_message, subscribe_to, + unsubscribe, + }, }; mod commands; @@ -29,8 +32,13 @@ fn main() { login, get_accounts, get_profile, + get_inboxes, get_chats, - get_chat_messages + get_chat_messages, + send_message, + subscribe_to, + unsubscribe, + drop_inbox ]); #[cfg(debug_assertions)] @@ -68,8 +76,8 @@ fn main() { let opts = Options::new() .automatic_authentication(true) .timeout(Duration::from_secs(5)) - .send_timeout(Some(Duration::from_secs(10))) - .connection_timeout(Some(Duration::from_secs(10))); + .send_timeout(Some(Duration::from_secs(5))) + .connection_timeout(Some(Duration::from_secs(20))); // Setup nostr client let client = match database { diff --git a/src/commands.ts b/src/commands.ts index 6e8d57c..0987ce7 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -23,6 +23,14 @@ try { else return { status: "error", error: e as any }; } }, +async getInboxes(id: string) : Promise> { +try { + return { status: "ok", data: await TAURI_INVOKE("get_inboxes", { id }) }; +} catch (e) { + if(e instanceof Error) throw e; + else return { status: "error", error: e as any }; +} +}, async getChats() : Promise> { try { return { status: "ok", data: await TAURI_INVOKE("get_chats") }; @@ -38,6 +46,38 @@ try { if(e instanceof Error) throw e; else return { status: "error", error: e as any }; } +}, +async sendMessage(to: string, message: string, relays: string[]) : Promise> { +try { + return { status: "ok", data: await TAURI_INVOKE("send_message", { to, message, relays }) }; +} catch (e) { + if(e instanceof Error) throw e; + else return { status: "error", error: e as any }; +} +}, +async subscribeTo(id: string) : Promise> { +try { + return { status: "ok", data: await TAURI_INVOKE("subscribe_to", { id }) }; +} catch (e) { + if(e instanceof Error) throw e; + else return { status: "error", error: e as any }; +} +}, +async unsubscribe(id: string) : Promise> { +try { + return { status: "ok", data: await TAURI_INVOKE("unsubscribe", { id }) }; +} catch (e) { + if(e instanceof Error) throw e; + else return { status: "error", error: e as any }; +} +}, +async dropInbox(relays: string[]) : Promise> { +try { + return { status: "ok", data: await TAURI_INVOKE("drop_inbox", { relays }) }; +} catch (e) { + if(e instanceof Error) throw e; + else return { status: "error", error: e as any }; +} } } diff --git a/src/routes/$account.chats.$id.tsx b/src/routes/$account.chats.$id.tsx index 4ffed8c..c2942b4 100644 --- a/src/routes/$account.chats.$id.tsx +++ b/src/routes/$account.chats.$id.tsx @@ -1,12 +1,14 @@ import { commands } from "@/commands"; import { cn, getReceivers, time } from "@/commons"; -import { ArrowUp } from "@phosphor-icons/react"; +import { Spinner } from "@/components/spinner"; +import { ArrowUp, CloudArrowUp, Paperclip } from "@phosphor-icons/react"; import * as ScrollArea from "@radix-ui/react-scroll-area"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { createFileRoute } from "@tanstack/react-router"; import { listen } from "@tauri-apps/api/event"; +import { message } from "@tauri-apps/plugin-dialog"; import type { NostrEvent } from "nostr-tools"; -import { useCallback, useRef } from "react"; +import { useCallback, useRef, useState, useTransition } from "react"; import { useEffect } from "react"; import { Virtualizer } from "virtua"; @@ -20,6 +22,26 @@ export const Route = createFileRoute("/$account/chats/$id")({ }); function Screen() { + const { id } = Route.useParams(); + + useEffect(() => { + commands.subscribeTo(id).then(() => console.log("sub: ", id)); + + return () => { + commands.unsubscribe(id).then(() => console.log("unsub: ", id)); + }; + }, []); + + return ( +
+
+ +
+
+ ); +} + +function List() { const { account, id } = Route.useParams(); const { isLoading, isError, data } = useQuery({ queryKey: ["chats", id], @@ -84,9 +106,10 @@ function Screen() { const event: NostrEvent = JSON.parse(data.payload.event); const sender = data.payload.sender; const receivers = getReceivers(event.tags); + const group = [account, id]; - if (sender !== account || sender !== id) return; - if (!receivers.includes(account) || !receivers.includes(id)) return; + if (!group.includes(sender)) return; + if (!group.some((item) => receivers.includes(item))) return; await queryClient.setQueryData( ["chats", id], @@ -106,47 +129,123 @@ function Screen() { }, []); return ( -
-
- + - - - {isLoading ? ( -

Loading...

- ) : isError || !data ? ( -

Error

- ) : ( - data.map((item) => renderItem(item)) - )} -
-
- - - - -
-
- - -
+ + {isLoading ? ( +

Loading...

+ ) : isError || !data ? ( +

Error

+ ) : ( + data.map((item) => renderItem(item)) + )} +
+ + + + + + + ); +} + +function Form() { + const { id } = Route.useParams(); + const { + isLoading, + isError, + data: relays, + } = useQuery({ + queryKey: ["inboxes", id], + queryFn: async () => { + const res = await commands.getInboxes(id); + + if (res.status === "ok") { + return res.data; + } else { + throw new Error(res.error); + } + }, + refetchOnWindowFocus: false, + refetchOnMount: false, + refetchOnReconnect: false, + }); + + const [newMessage, setNewMessage] = useState(""); + const [isPending, startTransition] = useTransition(); + + const submit = async () => { + startTransition(async () => { + if (newMessage.length < 1) return; + + const res = await commands.sendMessage(id, newMessage, relays); + + if (res.status === "ok") { + setNewMessage(""); + } else { + await message(res.error, { title: "Coop", kind: "error" }); + return; + } + }); + }; + + return ( +
+ {isLoading ? ( +
+ + Connecting to inbox relays +
+ ) : isError || !relays.length ? ( +
+ This user doesn't have inbox relays. You cannot send messages to them. +
+ ) : ( +
+
+
+ +
+
+ +
+
+ setNewMessage(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter") submit(); + }} + className="flex-1 h-9 rounded-full px-3.5 bg-transparent border border-neutral-200 dark:border-neutral-800 focus:outline-none focus:border-blue-500" + /> + +
+ )}
); } diff --git a/src/routes/$account.chats.tsx b/src/routes/$account.chats.tsx index daca201..1b5810e 100644 --- a/src/routes/$account.chats.tsx +++ b/src/routes/$account.chats.tsx @@ -73,9 +73,7 @@ function ChatList() { if (res.status === "ok") { const raw = res.data; - const events = raw - .map((item) => JSON.parse(item) as NostrEvent) - .sort((a, b) => b.created_at - a.created_at); + const events = raw.map((item) => JSON.parse(item) as NostrEvent); return events; } else { @@ -96,9 +94,9 @@ function ChatList() { await queryClient.setQueryData( ["chats"], (prevEvents: NostrEvent[]) => { - if (!prevEvents) { - return prevEvents; - } + if (!prevEvents) return prevEvents; + if (event.pubkey === account) return; + return [event, ...prevEvents]; // queryClient.invalidateQueries(['chats', id]); }, diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 3985dbd..b0d4830 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -5,7 +5,6 @@ import { Spinner } from "@/components/spinner"; import { User } from "@/components/user"; import { Plus } from "@phosphor-icons/react"; import { Link, createFileRoute, redirect } from "@tanstack/react-router"; -import { message } from "@tauri-apps/plugin-dialog"; import { useMemo, useState, useTransition } from "react"; export const Route = createFileRoute("/")({ @@ -44,21 +43,13 @@ function Screen() { const loginWith = async (npub: string) => { setValue(npub); startTransition(async () => { - try { - const res = await commands.login(npub); + const res = await commands.login(npub); - if (res.status === "ok") { - navigate({ - to: "/$account/chats", - params: { account: res.data }, - replace: true, - }); - } - } catch (e) { - setValue(""); - message(String(e), { - title: "Login", - kind: "error", + if (res.status === "ok") { + navigate({ + to: "/$account/chats", + params: { account: res.data }, + replace: true, }); } });