feat: Implement local profile name search fallback

- Updated `js/searchView.js` to augment NIP-50 relay search with a local scan of cached profiles.
- `performProfileSearch` now iterates `nostrClient.activeMap` (locally cached videos) to find unique authors.
- It retrieves profile metadata using `app.getProfileCacheEntry(pubkey)`.
- It matches queries against `name`, `about`, `nip05`, and `lightningAddress`.
- Local results are merged with relay results, ensuring robust search functionality even if relay connections are unstable.
- Preserved existing NIP-50 relay search logic.
This commit is contained in:
google-labs-jules[bot] committed 2026-01-17 03:56:09 +00:00
1 parent be5c39f6e8
commit 52043a1872
1 file changed
+77 -35
+77 -35
View File
@@ -232,55 +232,97 @@ async function renderSearchVideos(videos, container, app) {
}
async function performProfileSearch(query, token) {
if (!nostrClient || !nostrClient.pool) return [];
if (!query) return [];
const filter = {
kinds: [0],
search: query,
limit: 20
};
const normalizedQuery = query.toLowerCase();
const uniqueProfiles = new Map();
// If query looks like NIP-05 (user@domain), we could try to resolve it directly?
// For now, reliance on NIP-50 `search` is safer for broad queries.
// 1. Local Search (Scan known authors in cache)
if (nostrClient?.activeMap instanceof Map) {
const app = getApp();
if (app && typeof app.getProfileCacheEntry === 'function') {
const seenPubkeys = new Set();
for (const video of nostrClient.activeMap.values()) {
if (!video || !video.pubkey || seenPubkeys.has(video.pubkey)) continue;
// We should also search our local cache?
// The requirement says "search nostr relays".
seenPubkeys.add(video.pubkey);
const entry = app.getProfileCacheEntry(video.pubkey);
if (entry?.profile) {
const name = entry.profile.name || entry.profile.display_name || "";
const about = entry.profile.about || "";
const nip05 = entry.profile.nip05 || "";
const lightning = entry.profile.lightningAddress || "";
const relays = nostrClient.relays || [];
if (relays.length === 0) return [];
if (name.toLowerCase().includes(normalizedQuery) ||
about.toLowerCase().includes(normalizedQuery) ||
nip05.toLowerCase().includes(normalizedQuery) ||
lightning.toLowerCase().includes(normalizedQuery)) {
let events = [];
try {
events = await nostrClient.pool.list(relays, [filter]);
} catch (e) {
devLogger.warn("Profile search error", e);
return [];
uniqueProfiles.set(video.pubkey, {
pubkey: video.pubkey,
metadata: entry.profile,
created_at: entry.event?.created_at || 0
});
}
} else if (video.authorName && video.authorName.toLowerCase().includes(normalizedQuery)) {
// Fallback to video metadata if profile cache missing
uniqueProfiles.set(video.pubkey, {
pubkey: video.pubkey,
metadata: {
name: video.authorName,
picture: video.authorPicture,
nip05: video.authorNip05
},
created_at: 0
});
}
}
}
}
if (currentSearchToken !== token) return [];
const profiles = events.map(event => {
try {
const metadata = JSON.parse(event.content);
return {
pubkey: event.pubkey,
metadata,
created_at: event.created_at
// 2. Relay Search (NIP-50)
if (nostrClient && nostrClient.pool) {
const relays = nostrClient.relays || [];
if (relays.length > 0) {
const filter = {
kinds: [0],
search: query,
limit: 20
};
} catch (e) {
return null;
}
}).filter(Boolean);
// Deduplicate by pubkey, keeping newest
const map = new Map();
for (const p of profiles) {
if (!map.has(p.pubkey) || map.get(p.pubkey).created_at < p.created_at) {
map.set(p.pubkey, p);
try {
const events = await nostrClient.pool.list(relays, [filter]);
if (currentSearchToken === token) {
for (const event of events) {
try {
const metadata = JSON.parse(event.content);
// Merge/Overwrite with newer
const existing = uniqueProfiles.get(event.pubkey);
if (!existing || existing.created_at < event.created_at) {
uniqueProfiles.set(event.pubkey, {
pubkey: event.pubkey,
metadata,
created_at: event.created_at
});
}
} catch (e) {
// ignore invalid content
}
}
}
} catch (e) {
devLogger.warn("Profile relay search error", e);
}
}
}
return Array.from(map.values());
if (currentSearchToken !== token) return [];
// Convert map to array and sort by something relevant (maybe creation time or exact match?)
return Array.from(uniqueProfiles.values());
}
async function performVideoSearch(query, token) {