SkateSpots/src/components/UserStatusIndicator.tsx
SkateSpots 0b796440f1 feat(i18n): Spanish batch 2 — nav/chrome, events/shops/tours, crews/DMs, settings/auth, wallet/stash/newsletter/stats
~3,470 new keys (5,770 total, EN=ES), ~115 more files wired.
- nav + chromeUi: header, More menu, burger menu, footer, map toolbar widgets
  (sort/trust/weather), spot-type label helper.
- placesUi: events, shops, tours, food. socialUi: crews, DMs, notifications,
  collectives, crew chat/coordinator. settingsUi: settings, auth/signup,
  onboarding, profile edit, legal. walletUi: wallet/Cashu, stash, mints,
  app shop, newsletter, community stats, leaderboards, profile.
- Fixed 738 usages where an agent dropped the socialUi. prefix (caught by check:i18n).
2026-10-02 10:11:06 +07:00

96 lines
2.8 KiB
TypeScript

import { useUserStatus, SKATING_STATUSES } from '@/hooks/useUserStatus';
import { useTranslation } from '@/contexts/language';
import { Badge } from '@/components/ui/badge';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
import { cn } from '@/lib/utils';
interface UserStatusIndicatorProps {
pubkey: string;
className?: string;
showLabel?: boolean;
size?: 'sm' | 'md' | 'lg';
}
export function UserStatusIndicator({
pubkey,
className,
showLabel = false,
size = 'sm',
}: UserStatusIndicatorProps) {
const { t } = useTranslation();
const { data: status, isLoading } = useUserStatus(pubkey);
if (isLoading || !status) return null;
const preset = status.statusId
? SKATING_STATUSES.find(s => s.id === status.statusId)
: null;
const emoji = status.emoji || preset?.emoji || '🛹';
const label = preset?.label || status.status;
const sizeClasses = {
sm: 'text-xs px-1.5 py-0',
md: 'text-sm px-2 py-0.5',
lg: 'text-base px-3 py-1',
};
const dotSizeClasses = {
sm: 'w-2 h-2',
md: 'w-2.5 h-2.5',
lg: 'w-3 h-3',
};
return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div className={cn('inline-flex items-center gap-1', className)}>
{/* Pulsing dot indicator */}
<span className="relative flex">
<span
className={cn(
'animate-ping absolute inline-flex rounded-full bg-green-400 opacity-75',
dotSizeClasses[size]
)}
/>
<span
className={cn(
'relative inline-flex rounded-full bg-green-500',
dotSizeClasses[size]
)}
/>
</span>
{showLabel && (
<Badge
variant="outline"
className={cn(
'border-green-500/50 text-green-400 font-medium',
sizeClasses[size]
)}
>
{emoji} {label}
</Badge>
)}
</div>
</TooltipTrigger>
<TooltipContent side="top" className="max-w-[200px]">
<div className="text-center">
<div className="text-lg mb-1">{emoji}</div>
<p className="font-bold">{label}</p>
{status.linkedSpot && (
<p className="text-xs text-muted-foreground mt-1">
{t('socialUi.userStatus.atSpot')}
</p>
)}
{status.expiration && (
<p className="text-xs text-muted-foreground mt-1">
{t('socialUi.userStatus.autoClears', { min: Math.round((status.expiration - Date.now() / 1000) / 60) })}
</p>
)}
</div>
</TooltipContent>
</Tooltip>
</TooltipProvider>
);
}