// LinkCard — preview card for a pasted Avito URL. // Replaces the plain text list (AddedLinksList) used previously in OrderForm. // // Props: // url — the Avito URL (canonical, already trimmed by parseAvitoUrls) // meta — { status: 'loading'|'ok'|'not_found'|'fetch_failed', image_url?, title? } // onRemove — callback when user clicks "×" // // States rendered: // loading → neutral-gray thumb + CSS spinner, skeleton bar for title // ok → from image_url, title shown // not_found / fetch_failed → green "A" placeholder, fallback title = url path function LinkCard({ url, meta, onRemove }) { const status = (meta && meta.status) || 'loading'; const isLoading = status === 'loading'; const hasImage = status === 'ok' && meta && meta.image_url; // Show green "A" tile only when we know there's no preview to load. // (Without this gate the green flashes between loading and the // actually rendering its pixels.) const showFallback = !isLoading && !hasImage; const titleText = (meta && meta.title) || _urlShortPath(url); return (
window.open(url, '_blank', 'noopener,noreferrer')} style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '8px 10px', border: '1px solid var(--border)', borderRadius: 'var(--radius-sm, 8px)', marginBottom: 8, background: 'var(--surface)', cursor: 'pointer', minWidth: 0, }} >
is still pulling pixels. background: showFallback ? 'linear-gradient(135deg, #00aa00 0%, #007f00 100%)' : 'var(--surface-2, #ececec)', display: 'flex', alignItems: 'center', justifyContent: 'center', }}> {isLoading && (
)} {hasImage && ( { e.currentTarget.style.display = 'none'; }} style={{ width: '100%', height: '100%', objectFit: 'cover' }} /> )} {showFallback && ( A )}
{isLoading ? ( // Skeleton bar — clearly signals "loading" alongside the spinner.
) : (
{titleText}
)}
{_urlShortPath(url)}
); } function _urlShortPath(url) { try { const u = new URL(url); return u.pathname.length > 50 ? u.pathname.slice(0, 50) + '…' : u.pathname; } catch (_) { return url; } } // Inject spinner + skeleton-pulse keyframes once. (function _injectLinkCardStyles() { if (document.getElementById('linkcard-anim-style')) return; const s = document.createElement('style'); s.id = 'linkcard-anim-style'; s.textContent = [ '@keyframes linkcard-spin { to { transform: rotate(360deg); } }', '@keyframes linkcard-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.5; } }', ].join(' '); document.head.appendChild(s); })(); Object.assign(window, { LinkCard });