import { useEffect, useState, useCallback } from 'react'; import { Clock, Trash2, Loader2, FileBox } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { listRecentFiles, loadRecentFile, removeRecentFile, type RecentFileMeta } from '@/lib/recentFiles'; import { useModelStore } from '@/stores/useModelStore'; import { toast } from 'sonner'; function formatSize(bytes: number): string { if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; } function formatWhen(ts: number): string { const diff = Date.now() - ts; const m = Math.floor(diff / 60000); if (m < 1) return 'agora'; if (m < 60) return `${m} min`; const h = Math.floor(m / 60); if (h < 24) return `${h} h`; const d = Math.floor(h / 24); return `${d} d`; } export function RecentFilesList() { const [items, setItems] = useState([]); const [loadingId, setLoadingId] = useState(null); const addModel = useModelStore((s) => s.addModel); const models = useModelStore((s) => s.models); const maxModels = useModelStore((s) => s.maxModels); const refresh = useCallback(() => setItems(listRecentFiles()), []); useEffect(() => { refresh(); const onStorage = (e: StorageEvent) => { if (e.key === 'tsxr_recent_files_v1') refresh(); }; window.addEventListener('storage', onStorage); // Polling leve para refletir adições no mesmo tab const t = setInterval(refresh, 2000); return () => { window.removeEventListener('storage', onStorage); clearInterval(t); }; }, [refresh]); const handleLoad = useCallback(async (meta: RecentFileMeta) => { if (models.length >= maxModels) { toast.error(`Limite de ${maxModels} peças simultâneas atingido`); return; } setLoadingId(meta.id); try { const res = await loadRecentFile(meta.id); if (!res) { toast.error('Arquivo recente não encontrado — pode ter sido limpo'); await removeRecentFile(meta.id); refresh(); return; } addModel({ fileName: meta.fileName, fileSize: meta.fileSize, url: res.url }); toast.success(`"${meta.fileName}" carregado dos recentes`); } catch (e) { console.error(e); toast.error('Falha ao carregar arquivo recente'); } finally { setLoadingId(null); } }, [addModel, models.length, maxModels, refresh]); const handleRemove = useCallback(async (e: React.MouseEvent, meta: RecentFileMeta) => { e.stopPropagation(); await removeRecentFile(meta.id); refresh(); }, [refresh]); if (items.length === 0) { return (

Nenhum arquivo recente — os modelos carregados aparecerão aqui

); } return (
Recentes {items.length}/6
{items.map((r) => { const isLoading = loadingId === r.id; return (
!isLoading && handleLoad(r)} title="Clique para carregar" > {isLoading ? : }

{r.fileName}

{formatSize(r.fileSize)} · {formatWhen(r.addedAt)} atrás

); })}
); }