diff --git a/static/js/api/baseModelApi.js b/static/js/api/baseModelApi.js index 4259adf2..793fe536 100644 --- a/static/js/api/baseModelApi.js +++ b/static/js/api/baseModelApi.js @@ -1,5 +1,5 @@ import { state, getCurrentPageState } from '../state/index.js'; -import { showToast, updateFolderTags } from '../utils/uiHelpers.js'; +import { showToast } from '../utils/uiHelpers.js'; import { getStorageItem, getSessionItem, saveMapToStorage } from '../utils/storageHelpers.js'; import { getCompleteApiConfig, @@ -9,6 +9,7 @@ import { WS_ENDPOINTS } from './apiConfig.js'; import { resetAndReload } from './modelApiFactory.js'; +import { sidebarManager } from '../components/SidebarManager.js'; /** * Abstract base class for all model API clients @@ -103,15 +104,7 @@ export class BaseModelApiClient { pageState.currentPage = pageState.currentPage + 1; if (updateFolders) { - const response = await fetch(this.apiConfig.endpoints.folders); - if (response.ok) { - const data = await response.json(); - updateFolderTags(data.folders); - } else { - const errorData = await response.json().catch(() => ({})); - const errorMsg = errorData && errorData.error ? errorData.error : response.statusText; - console.error(`Error getting folders: ${errorMsg}`); - } + sidebarManager.refresh(); } return result; diff --git a/static/js/components/SidebarManager.js b/static/js/components/SidebarManager.js index 7d5fca9f..0b7f06c8 100644 --- a/static/js/components/SidebarManager.js +++ b/static/js/components/SidebarManager.js @@ -5,9 +5,9 @@ import { getStorageItem, setStorageItem } from '../utils/storageHelpers.js'; import { getModelApiClient } from '../api/modelApiFactory.js'; export class SidebarManager { - constructor(pageControls) { - this.pageControls = pageControls; - this.pageType = pageControls.pageType; + constructor() { + this.pageControls = null; + this.pageType = null; this.treeData = {}; this.selectedPath = ''; this.expandedNodes = new Set(); @@ -17,6 +17,7 @@ export class SidebarManager { this.openDropdown = null; this.hoverTimeout = null; this.isHovering = false; + this.isInitialized = false; // Bind methods this.handleTreeClick = this.handleTreeClick.bind(this); @@ -29,8 +30,95 @@ export class SidebarManager { this.handleMouseLeave = this.handleMouseLeave.bind(this); this.handleHoverAreaEnter = this.handleHoverAreaEnter.bind(this); this.handleHoverAreaLeave = this.handleHoverAreaLeave.bind(this); + } + + async initialize(pageControls) { + // Clean up previous initialization if exists + if (this.isInitialized) { + this.cleanup(); + } + + this.pageControls = pageControls; + this.pageType = pageControls.pageType; + this.apiClient = getModelApiClient(); - this.init(); + // Set initial sidebar state immediately (hidden by default) + this.setInitialSidebarState(); + + this.setupEventHandlers(); + this.updateSidebarTitle(); + this.restoreSidebarState(); + await this.loadFolderTree(); + this.restoreSelectedFolder(); + + // Apply final state with animation after everything is loaded + this.applyFinalSidebarState(); + + this.isInitialized = true; + console.log(`SidebarManager initialized for ${this.pageType} page`); + } + + cleanup() { + if (!this.isInitialized) return; + + // Clear any pending timeouts + if (this.hoverTimeout) { + clearTimeout(this.hoverTimeout); + this.hoverTimeout = null; + } + + // Clean up event handlers + this.removeEventHandlers(); + + // Reset state + this.pageControls = null; + this.pageType = null; + this.treeData = {}; + this.selectedPath = ''; + this.expandedNodes = new Set(); + this.openDropdown = null; + this.isHovering = false; + this.apiClient = null; + this.isInitialized = false; + + console.log('SidebarManager cleaned up'); + } + + removeEventHandlers() { + const pinToggleBtn = document.getElementById('sidebarPinToggle'); + const collapseAllBtn = document.getElementById('sidebarCollapseAll'); + const folderTree = document.getElementById('sidebarFolderTree'); + const sidebarBreadcrumbNav = document.getElementById('sidebarBreadcrumbNav'); + const sidebarHeader = document.getElementById('sidebarHeader'); + const sidebar = document.getElementById('folderSidebar'); + const hoverArea = document.getElementById('sidebarHoverArea'); + + if (pinToggleBtn) { + pinToggleBtn.removeEventListener('click', this.handlePinToggle); + } + if (collapseAllBtn) { + collapseAllBtn.removeEventListener('click', this.handleCollapseAll); + } + if (folderTree) { + folderTree.removeEventListener('click', this.handleTreeClick); + } + if (sidebarBreadcrumbNav) { + sidebarBreadcrumbNav.removeEventListener('click', this.handleBreadcrumbClick); + } + if (sidebarHeader) { + sidebarHeader.removeEventListener('click', this.handleSidebarHeaderClick); + } + if (sidebar) { + sidebar.removeEventListener('mouseenter', this.handleMouseEnter); + sidebar.removeEventListener('mouseleave', this.handleMouseLeave); + } + if (hoverArea) { + hoverArea.removeEventListener('mouseenter', this.handleHoverAreaEnter); + hoverArea.removeEventListener('mouseleave', this.handleHoverAreaLeave); + } + + // Remove document click handler + document.removeEventListener('click', this.handleDocumentClick); } async init() { @@ -692,45 +780,9 @@ export class SidebarManager { } destroy() { - // Clear any pending timeouts - if (this.hoverTimeout) { - clearTimeout(this.hoverTimeout); - } - - // Clean up event handlers - const pinToggleBtn = document.getElementById('sidebarPinToggle'); - const collapseAllBtn = document.getElementById('sidebarCollapseAll'); - const folderTree = document.getElementById('sidebarFolderTree'); - const sidebarBreadcrumbNav = document.getElementById('sidebarBreadcrumbNav'); - const sidebarHeader = document.getElementById('sidebarHeader'); - const sidebar = document.getElementById('folderSidebar'); - const hoverArea = document.getElementById('sidebarHoverArea'); - - if (pinToggleBtn) { - pinToggleBtn.removeEventListener('click', this.handlePinToggle); - } - if (collapseAllBtn) { - collapseAllBtn.removeEventListener('click', this.handleCollapseAll); - } - if (folderTree) { - folderTree.removeEventListener('click', this.handleTreeClick); - } - if (sidebarBreadcrumbNav) { - sidebarBreadcrumbNav.removeEventListener('click', this.handleBreadcrumbClick); - } - if (sidebarHeader) { - sidebarHeader.removeEventListener('click', this.handleSidebarHeaderClick); - } - if (sidebar) { - sidebar.removeEventListener('mouseenter', this.handleMouseEnter); - sidebar.removeEventListener('mouseleave', this.handleMouseLeave); - } - if (hoverArea) { - hoverArea.removeEventListener('mouseenter', this.handleHoverAreaEnter); - hoverArea.removeEventListener('mouseleave', this.handleHoverAreaLeave); - } - - // Remove document click handler - document.removeEventListener('click', this.handleDocumentClick); + this.cleanup(); } } + +// Create and export global instance +export const sidebarManager = new SidebarManager(); \ No newline at end of file diff --git a/static/js/components/controls/PageControls.js b/static/js/components/controls/PageControls.js index 60340231..e00a5b02 100644 --- a/static/js/components/controls/PageControls.js +++ b/static/js/components/controls/PageControls.js @@ -3,6 +3,7 @@ import { getCurrentPageState, setCurrentPageType } from '../../state/index.js'; import { getStorageItem, setStorageItem, getSessionItem, setSessionItem } from '../../utils/storageHelpers.js'; import { showToast } from '../../utils/uiHelpers.js'; import { SidebarManager } from '../SidebarManager.js'; +import { sidebarManager } from '../SidebarManager.js'; /** * PageControls class - Unified control management for model pages @@ -24,8 +25,8 @@ export class PageControls { // Store API methods this.api = null; - // Initialize sidebar manager - this.sidebarManager = null; + // Use global sidebar manager + this.sidebarManager = sidebarManager; // Initialize event listeners this.initEventListeners(); @@ -69,7 +70,7 @@ export class PageControls { */ async initSidebarManager() { try { - this.sidebarManager = new SidebarManager(this); + await this.sidebarManager.initialize(this); console.log('SidebarManager initialized'); } catch (error) { console.error('Failed to initialize SidebarManager:', error); @@ -440,8 +441,10 @@ export class PageControls { * Clean up resources */ destroy() { - if (this.sidebarManager) { - this.sidebarManager.destroy(); + // Note: We don't destroy the global sidebar manager, just clean it up + // The global instance will be reused for other page controls + if (this.sidebarManager && this.sidebarManager.isInitialized) { + this.sidebarManager.cleanup(); } } } \ No newline at end of file diff --git a/static/js/managers/MoveManager.js b/static/js/managers/MoveManager.js index fac44040..3299fc32 100644 --- a/static/js/managers/MoveManager.js +++ b/static/js/managers/MoveManager.js @@ -1,10 +1,11 @@ -import { showToast, updateFolderTags } from '../utils/uiHelpers.js'; +import { showToast } from '../utils/uiHelpers.js'; import { state, getCurrentPageState } from '../state/index.js'; import { modalManager } from './ModalManager.js'; import { bulkManager } from './BulkManager.js'; import { getStorageItem } from '../utils/storageHelpers.js'; import { getModelApiClient } from '../api/modelApiFactory.js'; import { FolderTreeManager } from '../components/FolderTreeManager.js'; +import { sidebarManager } from '../components/SidebarManager.js'; class MoveManager { constructor() { @@ -224,12 +225,7 @@ class MoveManager { } // Refresh folder tags after successful move - try { - const foldersData = await apiClient.fetchModelFolders(); - updateFolderTags(foldersData.folders); - } catch (error) { - console.error('Error refreshing folder tags:', error); - } + sidebarManager.refresh(); modalManager.closeModal('moveModal'); diff --git a/static/js/utils/uiHelpers.js b/static/js/utils/uiHelpers.js index e17a19d9..cee63952 100644 --- a/static/js/utils/uiHelpers.js +++ b/static/js/utils/uiHelpers.js @@ -665,32 +665,4 @@ export async function openExampleImagesFolder(modelHash) { showToast('Failed to open example images folder', 'error'); return false; } -} - -/** - * Update the folder tags display with new folder list - * @param {Array} folders - List of folder names - */ -export function updateFolderTags(folders) { - const folderTagsContainer = document.querySelector('.folder-tags'); - if (!folderTagsContainer) return; - - // Keep track of currently selected folder - const pageState = getCurrentPageState(); - const currentFolder = pageState.activeFolder; - - // Create HTML for folder tags - const tagsHTML = folders.map(folder => { - const isActive = folder === currentFolder; - return `