Query featured organizations by author

This commit is contained in:
lemon
2026-05-20 10:53:43 -07:00
parent b9e82da61e
commit 5d7547f70b
+17 -41
View File
@@ -7,31 +7,18 @@ import {
parseCommunityEvent,
type ParsedCommunity,
} from '@/lib/communityUtils';
import { dedupeAddressableLatest } from '@/lib/addressableEvents';
/**
* Hand-curated list of featured organization event IDs.
* Hand-curated list of featured organization authors.
*
* These are kind 34550 (NIP-72 community definition) event IDs that the
* Agora team has selected to showcase on the Organize page until a
* data-driven featuring mechanism exists.
*
* NIP-72 communities are addressable events keyed by
* \`(pubkey, d-tag)\`, so the same community can have many revisions
* over time — pinning a specific event ID locks the featured card to
* the revision the curator approved. To promote a newer revision,
* update the ID below.
*
* Replace this constant with a configurable source (AppConfig field,
* NIP-51 list, etc.) when the featuring story matures.
* We query all organizations authored by these accounts in one relay request,
* then latest-wins dedupe addressable revisions client-side.
*/
export const FEATURED_ORGANIZATION_EVENT_IDS = [
'd29b870d2a02f2c098380f89d435748cf8d0b89f807f1e5e673cc58e3a1a7891',
'007e55ccbf16bd21bcf0efe920eb9cc397bbfa5b97046a6fb9e76d4fc68a5353',
'ee4cea2b458ff514fc1dc6b554da74520f4c551cfd0670768f6475de07f69ba3',
'b0ea85386358346a4549f0ad0039e9ae00e7eebe188f08556f185a1230376739',
'790b08be1c8d90ea4738d146ce2b749b42e528bba5dd8c5704804ff52e69b715',
'fa5159b095aa376cea652967889fd5340367ec99cec64a355cfe459a68a974fc',
'7ca2a21e9328711f7f0423cd1d9c6f3c7c23c4c7e85dd544a248f963d0d96728',
export const FEATURED_ORGANIZATION_AUTHORS = [
'932614571afcbad4d17a191ee281e39eebbb41b93fac8fd87829622aeb112f4d',
'be7358c4fe50148cccafc02ea205d80145e253889aa3958daafa8637047c840e',
'3f770d65d3a764a9c5cb503ae123e62ec7598ad035d836e2a810f3877a745b24',
] as const;
export interface FeaturedOrganization {
@@ -40,48 +27,37 @@ export interface FeaturedOrganization {
}
/**
* Fetch the hand-curated featured organizations by event ID.
* Fetch featured organizations by author.
*
* Results are returned in the same order as
* {@link FEATURED_ORGANIZATION_EVENT_IDS} so the curator controls the
* shelf ordering. Events that can't be parsed as a valid community
* definition are dropped without failing the whole shelf.
*
* Single-letter \`ids\` filter is indexed by relays, so this is one
* cheap round-trip regardless of list length.
* One author-filtered query is more reliable than pinning individual event IDs
* because kind 34550 definitions are addressable and can be revised.
*/
export function useFeaturedOrganizations() {
const { nostr } = useNostr();
return useQuery<FeaturedOrganization[]>({
queryKey: ['featured-organizations', FEATURED_ORGANIZATION_EVENT_IDS.join(',')],
queryKey: ['featured-organizations', FEATURED_ORGANIZATION_AUTHORS.join(',')],
queryFn: async ({ signal }) => {
const combinedSignal = AbortSignal.any([signal, AbortSignal.timeout(8000)]);
const events = await nostr.query(
[{
kinds: [COMMUNITY_DEFINITION_KIND],
ids: [...FEATURED_ORGANIZATION_EVENT_IDS],
limit: FEATURED_ORGANIZATION_EVENT_IDS.length,
authors: [...FEATURED_ORGANIZATION_AUTHORS],
limit: 60,
}],
{ signal: combinedSignal },
);
// Index by id so we can preserve the curator's ordering.
const byId = new Map<string, NostrEvent>();
for (const event of events) {
byId.set(event.id, event);
}
const entries: FeaturedOrganization[] = [];
for (const id of FEATURED_ORGANIZATION_EVENT_IDS) {
const event = byId.get(id);
if (!event) continue;
for (const event of dedupeAddressableLatest(events)) {
const community = parseCommunityEvent(event);
if (!community) continue;
entries.push({ community, event });
}
entries.sort((a, b) => b.event.created_at - a.event.created_at);
return entries;
},
// 5 minutes — featured list is hand-curated, doesn't churn.