Refactor state management to support hierarchical structure and page-specific states

- Introduced a new hierarchical state structure to manage global and page-specific states, enhancing organization and maintainability.
- Updated various managers and components to utilize the new state structure, ensuring consistent access to page-specific data.
- Removed the initSettings function and replaced it with initPageState for better initialization of page-specific states.
- Adjusted imports across multiple files to accommodate the new state management approach, improving code clarity.
This commit is contained in:
Will Miao
2025-03-19 21:12:04 +08:00
parent d7a253cba3
commit 90f74018ae
9 changed files with 240 additions and 121 deletions

View File

@@ -91,16 +91,17 @@ export class BulkManager {
// Set text content without the icon
countElement.textContent = `${state.selectedLoras.size} selected `;
// Re-add the caret icon with proper direction
const caretIcon = document.createElement('i');
// Use down arrow if strip is visible, up arrow if not
caretIcon.className = `fas fa-caret-${this.isStripVisible ? 'down' : 'up'} dropdown-caret`;
caretIcon.style.visibility = state.selectedLoras.size > 0 ? 'visible' : 'hidden';
countElement.appendChild(caretIcon);
// If there are no selections, hide the thumbnail strip
if (state.selectedLoras.size === 0) {
this.hideThumbnailStrip();
// Update caret icon if it exists
const existingCaret = countElement.querySelector('.dropdown-caret');
if (existingCaret) {
existingCaret.className = `fas fa-caret-${this.isStripVisible ? 'down' : 'up'} dropdown-caret`;
existingCaret.style.visibility = state.selectedLoras.size > 0 ? 'visible' : 'hidden';
} else {
// Create new caret icon if it doesn't exist
const caretIcon = document.createElement('i');
caretIcon.className = `fas fa-caret-${this.isStripVisible ? 'down' : 'up'} dropdown-caret`;
caretIcon.style.visibility = state.selectedLoras.size > 0 ? 'visible' : 'hidden';
countElement.appendChild(caretIcon);
}
}
}
@@ -252,12 +253,20 @@ export class BulkManager {
hideThumbnailStrip() {
const strip = document.querySelector('.selected-thumbnails-strip');
if (strip) {
if (strip && this.isStripVisible) { // Only hide if actually visible
strip.classList.remove('visible');
// Update strip visibility state and caret direction
// Update strip visibility state
this.isStripVisible = false;
this.updateSelectedCount(); // Update caret
// Update caret without triggering another hide
const countElement = document.getElementById('selectedCount');
if (countElement) {
const caret = countElement.querySelector('.dropdown-caret');
if (caret) {
caret.className = 'fas fa-caret-up dropdown-caret';
}
}
// Wait for animation to complete before removing
setTimeout(() => {
@@ -340,4 +349,4 @@ export class BulkManager {
}
// Create a singleton instance
export const bulkManager = new BulkManager();
export const bulkManager = new BulkManager();

View File

@@ -1,11 +1,12 @@
import { BASE_MODELS, BASE_MODEL_CLASSES } from '../utils/constants.js';
import { state } from '../state/index.js';
import { state, getCurrentPageState } from '../state/index.js';
import { showToast } from '../utils/uiHelpers.js';
import { resetAndReload } from '../api/loraApi.js';
export class FilterManager {
constructor() {
this.filters = {
const pageState = getCurrentPageState();
this.filters = pageState.filters || {
baseModel: [],
tags: []
};
@@ -219,7 +220,8 @@ export class FilterManager {
localStorage.setItem('loraFilters', JSON.stringify(this.filters));
// Update state with current filters
state.filters = { ...this.filters };
const pageState = getCurrentPageState();
pageState.filters = { ...this.filters };
// Reload loras with filters applied
await resetAndReload();
@@ -258,7 +260,8 @@ export class FilterManager {
};
// Update state
state.filters = { ...this.filters };
const pageState = getCurrentPageState();
pageState.filters = { ...this.filters };
// Update UI
this.updateTagSelections();

View File

@@ -5,7 +5,7 @@
import { SearchManager } from './SearchManager.js';
import { appendLoraCards } from '../api/loraApi.js';
import { resetAndReload } from '../api/loraApi.js';
import { state } from '../state/index.js';
import { state, getCurrentPageState } from '../state/index.js';
import { showToast } from '../utils/uiHelpers.js';
export class LoraSearchManager extends SearchManager {
@@ -19,12 +19,14 @@ export class LoraSearchManager extends SearchManager {
// Store this instance in the state
if (state) {
state.searchManager = this;
const pageState = getCurrentPageState();
pageState.searchManager = this;
}
}
async performSearch() {
const searchTerm = this.searchInput.value.trim().toLowerCase();
const pageState = getCurrentPageState();
// Log the search attempt for debugging
console.log('LoraSearchManager performSearch called with:', searchTerm);
@@ -42,8 +44,8 @@ export class LoraSearchManager extends SearchManager {
}
if (!searchTerm) {
if (state) {
state.currentPage = 1;
if (pageState) {
pageState.currentPage = 1;
}
await resetAndReload();
return;
@@ -58,15 +60,15 @@ export class LoraSearchManager extends SearchManager {
// Store current scroll position
const scrollPosition = window.pageYOffset || document.documentElement.scrollTop;
if (state) {
state.currentPage = 1;
state.hasMore = true;
if (pageState) {
pageState.currentPage = 1;
pageState.hasMore = true;
}
const url = new URL('/api/loras', window.location.origin);
url.searchParams.set('page', '1');
url.searchParams.set('page_size', '20');
url.searchParams.set('sort_by', state ? state.sortBy : 'name');
url.searchParams.set('sort_by', pageState ? pageState.sortBy : 'name');
url.searchParams.set('search', searchTerm);
url.searchParams.set('fuzzy', 'true');
@@ -80,8 +82,8 @@ export class LoraSearchManager extends SearchManager {
url.searchParams.set('search_tags', searchOptions.tags ? 'true' : 'false');
// Always send folder parameter if there is an active folder
if (state && state.activeFolder) {
url.searchParams.set('folder', state.activeFolder);
if (pageState && pageState.activeFolder) {
url.searchParams.set('folder', pageState.activeFolder);
// Add recursive parameter when recursive search is enabled
const recursive = this.recursiveSearchToggle ? this.recursiveSearchToggle.checked : false;
url.searchParams.set('recursive', recursive.toString());
@@ -102,14 +104,14 @@ export class LoraSearchManager extends SearchManager {
if (data.items.length === 0) {
grid.innerHTML = '<div class="no-results">No matching loras found</div>';
if (state) {
state.hasMore = false;
if (pageState) {
pageState.hasMore = false;
}
} else {
appendLoraCards(data.items);
if (state) {
state.hasMore = state.currentPage < data.total_pages;
state.currentPage++;
if (pageState) {
pageState.hasMore = pageState.currentPage < data.total_pages;
pageState.currentPage++;
}
}

View File

@@ -1,6 +1,6 @@
import { modalManager } from './ModalManager.js';
import { showToast } from '../utils/uiHelpers.js';
import { state, saveSettings } from '../state/index.js';
import { state } from '../state/index.js';
import { resetAndReload } from '../api/loraApi.js';
export class SettingsManager {
@@ -41,13 +41,13 @@ export class SettingsManager {
// Set frontend settings from state
const blurMatureContentCheckbox = document.getElementById('blurMatureContent');
if (blurMatureContentCheckbox) {
blurMatureContentCheckbox.checked = state.settings.blurMatureContent;
blurMatureContentCheckbox.checked = state.global.settings.blurMatureContent;
}
const showOnlySFWCheckbox = document.getElementById('showOnlySFW');
if (showOnlySFWCheckbox) {
// Sync with state (backend will set this via template)
state.settings.show_only_sfw = showOnlySFWCheckbox.checked;
state.global.settings.show_only_sfw = showOnlySFWCheckbox.checked;
}
// Backend settings are loaded from the template directly
@@ -71,9 +71,11 @@ export class SettingsManager {
const showOnlySFW = document.getElementById('showOnlySFW').checked;
// Update frontend state and save to localStorage
state.settings.blurMatureContent = blurMatureContent;
state.settings.show_only_sfw = showOnlySFW;
saveSettings();
state.global.settings.blurMatureContent = blurMatureContent;
state.global.settings.show_only_sfw = showOnlySFW;
// Save settings to localStorage
localStorage.setItem('settings', JSON.stringify(state.global.settings));
try {
// Save backend settings via API
@@ -107,7 +109,7 @@ export class SettingsManager {
applyFrontendSettings() {
// Apply blur setting to existing content
const blurSetting = state.settings.blurMatureContent;
const blurSetting = state.global.settings.blurMatureContent;
document.querySelectorAll('.lora-card[data-nsfw="true"] .card-image').forEach(img => {
if (blurSetting) {
img.classList.add('nsfw-blur');