diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index 7de82a3..3505326 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -1390,7 +1390,11 @@ pub async fn publish_nostr_profile( lud16: Option, banner: Option, ) -> Result<(), String> { - let (keys, client) = get_keys_and_client(&app).await?; + // Use the signer-trait path so both local nsec users and NIP-46 + // remote signers can update kind:0 — `get_keys_and_client` + // would have errored out for remote signers because the signer + // backend isn't `Keys`. + let (signer, client) = get_signer_and_client(&app).await?; let profile = discovery::NostrProfile { picture, banner, @@ -1401,25 +1405,35 @@ pub async fn publish_nostr_profile( nip05, lud16, }; - discovery::publish_profile(&keys, &client, &profile).await + discovery::publish_profile(&signer, &client, &profile).await } -/// Create a NIP-98 HTTP Auth header (kind 27235) for authenticated uploads. +/// Create a NIP-98 HTTP Auth header (kind 27235) for authenticated +/// uploads. Goes through the `NostrSigner` trait so remote-signer +/// users can also authenticate uploads — image hosts like +/// blossom.primal.net use NIP-98 to verify the uploader, and +/// hardcoding to local keys would break that flow for NIP-46 users. #[tauri::command] pub async fn create_nip98_auth( app: tauri::AppHandle, url: String, method: String, ) -> Result { - let (keys, _client) = get_keys_and_client(&app).await?; + let (signer, _client) = get_signer_and_client(&app).await?; use nostr_sdk::prelude::*; - let event = EventBuilder::new(Kind::Custom(27235), "") + let author = signer + .get_public_key() + .await + .map_err(|e| format!("get_public_key failed: {e}"))?; + let unsigned = EventBuilder::new(Kind::Custom(27235), "") .tags(vec![ Tag::parse(vec!["u".to_string(), url]).map_err(|e| format!("tag error: {e}"))?, Tag::parse(vec!["method".to_string(), method.to_uppercase()]) .map_err(|e| format!("tag error: {e}"))?, ]) - .sign(&keys) + .build(author); + let event = signer + .sign_event(unsigned) .await .map_err(|e| format!("sign error: {e}"))?; let json = event.as_json(); diff --git a/src-tauri/src/discovery.rs b/src-tauri/src/discovery.rs index e59373a..956b3f8 100644 --- a/src-tauri/src/discovery.rs +++ b/src-tauri/src/discovery.rs @@ -1,5 +1,6 @@ use nostr_sdk::{ - Client, Event, EventBuilder, Filter, Keys, Kind, PublicKey, SecretKey, Tag, TagKind, + Client, Event, EventBuilder, Filter, Keys, Kind, NostrSigner, PublicKey, SecretKey, Tag, + TagKind, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -403,9 +404,11 @@ pub async fn fetch_profile( } /// Publish a kind 0 metadata event with profile fields. -/// Fetches the existing kind 0 first and merges to avoid clobbering unset fields. +/// Fetches the existing kind 0 first and merges to avoid clobbering +/// unset fields. Signs through the `NostrSigner` trait so both local +/// keys and NIP-46 remote signers work without branching. pub async fn publish_profile( - keys: &Keys, + signer: &std::sync::Arc, client: &Client, profile: &NostrProfile, ) -> Result<(), String> { @@ -414,11 +417,16 @@ pub async fn publish_profile( .as_deref() .ok_or_else(|| "profile name is required".to_string())?; + let author_pubkey = signer + .get_public_key() + .await + .map_err(|e| format!("get_public_key failed: {e}"))?; + // Fetch existing kind 0 to merge with let mut meta = { let filter = Filter::new() .kind(Kind::Metadata) - .author(keys.public_key()) + .author(author_pubkey) .limit(1); let events = client .fetch_events(vec![filter], Duration::from_secs(5)) @@ -466,9 +474,11 @@ pub async fn publish_profile( } let content = meta.to_string(); - let event = EventBuilder::new(Kind::Metadata, content) + let unsigned = EventBuilder::new(Kind::Metadata, content) .tags(vec![deadcat_sdk::client_tag()]) - .sign(keys) + .build(author_pubkey); + let event = signer + .sign_event(unsigned) .await .map_err(|e| format!("failed to sign profile event: {e}"))?; client