Fix crew detail page 404 with robust d-tag fallback lookup

useCrew previously relied on a single combined filter (kind + #d + authors,
limit 1). Some relays don't reliably match combined author + d-tag filters,
causing the crew detail page to render NotFound even when the crew exists.

Now, if the precise addressable lookup returns nothing and the author pubkey
is known, we fall back to fetching that author's crews (kind + authors) and
matching the identifier client-side. This survives relays with quirky tag
indexing while still preferring the exact match when available.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
This commit is contained in:
shakespeare.diy 2026-08-16 17:44:52 +07:00
parent ede0440244
commit b7e932d514

View file

@ -90,6 +90,14 @@ export function useCrew(identifier: string, pubkey?: string) {
queryFn: async () => { queryFn: async () => {
const signal = AbortSignal.timeout(10000); const signal = AbortSignal.timeout(10000);
const pickMostRecent = (events: { created_at: number }[]) =>
events.length
? events.reduce((latest, current) =>
current.created_at > latest.created_at ? current : latest
)
: undefined;
// 1) Precise addressable lookup: kind + d-tag (+ author when known).
const filter: { const filter: {
kinds: number[]; kinds: number[];
'#d': string[]; '#d': string[];
@ -102,14 +110,25 @@ export function useCrew(identifier: string, pubkey?: string) {
}; };
if (pubkey) filter.authors = [pubkey]; if (pubkey) filter.authors = [pubkey];
const events = await querySkateContent([filter], { signal }); let events = await querySkateContent([filter], { signal });
if (events.length === 0) return null;
const mostRecent = events.reduce((latest, current) => // 2) Fallback: some relays don't index the `d` tag reliably (or choke on
current.created_at > latest.created_at ? current : latest // combined author + tag filters). If nothing came back and we know the
); // author, fetch that author's crews and match the identifier locally.
if (events.length === 0 && pubkey) {
const byAuthor = await querySkateContent(
[{ kinds: [SKATE_CREW_KIND], authors: [pubkey], limit: 200 }],
{ signal }
);
events = byAuthor.filter((e) =>
e.tags.some(([name, value]) => name === 'd' && value === identifier)
);
}
return parseSkateCrew(mostRecent); const mostRecent = pickMostRecent(events);
if (!mostRecent) return null;
return parseSkateCrew(mostRecent as typeof events[number]);
}, },
enabled: !!identifier, enabled: !!identifier,
staleTime: 5 * 60 * 1000, staleTime: 5 * 60 * 1000,