export class FileManager { constructor(roomManager) { console.log('[FileManager] Initializing...'); this.roomManager = roomManager; this.currentFiles = []; this.selectedItems = new Set(); this.lastSelectedIndex = -1; this.batchDeleteItems = null; this.fileToDelete = null; this.fileToDeletePath = ''; console.log('[FileManager] Initialized with roomManager:', roomManager); } async fetchFiles() { console.log('[FileManager] Fetching files...'); try { const url = `/api/rooms/${this.roomManager.roomId}/files${this.roomManager.currentPath ? `?path=${encodeURIComponent(this.roomManager.currentPath)}` : ''}`; console.log('[FileManager] Fetching from URL:', url); const response = await fetch(url); console.log('[FileManager] Response status:', response.status); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); console.log('[FileManager] Received data:', data); this.currentFiles = data.sort((a, b) => a.name.localeCompare(b.name)); console.log('[FileManager] Sorted files:', this.currentFiles); // Update the view await this.roomManager.viewManager.renderFiles(this.currentFiles); console.log('[FileManager] Files rendered in view'); return this.currentFiles; } catch (error) { console.error('[FileManager] Error fetching files:', error); document.getElementById('fileError').textContent = 'Failed to load files. Please try again.'; throw error; } } async deleteFile(fileId) { console.log('[FileManager] Deleting file:', fileId); try { const response = await fetch(`/api/rooms/${this.roomManager.roomId}/files/${fileId}`, { method: 'DELETE', headers: { 'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content') } }); console.log('[FileManager] Delete response status:', response.status); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const result = await response.json(); console.log('[FileManager] Delete result:', result); if (result.success) { this.currentFiles = this.currentFiles.filter(file => file.id !== fileId); await this.roomManager.viewManager.renderFiles(this.currentFiles); console.log('[FileManager] File deleted and view updated'); return { success: true, message: 'File moved to trash' }; } else { throw new Error(result.message || 'Failed to delete file'); } } catch (error) { console.error('[FileManager] Error deleting file:', error); return { success: false, message: error.message }; } } async renameFile(fileId, newName) { console.log('[FileManager] Renaming file:', { fileId, newName }); try { const response = await fetch(`/api/rooms/${this.roomManager.roomId}/files/${fileId}/rename`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content') }, body: JSON.stringify({ new_name: newName }) }); console.log('[FileManager] Rename response status:', response.status); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const result = await response.json(); console.log('[FileManager] Rename result:', result); if (result.success) { const fileIndex = this.currentFiles.findIndex(file => file.id === fileId); if (fileIndex !== -1) { this.currentFiles[fileIndex].name = newName; await this.roomManager.viewManager.renderFiles(this.currentFiles); console.log('[FileManager] File renamed and view updated'); } return { success: true, message: 'File renamed successfully' }; } else { throw new Error(result.message || 'Failed to rename file'); } } catch (error) { console.error('[FileManager] Error renaming file:', error); return { success: false, message: error.message }; } } async moveFile(fileId, targetPath) { console.log('[FileManager] Moving file:', { fileId, targetPath }); try { const response = await fetch(`/api/rooms/${this.roomManager.roomId}/files/${fileId}/move`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content') }, body: JSON.stringify({ target_path: targetPath }) }); console.log('[FileManager] Move response status:', response.status); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const result = await response.json(); console.log('[FileManager] Move result:', result); if (result.success) { this.currentFiles = this.currentFiles.filter(file => file.id !== fileId); await this.roomManager.viewManager.renderFiles(this.currentFiles); console.log('[FileManager] File moved and view updated'); return { success: true, message: 'File moved successfully' }; } else { throw new Error(result.message || 'Failed to move file'); } } catch (error) { console.error('[FileManager] Error moving file:', error); return { success: false, message: error.message }; } } async toggleStar(filename, path) { console.log('[FileManager] Toggling star for:', filename, 'path:', path); const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content'); // Find and update the button immediately for better UX const starButton = event.target.closest('.file-action-btn'); if (starButton) { // Get the current state from the button's title const isStarred = starButton.title === 'Unstar'; // Update button appearance using CSS variables starButton.style.backgroundColor = isStarred ? 'var(--primary-opacity-8)' : 'var(--warning-opacity-15)'; starButton.style.color = isStarred ? 'var(--primary-color)' : 'var(--warning-color)'; starButton.title = isStarred ? 'Star' : 'Unstar'; } try { const response = await fetch(`/api/rooms/${this.roomManager.roomId}/star`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken }, body: JSON.stringify({ filename: filename, path: path || '' }) }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); console.log('[FileManager] Star toggle response:', data); if (data.success) { // Update the file's starred status in currentFiles const fileIndex = this.currentFiles.findIndex(f => f.name === filename && f.path === path); if (fileIndex !== -1) { this.currentFiles[fileIndex].starred = data.starred; } } else { // Revert the button if the server request failed if (starButton) { const isStarred = starButton.title === 'Unstar'; starButton.style.backgroundColor = isStarred ? 'var(--primary-opacity-8)' : 'var(--warning-opacity-15)'; starButton.style.color = isStarred ? 'var(--primary-color)' : 'var(--warning-color)'; starButton.title = isStarred ? 'Star' : 'Unstar'; } throw new Error(data.error || 'Failed to toggle star'); } } catch (error) { // Revert the button if there was an error if (starButton) { const isStarred = starButton.title === 'Unstar'; starButton.style.backgroundColor = isStarred ? 'var(--primary-opacity-8)' : 'var(--warning-opacity-15)'; starButton.style.color = isStarred ? 'var(--primary-color)' : 'var(--warning-color)'; starButton.title = isStarred ? 'Star' : 'Unstar'; } console.error('[FileManager] Error toggling star:', error); throw error; } } downloadFile(fileId) { console.log('[FileManager] Downloading file:', fileId); const url = `/api/rooms/${this.roomManager.roomId}/files/${fileId}/download`; console.log('[FileManager] Download URL:', url); window.location.href = url; } async downloadSelected() { console.log('[FileManager] Downloading selected files...'); const selectedItems = this.getSelectedItems(); console.log('[FileManager] Selected items:', selectedItems); if (selectedItems.length === 0) { console.log('[FileManager] No files selected for download'); return; } try { const response = await fetch(`/api/rooms/${this.roomManager.roomId}/files/download`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content') }, body: JSON.stringify({ file_ids: selectedItems.map(item => item.id) }) }); console.log('[FileManager] Download response status:', response.status); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const blob = await response.blob(); console.log('[FileManager] Received blob:', blob); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'files.zip'; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); document.body.removeChild(a); console.log('[FileManager] Download initiated'); } catch (error) { console.error('[FileManager] Error downloading files:', error); throw error; } } async deleteFileConfirmed() { const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content'); if (this.batchDeleteItems && this.batchDeleteItems.length) { // Batch delete let completed = 0; const deleteNext = async () => { if (completed >= this.batchDeleteItems.length) { await this.fetchFiles(); this.batchDeleteItems = null; this.fileToDelete = null; this.fileToDeletePath = ''; this.roomManager.modalManager.deleteModal.hide(); document.getElementById('fileError').textContent = ''; return; } const item = this.batchDeleteItems[completed]; let url = `/api/rooms/${this.roomManager.roomId}/files/${encodeURIComponent(item.name)}`; if (item.path) url += `?path=${encodeURIComponent(item.path)}`; try { const response = await fetch(url, { method: 'DELETE', headers: { 'X-CSRFToken': csrfToken } }); const result = await response.json(); if (!result.success) { throw new Error(result.error || 'Delete failed'); } } catch (error) { console.error('[FileManager] Error deleting file:', error); } completed++; await deleteNext(); }; await deleteNext(); return; } if (!this.fileToDelete) return; let url = `/api/rooms/${this.roomManager.roomId}/files/${encodeURIComponent(this.fileToDelete)}`; if (this.fileToDeletePath) url += `?path=${encodeURIComponent(this.fileToDeletePath)}`; try { const response = await fetch(url, { method: 'DELETE', headers: { 'X-CSRFToken': csrfToken } }); const result = await response.json(); if (result.success) { await this.fetchFiles(); this.fileToDelete = null; this.fileToDeletePath = ''; this.roomManager.modalManager.deleteModal.hide(); document.getElementById('fileError').textContent = ''; } else { document.getElementById('fileError').textContent = result.error || 'Delete failed.'; } } catch (error) { console.error('[FileManager] Error deleting file:', error); document.getElementById('fileError').textContent = 'Delete failed.'; } } getSelectedItems() { console.log('[FileManager] Getting selected items'); return Array.from(this.selectedItems).map(index => this.currentFiles[index]); } updateSelection(index, event) { console.log('[FileManager] Updating selection:', { index, event }); // Implementation of selection logic } navigateToParent() { if (!this.roomManager.currentPath) return; const parts = this.roomManager.currentPath.split('/'); parts.pop(); // Remove the last part const parentPath = parts.join('/'); this.roomManager.navigateTo(parentPath); } }