'use client'; import React, { useState, useEffect, useMemo, useRef } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { getAvatarInitials, getAvatarColor } from '../../utils/inboxUtils'; import { getAvatarFromCache, setAvatarInCache } from '../../hooks/useAvatarCache'; import { avatarApi } from '../../services/chatApiService'; interface AvatarProps { chat: any; size?: number; className?: string; } export default function Avatar({ chat, size = 46, className = '' }: AvatarProps) { const [displayUrl, setDisplayUrl] = useState(null); const [isLoaded, setIsLoaded] = useState(false); const [showInitials, setShowInitials] = useState(false); const cacheAttempted = useRef(false); const refreshTried = useRef(false); const imageUrl = chat?.avatar_url || null; const version = chat?.avatar_version || null; const remoteJid = chat?.remote_jid || ''; const instanceName = chat?.instance_name || ''; // Quando jid ou version muda, tenta servir do cache IndexedDB antes de fazer request HTTP. // Se não houver cache (primeira vez), usa a URL do proxy normalmente. useEffect(() => { cacheAttempted.current = false; refreshTried.current = false; setIsLoaded(false); if (!imageUrl) { setShowInitials(true); setDisplayUrl(null); return; } setShowInitials(false); // Sem versão (ex.: satélite, cujo ext /inbox não envia avatar_version) → // usa a URL do CDN direto, sem cache IndexedDB (o cache depende da versão // para invalidar). A imagem ainda carrega normalmente. if (!version) { setDisplayUrl(imageUrl); cacheAttempted.current = true; return; } // Tenta cache IndexedDB — resultado síncrono do ponto de vista do usuário getAvatarFromCache(remoteJid, version).then((cached) => { if (cached) { setDisplayUrl(cached); setIsLoaded(true); // já temos os bytes — sem skeleton } else { setDisplayUrl(imageUrl); // busca pelo proxy normalmente } cacheAttempted.current = true; }).catch(() => { setDisplayUrl(imageUrl); cacheAttempted.current = true; }); }, [remoteJid, version, imageUrl]); const handleLoad = (e: React.SyntheticEvent) => { setIsLoaded(true); // Salva no IndexedDB após primeira carga bem-sucedida do proxy. // Só salva se a versão é conhecida e o displayUrl não é já um dataUrl (evita re-cache). if (!version || !remoteJid || !displayUrl || displayUrl.startsWith('data:')) return; try { const img = e.currentTarget; const canvas = document.createElement('canvas'); canvas.width = img.naturalWidth || size; canvas.height = img.naturalHeight || size; const ctx = canvas.getContext('2d'); if (!ctx) return; ctx.drawImage(img, 0, 0); const dataUrl = canvas.toDataURL('image/jpeg', 0.85); setAvatarInCache(remoteJid, version, dataUrl); } catch { /* CORS ou canvas tainted — ignora, cache não é crítico */ } }; const handleError = async () => { // Self-heal: a URL do CDN do WhatsApp pode ter expirado (404). Tenta UMA // vez re-buscar a foto atual pela conexão viva do motor antes de desistir. if (!refreshTried.current && instanceName && remoteJid) { refreshTried.current = true; const fresh = await avatarApi.refresh(instanceName, remoteJid); if (fresh && fresh !== displayUrl) { setIsLoaded(false); setDisplayUrl(fresh); return; } } setShowInitials(true); setDisplayUrl(null); }; const initials = useMemo(() => getAvatarInitials(chat), [chat]); const bgColor = useMemo( () => getAvatarColor(chat?.remote_jid || chat?.phone || chat?.displayName || 'anon'), [chat] ); return (
{!showInitials && displayUrl ? ( ) : ( {initials} )} {/* Skeleton apenas quando buscando pelo proxy (não quando servindo do cache) */} {!isLoaded && !showInitials && displayUrl && !displayUrl.startsWith('data:') && (
)}
); }