Files
Oxicloud/static/js/recent.js
T
2025-04-02 05:08:30 +02:00

291 lines
10 KiB
JavaScript

/**
* OxiCloud - Recent Files Module
* This file handles tracking and displaying recently accessed files
*/
// Recent Files Module
const recent = {
// Key for storing recent files in localStorage
STORAGE_KEY: 'oxicloud_recent_files',
// Maximum number of recent files to store
MAX_RECENT_FILES: 20,
/**
* Initialize recent files module
*/
init() {
console.log('Initializing recent files module');
this.ensureRecentFilesStorage();
this.setupEventListeners();
},
/**
* Make sure the recent files storage is initialized
*/
ensureRecentFilesStorage() {
if (!localStorage.getItem(this.STORAGE_KEY)) {
localStorage.setItem(this.STORAGE_KEY, JSON.stringify([]));
}
},
/**
* Set up event listeners to track file access
*/
setupEventListeners() {
// Listen for custom event when a file is accessed
document.addEventListener('file-accessed', (event) => {
if (event.detail && event.detail.file) {
this.addRecentFile(event.detail.file);
}
});
},
/**
* Add a file to recent files
* @param {Object} file - File object containing id, name, folder_id, etc.
*/
addRecentFile(file) {
// Don't add if no file or no ID
if (!file || !file.id) {
return;
}
// Get current recent files
const recentFiles = this.getRecentFiles();
// Remove if file already exists in recent files
const existingIndex = recentFiles.findIndex(item => item.id === file.id);
if (existingIndex !== -1) {
recentFiles.splice(existingIndex, 1);
}
// Add file with timestamp to the beginning of the array
const fileWithTimestamp = {
...file,
accessedAt: Date.now()
};
recentFiles.unshift(fileWithTimestamp);
// Keep only the most recent files (limit to MAX_RECENT_FILES)
const trimmedFiles = recentFiles.slice(0, this.MAX_RECENT_FILES);
// Save back to localStorage
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(trimmedFiles));
},
/**
* Get recent files from localStorage
* @returns {Array} Array of recent file objects with timestamps
*/
getRecentFiles() {
try {
const recentFilesJson = localStorage.getItem(this.STORAGE_KEY);
return recentFilesJson ? JSON.parse(recentFilesJson) : [];
} catch (error) {
console.error('Error loading recent files:', error);
return [];
}
},
/**
* Clear all recent files
*/
clearRecentFiles() {
localStorage.setItem(this.STORAGE_KEY, JSON.stringify([]));
},
/**
* Display recent files in the UI
*/
async displayRecentFiles() {
try {
const recentFiles = this.getRecentFiles();
// Clear existing content
const filesGrid = document.getElementById('files-grid');
const filesListView = document.getElementById('files-list-view');
filesGrid.innerHTML = '';
filesListView.innerHTML = `
<div class="list-header">
<div data-i18n="files.name">Nombre</div>
<div data-i18n="files.type">Tipo</div>
<div data-i18n="files.size">Tamaño</div>
<div data-i18n="files.last_accessed">Último acceso</div>
</div>
`;
// Update breadcrumb for recents
window.ui.updateBreadcrumb(window.i18n ? window.i18n.t('nav.recent') : 'Recientes');
// Show empty state if no recent files
if (recentFiles.length === 0) {
const emptyState = document.createElement('div');
emptyState.className = 'empty-state';
emptyState.innerHTML = `
<i class="fas fa-clock" style="font-size: 48px; color: #ddd; margin-bottom: 16px;"></i>
<p>${window.i18n ? window.i18n.t('recent.empty_state') : 'No hay archivos recientes'}</p>
<p>${window.i18n ? window.i18n.t('recent.empty_hint') : 'Los archivos que abras aparecerán aquí'}</p>
`;
filesGrid.appendChild(emptyState);
return;
}
// Process each recent file
for (const recentFile of recentFiles) {
this.createRecentFileElement(recentFile, filesGrid, filesListView);
}
// Update file icons
window.ui.updateFileIcons();
} catch (error) {
console.error('Error displaying recent files:', error);
window.ui.showNotification('Error', 'Error al cargar archivos recientes');
}
},
/**
* Create a file element for a recent file
* @param {Object} file - Recent file object
* @param {HTMLElement} filesGrid - Grid view container
* @param {HTMLElement} filesListView - List view container
*/
createRecentFileElement(file, filesGrid, filesListView) {
// Determine icon and type
let iconClass = 'fas fa-file';
let iconSpecialClass = '';
let typeLabel = 'Documento';
if (file.mime_type) {
if (file.mime_type.startsWith('image/')) {
iconClass = 'fas fa-file-image';
iconSpecialClass = 'image-icon';
typeLabel = window.i18n ? window.i18n.t('files.file_types.image') : 'Imagen';
} else if (file.mime_type.startsWith('text/')) {
iconClass = 'fas fa-file-alt';
iconSpecialClass = 'text-icon';
typeLabel = window.i18n ? window.i18n.t('files.file_types.text') : 'Texto';
} else if (file.mime_type.startsWith('video/')) {
iconClass = 'fas fa-file-video';
iconSpecialClass = 'video-icon';
typeLabel = window.i18n ? window.i18n.t('files.file_types.video') : 'Video';
} else if (file.mime_type.startsWith('audio/')) {
iconClass = 'fas fa-file-audio';
iconSpecialClass = 'audio-icon';
typeLabel = window.i18n ? window.i18n.t('files.file_types.audio') : 'Audio';
} else if (file.mime_type === 'application/pdf') {
iconClass = 'fas fa-file-pdf';
iconSpecialClass = 'pdf-icon';
typeLabel = window.i18n ? window.i18n.t('files.file_types.pdf') : 'PDF';
}
}
// Format size and date
const fileSize = window.formatFileSize ? window.formatFileSize(file.size || 0) : '0 B';
const accessedDate = new Date(file.accessedAt);
const formattedDate = accessedDate.toLocaleDateString() + ' ' +
accessedDate.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
// Grid view element
const fileGridElement = document.createElement('div');
fileGridElement.className = 'file-card recent-item';
fileGridElement.dataset.fileId = file.id;
fileGridElement.dataset.fileName = file.name;
fileGridElement.dataset.folderId = file.folder_id || "";
fileGridElement.innerHTML = `
<div class="recent-indicator">
<i class="fas fa-clock"></i>
</div>
<div class="file-icon ${iconSpecialClass}">
<i class="${iconClass}"></i>
</div>
<div class="file-name">${file.name}</div>
<div class="file-info">Accedido ${formattedDate.split(' ')[0]}</div>
`;
// Download on click
fileGridElement.addEventListener('click', () => {
window.location.href = `/api/files/${file.id}`;
// Dispatch custom event to update recent files
document.dispatchEvent(new CustomEvent('file-accessed', {
detail: { file }
}));
});
// Context menu
fileGridElement.addEventListener('contextmenu', (e) => {
e.preventDefault();
window.app.contextMenuTargetFile = {
id: file.id,
name: file.name,
folder_id: file.folder_id || ""
};
let fileContextMenu = document.getElementById('file-context-menu');
fileContextMenu.style.left = `${e.pageX}px`;
fileContextMenu.style.top = `${e.pageY}px`;
fileContextMenu.style.display = 'block';
});
filesGrid.appendChild(fileGridElement);
// List view element
const fileListElement = document.createElement('div');
fileListElement.className = 'file-item recent-item';
fileListElement.dataset.fileId = file.id;
fileListElement.dataset.fileName = file.name;
fileListElement.dataset.folderId = file.folder_id || "";
fileListElement.innerHTML = `
<div class="recent-indicator">
<i class="fas fa-clock"></i>
</div>
<div class="name-cell">
<div class="file-icon ${iconSpecialClass}">
<i class="${iconClass}"></i>
</div>
<span>${file.name}</span>
</div>
<div class="type-cell">${typeLabel}</div>
<div class="size-cell">${fileSize}</div>
<div class="date-cell">${formattedDate}</div>
`;
// Download on click
fileListElement.addEventListener('click', () => {
window.location.href = `/api/files/${file.id}`;
// Dispatch custom event to update recent files
document.dispatchEvent(new CustomEvent('file-accessed', {
detail: { file }
}));
});
// Context menu
fileListElement.addEventListener('contextmenu', (e) => {
e.preventDefault();
window.app.contextMenuTargetFile = {
id: file.id,
name: file.name,
folder_id: file.folder_id || ""
};
let fileContextMenu = document.getElementById('file-context-menu');
fileContextMenu.style.left = `${e.pageX}px`;
fileContextMenu.style.top = `${e.pageY}px`;
fileContextMenu.style.display = 'block';
});
filesListView.appendChild(fileListElement);
}
};
// Expose recent module globally
window.recent = recent;