Second pass of TRUST_SCORE.md. - useTrustAssertions now batches: every TrustBadge registers its subject into a shared per-viewer union and ONE query fetches the union (no per-card query), with a debounced refetch when a list grows. Same hook API. - A direct follow (or self) is no longer shown as Verified - only a friend-of-friend or a provider assertion counts; Flagged is never suppressed. - useUserReports reads public NIP-56 reports for an account, and TrustFlaggedNotice shows a flagged counterparty with the reporters grouped by the viewer's network plus Report / Dismiss. Wired into the listing seller card and the coach detail page. - New walletUi.trust.* keys (en/es).
101 lines
3.3 KiB
TypeScript
101 lines
3.3 KiB
TypeScript
import { Badge } from '@/components/ui/badge';
|
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
|
import { CheckCircle2, HelpCircle, Flag } from 'lucide-react';
|
|
import { useTrustBucket } from '@/hooks/useTrustBucket';
|
|
import { bucketDescKey, bucketLabelKey, type TrustBucket } from '@/lib/trustLadder';
|
|
import { cn } from '@/lib/utils';
|
|
import { useTranslation } from '@/contexts/language';
|
|
|
|
interface TrustBadgeProps {
|
|
pubkey: string | undefined;
|
|
className?: string;
|
|
showLabel?: boolean;
|
|
size?: 'sm' | 'md' | 'lg';
|
|
/**
|
|
* Also render the neutral "Unknown" bucket. Off by default so a card row is
|
|
* not flooded with a badge on every author; surfaces with room can turn it on.
|
|
*/
|
|
showUnknown?: boolean;
|
|
}
|
|
|
|
const BUCKET_STYLE: Record<
|
|
TrustBucket,
|
|
{ className: string; Icon: typeof CheckCircle2 }
|
|
> = {
|
|
verified: {
|
|
className: 'border-green-500/50 text-green-400 bg-green-500/10',
|
|
Icon: CheckCircle2,
|
|
},
|
|
unknown: {
|
|
className: 'border-gray-500/50 text-gray-400',
|
|
Icon: HelpCircle,
|
|
},
|
|
flagged: {
|
|
className: 'border-red-500/60 text-red-400 bg-red-500/10',
|
|
Icon: Flag,
|
|
},
|
|
};
|
|
|
|
/**
|
|
* TrustBadge shows one of three buckets for an author:
|
|
* verified (someone in your network vouches), unknown (nothing said yet), or
|
|
* flagged (trusted people reported them). Prefers a NIP-85 assertion, falls
|
|
* back to the local web-of-trust score. Flagged always shows.
|
|
*/
|
|
export function TrustBadge({
|
|
pubkey,
|
|
className,
|
|
showLabel = true,
|
|
size = 'sm',
|
|
showUnknown = false,
|
|
}: TrustBadgeProps) {
|
|
const { t } = useTranslation();
|
|
const { bucket, assertion, directFollow, isLoading } = useTrustBucket(pubkey);
|
|
|
|
if (isLoading) return null;
|
|
// A direct follow (or yourself) is already known to the viewer — a "Verified"
|
|
// mark there is redundant. Flagged is a safety signal and is never suppressed.
|
|
if (bucket === 'verified' && directFollow) return null;
|
|
if (bucket === 'unknown' && !showUnknown) return null;
|
|
|
|
const { className: bucketClass, Icon } = BUCKET_STYLE[bucket];
|
|
|
|
const sizeClasses = {
|
|
sm: 'text-[9px] px-1.5 py-0 h-4 gap-0.5',
|
|
md: 'text-xs px-2 py-0.5 h-5 gap-1',
|
|
lg: 'text-sm px-3 py-1 h-6 gap-1.5',
|
|
};
|
|
|
|
const iconSizes = {
|
|
sm: 'h-2.5 w-2.5',
|
|
md: 'h-3 w-3',
|
|
lg: 'h-4 w-4',
|
|
};
|
|
|
|
return (
|
|
<TooltipProvider>
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>
|
|
<Badge
|
|
variant="outline"
|
|
className={cn(sizeClasses[size], bucketClass, className, 'cursor-help select-none')}
|
|
>
|
|
<Icon className={iconSizes[size]} />
|
|
{showLabel && <span className="font-bold uppercase">{t(bucketLabelKey(bucket))}</span>}
|
|
</Badge>
|
|
</TooltipTrigger>
|
|
<TooltipContent side="bottom" className="max-w-xs">
|
|
<div className="space-y-1">
|
|
<p className="font-bold">{t(bucketLabelKey(bucket))}</p>
|
|
<p className="text-xs text-muted-foreground">{t(bucketDescKey(bucket))}</p>
|
|
{bucket === 'flagged' && assertion?.reporters !== undefined && assertion.reporters > 0 && (
|
|
<p className="text-xs text-muted-foreground">
|
|
{t('walletUi.trust.reportedByTrusted', { count: assertion.reporters })}
|
|
</p>
|
|
)}
|
|
</div>
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
</TooltipProvider>
|
|
);
|
|
}
|