From cae96157ca65d0a578e1a57665bfc7be4829e199 Mon Sep 17 00:00:00 2001 From: reya Date: Thu, 13 Mar 2025 13:02:58 +0700 Subject: [PATCH] chore: release 0.1.4 --- Cargo.lock | 2 +- crates/app/Cargo.toml | 2 +- crates/app/src/device.rs | 4 ++ crates/app/src/main.rs | 30 ++++++++------- crates/app/src/views/chat.rs | 19 ++++++++- crates/app/src/views/onboarding.rs | 8 +++- crates/chats/src/room.rs | 62 +++++++++++++++--------------- crates/common/src/utils.rs | 9 +++++ 8 files changed, 85 insertions(+), 51 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 60eaa2a..0694f0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1174,7 +1174,7 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "coop" -version = "0.1.3" +version = "0.1.4" dependencies = [ "anyhow", "chats", diff --git a/crates/app/Cargo.toml b/crates/app/Cargo.toml index 343c404..a7fd1b4 100644 --- a/crates/app/Cargo.toml +++ b/crates/app/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "coop" -version = "0.1.3" +version = "0.1.4" edition = "2021" publish = false diff --git a/crates/app/src/device.rs b/crates/app/src/device.rs index d646c1d..4d4a5c7 100644 --- a/crates/app/src/device.rs +++ b/crates/app/src/device.rs @@ -211,6 +211,10 @@ impl Device { cx.set_global(GlobalDevice(device)); } + pub fn client_keys(&self) -> Arc { + self.client_keys.clone() + } + pub fn profile(&self) -> Option<&NostrProfile> { self.profile.as_ref() } diff --git a/crates/app/src/main.rs b/crates/app/src/main.rs index 587d37d..6569076 100644 --- a/crates/app/src/main.rs +++ b/crates/app/src/main.rs @@ -141,19 +141,15 @@ fn main() { pubkeys.push(event.pubkey); // Save the event to the database, use for query directly. - if let Err(e) = - client.database().save_event(&event).await - { - log::error!("Failed to save event: {}", e); - } - - // Send all pubkeys to the batch - _ = batch_tx.send(pubkeys).await; + _ = client.database().save_event(&event).await; // Send this event to the GPUI if new_id == *subscription_id { _ = event_tx.send(Signal::Event(event)).await; } + + // Send all pubkeys to the batch + _ = batch_tx.send(pubkeys).await; } } } @@ -180,12 +176,18 @@ fn main() { Kind::Custom(DEVICE_ANNOUNCEMENT_KIND) => { log::info!("Device Announcement received"); - if let Some(tag) = event - .tags - .find(TagKind::custom("client")) - .and_then(|tag| tag.content()) - { - set_device_name(tag).await; + if let Ok(signer) = client.signer().await { + if let Ok(public_key) = signer.get_public_key().await { + if event.pubkey == public_key { + if let Some(tag) = event + .tags + .find(TagKind::custom("client")) + .and_then(|tag| tag.content()) + { + set_device_name(tag).await; + } + } + } } } _ => {} diff --git a/crates/app/src/views/chat.rs b/crates/app/src/views/chat.rs index 8747867..ca3d649 100644 --- a/crates/app/src/views/chat.rs +++ b/crates/app/src/views/chat.rs @@ -98,6 +98,7 @@ pub struct Chat { // Chat Room room: WeakEntity, messages: Entity>, + seens: Entity>, list_state: ListState, #[allow(dead_code)] subscriptions: Vec, @@ -116,6 +117,7 @@ impl Chat { cx: &mut App, ) -> Entity { let messages = cx.new(|_| vec![Message::placeholder()]); + let seens = cx.new(|_| vec![]); let attaches = cx.new(|_| None); let input = cx.new(|cx| { TextInput::new(window, cx) @@ -165,6 +167,7 @@ impl Chat { id: id.to_string().into(), room, messages, + seens, list_state, input, attaches, @@ -244,11 +247,18 @@ impl Chat { self.list_state.splice(old_len..old_len, 1); } - fn push_message(&self, event: &Event, _window: &mut Window, cx: &mut Context) { + fn push_message(&mut self, event: &Event, _window: &mut Window, cx: &mut Context) { let Some(model) = self.room.upgrade() else { return; }; + // Prevent duplicate messages + if self.seens.read(cx).iter().any(|id| id == &event.id) { + return; + } + // Add ID to seen list + self.seen(event.id, cx); + let old_len = self.messages.read(cx).len(); let room = model.read(cx); @@ -450,6 +460,13 @@ impl Chat { cx.notify(); } + fn seen(&mut self, id: EventId, cx: &mut Context) { + self.seens.update(cx, |this, cx| { + this.push(id); + cx.notify(); + }); + } + fn render_message( &self, ix: usize, diff --git a/crates/app/src/views/onboarding.rs b/crates/app/src/views/onboarding.rs index 5f09f3f..3db3f17 100644 --- a/crates/app/src/views/onboarding.rs +++ b/crates/app/src/views/onboarding.rs @@ -5,7 +5,7 @@ use gpui::{ }; use nostr_connect::prelude::*; use smallvec::{smallvec, SmallVec}; -use std::{path::PathBuf, time::Duration}; +use std::{path::PathBuf, sync::Arc, time::Duration}; use ui::{ button::{Button, ButtonCustomVariant, ButtonVariants}, input::{InputEvent, TextInput}, @@ -104,8 +104,12 @@ impl Onboarding { } fn connect(&mut self, window: &mut Window, cx: &mut Context) { + let Some(model) = Device::global(cx) else { + return; + }; + let text = self.bunker_input.read(cx).text().to_string(); - let keys = Keys::generate(); + let keys = Arc::unwrap_or_clone(model.read(cx).client_keys()); self.set_loading(true, cx); diff --git a/crates/chats/src/room.rs b/crates/chats/src/room.rs index 9256c9f..f699c5d 100644 --- a/crates/chats/src/room.rs +++ b/crates/chats/src/room.rs @@ -1,7 +1,11 @@ use std::collections::HashSet; -use anyhow::{anyhow, Context, Error}; -use common::{last_seen::LastSeen, profile::NostrProfile, utils::room_hash}; +use anyhow::{anyhow, Error}; +use common::{ + last_seen::LastSeen, + profile::NostrProfile, + utils::{device_pubkey, room_hash}, +}; use global::{constants::DEVICE_ANNOUNCEMENT_KIND, get_client, get_device_keys}; use gpui::{App, AppContext, Entity, EventEmitter, SharedString, Task}; use nostr_sdk::prelude::*; @@ -194,41 +198,35 @@ impl Room { // Check if the pubkey has a device announcement, // then choose the appropriate signer based on device presence - if let Some(event) = client.database().query(filter).await?.first_owned() { - log::info!("Use device signer to send message"); - let signer = &device; + let event = match client.database().query(filter).await?.first() { + Some(event) => { + log::info!("Use device signer to send message"); + let signer = &device; + // Get the device's public key of other user + let device_pubkey = device_pubkey(event)?; - // Get the device's public key of other user - let n_tag = event.tags.find(TagKind::custom("n")).context("Not found")?; - let hex = n_tag.content().context("Not found")?; - let target_pubkey = PublicKey::parse(hex)?; + let rumor = EventBuilder::private_msg_rumor(*pubkey, &content) + .tags(tags.clone()) + .build(user_pubkey); - let rumor = EventBuilder::private_msg_rumor(*pubkey, &content) - .tags(tags.clone()) - .build(user_pubkey); - - let event = EventBuilder::gift_wrap( - signer, - &target_pubkey, - rumor, - vec![Tag::public_key(*pubkey)], - ) - .await?; - - if let Err(e) = client.send_event(&event).await { - // Convert error into string, then push it to the report - report.push(e.to_string()); + EventBuilder::gift_wrap( + signer, + &device_pubkey, + rumor, + vec![Tag::public_key(*pubkey)], + ) + .await? } - } else { - log::info!("Use user signer to send message"); - let signer = &client.signer().await?; + None => { + log::info!("Use user signer to send message"); + let signer = client.signer().await?; - let event = - EventBuilder::private_msg(signer, *pubkey, &content, tags.clone()).await?; - - if let Err(e) = client.send_event(&event).await { - report.push(e.to_string()); + EventBuilder::private_msg(&signer, *pubkey, &content, tags.clone()).await? } + }; + + if let Err(e) = client.send_event(&event).await { + report.push(e.to_string()); } } diff --git a/crates/common/src/utils.rs b/crates/common/src/utils.rs index 641e2ee..7ccb81e 100644 --- a/crates/common/src/utils.rs +++ b/crates/common/src/utils.rs @@ -1,3 +1,4 @@ +use anyhow::Context; use global::constants::NIP96_SERVER; use itertools::Itertools; use nostr_sdk::prelude::*; @@ -43,6 +44,14 @@ pub fn room_hash(event: &Event) -> u64 { hasher.finish() } +pub fn device_pubkey(event: &Event) -> Result { + let n_tag = event.tags.find(TagKind::custom("n")).context("Invalid")?; + let hex = n_tag.content().context("Invalid")?; + let pubkey = PublicKey::parse(hex)?; + + Ok(pubkey) +} + pub fn random_name(length: usize) -> String { let rng = RNG::from(&Language::Roman); rng.generate_names(length, true).join("-").to_lowercase()