chore: update gpui and nostr-sdk

This commit is contained in:
2025-06-30 08:20:15 +07:00
parent b212095334
commit abb1474300
22 changed files with 196 additions and 120 deletions

View File

@@ -51,7 +51,7 @@ impl Drop for MacOsUnmounter {
);
}
Err(error) => {
log::error!("Error while trying to unmount disk image: {:?}", error);
log::error!("Error while trying to unmount disk image: {error:?}");
}
}
}
@@ -154,7 +154,7 @@ impl AutoUpdater {
target_file.write_all(&chunk).await?;
}
log::info!("downloaded update. path:{:?}", downloaded_asset);
log::info!("downloaded update. path: {downloaded_asset:?}");
Ok((temp_dir, downloaded_asset))
} else {
@@ -271,7 +271,7 @@ impl AutoUpdater {
let from = extracted.join(&app_folder_name);
let mut to = home_dir.join(".local");
let expected_suffix = format!("{}/libexec/coop", app_folder_name);
let expected_suffix = format!("{app_folder_name}/libexec/coop");
if let Some(prefix) = running_app_path
.to_str()

View File

@@ -2,48 +2,17 @@ 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 nip05;
pub mod nip96;
pub mod profile;
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);
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)
}
pub fn room_hash(event: &Event) -> u64 {
let mut hasher = DefaultHasher::new();
let mut pubkeys: Vec<PublicKey> = vec![];

View File

@@ -0,0 +1,31 @@
use anyhow::anyhow;
use nostr::prelude::*;
use reqwest::Client as ReqClient;
pub async fn nip05_verify(public_key: PublicKey, address: &str) -> Result<bool, anyhow::Error> {
let req_client = ReqClient::new();
let address = Nip05Address::parse(address)?;
// Get NIP-05 response
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);
Ok(verify)
}
pub async fn nip05_profile(address: &str) -> Result<Nip05Profile, anyhow::Error> {
let req_client = ReqClient::new();
let address = Nip05Address::parse(address)?;
// Get NIP-05 response
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"))
}
}

View File

@@ -0,0 +1,82 @@
use anyhow::anyhow;
use nostr::hashes::sha256::Hash as Sha256Hash;
use nostr::hashes::Hash;
use nostr::prelude::*;
use nostr_sdk::prelude::*;
use reqwest::multipart;
use reqwest::Client as ReqClient;
use reqwest::Response;
pub(crate) fn make_multipart_form(
file_data: Vec<u8>,
mime_type: Option<&str>,
) -> Result<multipart::Form, anyhow::Error> {
let form_file_part = multipart::Part::bytes(file_data).file_name("filename");
// Set the part's MIME type, or leave it as is if mime_type is None
let part = match mime_type {
Some(mime) => form_file_part.mime_str(mime)?,
None => form_file_part,
};
Ok(multipart::Form::new().part("file", part))
}
pub(crate) async fn upload<T>(
signer: &T,
desc: &ServerConfig,
file_data: Vec<u8>,
mime_type: Option<&str>,
) -> Result<Url, anyhow::Error>
where
T: NostrSigner,
{
let payload: Sha256Hash = Sha256Hash::hash(&file_data);
let data: HttpData = HttpData::new(desc.api_url.clone(), HttpMethod::POST).payload(payload);
let nip98_auth: String = data.to_authorization(signer).await?;
// Make form
let form: multipart::Form = make_multipart_form(file_data, mime_type)?;
// Make req client
let req_client = ReqClient::new();
// Send
let response: Response = req_client
.post(desc.api_url.clone())
.header("Authorization", nip98_auth)
.multipart(form)
.send()
.await?;
// Parse response
let json: Value = response.json().await?;
let upload_response = nip96::UploadResponse::from_json(json.to_string())?;
if upload_response.status == UploadResponseStatus::Error {
return Err(anyhow!(upload_response.message));
}
Ok(upload_response.download_url()?.to_owned())
}
pub async fn nip96_upload(
client: &Client,
server: &Url,
file: Vec<u8>,
) -> Result<Url, anyhow::Error> {
let req_client = ReqClient::new();
let config_url = nip96::get_server_config_url(server)?;
// Get
let res = req_client.get(config_url.to_string()).send().await?;
let json: Value = res.json().await?;
let config = nip96::ServerConfig::from_json(json.to_string())?;
let signer = client.signer().await?;
let url = upload(&signer, &config, file, None).await?;
Ok(url)
}

View File

@@ -18,8 +18,7 @@ impl RenderProfile for Profile {
.map(|picture| {
if proxy {
format!(
"{}/?url={}&w=100&h=100&fit=cover&mask=circle&default={}&n=-1",
IMAGE_RESIZE_SERVICE, picture, FALLBACK_IMG
"{IMAGE_RESIZE_SERVICE}/?url={picture}&w=100&h=100&fit=cover&mask=circle&default={FALLBACK_IMG}&n=-1"
)
.into()
} else {

View File

@@ -5,7 +5,7 @@ use std::sync::Arc;
use chats::message::Message;
use chats::room::{Room, RoomKind, SendError};
use common::nip96_upload;
use common::nip96::nip96_upload;
use common::profile::RenderProfile;
use global::shared_state;
use gpui::prelude::FluentBuilder;
@@ -390,16 +390,9 @@ impl Chat {
// Spawn task via async utility instead of GPUI context
nostr_sdk::async_utility::task::spawn(async move {
let url = match nip96_upload(shared_state().client(), nip96, file_data)
let url = nip96_upload(shared_state().client(), &nip96, file_data)
.await
{
Ok(url) => Some(url),
Err(e) => {
log::error!("Upload error: {e}");
None
}
};
.ok();
_ = tx.send(url);
});

View File

@@ -4,7 +4,7 @@ use std::time::Duration;
use anyhow::{anyhow, Error};
use chats::room::{Room, RoomKind};
use chats::ChatRegistry;
use common::nip05_profile;
use common::nip05::nip05_profile;
use common::profile::RenderProfile;
use global::shared_state;
use gpui::prelude::FluentBuilder;

View File

@@ -592,8 +592,7 @@ impl Render for Login {
.text_center()
.text_color(cx.theme().text_muted)
.child(SharedString::from(format!(
"Approve connection request from your signer in {} seconds",
i
"Approve connection request from your signer in {i} seconds"
))),
)
})

View File

@@ -1,4 +1,4 @@
use common::nip96_upload;
use common::nip96::nip96_upload;
use global::shared_state;
use gpui::prelude::FluentBuilder;
use gpui::{
@@ -158,7 +158,7 @@ impl NewAccount {
nostr_sdk::async_utility::task::spawn(async move {
if let Ok(url) =
nip96_upload(shared_state().client(), nip96, file_data).await
nip96_upload(shared_state().client(), &nip96, file_data).await
{
_ = tx.send(url);
}

View File

@@ -1,7 +1,7 @@
use std::str::FromStr;
use std::time::Duration;
use common::nip96_upload;
use common::nip96::nip96_upload;
use global::shared_state;
use gpui::prelude::FluentBuilder;
use gpui::{
@@ -104,7 +104,7 @@ impl Profile {
}
fn upload(&mut self, window: &mut Window, cx: &mut Context<Self>) {
let nip96 = AppSettings::get_global(cx).settings.media_server.clone();
let nip96_server = AppSettings::get_global(cx).settings.media_server.clone();
let avatar_input = self.avatar_input.downgrade();
let paths = cx.prompt_for_paths(PathPromptOptions {
files: true,
@@ -125,7 +125,8 @@ impl Profile {
nostr_sdk::async_utility::task::spawn(async move {
if let Ok(url) =
nip96_upload(shared_state().client(), nip96, file_data).await
nip96_upload(shared_state().client(), &nip96_server, file_data)
.await
{
_ = tx.send(url);
}

View File

@@ -118,7 +118,7 @@ impl Relays {
]);
if let Err(e) = client.send_event_builder(builder).await {
log::error!("Failed to send relay list event: {}", e);
log::error!("Failed to send relay list event: {e}");
}
}
@@ -153,7 +153,7 @@ impl Relays {
)
.await
{
log::error!("Failed to subscribe to new messages: {}", e);
log::error!("Failed to subscribe to new messages: {e}");
}
Ok(output.val)

View File

@@ -6,8 +6,8 @@ use anyhow::Error;
use chats::room::{Room, RoomKind};
use chats::{ChatRegistry, RoomEmitter};
use common::debounced_delay::DebouncedDelay;
use common::nip05::nip05_verify;
use common::profile::RenderProfile;
use common::verify_nip05;
use element::DisplayRoom;
use global::constants::{DEFAULT_MODAL_WIDTH, SEARCH_RELAYS};
use global::shared_state;
@@ -185,7 +185,7 @@ impl Sidebar {
continue;
};
if !verify_nip05(event.pubkey, target).await.unwrap_or(false) {
if !nip05_verify(event.pubkey, target).await.unwrap_or(false) {
// Skip if NIP-05 is not valid or failed to verify
continue;
};
@@ -218,7 +218,7 @@ impl Sidebar {
this.update(cx, |this, cx| {
if result.is_empty() {
let msg =
format!("There are no users matching query {}", query_cloned);
format!("There are no users matching query {query_cloned}");
window.push_notification(Notification::info(msg), cx);
this.set_finding(false, cx);
} else {

View File

@@ -369,13 +369,13 @@ impl Globals {
pub(crate) async fn connect(&self) {
for relay in BOOTSTRAP_RELAYS.into_iter() {
if let Err(e) = self.client.add_relay(relay).await {
log::error!("Failed to add relay {}: {}", relay, e);
log::error!("Failed to add relay {relay}: {e}");
}
}
for relay in SEARCH_RELAYS.into_iter() {
if let Err(e) = self.client.add_relay(relay).await {
log::error!("Failed to add relay {}: {}", relay, e);
log::error!("Failed to add relay {relay}: {e}");
}
}
@@ -470,7 +470,7 @@ impl Globals {
.subscribe_to(BOOTSTRAP_RELAYS, filter, Some(opts))
.await
{
log::error!("Failed to subscribe for app updates: {}", e);
log::error!("Failed to subscribe for app updates: {e}");
}
log::info!("Subscribed to app updates");
@@ -623,7 +623,7 @@ impl Globals {
.subscribe_to(BOOTSTRAP_RELAYS, filter, Some(opts))
.await
{
log::error!("Failed to subscribe for file metadata: {}", e);
log::error!("Failed to subscribe for file metadata: {e}");
} else {
self.send_signal(NostrSignal::AppUpdate(event.to_owned()))
.await;

View File

@@ -408,7 +408,7 @@ impl Identity {
);
if let Err(e) = client.send_event_builder(builder).await {
log::error!("Failed to send relay list event: {}", e);
log::error!("Failed to send relay list event: {e}");
};
// Create messaging relay list
@@ -423,7 +423,7 @@ impl Identity {
);
if let Err(e) = client.send_event_builder(builder).await {
log::error!("Failed to send messaging relay list event: {}", e);
log::error!("Failed to send messaging relay list event: {e}");
};
// Subscribe for user's data

View File

@@ -518,7 +518,7 @@ impl TabPanel {
};
Some(
Button::new(SharedString::from(format!("toggle-dock:{:?}", placement)))
Button::new(SharedString::from(format!("toggle-dock:{placement:?}")))
.icon(icon)
.small()
.ghost()

View File

@@ -102,11 +102,11 @@ impl RenderOnce for EmojiPicker {
input.update(cx, |this, cx| {
let current = this.value();
let new_text = if current.is_empty() {
format!("{}", item)
format!("{item}")
} else if current.ends_with(" ") {
format!("{}{}", current, item)
format!("{current}{item}")
} else {
format!("{} {}", current, item)
format!("{current} {item}")
};
this.set_value(new_text, window, cx);
});

View File

@@ -302,7 +302,7 @@ impl MaskPattern {
if fraction == &Some(0) {
int_with_sep
} else {
format!("{}.{}", int_with_sep, frac)
format!("{int_with_sep}.{frac}")
}
} else {
int_with_sep

View File

@@ -50,7 +50,7 @@ pub trait PopupMenuExt: Styled + Selectable + IntoElement + 'static {
let style = self.style().clone();
let element_id = self.element_id();
Popover::new(SharedString::from(format!("popup-menu:{:?}", element_id)))
Popover::new(SharedString::from(format!("popup-menu:{element_id:?}")))
.no_style()
.trigger(self)
.trigger_style(style)
@@ -716,7 +716,7 @@ impl Render for PopupMenu {
/// Return the Platform specific keybinding string by KeyStroke
pub fn key_shortcut(key: Keystroke) -> String {
if cfg!(target_os = "macos") {
return format!("{}", key);
return format!("{key}");
}
let mut parts = vec![];

View File

@@ -293,7 +293,7 @@ pub fn render_plain_text_mut(
// Make it clickable
link_ranges.push(new_range);
link_urls.push(format!("mention:{}", entity_without_prefix));
link_urls.push(format!("mention:{entity_without_prefix}"));
// Adjust subsequent ranges if needed
if length_diff != 0 {
@@ -301,11 +301,11 @@ pub fn render_plain_text_mut(
}
} else {
// No profile match or not a profile entity - create njump.me link
let njump_url = format!("https://njump.me/{}", entity_without_prefix);
let njump_url = format!("https://njump.me/{entity_without_prefix}");
// Create a shortened display format for the URL
let shortened_entity = format_shortened_entity(entity_without_prefix);
let display_text = format!("https://njump.me/{}", shortened_entity);
let display_text = format!("https://njump.me/{shortened_entity}");
// Replace the original entity with the shortened display version
text.replace_range(range.clone(), &display_text);
@@ -350,7 +350,7 @@ fn format_shortened_entity(entity: &str) -> String {
let prefix = &entity[0..=prefix_end]; // Include the '1'
let suffix = &entity[entity.len() - 4..]; // Last 4 chars
format!("{}...{}", prefix, suffix)
format!("{prefix}...{suffix}")
} else {
entity.to_string()
}