import React, { useEffect, useMemo, useState } from 'react' import LevelBadge from '../xp/LevelBadge' const IMAGE_FALLBACK = 'https://files.skinbase.org/default/missing_md.webp' const AVATAR_FALLBACK = 'https://files.skinbase.org/default/avatar_default.webp' const numberFormatter = new Intl.NumberFormat(undefined, { notation: 'compact', maximumFractionDigits: 1, }) function cx(...parts) { return parts.filter(Boolean).join(' ') } function formatCount(value) { const numeric = Number(value ?? 0) if (!Number.isFinite(numeric)) return '0' return numberFormatter.format(numeric) } function formatRelativeTime(value) { if (!value) return '' const date = value instanceof Date ? value : new Date(value) if (Number.isNaN(date.getTime())) return '' const now = new Date() const diffMs = date.getTime() - now.getTime() const diffSeconds = Math.round(diffMs / 1000) const absSeconds = Math.abs(diffSeconds) const rtf = new Intl.RelativeTimeFormat(undefined, { numeric: 'auto' }) if (absSeconds < 60) return rtf.format(diffSeconds, 'second') const diffMinutes = Math.round(diffSeconds / 60) if (Math.abs(diffMinutes) < 60) return rtf.format(diffMinutes, 'minute') const diffHours = Math.round(diffSeconds / 3600) if (Math.abs(diffHours) < 24) return rtf.format(diffHours, 'hour') const diffDays = Math.round(diffSeconds / 86400) if (Math.abs(diffDays) < 7) return rtf.format(diffDays, 'day') const diffWeeks = Math.round(diffSeconds / 604800) if (Math.abs(diffWeeks) < 5) return rtf.format(diffWeeks, 'week') const diffMonths = Math.round(diffSeconds / 2629800) if (Math.abs(diffMonths) < 12) return rtf.format(diffMonths, 'month') const diffYears = Math.round(diffSeconds / 31557600) return rtf.format(diffYears, 'year') } function slugify(value) { return String(value ?? '') .toLowerCase() .replace(/[^a-z0-9]+/g, '-') .replace(/^-|-$/g, '') } function decodeHtml(value) { const text = String(value ?? '') if (!text.includes('&')) return text let decoded = text for (let index = 0; index < 3; index += 1) { decoded = decoded .replace(/&/gi, '&') .replace(/&(apos|#39);/gi, "'") .replace(/&(acute|#180|#x00B4);/gi, "'") .replace(/&(quot|#34);/gi, '"') .replace(/&(nbsp|#160);/gi, ' ') if (typeof document === 'undefined') { break } const textarea = document.createElement('textarea') textarea.innerHTML = decoded const nextValue = textarea.value if (nextValue === decoded) break decoded = nextValue } return decoded } function normalizeContentTypeLabel(value) { const raw = decodeHtml(value).trim() if (!raw) return '' const normalized = raw.toLowerCase() const knownLabels = { artworks: 'Artwork', artwork: 'Artwork', wallpapers: 'Wallpaper', wallpaper: 'Wallpaper', skins: 'Skin', skin: 'Skin', photography: 'Photography', photo: 'Photography', photos: 'Photography', other: 'Other', } if (knownLabels[normalized]) { return knownLabels[normalized] } return raw .replace(/[-_]+/g, ' ') .replace(/\b\w/g, (char) => char.toUpperCase()) } function getCsrfToken() { if (typeof document === 'undefined') return '' return document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '' } function HeartIcon(props) { return ( ) } function DownloadIcon(props) { return ( ) } function ViewIcon(props) { return ( ) } function ActionLink({ href, label, children, onClick }) { return ( {children} ) } function ActionButton({ label, children, onClick }) { return ( ) } function BadgePill({ className = '', iconClass = '', children }) { return ( {iconClass ?