import Content from '@/components/Content' import UserAvatar from '@/components/UserAvatar' import Username from '@/components/Username' import ProfileBadgeDetailDialog from './ProfileBadgeDetailDialog' import { replaceableEventDedupeKey } from '@/lib/event' import { formatAmount } from '@/lib/lightning' import { cn } from '@/lib/utils' import { toNote, toProfile } from '@/lib/link' import { useSecondaryPage } from '@/PageManager' import Emoji from '@/components/Emoji' import { getEmojiInfosFromEmojiTags } from '@/lib/tag' import type { TProfileZap } from '@/hooks/useProfileInteractions' import type { TProfileBadge } from '@/hooks/useProfileBadges' import type { TProfileFollowPack } from '@/hooks/useProfileFollowPacks' import { Flag, Zap, MessageCircle, ThumbsDown, ThumbsUp, Users } from 'lucide-react' import { Skeleton } from '@/components/ui/skeleton' import { useTranslation } from 'react-i18next' import { useState } from 'react' import { Event } from 'nostr-tools' type Props = { profilePubkey: string badgeRelayUrls: string[] zaps: TProfileZap[] reactions: Event[] comments: Event[] badges: TProfileBadge[] followPacks: TProfileFollowPack[] reports: Event[] loading: boolean badgesLoading: boolean followPacksLoading: boolean reportsLoading: boolean /** When false (logged out), the Reports section is omitted — reports use the viewer’s relays only. */ reportsEnabled: boolean } const ZAPS_PER_ROW = 4 const ZAP_ROWS = 3 const MAX_ZAPS = ZAPS_PER_ROW * ZAP_ROWS const LIKES_GRID_COLS = 4 const LIKES_GRID_ROWS = 3 const MAX_LIKES = LIKES_GRID_COLS * LIKES_GRID_ROWS const BADGES_PER_ROW = 6 const BADGE_ROWS = 2 const MAX_BADGES = BADGES_PER_ROW * BADGE_ROWS const BADGE_TILE_PX = 96 const MAX_FOLLOW_PACKS = 8 const MAX_REPORTS = 12 function reportSummaryFromEvent(event: Event): string { const reportTag = event.tags.find((t) => t[0] === 'report') const reason = reportTag?.[1]?.trim() if (reason) return reason const text = event.content.trim().replace(/\s+/g, ' ') if (text) return text.length > 48 ? `${text.slice(0, 45)}…` : text return '—' } function ZapBadge({ zap }: { zap: TProfileZap }) { const { push } = useSecondaryPage() return ( ) } function ReactionBadge({ event }: { event: Event }) { const { push } = useSecondaryPage() const emojiInfos = getEmojiInfosFromEmojiTags(event.tags) const displayContent = event.content.trim() || (emojiInfos[0] ? emojiInfos[0].shortcode : '+') const isPlus = displayContent === '+' const isMinus = displayContent === '-' return ( ) } function CommentBadge({ event }: { event: Event }) { const { push } = useSecondaryPage() return ( ) } function ReportBadge({ event }: { event: Event }) { const { push } = useSecondaryPage() const summary = reportSummaryFromEvent(event) return ( ) } function FollowPackBadge({ pack }: { pack: TProfileFollowPack }) { const { t } = useTranslation() const { push } = useSecondaryPage() const authorPk = pack.event.pubkey return ( ) } function BadgeItem({ badge, onOpenDetail }: { badge: TProfileBadge onOpenDetail: (b: TProfileBadge) => void }) { const { t } = useTranslation() const imageUrl = badge.thumb ?? badge.image const label = badge.name ?? badge.a.split(':').pop() ?? '' return ( ) } export default function ProfileHeaderInteractions({ profilePubkey, badgeRelayUrls, zaps, reactions, comments, badges, followPacks, reports, loading, badgesLoading, followPacksLoading, reportsLoading, reportsEnabled }: Props) { const { t } = useTranslation() const [badgeDialogOpen, setBadgeDialogOpen] = useState(false) const [selectedBadge, setSelectedBadge] = useState(null) const displayZaps = zaps.slice(0, MAX_ZAPS) const displayReactions = reactions.slice(0, MAX_LIKES) const displayBadges = badges.slice(0, MAX_BADGES) const displayFollowPacks = followPacks.slice(0, MAX_FOLLOW_PACKS) const displayReports = reports.slice(0, MAX_REPORTS) const Section = ({ title, isEmpty, isLoading, children, skeletonCount = 6, skeletonItemClassName, skeletonGridClassName }: { title: string isEmpty: boolean isLoading: boolean children: React.ReactNode skeletonCount?: number skeletonItemClassName?: string skeletonGridClassName?: string }) => (
{title}
{isLoading && isEmpty ? (
{Array.from({ length: skeletonCount }).map((_, i) => ( ))}
) : isEmpty ? (
{t('None')}
) : ( children )}
) return (
{displayZaps.map((item) => ( ))}
{displayReactions.map((item) => ( ))}
{comments.map((item) => ( ))}
{displayBadges.map((badge, index) => ( { setSelectedBadge(b) setBadgeDialogOpen(true) }} /> ))}
{ setBadgeDialogOpen(o) if (!o) setSelectedBadge(null) }} badge={selectedBadge} profilePubkey={profilePubkey} relayUrls={badgeRelayUrls} />
{displayFollowPacks.map((pack) => ( ))}
{reportsEnabled ? (
{displayReports.map((item) => ( ))}
) : null}
) }