|
|
|
|
@ -345,8 +345,137 @@
@@ -345,8 +345,137 @@
|
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Search repos from cache when there's a search query |
|
|
|
|
let searchRepos = $state<NostrEvent[]>([]); |
|
|
|
|
let searchingCache = $state(false); |
|
|
|
|
|
|
|
|
|
// When search query changes, search the cache directly |
|
|
|
|
$effect(() => { |
|
|
|
|
const query = searchQuery.trim(); |
|
|
|
|
if (!query) { |
|
|
|
|
searchRepos = []; |
|
|
|
|
searchingCache = false; |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Search cache directly for matching repos |
|
|
|
|
searchingCache = true; |
|
|
|
|
searchCacheForRepos(query).then(results => { |
|
|
|
|
searchRepos = results; |
|
|
|
|
searchingCache = false; |
|
|
|
|
}); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
async function searchCacheForRepos(query: string): Promise<NostrEvent[]> { |
|
|
|
|
try { |
|
|
|
|
// Get all cached repos (no limit for search) |
|
|
|
|
const cachedRepos = await getRecentCachedEvents([KIND.REPO_ANNOUNCEMENT], 365 * 24 * 60 * 60 * 1000, 10000); |
|
|
|
|
|
|
|
|
|
// For parameterized replaceable events, get the newest version of each (by pubkey + d tag) |
|
|
|
|
const reposByKey = new Map<string, NostrEvent>(); |
|
|
|
|
for (const event of cachedRepos) { |
|
|
|
|
const dTag = event.tags.find(t => t[0] === 'd')?.[1] || ''; |
|
|
|
|
const key = `${event.pubkey}:${dTag}`; |
|
|
|
|
const existing = reposByKey.get(key); |
|
|
|
|
if (!existing || event.created_at > existing.created_at) { |
|
|
|
|
reposByKey.set(key, event); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let filtered = Array.from(reposByKey.values()); |
|
|
|
|
|
|
|
|
|
// Filter by "See my repos" checkbox |
|
|
|
|
if (showMyRepos && currentPubkey) { |
|
|
|
|
filtered = filtered.filter(repo => { |
|
|
|
|
// Check if repo owner matches |
|
|
|
|
if (repo.pubkey.toLowerCase() === currentPubkey.toLowerCase()) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
// Check if user is a maintainer |
|
|
|
|
const maintainers = getMaintainers(repo); |
|
|
|
|
return maintainers.some(m => m.toLowerCase() === currentPubkey.toLowerCase()); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const queryLower = query.toLowerCase(); |
|
|
|
|
|
|
|
|
|
// Try to decode as pubkey (hex, npub, or nprofile) |
|
|
|
|
const decodedPubkey = decodePubkeyToHex(queryLower); |
|
|
|
|
if (decodedPubkey) { |
|
|
|
|
return filtered.filter(repo => { |
|
|
|
|
// Match by owner pubkey |
|
|
|
|
if (repo.pubkey.toLowerCase() === decodedPubkey) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
// Match by maintainer pubkey |
|
|
|
|
const maintainers = getMaintainers(repo); |
|
|
|
|
return maintainers.some(m => m.toLowerCase() === decodedPubkey); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Try to decode as event ID (hex, note, nevent, or naddr) |
|
|
|
|
const decodedEventId = decodeEventIdToHex(queryLower); |
|
|
|
|
if (decodedEventId) { |
|
|
|
|
return filtered.filter(repo => { |
|
|
|
|
// Match by event ID |
|
|
|
|
if (repo.id.toLowerCase() === decodedEventId.toLowerCase()) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
// Match by naddr |
|
|
|
|
const naddr = getNaddr(repo); |
|
|
|
|
if (naddr && naddr.toLowerCase() === decodedEventId.toLowerCase()) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Text search across all fields |
|
|
|
|
return filtered.filter(repo => { |
|
|
|
|
// Search in name |
|
|
|
|
const name = getRepoName(repo).toLowerCase(); |
|
|
|
|
if (name.includes(queryLower)) return true; |
|
|
|
|
|
|
|
|
|
// Search in description |
|
|
|
|
const desc = getRepoDescription(repo).toLowerCase(); |
|
|
|
|
if (desc.includes(queryLower)) return true; |
|
|
|
|
|
|
|
|
|
// Search in clone URLs |
|
|
|
|
const cloneUrls = getCloneUrls(repo); |
|
|
|
|
if (cloneUrls.some(url => url.toLowerCase().includes(queryLower))) return true; |
|
|
|
|
|
|
|
|
|
// Search in web URLs |
|
|
|
|
const webUrls = getWebUrls(repo); |
|
|
|
|
if (webUrls.some(url => url.toLowerCase().includes(queryLower))) return true; |
|
|
|
|
|
|
|
|
|
// Search in d-tag |
|
|
|
|
const dTag = getDTagFromEvent(repo).toLowerCase(); |
|
|
|
|
if (dTag.includes(queryLower)) return true; |
|
|
|
|
|
|
|
|
|
// Search in naddr |
|
|
|
|
const naddr = getNaddr(repo); |
|
|
|
|
if (naddr && naddr.toLowerCase().includes(queryLower)) return true; |
|
|
|
|
|
|
|
|
|
// Search in maintainer pubkeys (as hex) |
|
|
|
|
const maintainers = getMaintainers(repo); |
|
|
|
|
if (maintainers.some(m => m.toLowerCase().includes(queryLower))) return true; |
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
|
}); |
|
|
|
|
} catch (error) { |
|
|
|
|
console.error('Error searching cache for repos:', error); |
|
|
|
|
return []; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Filter repos based on search query and filters |
|
|
|
|
let filteredRepos = $derived.by(() => { |
|
|
|
|
// If there's a search query, use search results from cache |
|
|
|
|
if (searchQuery.trim()) { |
|
|
|
|
return searchRepos; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Otherwise, filter the loaded repos |
|
|
|
|
let filtered = repos; |
|
|
|
|
|
|
|
|
|
// Filter by "See my repos" checkbox |
|
|
|
|
@ -362,76 +491,7 @@
@@ -362,76 +491,7 @@
|
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// If no search query, return filtered list |
|
|
|
|
if (!searchQuery.trim()) { |
|
|
|
|
return filtered; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const query = searchQuery.trim().toLowerCase(); |
|
|
|
|
|
|
|
|
|
// Try to decode as pubkey (hex, npub, or nprofile) |
|
|
|
|
const decodedPubkey = decodePubkeyToHex(query); |
|
|
|
|
if (decodedPubkey) { |
|
|
|
|
return filtered.filter(repo => { |
|
|
|
|
// Match by owner pubkey |
|
|
|
|
if (repo.pubkey.toLowerCase() === decodedPubkey) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
// Match by maintainer pubkey |
|
|
|
|
const maintainers = getMaintainers(repo); |
|
|
|
|
return maintainers.some(m => m.toLowerCase() === decodedPubkey); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Try to decode as event ID (hex, note, nevent, or naddr) |
|
|
|
|
const decodedEventId = decodeEventIdToHex(query); |
|
|
|
|
if (decodedEventId) { |
|
|
|
|
return filtered.filter(repo => { |
|
|
|
|
// Match by event ID |
|
|
|
|
if (repo.id.toLowerCase() === decodedEventId.toLowerCase()) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
// Match by naddr |
|
|
|
|
const naddr = getNaddr(repo); |
|
|
|
|
if (naddr && naddr.toLowerCase() === decodedEventId.toLowerCase()) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Text search across all fields |
|
|
|
|
return filtered.filter(repo => { |
|
|
|
|
// Search in name |
|
|
|
|
const name = getRepoName(repo).toLowerCase(); |
|
|
|
|
if (name.includes(query)) return true; |
|
|
|
|
|
|
|
|
|
// Search in description |
|
|
|
|
const desc = getRepoDescription(repo).toLowerCase(); |
|
|
|
|
if (desc.includes(query)) return true; |
|
|
|
|
|
|
|
|
|
// Search in clone URLs |
|
|
|
|
const cloneUrls = getCloneUrls(repo); |
|
|
|
|
if (cloneUrls.some(url => url.toLowerCase().includes(query))) return true; |
|
|
|
|
|
|
|
|
|
// Search in web URLs |
|
|
|
|
const webUrls = getWebUrls(repo); |
|
|
|
|
if (webUrls.some(url => url.toLowerCase().includes(query))) return true; |
|
|
|
|
|
|
|
|
|
// Search in d-tag |
|
|
|
|
const dTag = getDTagFromEvent(repo).toLowerCase(); |
|
|
|
|
if (dTag.includes(query)) return true; |
|
|
|
|
|
|
|
|
|
// Search in naddr |
|
|
|
|
const naddr = getNaddr(repo); |
|
|
|
|
if (naddr && naddr.toLowerCase().includes(query)) return true; |
|
|
|
|
|
|
|
|
|
// Search in maintainer pubkeys (as hex) |
|
|
|
|
const maintainers = getMaintainers(repo); |
|
|
|
|
if (maintainers.some(m => m.toLowerCase().includes(query))) return true; |
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
|
}); |
|
|
|
|
return filtered; |
|
|
|
|
}); |
|
|
|
|
</script> |
|
|
|
|
|
|
|
|
|
|