'use client'; import React, { useState, useEffect, useCallback, useRef } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { Heart, Clock, Loader2 } from 'lucide-react'; import { stickerApi, type StickerRecord } from '../services/chatApiService'; import { getStickerFromCache, setStickerInCache } from '../hooks/useStickerCache'; const API = process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:3003'; const LIST_VERSION_KEY = 'nw:sticker-list-version'; interface StickerPanelProps { instanceId: string; jid: string; onSend: (blob: Blob) => void; } // Converte URL Wasabi → URL do proxy do backend function stickerSrcUrl(wasabiPath: string): string { const clean = wasabiPath.startsWith('/') ? wasabiPath.slice(1) : wasabiPath; return `${API}/api/storage/view/${clean}`; } // Carrega sticker: tenta IndexedDB, depois proxy, depois persiste no cache function useStickerBlob(sticker: StickerRecord) { const [dataUrl, setDataUrl] = useState(null); useEffect(() => { let cancelled = false; getStickerFromCache(sticker.sha256).then((cached) => { if (cancelled) return; if (cached) { setDataUrl(cached); return; } // Carrega do proxy e converte para dataURL via canvas const img = new Image(); img.crossOrigin = 'anonymous'; img.onload = () => { if (cancelled) return; try { const canvas = document.createElement('canvas'); canvas.width = img.naturalWidth || 96; canvas.height = img.naturalHeight || 96; const ctx = canvas.getContext('2d'); if (!ctx) { setDataUrl(img.src); return; } ctx.drawImage(img, 0, 0); const url = canvas.toDataURL('image/webp', 0.9); setDataUrl(url); setStickerInCache(sticker.sha256, url); } catch { setDataUrl(img.src); } }; img.onerror = () => { if (!cancelled) setDataUrl(null); }; img.src = stickerSrcUrl(sticker.wasabiPath); }); return () => { cancelled = true; }; }, [sticker.sha256, sticker.wasabiPath]); return dataUrl; } // Item individual de sticker function StickerItem({ sticker, onSend, onToggleFavorite, }: { sticker: StickerRecord; onSend: (s: StickerRecord) => void; onToggleFavorite: (s: StickerRecord) => void; }) { const dataUrl = useStickerBlob(sticker); const [hovering, setHovering] = useState(false); return (
setHovering(true)} onMouseLeave={() => setHovering(false)} onClick={() => dataUrl && onSend(sticker)} >
{dataUrl ? ( ) : (
)}
{/* Botão de favoritar — aparece no hover */} {hovering && ( )}
); } export default function StickerPanel({ instanceId, jid, onSend }: StickerPanelProps) { const [tab, setTab] = useState<'recent' | 'favorites'>('recent'); const [stickers, setStickers] = useState([]); const [loading, setLoading] = useState(true); const fetchedRef = useRef(false); const load = useCallback(async (force = false) => { setLoading(true); try { const { listVersion, stickers: list } = await stickerApi.list(); if (!force) { const cached = localStorage.getItem(LIST_VERSION_KEY); if (cached === listVersion) { // versão igual — sem novidades, mas ainda mostra o que temos } localStorage.setItem(LIST_VERSION_KEY, listVersion); } setStickers(list); } catch { // silencioso — sem stickers ainda } finally { setLoading(false); } }, []); useEffect(() => { if (fetchedRef.current) return; fetchedRef.current = true; load(); }, [load]); const handleSend = useCallback(async (sticker: StickerRecord) => { const dataUrl = await getStickerFromCache(sticker.sha256); if (!dataUrl) return; // Converte dataURL → Blob → envia const res = await fetch(dataUrl); const blob = await res.blob(); onSend(blob); }, [onSend]); const handleToggleFavorite = useCallback(async (sticker: StickerRecord) => { const result = await stickerApi.toggleFavorite(sticker.id); setStickers((prev) => prev.map((s) => s.id === sticker.id ? { ...s, isFavorite: result.isFavorite } : s) ); }, []); const displayed = tab === 'favorites' ? stickers.filter((s) => s.isFavorite) : stickers; // recent = all sorted by lastSeenAt (backend already sorts) return (
{/* Tabs */}
{/* Content */}
{loading ? (
) : displayed.length === 0 ? (
{tab === 'favorites' ? ( <> Nenhum favorito ainda ) : ( <> 🙂 As figurinhas que você receber
aparecem aqui automaticamente
)}
) : (
{displayed.map((s) => ( ))}
)}
); }