SkateSpots/src/hooks/useMarketplace.ts
shakespeare.diy b3a49499bb Rename "The Quiver" to "Stash" for brand consistency
Use the established "Stash" term everywhere instead of introducing a new
word. The profile section is now titled "Stash", visibility copy reads
"Shown in your public Stash on your profile", and internal identifiers
were renamed (useUserQuiver → useUserPublicStash, QuiverItem →
PublicStashItem, parseQuiverItem → parsePublicStashItem). NIP.md updated
to match.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-02 19:41:45 +07:00

90 lines
3 KiB
TypeScript

import { useQuery } from '@tanstack/react-query';
import { MARKETPLACE_ITEM_KIND, parseMarketplaceItem, type MarketplaceItem } from '@/lib/types';
import { useSkateRelays } from '@/hooks/useSkateRelays';
import { useAdminModeration, filterAdminHidden } from '@/hooks/useAdminModeration';
const STALE_TIME = 5 * 60 * 1000; // 5 min
const GC_TIME = 30 * 60 * 1000; // 30 min
const TIMEOUT = 8000;
export function useMarketplaceItems(limit = 100) {
const { querySkateContent, getQueryKeySuffix } = useSkateRelays();
const hidden = useAdminModeration();
return useQuery({
queryKey: ['marketplace-items', limit, getQueryKeySuffix()],
select: (items: MarketplaceItem[]) => filterAdminHidden(items, MARKETPLACE_ITEM_KIND, hidden),
queryFn: async () => {
const signal = AbortSignal.timeout(TIMEOUT);
const events = await querySkateContent(
[
{
kinds: [MARKETPLACE_ITEM_KIND],
'#t': ['skateboarding'],
limit,
},
],
{ signal }
);
const items: MarketplaceItem[] = [];
for (const event of events) {
// Skip showcase-only Stash items (public gear that is NOT for sale):
// they belong on the profile's public Stash, not the sellable marketplace.
const isShowcaseOnly = event.tags.some((t) => t[0] === 'showcase' && t[1] === 'true');
if (isShowcaseOnly) continue;
const item = parseMarketplaceItem(event);
if (item) items.push(item);
}
return items.sort((a, b) => b.createdAt - a.createdAt);
},
staleTime: STALE_TIME,
gcTime: GC_TIME,
refetchOnMount: true,
});
}
export function useMarketplaceAtSpot(spotPubkey: string, spotIdentifier: string, limit = 20) {
const { querySkateContent, getQueryKeySuffix } = useSkateRelays();
const hidden = useAdminModeration();
return useQuery({
queryKey: ['marketplace-at-spot', spotPubkey, spotIdentifier, getQueryKeySuffix()],
select: (items: MarketplaceItem[]) => filterAdminHidden(items, MARKETPLACE_ITEM_KIND, hidden),
queryFn: async () => {
const signal = AbortSignal.timeout(TIMEOUT);
const spotRef = `30818:${spotPubkey}:${spotIdentifier}`;
const events = await querySkateContent(
[
{
kinds: [MARKETPLACE_ITEM_KIND],
'#a': [spotRef],
'#t': ['skateboarding'],
limit,
},
],
{ signal }
);
const items: MarketplaceItem[] = [];
for (const event of events) {
const isShowcaseOnly = event.tags.some((t) => t[0] === 'showcase' && t[1] === 'true');
if (isShowcaseOnly) continue;
const item = parseMarketplaceItem(event);
if (item && (!item.status || item.status === 'active')) {
items.push(item);
}
}
return items.sort((a, b) => b.createdAt - a.createdAt);
},
enabled: !!spotPubkey && !!spotIdentifier,
staleTime: STALE_TIME,
gcTime: GC_TIME,
refetchOnMount: true,
});
}