mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-05-07 00:46:44 -03:00
833 lines
29 KiB
JavaScript
833 lines
29 KiB
JavaScript
// PageControls.js - Manages controls for both LoRAs and Checkpoints pages
|
|
import { state, getCurrentPageState, setCurrentPageType } from '../../state/index.js';
|
|
import { getStorageItem, setStorageItem, getSessionItem, setSessionItem } from '../../utils/storageHelpers.js';
|
|
import { showToast, openCivitaiByMetadata } from '../../utils/uiHelpers.js';
|
|
import { performModelUpdateCheck } from '../../utils/updateCheckHelpers.js';
|
|
import { sidebarManager } from '../SidebarManager.js';
|
|
|
|
/**
|
|
* PageControls class - Unified control management for model pages
|
|
*/
|
|
export class PageControls {
|
|
constructor(pageType) {
|
|
// Set the current page type in state
|
|
setCurrentPageType(pageType);
|
|
|
|
// Store the page type
|
|
this.pageType = pageType;
|
|
|
|
// Get the current page state
|
|
this.pageState = getCurrentPageState();
|
|
|
|
// Initialize state based on page type
|
|
this.initializeState();
|
|
|
|
// Store API methods
|
|
this.api = null;
|
|
|
|
// Use global sidebar manager
|
|
this.sidebarManager = sidebarManager;
|
|
|
|
this._updateCheckInProgress = false;
|
|
|
|
// Initialize event listeners
|
|
this.initEventListeners();
|
|
|
|
// Initialize update availability filter button state
|
|
this.initUpdateAvailableFilter();
|
|
|
|
// Initialize favorites filter button state
|
|
this.initFavoritesFilter();
|
|
|
|
this.initExcludedViewControls();
|
|
this.syncExcludedViewState();
|
|
|
|
console.log(`PageControls initialized for ${pageType} page`);
|
|
window.pageControls = this;
|
|
}
|
|
|
|
/**
|
|
* Initialize state based on page type
|
|
*/
|
|
initializeState() {
|
|
// Set default values
|
|
this.pageState.pageSize = 100;
|
|
this.pageState.isLoading = false;
|
|
this.pageState.hasMore = true;
|
|
|
|
// Set default sort based on page type
|
|
this.pageState.sortBy = this.pageType === 'loras' ? 'name:asc' : 'name:asc';
|
|
|
|
// Load sort preference
|
|
this.loadSortPreference();
|
|
|
|
if (!this.pageState.viewMode) {
|
|
this.pageState.viewMode = 'active';
|
|
}
|
|
if (!this.pageState.excludedViewState) {
|
|
this.pageState.excludedViewState = {
|
|
sortBy: 'name:asc',
|
|
search: '',
|
|
};
|
|
}
|
|
if (!this.pageState.filters?.search) {
|
|
this.pageState.filters.search = '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Register API methods for the page
|
|
* @param {Object} api - API methods for the page
|
|
*/
|
|
registerAPI(api) {
|
|
this.api = api;
|
|
console.log(`API methods registered for ${this.pageType} page`);
|
|
|
|
// Initialize sidebar manager after API is registered
|
|
this.initSidebarManager();
|
|
}
|
|
|
|
/**
|
|
* Initialize sidebar manager
|
|
*/
|
|
async initSidebarManager() {
|
|
try {
|
|
this.sidebarManager.setHostPageControls(this);
|
|
const shouldShowSidebar = state?.global?.settings?.show_folder_sidebar !== false;
|
|
await this.sidebarManager.setSidebarEnabled(shouldShowSidebar);
|
|
} catch (error) {
|
|
console.error('Failed to initialize SidebarManager:', error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize event listeners for controls
|
|
*/
|
|
initEventListeners() {
|
|
// Sort select handler
|
|
const sortSelect = document.getElementById('sortSelect');
|
|
if (sortSelect) {
|
|
sortSelect.value = this.pageState.sortBy;
|
|
sortSelect.addEventListener('change', async (e) => {
|
|
this.pageState.sortBy = e.target.value;
|
|
this.saveSortPreference(e.target.value);
|
|
await this.resetAndReload();
|
|
});
|
|
}
|
|
|
|
// Refresh button handler
|
|
const refreshBtn = document.querySelector('[data-action="refresh"]');
|
|
if (refreshBtn) {
|
|
refreshBtn.addEventListener('click', () => this.refreshModels(false)); // Regular refresh (incremental)
|
|
}
|
|
|
|
// Initialize dropdown functionality
|
|
this.initDropdowns();
|
|
|
|
// Clear custom filter handler
|
|
const clearFilterBtn = document.querySelector('.clear-filter');
|
|
if (clearFilterBtn) {
|
|
clearFilterBtn.addEventListener('click', () => this.clearCustomFilter());
|
|
}
|
|
|
|
// Page-specific event listeners
|
|
this.initPageSpecificListeners();
|
|
}
|
|
|
|
initExcludedViewControls() {
|
|
const backButton = document.getElementById('excludedViewBackBtn');
|
|
if (backButton) {
|
|
backButton.addEventListener('click', async () => {
|
|
await this.exitExcludedView();
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize dropdown functionality
|
|
*/
|
|
initDropdowns() {
|
|
// Handle dropdown toggles
|
|
const dropdownToggles = document.querySelectorAll('.dropdown-toggle');
|
|
dropdownToggles.forEach(toggle => {
|
|
toggle.addEventListener('click', (e) => {
|
|
e.stopPropagation(); // Prevent triggering parent button
|
|
const dropdownGroup = toggle.closest('.dropdown-group');
|
|
|
|
// Close all other open dropdowns first
|
|
document.querySelectorAll('.dropdown-group.active').forEach(group => {
|
|
if (group !== dropdownGroup) {
|
|
group.classList.remove('active');
|
|
}
|
|
});
|
|
|
|
// Toggle current dropdown
|
|
dropdownGroup.classList.toggle('active');
|
|
});
|
|
});
|
|
|
|
// Handle quick refresh option
|
|
const quickRefreshOption = document.querySelector('[data-action="quick-refresh"]');
|
|
if (quickRefreshOption) {
|
|
quickRefreshOption.addEventListener('click', (e) => {
|
|
e.stopPropagation();
|
|
this.refreshModels(false);
|
|
// Close the dropdown
|
|
document.querySelector('.dropdown-group.active')?.classList.remove('active');
|
|
});
|
|
}
|
|
|
|
// Handle full rebuild option
|
|
const fullRebuildOption = document.querySelector('[data-action="full-rebuild"]');
|
|
if (fullRebuildOption) {
|
|
fullRebuildOption.addEventListener('click', (e) => {
|
|
e.stopPropagation();
|
|
this.refreshModels(true);
|
|
// Close the dropdown
|
|
document.querySelector('.dropdown-group.active')?.classList.remove('active');
|
|
});
|
|
}
|
|
|
|
const checkUpdatesOption = document.getElementById('checkUpdatesMenuItem');
|
|
if (checkUpdatesOption) {
|
|
checkUpdatesOption.addEventListener('click', async (e) => {
|
|
e.stopPropagation();
|
|
await this.handleCheckModelUpdates(e.currentTarget);
|
|
});
|
|
}
|
|
|
|
// Close dropdowns when clicking outside
|
|
document.addEventListener('click', (e) => {
|
|
if (!e.target.closest('.dropdown-group')) {
|
|
document.querySelectorAll('.dropdown-group.active').forEach(group => {
|
|
group.classList.remove('active');
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
async handleCheckModelUpdates(menuItem) {
|
|
if (this._updateCheckInProgress) {
|
|
return;
|
|
}
|
|
|
|
const updateFilterBtn = document.getElementById('updateFilterBtn');
|
|
const dropdownToggle = document.getElementById('updateFilterMenuToggle');
|
|
const dropdownGroup = menuItem?.closest('.dropdown-group');
|
|
const iconElement = updateFilterBtn?.querySelector('i');
|
|
|
|
const setLoadingState = (isLoading) => {
|
|
if (updateFilterBtn) {
|
|
updateFilterBtn.disabled = isLoading;
|
|
updateFilterBtn.classList.toggle('loading', isLoading);
|
|
updateFilterBtn.setAttribute('aria-busy', isLoading ? 'true' : 'false');
|
|
|
|
if (iconElement) {
|
|
if (isLoading) {
|
|
if (!iconElement.dataset.originalClass) {
|
|
iconElement.dataset.originalClass = iconElement.className;
|
|
}
|
|
iconElement.className = 'fas fa-spinner fa-spin';
|
|
} else {
|
|
const originalClass = iconElement.dataset.originalClass;
|
|
if (originalClass) {
|
|
iconElement.className = originalClass;
|
|
delete iconElement.dataset.originalClass;
|
|
} else {
|
|
iconElement.classList.remove('fa-spinner', 'fa-spin');
|
|
if (!iconElement.classList.contains('fa-exclamation-circle')) {
|
|
iconElement.classList.add('fa-exclamation-circle');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dropdownToggle) {
|
|
dropdownToggle.disabled = isLoading;
|
|
dropdownToggle.classList.toggle('loading', isLoading);
|
|
}
|
|
|
|
if (menuItem) {
|
|
menuItem.classList.toggle('disabled', isLoading);
|
|
if (isLoading) {
|
|
menuItem.setAttribute('aria-disabled', 'true');
|
|
} else {
|
|
menuItem.removeAttribute('aria-disabled');
|
|
}
|
|
}
|
|
};
|
|
|
|
this._updateCheckInProgress = true;
|
|
setLoadingState(true);
|
|
|
|
const handleComplete = () => {
|
|
this._updateCheckInProgress = false;
|
|
setLoadingState(false);
|
|
};
|
|
|
|
try {
|
|
await performModelUpdateCheck({
|
|
onComplete: handleComplete,
|
|
});
|
|
} catch (error) {
|
|
console.error('Failed to check model updates:', error);
|
|
} finally {
|
|
if (this._updateCheckInProgress) {
|
|
this._updateCheckInProgress = false;
|
|
setLoadingState(false);
|
|
}
|
|
dropdownGroup?.classList.remove('active');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize page-specific event listeners
|
|
*/
|
|
initPageSpecificListeners() {
|
|
// Fetch from Civitai button - available for both loras and checkpoints
|
|
const fetchButton = document.querySelector('[data-action="fetch"]');
|
|
if (fetchButton) {
|
|
fetchButton.addEventListener('click', () => this.fetchFromCivitai());
|
|
}
|
|
|
|
const downloadButton = document.querySelector('[data-action="download"]');
|
|
if (downloadButton) {
|
|
downloadButton.addEventListener('click', () => this.showDownloadModal());
|
|
}
|
|
|
|
// Find duplicates button - available for both loras and checkpoints
|
|
const duplicatesButton = document.querySelector('[data-action="find-duplicates"]');
|
|
if (duplicatesButton) {
|
|
duplicatesButton.addEventListener('click', () => this.findDuplicates());
|
|
}
|
|
|
|
const bulkButton = document.querySelector('[data-action="bulk"]');
|
|
if (bulkButton) {
|
|
bulkButton.addEventListener('click', () => this.toggleBulkMode());
|
|
}
|
|
|
|
// Favorites filter button handler
|
|
const favoriteFilterBtn = document.getElementById('favoriteFilterBtn');
|
|
if (favoriteFilterBtn) {
|
|
favoriteFilterBtn.addEventListener('click', () => this.toggleFavoritesOnly());
|
|
}
|
|
|
|
const updateFilterBtn = document.getElementById('updateFilterBtn');
|
|
if (updateFilterBtn) {
|
|
updateFilterBtn.addEventListener('click', () => this.toggleUpdateAvailableOnly());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Load sort preference from storage
|
|
*/
|
|
loadSortPreference() {
|
|
const savedSort = getStorageItem(`${this.pageType}_sort`);
|
|
if (savedSort) {
|
|
// Handle legacy format conversion
|
|
const convertedSort = this.convertLegacySortFormat(savedSort);
|
|
this.pageState.sortBy = convertedSort;
|
|
const sortSelect = document.getElementById('sortSelect');
|
|
if (sortSelect) {
|
|
sortSelect.value = convertedSort;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert legacy sort format to new format
|
|
* @param {string} sortValue - The sort value to convert
|
|
* @returns {string} - Converted sort value
|
|
*/
|
|
convertLegacySortFormat(sortValue) {
|
|
// Convert old format to new format with direction
|
|
switch (sortValue) {
|
|
case 'name':
|
|
return 'name:asc';
|
|
case 'date':
|
|
return 'date:desc'; // Newest first is more intuitive default
|
|
case 'size':
|
|
return 'size:desc'; // Largest first is more intuitive default
|
|
default:
|
|
// If it's already in new format or unknown, return as is
|
|
return sortValue.includes(':') ? sortValue : 'name:asc';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Save sort preference to storage
|
|
* @param {string} sortValue - The sort value to save
|
|
*/
|
|
saveSortPreference(sortValue) {
|
|
if (this.pageState.viewMode === 'excluded') {
|
|
this.pageState.excludedViewState = {
|
|
...(this.pageState.excludedViewState || {}),
|
|
sortBy: sortValue,
|
|
};
|
|
return;
|
|
}
|
|
setStorageItem(`${this.pageType}_sort`, sortValue);
|
|
}
|
|
|
|
/**
|
|
* Open model page on Civitai
|
|
* @param {string} modelName - Name of the model
|
|
*/
|
|
openCivitai(modelName) {
|
|
// Get card selector based on page type
|
|
const cardSelector = this.pageType === 'loras'
|
|
? `.model-card[data-name="${modelName}"]`
|
|
: `.checkpoint-card[data-name="${modelName}"]`;
|
|
|
|
const card = document.querySelector(cardSelector);
|
|
if (!card) return;
|
|
|
|
const metaData = JSON.parse(card.dataset.meta);
|
|
const civitaiId = metaData.modelId;
|
|
const versionId = metaData.id;
|
|
|
|
openCivitaiByMetadata(civitaiId, versionId, modelName);
|
|
}
|
|
|
|
/**
|
|
* Reset and reload the models list
|
|
*/
|
|
async resetAndReload(updateFolders = false) {
|
|
if (!this.api) {
|
|
console.error('API methods not registered');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await this.api.resetAndReload(updateFolders);
|
|
|
|
// Refresh sidebar after reload if folders were updated
|
|
if (updateFolders && this.sidebarManager) {
|
|
await this.sidebarManager.refresh();
|
|
}
|
|
} catch (error) {
|
|
console.error(`Error reloading ${this.pageType}:`, error);
|
|
showToast('toast.controls.reloadFailed', { pageType: this.pageType, message: error.message }, 'error');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Refresh models list
|
|
* @param {boolean} fullRebuild - Whether to perform a full rebuild
|
|
*/
|
|
async refreshModels(fullRebuild = false) {
|
|
if (!this.api) {
|
|
console.error('API methods not registered');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await this.api.refreshModels(fullRebuild);
|
|
|
|
// Refresh sidebar after rebuild
|
|
if (this.sidebarManager) {
|
|
await this.sidebarManager.refresh();
|
|
}
|
|
} catch (error) {
|
|
console.error(`Error ${fullRebuild ? 'rebuilding' : 'refreshing'} ${this.pageType}:`, error);
|
|
showToast('toast.controls.refreshFailed', { action: fullRebuild ? 'rebuild' : 'refresh', pageType: this.pageType, message: error.message }, 'error');
|
|
}
|
|
|
|
if (window.modelDuplicatesManager) {
|
|
// Update duplicates badge after refresh
|
|
window.modelDuplicatesManager.updateDuplicatesBadgeAfterRefresh();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fetch metadata from Civitai (available for both LoRAs and Checkpoints)
|
|
*/
|
|
async fetchFromCivitai() {
|
|
if (!this.api) {
|
|
console.error('API methods not registered');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await this.api.fetchFromCivitai();
|
|
} catch (error) {
|
|
console.error('Error fetching metadata:', error);
|
|
showToast('toast.controls.fetchMetadataFailed', { message: error.message }, 'error');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Show download modal
|
|
*/
|
|
showDownloadModal() {
|
|
this.api.showDownloadModal();
|
|
}
|
|
|
|
/**
|
|
* Toggle bulk mode
|
|
*/
|
|
toggleBulkMode() {
|
|
this.api.toggleBulkMode();
|
|
}
|
|
|
|
/**
|
|
* Clear custom filter
|
|
*/
|
|
async clearCustomFilter() {
|
|
if (!this.api) {
|
|
console.error('API methods not registered');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await this.api.clearCustomFilter();
|
|
} catch (error) {
|
|
console.error('Error clearing custom filter:', error);
|
|
showToast('toast.controls.clearFilterFailed', { message: error.message }, 'error');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize the favorites filter button state
|
|
*/
|
|
initFavoritesFilter() {
|
|
const favoriteFilterBtn = document.getElementById('favoriteFilterBtn');
|
|
if (favoriteFilterBtn) {
|
|
// Get current state from session storage with page-specific key
|
|
const storageKey = `show_favorites_only_${this.pageType}`;
|
|
const showFavoritesOnly = getSessionItem(storageKey, false);
|
|
|
|
// Update button state
|
|
if (showFavoritesOnly) {
|
|
favoriteFilterBtn.classList.add('active');
|
|
}
|
|
|
|
// Update app state
|
|
this.pageState.showFavoritesOnly = showFavoritesOnly;
|
|
}
|
|
|
|
this.updateActionButtonStates();
|
|
}
|
|
|
|
/**
|
|
* Initialize update availability filter button state
|
|
*/
|
|
initUpdateAvailableFilter() {
|
|
const storageKey = `show_update_available_only_${this.pageType}`;
|
|
const storedValue = getSessionItem(storageKey, false);
|
|
const showUpdatesOnly = storedValue === true || storedValue === 'true';
|
|
|
|
this.pageState.showUpdateAvailableOnly = showUpdatesOnly;
|
|
|
|
const updateFilterBtn = document.getElementById('updateFilterBtn');
|
|
if (updateFilterBtn) {
|
|
updateFilterBtn.classList.toggle('active', showUpdatesOnly);
|
|
}
|
|
|
|
this.updateActionButtonStates();
|
|
}
|
|
|
|
/**
|
|
* Toggle favorites-only filter and reload models
|
|
*/
|
|
async toggleFavoritesOnly() {
|
|
if (this.pageState.viewMode === 'excluded') {
|
|
return;
|
|
}
|
|
const favoriteFilterBtn = document.getElementById('favoriteFilterBtn');
|
|
|
|
// Toggle the filter state in storage
|
|
const storageKey = `show_favorites_only_${this.pageType}`;
|
|
const currentState = this.pageState.showFavoritesOnly;
|
|
const newState = !currentState;
|
|
|
|
// Update session storage
|
|
setSessionItem(storageKey, newState);
|
|
|
|
// Update state
|
|
this.pageState.showFavoritesOnly = newState;
|
|
|
|
// Update button appearance
|
|
if (favoriteFilterBtn) {
|
|
favoriteFilterBtn.classList.toggle('active', newState);
|
|
}
|
|
|
|
// Reload models with new filter
|
|
await this.resetAndReload(true);
|
|
}
|
|
|
|
/**
|
|
* Toggle update-available-only filter and reload models
|
|
*/
|
|
async toggleUpdateAvailableOnly() {
|
|
if (this.pageState.viewMode === 'excluded') {
|
|
return;
|
|
}
|
|
const updateFilterBtn = document.getElementById('updateFilterBtn');
|
|
const storageKey = `show_update_available_only_${this.pageType}`;
|
|
const newState = !this.pageState.showUpdateAvailableOnly;
|
|
|
|
setSessionItem(storageKey, newState);
|
|
|
|
this.pageState.showUpdateAvailableOnly = newState;
|
|
|
|
if (updateFilterBtn) {
|
|
updateFilterBtn.classList.toggle('active', newState);
|
|
}
|
|
|
|
await this.resetAndReload(true);
|
|
}
|
|
|
|
cloneFilters(filters = this.pageState.filters) {
|
|
return JSON.parse(JSON.stringify(filters || {}));
|
|
}
|
|
|
|
buildExcludedFilters(search = '') {
|
|
return {
|
|
baseModel: [],
|
|
tags: {},
|
|
license: {},
|
|
modelTypes: [],
|
|
search,
|
|
tagLogic: 'any',
|
|
};
|
|
}
|
|
|
|
applyFilterState(filters) {
|
|
this.pageState.filters = filters;
|
|
|
|
if (window.filterManager) {
|
|
window.filterManager.filters = window.filterManager.initializeFilters(filters);
|
|
window.filterManager.updateActiveFiltersCount();
|
|
if (typeof window.filterManager.updateSelections === 'function') {
|
|
window.filterManager.updateSelections();
|
|
}
|
|
window.filterManager.closeFilterPanel();
|
|
}
|
|
}
|
|
|
|
updateActionButtonStates() {
|
|
const favoriteFilterBtn = document.getElementById('favoriteFilterBtn');
|
|
if (favoriteFilterBtn) {
|
|
favoriteFilterBtn.classList.toggle('active', Boolean(this.pageState.showFavoritesOnly));
|
|
}
|
|
|
|
const updateFilterBtn = document.getElementById('updateFilterBtn');
|
|
if (updateFilterBtn) {
|
|
updateFilterBtn.classList.toggle('active', Boolean(this.pageState.showUpdateAvailableOnly));
|
|
}
|
|
}
|
|
|
|
syncExcludedViewState() {
|
|
const isExcludedView = this.pageState.viewMode === 'excluded';
|
|
const sortSelect = document.getElementById('sortSelect');
|
|
const searchInput = document.getElementById('searchInput');
|
|
const excludedBanner = document.getElementById('excludedViewBanner');
|
|
const filterButton = document.getElementById('filterButton');
|
|
const breadcrumbContainer = document.getElementById('breadcrumbContainer');
|
|
const duplicatesBanner = document.getElementById('duplicatesBanner');
|
|
const alphabetBarContainer = document.querySelector('.alphabet-bar-container');
|
|
const hiddenSelectors = [
|
|
'[data-action="fetch"]',
|
|
'[data-action="download"]',
|
|
'[data-action="bulk"]',
|
|
'[data-action="find-duplicates"]',
|
|
'#favoriteFilterBtn',
|
|
'.update-filter-group',
|
|
];
|
|
const customFilterIndicator = document.getElementById('customFilterIndicator');
|
|
|
|
document.body.classList.toggle('excluded-view-active', isExcludedView);
|
|
excludedBanner?.classList.toggle('hidden', !isExcludedView);
|
|
breadcrumbContainer?.classList.toggle('hidden', isExcludedView);
|
|
alphabetBarContainer?.classList.toggle('hidden', isExcludedView);
|
|
|
|
if (duplicatesBanner && isExcludedView) {
|
|
duplicatesBanner.style.display = 'none';
|
|
}
|
|
|
|
hiddenSelectors.forEach((selector) => {
|
|
document.querySelectorAll(selector).forEach((element) => {
|
|
element.classList.toggle('hidden', isExcludedView);
|
|
});
|
|
});
|
|
|
|
if (customFilterIndicator && isExcludedView) {
|
|
customFilterIndicator.classList.add('hidden');
|
|
}
|
|
|
|
if (filterButton) {
|
|
filterButton.disabled = isExcludedView;
|
|
filterButton.classList.toggle('hidden', isExcludedView);
|
|
}
|
|
|
|
const activeFiltersCount = document.getElementById('activeFiltersCount');
|
|
if (activeFiltersCount && isExcludedView) {
|
|
activeFiltersCount.style.display = 'none';
|
|
}
|
|
|
|
if (sortSelect) {
|
|
sortSelect.value = this.pageState.sortBy;
|
|
}
|
|
if (searchInput) {
|
|
searchInput.value = this.pageState.filters?.search || '';
|
|
}
|
|
|
|
this.updateActionButtonStates();
|
|
|
|
if (this.sidebarManager) {
|
|
const shouldShowSidebar = !isExcludedView && state?.global?.settings?.show_folder_sidebar !== false;
|
|
this.sidebarManager.setSidebarEnabled(shouldShowSidebar).catch((error) => {
|
|
console.error('Failed to update sidebar visibility:', error);
|
|
});
|
|
}
|
|
}
|
|
|
|
suspendInteractiveModes() {
|
|
const snapshot = {
|
|
bulkMode: Boolean(state.bulkMode),
|
|
duplicatesMode: Boolean(this.pageState.duplicatesMode),
|
|
};
|
|
|
|
if (snapshot.bulkMode && window.bulkManager?.toggleBulkMode) {
|
|
window.bulkManager.toggleBulkMode();
|
|
}
|
|
|
|
if (snapshot.duplicatesMode && window.modelDuplicatesManager?.exitDuplicateMode) {
|
|
window.modelDuplicatesManager.exitDuplicateMode();
|
|
}
|
|
|
|
return snapshot;
|
|
}
|
|
|
|
async restoreInteractiveModes(snapshot = {}) {
|
|
if (snapshot.bulkMode && !state.bulkMode && window.bulkManager?.toggleBulkMode) {
|
|
window.bulkManager.toggleBulkMode();
|
|
}
|
|
|
|
if (!snapshot.duplicatesMode || this.pageState.duplicatesMode) {
|
|
return;
|
|
}
|
|
|
|
const duplicatesManager = window.modelDuplicatesManager;
|
|
if (!duplicatesManager) {
|
|
return;
|
|
}
|
|
|
|
if (typeof duplicatesManager.enterDuplicateMode === 'function' &&
|
|
Array.isArray(duplicatesManager.duplicateGroups) &&
|
|
duplicatesManager.duplicateGroups.length > 0) {
|
|
duplicatesManager.enterDuplicateMode();
|
|
return;
|
|
}
|
|
|
|
if (typeof duplicatesManager.findDuplicates === 'function') {
|
|
await duplicatesManager.findDuplicates();
|
|
}
|
|
}
|
|
|
|
syncCustomFilterIndicator() {
|
|
const indicator = document.getElementById('customFilterIndicator');
|
|
if (!indicator) {
|
|
return;
|
|
}
|
|
|
|
if (this.pageState.viewMode === 'excluded') {
|
|
indicator.classList.add('hidden');
|
|
return;
|
|
}
|
|
|
|
if (typeof this.checkCustomFilters === 'function') {
|
|
this.checkCustomFilters();
|
|
}
|
|
}
|
|
|
|
async enterExcludedView() {
|
|
if (this.pageState.viewMode === 'excluded') {
|
|
return;
|
|
}
|
|
|
|
const interactionSnapshot = this.suspendInteractiveModes();
|
|
|
|
this.pageState.activeViewSnapshot = {
|
|
sortBy: this.pageState.sortBy,
|
|
activeFolder: this.pageState.activeFolder,
|
|
activeLetterFilter: this.pageState.activeLetterFilter ?? null,
|
|
showFavoritesOnly: this.pageState.showFavoritesOnly,
|
|
showUpdateAvailableOnly: this.pageState.showUpdateAvailableOnly,
|
|
bulkMode: interactionSnapshot.bulkMode,
|
|
duplicatesMode: interactionSnapshot.duplicatesMode,
|
|
filters: this.cloneFilters(),
|
|
};
|
|
|
|
const excludedState = this.pageState.excludedViewState || {
|
|
sortBy: 'name:asc',
|
|
search: '',
|
|
};
|
|
|
|
this.pageState.viewMode = 'excluded';
|
|
this.pageState.sortBy = excludedState.sortBy || 'name:asc';
|
|
this.pageState.currentPage = 1;
|
|
this.pageState.activeFolder = null;
|
|
this.pageState.activeLetterFilter = null;
|
|
this.pageState.showFavoritesOnly = false;
|
|
this.pageState.showUpdateAvailableOnly = false;
|
|
|
|
this.applyFilterState(this.buildExcludedFilters(excludedState.search || ''));
|
|
this.syncExcludedViewState();
|
|
await this.resetAndReload(false);
|
|
}
|
|
|
|
async exitExcludedView() {
|
|
if (this.pageState.viewMode !== 'excluded') {
|
|
return;
|
|
}
|
|
|
|
this.pageState.excludedViewState = {
|
|
...(this.pageState.excludedViewState || {}),
|
|
sortBy: this.pageState.sortBy,
|
|
search: this.pageState.filters?.search || '',
|
|
};
|
|
|
|
const snapshot = this.pageState.activeViewSnapshot || {};
|
|
this.pageState.viewMode = 'active';
|
|
this.pageState.sortBy = snapshot.sortBy || this.convertLegacySortFormat(getStorageItem(`${this.pageType}_sort`) || 'name:asc');
|
|
this.pageState.currentPage = 1;
|
|
this.pageState.activeFolder = snapshot.activeFolder ?? getStorageItem(`${this.pageType}_activeFolder`);
|
|
this.pageState.activeLetterFilter = snapshot.activeLetterFilter ?? null;
|
|
this.pageState.showFavoritesOnly = Boolean(snapshot.showFavoritesOnly);
|
|
this.pageState.showUpdateAvailableOnly = Boolean(snapshot.showUpdateAvailableOnly);
|
|
this.applyFilterState(snapshot.filters || this.buildExcludedFilters(''));
|
|
this.pageState.activeViewSnapshot = null;
|
|
|
|
this.syncExcludedViewState();
|
|
await this.resetAndReload(true);
|
|
this.syncCustomFilterIndicator();
|
|
await this.restoreInteractiveModes(snapshot);
|
|
}
|
|
|
|
/**
|
|
* Find duplicate models
|
|
*/
|
|
findDuplicates() {
|
|
if (window.modelDuplicatesManager) {
|
|
// Change to toggle functionality
|
|
window.modelDuplicatesManager.toggleDuplicateMode();
|
|
} else {
|
|
console.error('Model duplicates manager not available');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clean up resources
|
|
*/
|
|
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();
|
|
}
|
|
}
|
|
}
|