fix(reputation): skeleton loading + no empty-card flash

The marketplace Reputation tab showed a bare 'Loading...' text under
'Reviews & trade records', and TradeRecommendationsCard rendered its
'No recommendations yet.' card while still loading (then it vanished for a
self/empty view) — together they read as broken.

- Reviews/trade-records now render row-shaped skeletons while loading.
- TradeRecommendationsCard renders nothing while its query is loading when the
  count is 0 (and only shows for a non-self viewer who can add one).
This commit is contained in:
SkateSpots 2026-10-03 10:34:25 +07:00
parent 52eff36bce
commit 86c9f6f856
2 changed files with 24 additions and 3 deletions

View file

@ -48,8 +48,10 @@ export function TradeRecommendationsCard({ pubkey, className }: { pubkey: string
const isSelf = user?.pubkey === pubkey;
const count = recs?.length ?? 0;
// Nothing to show: no recommendations and the viewer can't add one.
if (!isLoading && count === 0 && (!user || isSelf)) return null;
// Don't flash an empty "No recommendations yet." card while the query is still
// loading (it then vanishes for a self/empty view) — render nothing until we
// know, and only show the card when there's something (or the viewer can add).
if (count === 0 && (isLoading || !user || isSelf)) return null;
const handleRecommend = async () => {
setBusy(true);

View file

@ -3,6 +3,7 @@ import { Link } from 'react-router-dom';
import type { NostrEvent } from '@nostrify/nostrify';
import { Star, ThumbsUp, ArrowUpRight } from 'lucide-react';
import { Card, CardContent } from '@/components/ui/card';
import { Skeleton } from '@/components/ui/skeleton';
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { TradeReputationCard } from '@/components/TradeReputationCard';
import { TradeRecommendationsCard } from '@/components/TradeRecommendationsCard';
@ -72,6 +73,20 @@ function ReviewRow({ ev, selfPubkey }: { ev: NostrEvent; selfPubkey: string }) {
);
}
/** Loading placeholder shaped like a review row. */
function ReviewSkeleton() {
return (
<div className="flex items-start gap-3 rounded-xl border border-white/8 bg-white/[0.03] p-3">
<Skeleton className="h-9 w-9 shrink-0 rounded-full" />
<div className="min-w-0 flex-1 space-y-2">
<Skeleton className="h-3.5 w-40" />
<Skeleton className="h-3 w-full" />
<Skeleton className="h-3 w-24" />
</div>
</div>
);
}
/**
* Marketplace → Reputation tab: this trader's reputation, peer recommendations,
* and their trade records / reviews (from kind-31871 attestations).
@ -112,7 +127,11 @@ export function TradeReputationPanel({ pubkey }: { pubkey: string }) {
</h3>
{isLoading ? (
<p className="text-sm text-gray-500">{t('common.loading')}</p>
<div className="space-y-2">
<ReviewSkeleton />
<ReviewSkeleton />
<ReviewSkeleton />
</div>
) : attestations.length === 0 ? (
<Card className="border-dashed border-white/10">
<CardContent className="py-10 text-center">