chore: update gpui and nostr sdk

This commit is contained in:
2025-06-25 20:00:05 +07:00
parent edee9305cc
commit 3c2eaabab2
14 changed files with 209 additions and 295 deletions

View File

@@ -12,7 +12,7 @@ gpui.workspace = true
nostr-sdk.workspace = true
anyhow.workspace = true
smol.workspace = true
reqwest.workspace = true
log.workspace = true
tempfile = "3.19.1"
reqwest = { version = "0.12", features = ["stream"] }

View File

@@ -4,7 +4,6 @@ use std::sync::Arc;
use anyhow::{anyhow, Error};
use chrono::{Local, TimeZone};
use common::profile::RenderProfile;
use common::{compare, room_hash};
use global::shared_state;
use gpui::{App, AppContext, Context, EventEmitter, SharedString, Task, Window};
use identity::Identity;
@@ -79,7 +78,7 @@ impl Room {
///
/// A new Room instance with information extracted from the event
pub fn new(event: &Event) -> Self {
let id = room_hash(event);
let id = common::room_hash(event);
let created_at = event.created_at;
// Get all pubkeys from the event's tags
@@ -405,7 +404,7 @@ impl Room {
let mut other_pubkeys = ev.tags.public_keys().copied().collect::<Vec<_>>();
other_pubkeys.push(ev.pubkey);
// Check if the event is from a member of the room
compare(&other_pubkeys, &pubkeys)
common::compare(&other_pubkeys, &pubkeys)
})
.collect::<Vec<_>>();

View File

@@ -10,12 +10,14 @@ global = { path = "../global" }
gpui.workspace = true
nostr-connect.workspace = true
nostr-sdk.workspace = true
nostr.workspace = true
anyhow.workspace = true
itertools.workspace = true
chrono.workspace = true
smallvec.workspace = true
smol.workspace = true
futures.workspace = true
reqwest.workspace = true
webbrowser = "1.0.4"
qrcode-generator = "5.0.0"

View File

@@ -2,23 +2,43 @@ use std::collections::HashSet;
use std::hash::{DefaultHasher, Hash, Hasher};
use std::sync::Arc;
use anyhow::{anyhow, Error, Result};
use gpui::{Image, ImageFormat};
use itertools::Itertools;
use nostr_sdk::prelude::*;
use qrcode_generator::QrCodeEcc;
use reqwest::Client as ReqClient;
pub mod debounced_delay;
pub mod handle_auth;
pub mod profile;
pub async fn nip96_upload(
client: &Client,
upload_to: Url,
file: Vec<u8>,
) -> anyhow::Result<Url, anyhow::Error> {
let signer = client.signer().await?;
pub async fn verify_nip05(public_key: PublicKey, address: &str) -> Result<bool, Error> {
let req_client = ReqClient::new();
let address = Nip05Address::parse(address)?;
let res = req_client.get(address.url().to_string()).send().await?;
let json: Value = res.json().await?;
let verify = nip05::verify_from_json(&public_key, &address, &json);
let config: ServerConfig = nip96::get_server_config(upload_to.to_owned(), None).await?;
Ok(verify)
}
pub async fn nip05_profile(address: &str) -> Result<Nip05Profile, Error> {
let req_client = ReqClient::new();
let address = Nip05Address::parse(address)?;
let res = req_client.get(address.url().to_string()).send().await?;
let json: Value = res.json().await?;
if let Ok(profile) = Nip05Profile::from_json(&address, &json) {
Ok(profile)
} else {
Err(anyhow!("Failed to get NIP-05 profile"))
}
}
pub async fn nip96_upload(client: &Client, server: Url, file: Vec<u8>) -> Result<Url, Error> {
let signer = client.signer().await?;
let config = nip96::get_server_config(server.to_owned(), None).await?;
let url = nip96::upload_data(&signer, &config, file, None, None).await?;
Ok(url)
@@ -26,10 +46,10 @@ pub async fn nip96_upload(
pub fn room_hash(event: &Event) -> u64 {
let mut hasher = DefaultHasher::new();
let mut pubkeys: Vec<&PublicKey> = vec![];
let mut pubkeys: Vec<PublicKey> = vec![];
// Add all public keys from event
pubkeys.push(&event.pubkey);
pubkeys.push(event.pubkey);
pubkeys.extend(event.tags.public_keys().collect::<Vec<_>>());
// Generate unique hash

View File

@@ -24,6 +24,7 @@ reqwest_client.workspace = true
nostr-connect.workspace = true
nostr-sdk.workspace = true
nostr.workspace = true
anyhow.workspace = true
serde.workspace = true

View File

@@ -7,8 +7,8 @@ use global::constants::{DEFAULT_MODAL_WIDTH, DEFAULT_SIDEBAR_WIDTH};
use global::shared_state;
use gpui::prelude::FluentBuilder;
use gpui::{
div, impl_internal_actions, px, relative, App, AppContext, Axis, Context, Entity, IntoElement,
ParentElement, Render, Styled, Subscription, Task, Window,
div, px, relative, Action, App, AppContext, Axis, Context, Entity, IntoElement, ParentElement,
Render, Styled, Subscription, Task, Window,
};
use identity::Identity;
use nostr_connect::prelude::*;
@@ -25,8 +25,6 @@ use ui::{ContextModal, IconName, Root, Sizable, StyledExt, TitleBar};
use crate::views::chat::{self, Chat};
use crate::views::{login, new_account, onboarding, preferences, sidebar, startup, welcome};
impl_internal_actions!(dock, [ToggleModal]);
pub fn init(window: &mut Window, cx: &mut App) -> Entity<ChatSpace> {
ChatSpace::new(window, cx)
}
@@ -56,7 +54,8 @@ pub enum ModalKind {
SetupRelay,
}
#[derive(Clone, PartialEq, Eq, Deserialize)]
#[derive(Action, Clone, PartialEq, Eq, Deserialize)]
#[action(namespace = modal, no_json)]
pub struct ToggleModal {
pub modal: ModalKind,
}

View File

@@ -10,11 +10,11 @@ use common::profile::RenderProfile;
use global::shared_state;
use gpui::prelude::FluentBuilder;
use gpui::{
div, img, impl_internal_actions, list, px, red, relative, rems, svg, white, AnyElement, App,
AppContext, ClipboardItem, Context, Div, Element, Empty, Entity, EventEmitter, Flatten,
FocusHandle, Focusable, InteractiveElement, IntoElement, ListAlignment, ListState, ObjectFit,
ParentElement, PathPromptOptions, Render, RetainAllImageCache, SharedString,
StatefulInteractiveElement, Styled, StyledImage, Subscription, Window,
div, img, list, px, red, relative, rems, svg, white, Action, AnyElement, App, AppContext,
ClipboardItem, Context, Div, Element, Empty, Entity, EventEmitter, Flatten, FocusHandle,
Focusable, InteractiveElement, IntoElement, ListAlignment, ListState, ObjectFit, ParentElement,
PathPromptOptions, Render, RetainAllImageCache, SharedString, StatefulInteractiveElement,
Styled, StyledImage, Subscription, Window,
};
use identity::Identity;
use itertools::Itertools;
@@ -38,11 +38,10 @@ use ui::{
use crate::views::subject;
#[derive(Clone, PartialEq, Eq, Deserialize)]
#[derive(Action, Clone, PartialEq, Eq, Deserialize)]
#[action(namespace = chat, no_json)]
pub struct ChangeSubject(pub String);
impl_internal_actions!(chat, [ChangeSubject]);
pub fn init(room: Entity<Room>, window: &mut Window, cx: &mut App) -> Arc<Entity<Chat>> {
Arc::new(Chat::new(room, window, cx))
}

View File

@@ -4,17 +4,17 @@ use std::time::Duration;
use anyhow::{anyhow, Error};
use chats::room::{Room, RoomKind};
use chats::ChatRegistry;
use common::nip05_profile;
use common::profile::RenderProfile;
use global::shared_state;
use gpui::prelude::FluentBuilder;
use gpui::{
div, img, impl_internal_actions, px, red, relative, uniform_list, App, AppContext, Context,
Entity, InteractiveElement, IntoElement, ParentElement, Render, SharedString,
div, img, px, red, relative, uniform_list, App, AppContext, Context, Entity,
InteractiveElement, IntoElement, ParentElement, Render, SharedString,
StatefulInteractiveElement, Styled, Subscription, Task, TextAlign, Window,
};
use itertools::Itertools;
use nostr_sdk::prelude::*;
use serde::Deserialize;
use settings::AppSettings;
use smallvec::{smallvec, SmallVec};
use smol::Timer;
@@ -28,11 +28,6 @@ pub fn init(window: &mut Window, cx: &mut App) -> Entity<Compose> {
cx.new(|cx| Compose::new(window, cx))
}
#[derive(Clone, PartialEq, Eq, Deserialize)]
struct SelectContact(PublicKey);
impl_internal_actions!(contacts, [SelectContact]);
#[derive(Debug, Clone)]
struct Contact {
profile: Profile,
@@ -245,15 +240,14 @@ impl Compose {
let task: Task<Result<Contact, anyhow::Error>> = if content.contains("@") {
cx.background_spawn(async move {
let (tx, rx) = oneshot::channel::<Nip05Profile>();
let (tx, rx) = oneshot::channel::<Option<Nip05Profile>>();
nostr_sdk::async_utility::task::spawn(async move {
if let Ok(profile) = nip05::profile(&content, None).await {
tx.send(profile).ok();
}
let profile = nip05_profile(&content).await.ok();
tx.send(profile).ok();
});
if let Ok(profile) = rx.await {
if let Ok(Some(profile)) = rx.await {
let public_key = profile.public_key;
let metadata = shared_state()
.client()

View File

@@ -7,6 +7,7 @@ use chats::room::{Room, RoomKind};
use chats::{ChatRegistry, RoomEmitter};
use common::debounced_delay::DebouncedDelay;
use common::profile::RenderProfile;
use common::verify_nip05;
use element::DisplayRoom;
use global::constants::{DEFAULT_MODAL_WIDTH, SEARCH_RELAYS};
use global::shared_state;
@@ -184,13 +185,8 @@ impl Sidebar {
continue;
};
let Ok(verified) = nip05::verify(&event.pubkey, target, None).await else {
// Skip if NIP-05 verification fails
continue;
};
if !verified {
// Skip if NIP-05 is not valid
if !verify_nip05(event.pubkey, target).await.unwrap_or(false) {
// Skip if NIP-05 is not valid or failed to verify
continue;
};

View File

@@ -1,11 +1,11 @@
use gpui::{actions, impl_internal_actions};
use gpui::{actions, Action};
use serde::Deserialize;
#[derive(Clone, PartialEq, Eq, Deserialize)]
#[derive(Clone, Action, PartialEq, Eq, Deserialize)]
#[action(namespace = list, no_json)]
pub struct Confirm {
/// Is confirm with secondary.
pub secondary: bool,
}
actions!(list, [Cancel, SelectPrev, SelectNext]);
impl_internal_actions!(list, [Confirm]);

View File

@@ -2,9 +2,9 @@ use std::rc::Rc;
use gpui::prelude::FluentBuilder;
use gpui::{
div, impl_internal_actions, px, App, AppContext, Corner, Element, InteractiveElement,
IntoElement, ParentElement, RenderOnce, SharedString, StatefulInteractiveElement, Styled,
WeakEntity, Window,
div, px, Action, App, AppContext, Corner, Element, InteractiveElement, IntoElement,
ParentElement, RenderOnce, SharedString, StatefulInteractiveElement, Styled, WeakEntity,
Window,
};
use serde::Deserialize;
use theme::ActiveTheme;
@@ -14,11 +14,10 @@ use crate::input::InputState;
use crate::popover::{Popover, PopoverContent};
use crate::Icon;
#[derive(PartialEq, Clone, Debug, Deserialize)]
#[derive(Action, PartialEq, Clone, Debug, Deserialize)]
#[action(namespace = emoji, no_json)]
pub struct EmitEmoji(pub SharedString);
impl_internal_actions!(emoji, [EmitEmoji]);
#[derive(IntoElement)]
pub struct EmojiPicker {
icon: Option<Icon>,

View File

@@ -4,8 +4,8 @@ use std::rc::Rc;
use gpui::prelude::FluentBuilder as _;
use gpui::{
actions, div, impl_internal_actions, point, px, App, AppContext, Bounds, ClipboardItem,
Context, DefiniteLength, Entity, EntityInputHandler, EventEmitter, FocusHandle, Focusable,
actions, div, point, px, Action, App, AppContext, Bounds, ClipboardItem, Context,
DefiniteLength, Entity, EntityInputHandler, EventEmitter, FocusHandle, Focusable,
InteractiveElement as _, IntoElement, KeyBinding, KeyDownEvent, MouseButton, MouseDownEvent,
MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels, Point, Render, ScrollHandle,
ScrollWheelEvent, SharedString, Styled as _, Subscription, UTF16Selection, Window, WrappedLine,
@@ -24,14 +24,13 @@ use crate::history::History;
use crate::scroll::ScrollbarState;
use crate::Root;
#[derive(Clone, PartialEq, Eq, Deserialize)]
#[derive(Action, Clone, PartialEq, Eq, Deserialize)]
#[action(namespace = input, no_json)]
pub struct Enter {
/// Is confirm with secondary.
pub secondary: bool,
}
impl_internal_actions!(input, [Enter]);
actions!(
input,
[