fix(trust): H15 — relay outage must not read as 'not found'/'no events'
- useMarketplaceItem / useSkateEvents now pass requireAny, so a total outage throws (isError) instead of silently returning []. - ListingDetailPage and EventsPage render the shared ErrorState with a retry instead of a false 'Listing not found' / 'No upcoming events'.
This commit is contained in:
parent
9189fc6584
commit
64ba187ad4
4 changed files with 19 additions and 5 deletions
|
|
@ -106,7 +106,7 @@ export function useMarketplaceItem(pubkey?: string, identifier?: string) {
|
|||
const signal = AbortSignal.timeout(TIMEOUT);
|
||||
const events = await querySkateContent(
|
||||
[{ kinds: [MARKETPLACE_ITEM_KIND], authors: [pubkey], '#d': [identifier], limit: 5 }],
|
||||
{ signal },
|
||||
{ signal, requireAny: true },
|
||||
);
|
||||
const newest = [...events].sort((a, b) => b.created_at - a.created_at)[0];
|
||||
return newest ? parseMarketplaceItem(newest) : null;
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export function useSkateEvents(limit = 50, options: { includeHidden?: boolean }
|
|||
limit,
|
||||
},
|
||||
],
|
||||
{ signal }
|
||||
{ signal, requireAny: true }
|
||||
);
|
||||
|
||||
const skateEvents: SkateEvent[] = [];
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ import {
|
|||
} from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { EmptyState as SharedEmptyState, type EmptyStateAction } from '@/components/EmptyState';
|
||||
import { ErrorState } from '@/components/ErrorState';
|
||||
|
||||
// ─── Accent colours ───────────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -822,8 +823,8 @@ export default function EventsPage() {
|
|||
const followCount = followingList?.length ?? 0;
|
||||
const smartDefault: TrustFilterLevel = (user && followCount >= 5) ? 'trusted' : 'all';
|
||||
|
||||
const { data: events, isLoading: eventsLoading } = useSkateEvents(100);
|
||||
const { data: tours, isLoading: toursLoading } = useTours(100);
|
||||
const { data: events, isLoading: eventsLoading, isError: eventsError, refetch: refetchEvents } = useSkateEvents(100);
|
||||
const { data: tours, isLoading: toursLoading, isError: toursError, refetch: refetchTours } = useTours(100);
|
||||
|
||||
// ── View state ──────────────────────────────────────────────────────────────
|
||||
const [mainView, setMainView] = useState<MainView>('events');
|
||||
|
|
@ -1228,6 +1229,8 @@ export default function EventsPage() {
|
|||
{mainView === 'events' && layout === 'list' && (
|
||||
eventsLoading ? (
|
||||
<SkeletonGrid />
|
||||
) : eventsError ? (
|
||||
<ErrorState onRetry={() => { void refetchEvents(); }} />
|
||||
) : activeTab === 'upcoming' ? (
|
||||
filteredEvents.upcoming.length > 0 ? (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 stagger-children">
|
||||
|
|
@ -1280,6 +1283,8 @@ export default function EventsPage() {
|
|||
{mainView === 'tours' && layout === 'list' && (
|
||||
toursLoading ? (
|
||||
<TourSkeletonGrid />
|
||||
) : toursError ? (
|
||||
<ErrorState onRetry={() => { void refetchTours(); }} />
|
||||
) : filteredTours.length > 0 ? (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 stagger-children">
|
||||
{filteredTours.map((tour) => (
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import { useCurrentUser } from '@/hooks/useCurrentUser';
|
|||
import { useAuthor } from '@/hooks/useAuthor';
|
||||
import { useSellerInfo } from '@/hooks/useSellerInfo';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { ErrorState } from '@/components/ErrorState';
|
||||
import { formatPrice, GEAR_CONDITIONS, GEAR_CATEGORIES } from '@/lib/types';
|
||||
import { GEAR_CATEGORY_KEYS, GEAR_CONDITION_KEYS } from '@/lib/labelKeys';
|
||||
import { genUserName } from '@/lib/genUserName';
|
||||
|
|
@ -54,7 +55,7 @@ export default function ListingDetailPage() {
|
|||
const { user } = useCurrentUser();
|
||||
const { toast } = useToast();
|
||||
|
||||
const { data: item, isLoading } = useMarketplaceItem(pubkey, identifier);
|
||||
const { data: item, isLoading, isError, refetch } = useMarketplaceItem(pubkey, identifier);
|
||||
const author = useAuthor(pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const { data: sellerInfo } = useSellerInfo(pubkey);
|
||||
|
|
@ -83,6 +84,14 @@ export default function ListingDetailPage() {
|
|||
);
|
||||
}
|
||||
|
||||
if (isError) {
|
||||
return (
|
||||
<div className="container mx-auto max-w-md px-4 py-16">
|
||||
<ErrorState onRetry={() => { void refetch(); }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!item) {
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
Loading…
Reference in a new issue