diff --git a/desktop/src-tauri/src/models.rs b/desktop/src-tauri/src/models.rs index 9693f1563a..1433734637 100644 --- a/desktop/src-tauri/src/models.rs +++ b/desktop/src-tauri/src/models.rs @@ -66,6 +66,8 @@ pub struct UsersBatchResponse { pub struct UserSearchResultInfo { pub pubkey: String, pub display_name: Option, + #[serde(default)] + pub name: Option, pub avatar_url: Option, pub nip05_handle: Option, pub owner_pubkey: Option, diff --git a/desktop/src-tauri/src/nostr_convert/user_search.rs b/desktop/src-tauri/src/nostr_convert/user_search.rs index 43b4288abb..c97efe2069 100644 --- a/desktop/src-tauri/src/nostr_convert/user_search.rs +++ b/desktop/src-tauri/src/nostr_convert/user_search.rs @@ -18,6 +18,7 @@ pub fn user_search_result_from_event(ev: &Event) -> UserSearchResultInfo { .and_then(Value::as_str) .or_else(|| v.get("name").and_then(Value::as_str)) .map(str::to_string), + name: v.get("name").and_then(Value::as_str).map(str::to_string), avatar_url: v.get("picture").and_then(Value::as_str).map(str::to_string), nip05_handle: v.get("nip05").and_then(Value::as_str).map(str::to_string), is_agent: owner_pubkey.is_some(), @@ -126,10 +127,11 @@ pub fn rank_user_search_results( let info = user_search_result_from_event(ev); let display = info.display_name.as_deref().unwrap_or("").to_lowercase(); + let name = info.name.as_deref().unwrap_or("").to_lowercase(); let nip05 = info.nip05_handle.as_deref().unwrap_or("").to_lowercase(); let pubkey = info.pubkey.to_lowercase(); - let score = match_score(&q, &display, &nip05, &pubkey); + let score = match_score(&q, &display, &name, &nip05, &pubkey); if score == 0 { continue; } @@ -156,7 +158,7 @@ pub fn rank_user_search_results( } } -fn match_score(q: &str, display_name: &str, nip05: &str, pubkey_hex: &str) -> u32 { +fn match_score(q: &str, display_name: &str, name: &str, nip05: &str, pubkey_hex: &str) -> u32 { const DISPLAY_EXACT: u32 = 1000; const DISPLAY_PREFIX: u32 = 900; const DISPLAY_CONTAINS: u32 = 800; @@ -184,7 +186,13 @@ fn match_score(q: &str, display_name: &str, nip05: &str, pubkey_hex: &str) -> u3 DISPLAY_EXACT, DISPLAY_PREFIX, DISPLAY_CONTAINS, - ); + ) + .max(score_field( + name, + DISPLAY_EXACT, + DISPLAY_PREFIX, + DISPLAY_CONTAINS, + )); let nip05_score = score_field(nip05, NIP05_EXACT, NIP05_PREFIX, NIP05_CONTAINS); let pubkey_score = if !pubkey_hex.is_empty() && pubkey_hex.starts_with(q) { PUBKEY_PREFIX @@ -406,4 +414,13 @@ mod tests { assert_eq!(r.users.len(), 1); assert_eq!(r.users[0].display_name.as_deref(), Some("alice")); } + + #[test] + fn rank_matches_name_when_display_name_differs() { + let e = ev(0, r#"{"name":"Starfish Sol","display_name":"S"}"#, vec![]); + let r = rank_user_search_results(&[e], "starfish", 10); + assert_eq!(r.users.len(), 1); + assert_eq!(r.users[0].display_name.as_deref(), Some("S")); + assert_eq!(r.users[0].name.as_deref(), Some("Starfish Sol")); + } } diff --git a/desktop/src/features/channels/ui/MembersSidebar.tsx b/desktop/src/features/channels/ui/MembersSidebar.tsx index 2a125e0d01..ffcc196525 100644 --- a/desktop/src/features/channels/ui/MembersSidebar.tsx +++ b/desktop/src/features/channels/ui/MembersSidebar.tsx @@ -309,6 +309,7 @@ export function MembersSidebar({ : current.isAgent ? currentName : (currentName ?? candidateName), + name: current.name ?? candidate.name ?? null, nip05Handle: current.nip05Handle ?? candidate.nip05Handle ?? null, ownerPubkey: current.ownerPubkey ?? candidate.ownerPubkey ?? null, isAgent: current.isAgent || candidate.isAgent, diff --git a/desktop/src/features/profile/lib/userCandidateSearch.test.mjs b/desktop/src/features/profile/lib/userCandidateSearch.test.mjs index bc94fc528d..dc5a3c92a8 100644 --- a/desktop/src/features/profile/lib/userCandidateSearch.test.mjs +++ b/desktop/src/features/profile/lib/userCandidateSearch.test.mjs @@ -47,6 +47,13 @@ test("scoreUserCandidate ranks display labels before pubkeys", () => { ); }); +test("scoreUserCandidate matches a profile name that differs from the display name", () => { + const user = makeUser({ displayName: "S", name: "Starfish Sol" }); + + assert.equal(scoreUserCandidate({ label: "S", query: "starfish", user }), 0); + assert.equal(scoreUserCandidate({ label: "S", query: "sol", user }), 1); +}); + test("scoreUserCandidate supports agent labels and empty-query defaults", () => { const agent = makeUser({ isAgent: true }); diff --git a/desktop/src/features/profile/lib/userCandidateSearch.ts b/desktop/src/features/profile/lib/userCandidateSearch.ts index 570f293c65..45f6e6e9b0 100644 --- a/desktop/src/features/profile/lib/userCandidateSearch.ts +++ b/desktop/src/features/profile/lib/userCandidateSearch.ts @@ -37,6 +37,7 @@ export function scoreUserCandidate({ const labels = [ label, + user.name?.trim() ?? "", user.nip05Handle?.trim() ?? "", user.isAgent ? "agent" : "", ]; diff --git a/desktop/src/shared/api/tauriProfiles.ts b/desktop/src/shared/api/tauriProfiles.ts index c8e52f5169..c7b907a018 100644 --- a/desktop/src/shared/api/tauriProfiles.ts +++ b/desktop/src/shared/api/tauriProfiles.ts @@ -64,6 +64,7 @@ function fromRawUserSearchResult(user: RawUserSearchResult): UserSearchResult { return { pubkey: user.pubkey, displayName: user.display_name, + name: user.name ?? null, avatarUrl: user.avatar_url, nip05Handle: user.nip05_handle, ownerPubkey: user.owner_pubkey, diff --git a/desktop/src/shared/api/types.ts b/desktop/src/shared/api/types.ts index 3460494a44..cee0166f8e 100644 --- a/desktop/src/shared/api/types.ts +++ b/desktop/src/shared/api/types.ts @@ -126,6 +126,7 @@ export type UsersBatchResponse = { export type UserSearchResult = { pubkey: string; displayName: string | null; + name?: string | null; avatarUrl: string | null; nip05Handle: string | null; ownerPubkey: string | null;