662 lines
27 KiB
JavaScript
662 lines
27 KiB
JavaScript
/**
|
|
* @fileoverview Manages file operations and state for the room interface.
|
|
* This file handles:
|
|
* - File fetching and state management
|
|
* - File operations (delete, rename, move, download)
|
|
* - File selection and batch operations
|
|
* - Star/unstar functionality
|
|
* - Navigation and path management
|
|
*/
|
|
|
|
/**
|
|
* @class FileManager
|
|
* @classdesc Manages all file-related operations and state in the room interface.
|
|
* Handles file operations, selection, and navigation.
|
|
*/
|
|
export class FileManager {
|
|
/**
|
|
* Creates a new FileManager instance.
|
|
* @param {RoomManager} roomManager - The parent RoomManager instance
|
|
*/
|
|
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 = '';
|
|
this.fileToMove = null;
|
|
this.fileToMovePath = '';
|
|
this.fileToRename = null;
|
|
this.fileToRenamePath = '';
|
|
console.log('[FileManager] Initialized with roomManager:', roomManager);
|
|
}
|
|
|
|
/**
|
|
* Fetches files from the server for the current path.
|
|
* @async
|
|
* @returns {Promise<Array>} A promise that resolves with the array of files
|
|
* @throws {Error} If the fetch operation fails
|
|
*/
|
|
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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deletes a file from the server.
|
|
* @async
|
|
* @param {string} filename - The name of the file to delete
|
|
* @param {string} [path=''] - The path of the file to delete
|
|
* @throws {Error} If the delete operation fails
|
|
*/
|
|
async deleteFile(filename, path = '') {
|
|
console.log('[FileManager] Deleting file:', { filename, path });
|
|
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
|
|
|
try {
|
|
let url = `/api/rooms/${this.roomManager.roomId}/files/${encodeURIComponent(filename)}`;
|
|
if (path) {
|
|
url += `?path=${encodeURIComponent(path)}`;
|
|
}
|
|
|
|
const response = await fetch(url, {
|
|
method: 'DELETE',
|
|
headers: {
|
|
'X-CSRF-Token': csrfToken
|
|
}
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
const result = await response.json();
|
|
if (result.success) {
|
|
console.log('[FileManager] File deleted successfully');
|
|
await this.fetchFiles();
|
|
// Clear any existing error message
|
|
const errorEl = document.getElementById('fileError');
|
|
if (errorEl) {
|
|
errorEl.textContent = '';
|
|
}
|
|
} else {
|
|
console.error('[FileManager] Failed to delete file:', result.error);
|
|
const errorEl = document.getElementById('fileError');
|
|
if (errorEl) {
|
|
errorEl.textContent = result.error || 'Failed to delete file.';
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('[FileManager] Error deleting file:', error);
|
|
const errorEl = document.getElementById('fileError');
|
|
if (errorEl) {
|
|
errorEl.textContent = 'Failed to delete file. Please try again.';
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Renames a file on the server.
|
|
* @async
|
|
* @param {string} filename - The name of the file to rename
|
|
* @param {string} newName - The new name for the file
|
|
* @returns {Promise<Object>} A promise that resolves with the result of the rename operation
|
|
* @throws {Error} If the rename operation fails
|
|
*/
|
|
async renameFile(filename, newName) {
|
|
console.log('[FileManager] Renaming file:', { filename, newName });
|
|
|
|
// Check if the file has an extension
|
|
const hasExtension = filename.includes('.');
|
|
if (hasExtension) {
|
|
const oldExt = filename.substring(filename.lastIndexOf('.'));
|
|
const newExt = newName.includes('.') ? newName.substring(newName.lastIndexOf('.')) : '';
|
|
|
|
// If the new name doesn't have the same extension, append the old extension
|
|
if (newExt !== oldExt) {
|
|
newName = newName + oldExt;
|
|
}
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(`/api/rooms/${this.roomManager.roomId}/rename`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
|
},
|
|
body: JSON.stringify({
|
|
old_name: filename,
|
|
new_name: newName,
|
|
path: this.fileToRenamePath
|
|
})
|
|
});
|
|
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) {
|
|
// Update the file's name in currentFiles
|
|
const fileIndex = this.currentFiles.findIndex(file => file.name === filename && file.path === this.fileToRenamePath);
|
|
if (fileIndex !== -1) {
|
|
this.currentFiles[fileIndex].name = newName;
|
|
await this.roomManager.viewManager.renderFiles(this.currentFiles);
|
|
console.log('[FileManager] File renamed and view updated');
|
|
}
|
|
// Clear the rename state
|
|
this.fileToRename = null;
|
|
this.fileToRenamePath = '';
|
|
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 };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Moves a file to a new location.
|
|
* @async
|
|
* @param {string} fileId - The ID of the file to move
|
|
* @param {string} targetPath - The target path to move the file to
|
|
* @returns {Promise<Object>} A promise that resolves with the result of the move operation
|
|
* @throws {Error} If the move operation fails
|
|
*/
|
|
async moveFile(fileId, targetPath) {
|
|
console.log('[FileManager] Starting moveFile...');
|
|
console.log('[FileManager] Parameters:', { fileId, targetPath });
|
|
|
|
try {
|
|
const file = this.currentFiles.find(f => f.id === fileId);
|
|
console.log('[FileManager] Found file to move:', file);
|
|
|
|
if (!file) {
|
|
console.error('[FileManager] File not found with ID:', fileId);
|
|
throw new Error('File not found');
|
|
}
|
|
|
|
console.log('[FileManager] Sending move request...');
|
|
const response = await fetch(`/api/rooms/${this.roomManager.roomId}/move`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
|
},
|
|
body: JSON.stringify({
|
|
filename: file.name,
|
|
source_path: file.path || '',
|
|
target_path: targetPath
|
|
})
|
|
});
|
|
|
|
console.log('[FileManager] Move response status:', response.status);
|
|
const result = await response.json();
|
|
console.log('[FileManager] Move response data:', result);
|
|
|
|
if (result.success) {
|
|
console.log('[FileManager] Move successful, updating view...');
|
|
this.currentFiles = this.currentFiles.filter(f => f.id !== fileId);
|
|
await this.roomManager.viewManager.renderFiles(this.currentFiles);
|
|
console.log('[FileManager] View updated after move');
|
|
return { success: true, message: 'File moved successfully' };
|
|
} else {
|
|
console.error('[FileManager] Move failed:', result.message);
|
|
throw new Error(result.message || 'Failed to move file');
|
|
}
|
|
} catch (error) {
|
|
console.error('[FileManager] Error in moveFile:', error);
|
|
console.error('[FileManager] Error details:', {
|
|
message: error.message,
|
|
stack: error.stack
|
|
});
|
|
return { success: false, message: error.message };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles the confirmed move operation after user confirmation.
|
|
* @async
|
|
* @throws {Error} If the move operation fails
|
|
*/
|
|
async moveFileConfirmed() {
|
|
console.log('[FileManager] Starting moveFileConfirmed...');
|
|
console.log('[FileManager] Current state:', {
|
|
fileToMove: this.fileToMove,
|
|
fileToMovePath: this.fileToMovePath,
|
|
currentFiles: this.currentFiles
|
|
});
|
|
|
|
if (!this.fileToMove) {
|
|
console.error('[FileManager] No file selected for move operation');
|
|
document.getElementById('moveError').textContent = 'No file selected for move.';
|
|
return;
|
|
}
|
|
|
|
const targetPath = document.getElementById('moveTargetFolder').value;
|
|
console.log('[FileManager] Selected target path:', targetPath);
|
|
|
|
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
|
console.log('[FileManager] CSRF Token:', csrfToken ? 'Present' : 'Missing');
|
|
|
|
// Get the file from currentFiles using the filename
|
|
console.log('[FileManager] Attempting to find file with name:', this.fileToMove);
|
|
const file = this.currentFiles.find(f => f.name === this.fileToMove);
|
|
console.log('[FileManager] Found file:', file);
|
|
|
|
if (!file) {
|
|
console.error('[FileManager] File not found in currentFiles. Available files:', this.currentFiles);
|
|
document.getElementById('moveError').textContent = 'File not found.';
|
|
return;
|
|
}
|
|
|
|
console.log('[FileManager] Preparing move request with data:', {
|
|
filename: file.name,
|
|
source_path: this.fileToMovePath,
|
|
target_path: targetPath
|
|
});
|
|
|
|
try {
|
|
const url = `/api/rooms/${this.roomManager.roomId}/move`;
|
|
console.log('[FileManager] Sending request to:', url);
|
|
|
|
const response = await fetch(url, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-Token': csrfToken
|
|
},
|
|
body: JSON.stringify({
|
|
filename: file.name,
|
|
source_path: this.fileToMovePath,
|
|
target_path: targetPath
|
|
})
|
|
});
|
|
|
|
console.log('[FileManager] Response status:', response.status);
|
|
const result = await response.json();
|
|
console.log('[FileManager] Response data:', result);
|
|
|
|
if (result.success) {
|
|
console.log('[FileManager] Move successful, refreshing files...');
|
|
await this.fetchFiles();
|
|
this.fileToMove = null;
|
|
this.fileToMovePath = '';
|
|
this.roomManager.modalManager.moveModal.hide();
|
|
document.getElementById('moveError').textContent = '';
|
|
console.log('[FileManager] Move operation completed successfully');
|
|
} else {
|
|
console.error('[FileManager] Move failed:', result.error);
|
|
document.getElementById('moveError').textContent = result.error || 'Move failed.';
|
|
}
|
|
} catch (error) {
|
|
console.error('[FileManager] Error during move operation:', error);
|
|
console.error('[FileManager] Error details:', {
|
|
message: error.message,
|
|
stack: error.stack
|
|
});
|
|
document.getElementById('moveError').textContent = 'Move failed.';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Toggles the star status of a file.
|
|
* @async
|
|
* @param {string} filename - The name of the file to toggle star for
|
|
* @param {string} path - The path of the file
|
|
* @throws {Error} If the star toggle operation fails
|
|
*/
|
|
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-CSRF-Token': 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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Downloads a single file.
|
|
* @async
|
|
* @param {string} filename - The name of the file to download
|
|
* @param {string} [path=''] - The path of the file
|
|
* @throws {Error} If the download operation fails
|
|
*/
|
|
async downloadFile(filename, path = '') {
|
|
console.log('[FileManager] Downloading file:', { filename, path });
|
|
let url = `/api/rooms/${this.roomManager.roomId}/files/${encodeURIComponent(filename)}`;
|
|
if (path) {
|
|
url += `?path=${encodeURIComponent(path)}`;
|
|
}
|
|
console.log('[FileManager] Download URL:', url);
|
|
|
|
try {
|
|
const response = await fetch(url, {
|
|
method: 'GET',
|
|
headers: {
|
|
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
|
}
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
const blob = await response.blob();
|
|
const downloadUrl = window.URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = downloadUrl;
|
|
a.download = filename;
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
window.URL.revokeObjectURL(downloadUrl);
|
|
document.body.removeChild(a);
|
|
console.log('[FileManager] Download initiated');
|
|
} catch (error) {
|
|
console.error('[FileManager] Error downloading file:', error);
|
|
document.getElementById('fileError').textContent = 'Failed to download file. Please try again.';
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Downloads multiple selected files as a zip archive.
|
|
* @async
|
|
* @throws {Error} If the download operation fails
|
|
*/
|
|
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;
|
|
}
|
|
|
|
// Filter out folders and get only file IDs
|
|
const fileIds = selectedItems
|
|
.filter(item => item.type !== 'folder')
|
|
.map(item => item.id);
|
|
|
|
if (fileIds.length === 0) {
|
|
console.log('[FileManager] No files to download (only folders selected)');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(`/api/rooms/${this.roomManager.roomId}/download-zip`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
|
},
|
|
body: JSON.stringify({ items: selectedItems })
|
|
});
|
|
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);
|
|
document.getElementById('fileError').textContent = 'Failed to download files. Please try again.';
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles the confirmed delete operation after user confirmation.
|
|
* Supports both single file and batch deletion.
|
|
* @async
|
|
* @throws {Error} If the delete operation fails
|
|
*/
|
|
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-CSRF-Token': 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-CSRF-Token': 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.';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates the file selection based on user interaction.
|
|
* Supports single selection, CTRL+click for multiple selection, and SHIFT+click for range selection.
|
|
* @param {number} index - The index of the file being selected
|
|
* @param {Event} event - The click event that triggered the selection
|
|
*/
|
|
updateSelection(index, event) {
|
|
console.log('[FileManager] Updating selection:', { index, event });
|
|
|
|
// Prevent selection if clicking on a checkbox or action button
|
|
if (event.target.classList.contains('select-item-checkbox') || event.target.closest('.file-action-btn')) {
|
|
return;
|
|
}
|
|
|
|
const checkboxes = document.querySelectorAll('.select-item-checkbox');
|
|
const checkbox = checkboxes[index];
|
|
if (!checkbox) return;
|
|
|
|
if (event.ctrlKey) {
|
|
// CTRL + Click: Toggle individual selection
|
|
checkbox.checked = !checkbox.checked;
|
|
if (checkbox.checked) {
|
|
this.selectedItems.add(index);
|
|
} else {
|
|
this.selectedItems.delete(index);
|
|
}
|
|
} else if (event.shiftKey && this.lastSelectedIndex !== -1) {
|
|
// SHIFT + Click: Select range
|
|
const start = Math.min(this.lastSelectedIndex, index);
|
|
const end = Math.max(this.lastSelectedIndex, index);
|
|
for (let i = start; i <= end; i++) {
|
|
checkboxes[i].checked = true;
|
|
this.selectedItems.add(i);
|
|
}
|
|
} else {
|
|
// Normal click: Select single item
|
|
const wasChecked = checkbox.checked;
|
|
checkboxes.forEach(cb => {
|
|
cb.checked = false;
|
|
this.selectedItems.delete(parseInt(cb.dataset.index));
|
|
});
|
|
checkbox.checked = !wasChecked;
|
|
if (!wasChecked) {
|
|
this.selectedItems.add(index);
|
|
}
|
|
}
|
|
|
|
this.lastSelectedIndex = index;
|
|
this.roomManager.viewManager.updateMultiSelectUI();
|
|
}
|
|
|
|
/**
|
|
* Gets an array of currently selected file objects.
|
|
* @returns {Array<Object>} Array of selected file objects
|
|
*/
|
|
getSelectedItems() {
|
|
console.log('[FileManager] Getting selected items');
|
|
return Array.from(this.selectedItems).map(index => this.currentFiles[index]);
|
|
}
|
|
|
|
/**
|
|
* Clears all file selections and updates the UI.
|
|
*/
|
|
clearSelection() {
|
|
console.log('[FileManager] Clearing selection');
|
|
this.selectedItems.clear();
|
|
this.lastSelectedIndex = -1;
|
|
const checkboxes = document.querySelectorAll('.select-item-checkbox');
|
|
checkboxes.forEach(cb => cb.checked = false);
|
|
this.roomManager.viewManager.updateMultiSelectUI();
|
|
}
|
|
|
|
/**
|
|
* Navigates to the parent folder of the current path.
|
|
*/
|
|
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);
|
|
}
|
|
}
|