From 3ee83896d66fda6dfa2382b94bc0d612602883d5 Mon Sep 17 00:00:00 2001 From: George Wu Date: Tue, 24 Feb 2026 19:38:05 -0800 Subject: [PATCH] Add Escape key handler to close move dialog --- static/js/features/files/contextMenus.js | 131 ++++++++++------------- 1 file changed, 55 insertions(+), 76 deletions(-) diff --git a/static/js/features/files/contextMenus.js b/static/js/features/files/contextMenus.js index 203bea87..8c63e75f 100644 --- a/static/js/features/files/contextMenus.js +++ b/static/js/features/files/contextMenus.js @@ -260,9 +260,23 @@ const contextMenus = { const moveCancelBtn = document.getElementById('move-cancel-btn'); const moveConfirmBtn = document.getElementById('move-confirm-btn'); const copyConfirmBtn = document.getElementById('copy-confirm-btn'); + const moveFileDialog = document.getElementById('move-file-dialog'); moveCancelBtn.addEventListener('click', this.closeMoveDialog); + // Close move dialog on Escape key + // Store handler reference to avoid duplicate listeners + // Note: We don't use stopPropagation because all Escape handlers are on document level + // Each handler checks its own state, so multiple dialogs can be closed with multiple Escape presses + if (!window._moveDialogEscapeHandler) { + window._moveDialogEscapeHandler = (e) => { + if (e.key === 'Escape' && moveFileDialog.style.display === 'flex') { + this.closeMoveDialog(); + } + }; + document.addEventListener('keydown', window._moveDialogEscapeHandler); + } + // Copy button handler copyConfirmBtn.addEventListener('click', async () => { // Batch copy mode (from multiSelect) @@ -473,8 +487,14 @@ const contextMenus = { // Initialize dialog navigation state // Start at the parent of the item being moved (so user sees siblings and can navigate) let startFolderId = null; + let startFolderName = null; if (mode === 'file' && item.folder_id) { startFolderId = item.folder_id; + // We need the folder name for breadcrumb - try to get it from current view + const folderEl = document.querySelector(`[data-folder-id="${startFolderId}"]`); + if (folderEl) { + startFolderName = folderEl.querySelector('.folder-name, .item-name')?.textContent || null; + } } else if (mode === 'folder' && item.parent_id) { startFolderId = item.parent_id; } else { @@ -488,7 +508,15 @@ const contextMenus = { window.app.moveDialogItemId = item.id; window.app.moveDialogItemMode = mode; window.app.moveDialogCurrentFolderId = startFolderId; - window.app.moveDialogBreadcrumb = []; + + // Build initial breadcrumb if starting at a non-home folder + // This allows proper navigation back to home + const breadcrumb = []; + if (startFolderId && startFolderId !== window.app.userHomeFolderId && startFolderName) { + // We have the folder name, add it to breadcrumb + breadcrumb.push({ id: startFolderId, name: startFolderName }); + } + window.app.moveDialogBreadcrumb = breadcrumb; // Update dialog title (preserve icon) const dialogHeader = document.getElementById('move-file-dialog').querySelector('.rename-dialog-header'); @@ -562,24 +590,21 @@ const contextMenus = { // Ensure we have the home folder ID before proceeding if (!window.app.userHomeFolderId) { - console.log('[Move Dialog] Home folder ID not set, resolving...'); await window.resolveHomeFolder(); } - // Build URL - use /api/folders/{id}/contents to get CHILDREN of the folder - let url; - let effectiveParentId = parentFolderId || window.app.userHomeFolderId; + // Get the effective folder ID + const effectiveParentId = parentFolderId || window.app.userHomeFolderId; - if (effectiveParentId) { - // Use the contents endpoint to get children (not the folder itself) - url = `/api/folders/${effectiveParentId}/contents`; - } else { - // Last resort fallback: this returns root folders (home folder) - // We should NOT use this as it returns the folder itself, not its contents - console.error('[Move Dialog] No folder ID available, using fallback'); - url = '/api/folders'; + // Must have a folder ID to proceed + if (!effectiveParentId) { + console.error('[Move Dialog] Cannot load folders - no folder ID available'); + return; } + // Use the contents endpoint to get children + const url = `/api/folders/${effectiveParentId}/contents`; + console.log('[Move Dialog] Loading folders from:', url, 'effectiveParentId:', effectiveParentId); const response = await fetch(url, { headers }); if (!response.ok) { @@ -606,13 +631,9 @@ const contextMenus = { const mode = window.app.moveDialogItemMode; const breadcrumb = window.app.moveDialogBreadcrumb || []; - // Only show breadcrumb when we've navigated into subfolders - if (breadcrumb.length > 0) { - this._renderMoveDialogBreadcrumb(breadcrumbContainer, breadcrumb, effectiveParentId); - breadcrumbContainer.style.display = 'flex'; - } else { - breadcrumbContainer.style.display = 'none'; - } + // Always show breadcrumb to allow navigation back to home + this._renderMoveDialogBreadcrumb(breadcrumbContainer, breadcrumb, effectiveParentId); + breadcrumbContainer.style.display = 'flex'; // Option to select current folder as destination (only after navigating into subfolders) if (breadcrumb.length > 0 && effectiveParentId && effectiveParentId !== itemId) { @@ -632,8 +653,9 @@ const contextMenus = { folderSelectContainer.appendChild(currentFolderOption); } - // Add "Go to parent" option if we have navigated into subfolders - if (breadcrumb.length > 0) { + // Add "Go to parent" option if not at home folder + const isAtHomeFolder = effectiveParentId === window.app.userHomeFolderId; + if (!isAtHomeFolder || breadcrumb.length > 0) { const parentOption = document.createElement('div'); parentOption.className = 'folder-select-item folder-navigate-up'; parentOption.innerHTML = ` @@ -654,6 +676,7 @@ const contextMenus = { this.loadMoveDialogFolders(parentFolder ? parentFolder.id : null); } else { // Go to root (home folder) + window.app.moveDialogBreadcrumb = []; window.app.moveDialogCurrentFolderId = window.app.userHomeFolderId || null; this.loadMoveDialogFolders(window.app.userHomeFolderId || null); } @@ -798,63 +821,19 @@ const contextMenus = { }, /** - * Load all folders for the move dialog (legacy - kept for batch operations) - * @param {string} itemId - ID of the item being moved - * @param {string} mode - 'file' or 'folder' + * Load all folders for the move dialog (batch operations) + * Uses the same navigation pattern as loadMoveDialogFolders + * @param {string} itemId - ID of the item being moved (unused, kept for compatibility) + * @param {string} mode - 'batch' for batch operations */ async loadAllFolders(itemId, mode) { - // For batch mode, use the old behavior but start from home folder - try { - const token = localStorage.getItem('oxicloud_token'); - const headers = token ? { 'Authorization': `Bearer ${token}` } : {}; - const response = await fetch('/api/folders', { headers }); - if (response.ok) { - const folders = await response.json(); - const folderSelectContainer = document.getElementById('folder-select-container'); + // For batch mode, use the same navigation as regular move dialog + // Initialize navigation state starting at home folder + window.app.moveDialogBreadcrumb = []; + window.app.moveDialogCurrentFolderId = window.app.userHomeFolderId || null; - // Clear container - folderSelectContainer.innerHTML = ''; - - // Add all available folders - if (Array.isArray(folders)) { - const excludeIds = itemId ? [itemId] : []; - folders.forEach(folder => { - // Skip folders that would create cycles - if (excludeIds.includes(folder.id)) { - return; - } - - const folderItem = document.createElement('div'); - folderItem.className = 'folder-select-item'; - folderItem.dataset.folderId = folder.id; - folderItem.innerHTML = ` ${escapeHtml(folder.name)}`; - - folderItem.addEventListener('click', () => { - // Deselect all - document.querySelectorAll('.folder-select-item').forEach(item => { - item.classList.remove('selected'); - }); - - // Select this one - folderItem.classList.add('selected'); - window.app.selectedTargetFolderId = folder.id; - }); - - folderSelectContainer.appendChild(folderItem); - }); - } - - // Set default selection - window.app.selectedTargetFolderId = ""; - - // Translate new elements (scoped to container) - if (window.i18n && window.i18n.translateElement) { - window.i18n.translateElement(folderSelectContainer); - } - } - } catch (error) { - console.error('Error loading folders:', error); - } + // Use loadMoveDialogFolders which uses /api/folders/{id}/contents + await this.loadMoveDialogFolders(window.app.userHomeFolderId || null); }, /** * Show share dialog for files or folders