diff --git a/src/profiles.rs b/src/profiles.rs index 7d66d18..65a4dfa 100644 --- a/src/profiles.rs +++ b/src/profiles.rs @@ -561,7 +561,7 @@ fn publish_metadata_blocking( /// Best-effort lookup of the account's latest kind-0 metadata. Import must /// still succeed when relays are unavailable, so lookup failures are ignored. -fn fetch_profile_metadata(public_key: &PublicKey, relay_urls: &[String]) -> Option { +pub fn fetch_profile_metadata(public_key: &PublicKey, relay_urls: &[String]) -> Option { if relay_urls.is_empty() { return None; } diff --git a/src/signer/nip46_client.rs b/src/signer/nip46_client.rs index 3a0e9c2..f9a9fa8 100644 --- a/src/signer/nip46_client.rs +++ b/src/signer/nip46_client.rs @@ -793,7 +793,7 @@ impl Nip46ClientSigner { let message = message.into(); eprintln!("[nip46] session failed: {message}"); if let Ok(mut inner) = self.inner.try_lock() { - inner.phase = Nip46Phase::Error(message.into()); + inner.phase = Nip46Phase::Error(message); inner.task = None; inner.client = None; inner.conversation_key = None; @@ -1450,13 +1450,58 @@ impl Nip46ClientSigner { .to_bech32() .map_err(|e| format!("Could not encode identity npub: {e}"))?; + // Best-effort: look up the identity's kind-0 metadata so the profile + // row carries the real display name / picture / nip05 instead of a + // bare npub or the generic pairing label. Relay hiccups fall back to + // the pairing label — the connection must not depend on them. + let relays_for_meta = { + let app = self.app.lock().await; + crate::relays::enabled_urls(&app.settings) + }; + let meta_npub = identity; + let metadata = match tokio::time::timeout( + Duration::from_secs(3), + tokio::task::spawn_blocking(move || { + profiles::fetch_profile_metadata(&meta_npub, &relays_for_meta) + }), + ) + .await + { + Ok(joined) => joined.ok().flatten(), + Err(_elapsed) => None, + }; + let display_label = metadata + .as_ref() + .and_then(|m| m.display_name.as_deref().or(m.name.as_deref())) + .map(str::trim) + .filter(|name| !name.is_empty()) + .map(str::to_string) + .unwrap_or(label); + // Persist: profile row for the identity, connection + secret store // re-keyed under it. Refuses (fails the handshake) if the identity // collides with a local profile. { let mut app = self.app.lock().await; - profiles::store_remote_profile(&mut app.vault, &identity_npub, label) + profiles::store_remote_profile(&mut app.vault, &identity_npub, display_label) .map_err(|e| e.message().to_string())?; + // Carry the looked-up metadata (picture / nip05) onto the row so + // the profile shows its real identity, not just a name. + if let Some(meta) = &metadata { + if let Some(row) = app + .vault + .profiles + .iter_mut() + .find(|p| p.public_key == identity_npub) + { + if row.picture.is_none() { + row.picture = meta.picture.clone(); + } + if row.nip05.is_none() { + row.nip05 = meta.nip05.clone(); + } + } + } // Re-key the stored secret under the identity npub so the // connection row, VaultRef, and secret store all agree. let connect_ref = crate::signer::VaultRef::from_connection(&connection);