feat(signer): adopt real display name/picture for paired NIP-46 identities

A paired profile was stored under the generic pairing label (or a bare
npub). adopt_identity now does a best-effort, 3s-capped kind-0 metadata
lookup for the learned identity and stores display_name/name plus
picture/nip05 on the profile row, falling back to the pairing label when
relays are unavailable.
This commit is contained in:
Avi 2026-09-12 09:47:34 -05:00
commit 3d5302fbf3
2 changed files with 48 additions and 3 deletions

View file

@ -561,7 +561,7 @@ fn publish_metadata_blocking(
/// Best-effort lookup of the account's latest kind-0 metadata. Import must /// Best-effort lookup of the account's latest kind-0 metadata. Import must
/// still succeed when relays are unavailable, so lookup failures are ignored. /// still succeed when relays are unavailable, so lookup failures are ignored.
fn fetch_profile_metadata(public_key: &PublicKey, relay_urls: &[String]) -> Option<Metadata> { pub fn fetch_profile_metadata(public_key: &PublicKey, relay_urls: &[String]) -> Option<Metadata> {
if relay_urls.is_empty() { if relay_urls.is_empty() {
return None; return None;
} }

View file

@ -793,7 +793,7 @@ impl Nip46ClientSigner {
let message = message.into(); let message = message.into();
eprintln!("[nip46] session failed: {message}"); eprintln!("[nip46] session failed: {message}");
if let Ok(mut inner) = self.inner.try_lock() { if let Ok(mut inner) = self.inner.try_lock() {
inner.phase = Nip46Phase::Error(message.into()); inner.phase = Nip46Phase::Error(message);
inner.task = None; inner.task = None;
inner.client = None; inner.client = None;
inner.conversation_key = None; inner.conversation_key = None;
@ -1450,13 +1450,58 @@ impl Nip46ClientSigner {
.to_bech32() .to_bech32()
.map_err(|e| format!("Could not encode identity npub: {e}"))?; .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 // Persist: profile row for the identity, connection + secret store
// re-keyed under it. Refuses (fails the handshake) if the identity // re-keyed under it. Refuses (fails the handshake) if the identity
// collides with a local profile. // collides with a local profile.
{ {
let mut app = self.app.lock().await; 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())?; .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 // Re-key the stored secret under the identity npub so the
// connection row, VaultRef, and secret store all agree. // connection row, VaultRef, and secret store all agree.
let connect_ref = crate::signer::VaultRef::from_connection(&connection); let connect_ref = crate::signer::VaultRef::from_connection(&connection);