// Gestión de tema (light/dark) function initTheme() { const savedTheme = localStorage.getItem('theme') || 'light'; if (savedTheme === 'dark') { document.documentElement.classList.add('dark'); } else { document.documentElement.classList.remove('dark'); } } function toggleTheme() { const isDark = document.documentElement.classList.contains('dark'); if (isDark) { document.documentElement.classList.remove('dark'); localStorage.setItem('theme', 'light'); } else { document.documentElement.classList.add('dark'); localStorage.setItem('theme', 'dark'); } } // Gestión de sesión de usuario function getUserSession() { let session = localStorage.getItem('userSession'); if (!session) { session = 'user_' + Math.random().toString(36).substr(2, 9) + '_' + Date.now(); localStorage.setItem('userSession', session); } return session; } // Notificación tipo toast function showToast(message, type = 'success') { const toast = document.getElementById('toast'); const toastMessage = document.getElementById('toastMessage'); toastMessage.textContent = message; toast.classList.remove('bg-green-500', 'bg-red-500', 'bg-blue-500'); if (type === 'success') { toast.classList.add('bg-green-500'); } else if (type === 'error') { toast.classList.add('bg-red-500'); } else if (type === 'info') { toast.classList.add('bg-blue-500'); } toast.classList.remove('translate-x-full'); setTimeout(() => { toast.classList.add('translate-x-full'); }, 3000); } // Tooltip personalizado let tooltipTimeout; function showCustomTooltip(target, text) { let tooltip = document.getElementById('custom-tooltip'); if (!tooltip) { tooltip = document.createElement('div'); tooltip.id = 'custom-tooltip'; tooltip.className = 'custom-tooltip'; document.body.appendChild(tooltip); } tooltip.textContent = text; const rect = target.getBoundingClientRect(); tooltip.style.top = (window.scrollY + rect.bottom + 4) + 'px'; tooltip.style.left = (window.scrollX + rect.left) + 'px'; tooltip.classList.add('visible'); clearTimeout(tooltipTimeout); } function hideCustomTooltip() { const tooltip = document.getElementById('custom-tooltip'); if (tooltip) { tooltip.classList.remove('visible'); tooltipTimeout = setTimeout(() => { tooltip.remove(); }, 200); } } // Formateo de tamaño de archivo function formatFileSize(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } // Formateo de fecha function formatDate(dateString) { const date = new Date(dateString); return date.toLocaleDateString() + ' ' + date.toLocaleTimeString(); } // Tiempo restante function getTimeRemaining(expirationDate) { const now = new Date(); const expiry = new Date(expirationDate); const diff = expiry - now; if (diff <= 0) return 'Expired'; const hours = Math.floor(diff / (1000 * 60 * 60)); const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)); if (hours > 0) { return `${hours}h ${minutes}m remaining`; } else { return `${minutes}m remaining`; } } // Progreso de subida let uploadStartTime = 0; let uploadedBytes = 0; function updateProgress(loaded, total, isDone = false, processing = false) { const percent = isDone ? 100 : Math.round((loaded / total) * 100); const progressBar = document.getElementById('progressBar'); const progressPercent = document.getElementById('progressPercent'); const uploadSpeed = document.getElementById('uploadSpeed'); const timeRemaining = document.getElementById('timeRemaining'); progressBar.style.width = percent + '%'; if (processing) { progressPercent.innerHTML = 'Processing...'; } else { progressPercent.textContent = percent + '%'; } if (isDone || processing) { uploadSpeed.textContent = ''; timeRemaining.textContent = ''; return; } const elapsed = (Date.now() - uploadStartTime) / 1000; const speed = loaded / elapsed; const speedMB = (speed / (1024 * 1024)).toFixed(2); uploadSpeed.textContent = `${speedMB} MB/s`; if (speed > 0) { const remaining = (total - loaded) / speed; const minutes = Math.floor(remaining / 60); const seconds = Math.floor(remaining % 60); timeRemaining.textContent = `${minutes}:${seconds.toString().padStart(2, '0')} remaining`; } } // Manejo de selección de archivo function handleFileSelection(file) { const fileInfo = document.getElementById('file-info'); const fileName = document.getElementById('file-name'); const fileSize = document.getElementById('file-size'); const maxFileSizeMB = window.maxFileSizeMB || 16; if (file) { if (file.size > maxFileSizeMB * 1024 * 1024) { showToast(`File size exceeds the allowed limit (${maxFileSizeMB} MB)`, 'error'); clearFileSelection(); return; } fileName.textContent = file.name; fileName.classList.add('truncate'); fileName.setAttribute('data-tooltip', file.name); fileSize.textContent = formatFileSize(file.size); fileInfo.classList.remove('hidden'); } else { fileInfo.classList.add('hidden'); } } function clearFileSelection() { const fileInput = document.getElementById('file-upload'); const fileInfo = document.getElementById('file-info'); fileInput.value = ''; fileInfo.classList.add('hidden'); } // Cargar archivos del usuario async function loadUserFiles() { const userSession = getUserSession(); try { const response = await fetch(`/files/${userSession}`); const files = await response.json(); const filesList = document.getElementById('filesList'); const noFiles = document.getElementById('noFiles'); if (files.length === 0) { filesList.innerHTML = ''; noFiles.classList.remove('hidden'); } else { noFiles.classList.add('hidden'); filesList.innerHTML = files.map(file => `
`).join(''); } document.querySelectorAll('.file-tooltip').forEach(el => { el.addEventListener('mouseenter', function(e) { showCustomTooltip(e.target, e.target.getAttribute('data-tooltip')); }); el.addEventListener('mouseleave', hideCustomTooltip); }); updateRemainingTimes(); } catch (error) { showToast('Error loading files', 'error'); } } // Variables para el modal de eliminación let fileIdToDelete = null; function openDeleteModal(fileId) { fileIdToDelete = fileId; document.getElementById('deleteModal').classList.remove('hidden'); } function closeDeleteModal() { fileIdToDelete = null; document.getElementById('deleteModal').classList.add('hidden'); } // Eliminar archivo (ahora solo ejecuta si se confirma en el modal) async function confirmDeleteFile() { if (!fileIdToDelete) return; const userSession = getUserSession(); try { const response = await fetch(`/delete/${fileIdToDelete}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ user_session: userSession }) }); const result = await response.json(); if (result.success) { showToast('File deleted successfully'); loadUserFiles(); } else { showToast(result.error || 'Error deleting file', 'error'); } } catch (error) { showToast('Error deleting file: ' + error.message, 'error'); } finally { closeDeleteModal(); } } // Reemplazar deleteFile para abrir el modal window.deleteFile = openDeleteModal; // Eventos del modal function setupDeleteModalEvents() { document.getElementById('cancelDeleteBtn').addEventListener('click', closeDeleteModal); document.getElementById('confirmDeleteBtn').addEventListener('click', confirmDeleteFile); // Cerrar modal con Escape document.addEventListener('keydown', function(e) { if (e.key === 'Escape') closeDeleteModal(); }); } // Inicialización de eventos function setupEventListeners() { // Tema document.getElementById('themeToggle').addEventListener('click', toggleTheme); // Selección de archivo document.getElementById('file-upload').addEventListener('change', function(e) { const file = e.target.files[0]; handleFileSelection(file); }); // Limpiar selección document.getElementById('clear-file').addEventListener('click', clearFileSelection); // Subida de archivo document.getElementById('uploadForm').addEventListener('submit', async function(e) { e.preventDefault(); const fileInput = document.getElementById('file-upload'); const file = fileInput.files[0]; const duration = document.getElementById('duration').value; const uploadBtn = document.getElementById('uploadBtn'); const uploadBtnText = document.getElementById('uploadBtnText'); const uploadProgress = document.getElementById('uploadProgress'); const progressBar = document.getElementById('progressBar'); const progressPercent = document.getElementById('progressPercent'); const maxFileSizeMB = window.maxFileSizeMB || 16; if (!file) { showToast('Please select a file', 'error'); return; } if (file.size > maxFileSizeMB * 1024 * 1024) { showToast(`File size exceeds the allowed limit (${maxFileSizeMB} MB)`, 'error'); clearFileSelection(); return; } uploadBtn.disabled = true; uploadBtnText.textContent = 'Uploading...'; uploadProgress.classList.remove('hidden'); uploadStartTime = Date.now(); uploadedBytes = 0; const formData = new FormData(); formData.append('file', file); formData.append('user_session', getUserSession()); formData.append('duration_hours', duration); // Barra de progreso manual con fetch let waitingTimeout, errorTimeout; try { // Simular barra de progreso basada en tamaño y tiempo estimado let loaded = 0; const total = file.size; const chunkSize = 1024 * 256; // 256KB let isProcessing = false; function simulateProgress() { if (isProcessing) return; if (loaded < total) { loaded = Math.min(loaded + chunkSize, total); updateProgress(loaded, total); setTimeout(simulateProgress, 30); } else { // Barra llena, mostrar processing updateProgress(total, total, false, true); isProcessing = true; } } simulateProgress(); // Real upload const response = await fetch('/upload', { method: 'POST', body: formData }); clearTimeout(waitingTimeout); clearTimeout(errorTimeout); document.getElementById('waitingServer')?.remove(); if (response.ok) { const result = await response.json(); if (result.success) { updateProgress(total, total, true, false); showToast('File uploaded successfully!'); clearFileSelection(); loadUserFiles(); } else { showToast(result.error || 'Error uploading file', 'error'); } } else { showToast('Error uploading file', 'error'); } } catch (error) { showToast('Error uploading file: ' + error.message, 'error'); } finally { uploadBtn.disabled = false; uploadBtnText.textContent = 'Upload File'; uploadProgress.classList.add('hidden'); } }); // Refrescar lista document.getElementById('refreshBtn').addEventListener('click', loadUserFiles); } document.addEventListener('DOMContentLoaded', function() { initTheme(); setupEventListeners(); setupDeleteModalEvents(); loadUserFiles(); setInterval(updateRemainingTimes, 60000); }); function updateRemainingTimes() { document.querySelectorAll('.file-remaining-time').forEach(el => { const expiration = el.getAttribute('data-expiration'); el.textContent = getTimeRemaining(expiration); }); } window.copyDownloadUrl = function(url) { navigator.clipboard.writeText(url).then(() => { showToast('Download link copied!'); }, () => { showToast('Failed to copy link', 'error'); }); }