From 2f7e44a76fbae4a592b82f15baff1ef60a36f3b2 Mon Sep 17 00:00:00 2001 From: Will Miao <13051207myq@gmail.com> Date: Mon, 15 Sep 2025 10:30:06 +0800 Subject: [PATCH] refactor(settings): Update synchronization logic --- locales/de.json | 1 + locales/en.json | 1 + locales/es.json | 1 + locales/fr.json | 1 + locales/ja.json | 1 + locales/ko.json | 1 + locales/ru.json | 1 + locales/zh-CN.json | 19 ++-- locales/zh-TW.json | 1 + py/routes/misc_routes.py | 5 +- static/js/core.js | 4 +- static/js/managers/ExampleImagesManager.js | 104 ++++++--------------- static/js/managers/SettingsManager.js | 34 +++---- 13 files changed, 72 insertions(+), 102 deletions(-) diff --git a/locales/de.json b/locales/de.json index f0125334..0f667920 100644 --- a/locales/de.json +++ b/locales/de.json @@ -1166,6 +1166,7 @@ }, "exampleImages": { "pathUpdated": "Beispielbilder-Pfad erfolgreich aktualisiert", + "pathUpdateFailed": "Fehler beim Aktualisieren des Beispielbilder-Pfads: {message}", "downloadInProgress": "Download bereits in Bearbeitung", "enterLocationFirst": "Bitte geben Sie zuerst einen Download-Speicherort ein", "downloadStarted": "Beispielbilder-Download gestartet", diff --git a/locales/en.json b/locales/en.json index 24e4704e..306ba26b 100644 --- a/locales/en.json +++ b/locales/en.json @@ -1166,6 +1166,7 @@ }, "exampleImages": { "pathUpdated": "Example images path updated successfully", + "pathUpdateFailed": "Failed to update example images path: {message}", "downloadInProgress": "Download already in progress", "enterLocationFirst": "Please enter a download location first", "downloadStarted": "Example images download started", diff --git a/locales/es.json b/locales/es.json index a3e04cd8..f8aa33e9 100644 --- a/locales/es.json +++ b/locales/es.json @@ -1166,6 +1166,7 @@ }, "exampleImages": { "pathUpdated": "Ruta de imágenes de ejemplo actualizada exitosamente", + "pathUpdateFailed": "Error al actualizar la ruta de imágenes de ejemplo: {message}", "downloadInProgress": "Descarga ya en progreso", "enterLocationFirst": "Por favor introduce primero una ubicación de descarga", "downloadStarted": "Descarga de imágenes de ejemplo iniciada", diff --git a/locales/fr.json b/locales/fr.json index 38a6b1f5..7fc405b8 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -1166,6 +1166,7 @@ }, "exampleImages": { "pathUpdated": "Chemin des images d'exemple mis à jour avec succès", + "pathUpdateFailed": "Échec de la mise à jour du chemin des images d'exemple : {message}", "downloadInProgress": "Téléchargement déjà en cours", "enterLocationFirst": "Veuillez d'abord entrer un emplacement de téléchargement", "downloadStarted": "Téléchargement des images d'exemple démarré", diff --git a/locales/ja.json b/locales/ja.json index f8bdefc6..80488619 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -1166,6 +1166,7 @@ }, "exampleImages": { "pathUpdated": "例画像パスが正常に更新されました", + "pathUpdateFailed": "例画像パスの更新に失敗しました:{message}", "downloadInProgress": "ダウンロードは既に進行中です", "enterLocationFirst": "最初にダウンロード場所を入力してください", "downloadStarted": "例画像のダウンロードが開始されました", diff --git a/locales/ko.json b/locales/ko.json index 08ab8e38..bb8dbe37 100644 --- a/locales/ko.json +++ b/locales/ko.json @@ -1166,6 +1166,7 @@ }, "exampleImages": { "pathUpdated": "예시 이미지 경로가 성공적으로 업데이트되었습니다", + "pathUpdateFailed": "예시 이미지 경로 업데이트 실패: {message}", "downloadInProgress": "이미 다운로드가 진행 중입니다", "enterLocationFirst": "먼저 다운로드 위치를 입력해주세요", "downloadStarted": "예시 이미지 다운로드가 시작되었습니다", diff --git a/locales/ru.json b/locales/ru.json index 522ac612..99f61f5e 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -1166,6 +1166,7 @@ }, "exampleImages": { "pathUpdated": "Путь к примерам изображений успешно обновлен", + "pathUpdateFailed": "Не удалось обновить путь к примерам изображений: {message}", "downloadInProgress": "Загрузка уже в процессе", "enterLocationFirst": "Пожалуйста, сначала введите место загрузки", "downloadStarted": "Загрузка примеров изображений начата", diff --git a/locales/zh-CN.json b/locales/zh-CN.json index 5884f1fc..7c01d9ac 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -21,17 +21,17 @@ "disabled": "已禁用" }, "language": { - "select": "语言", - "select_help": "选择你喜欢的界面语言", + "select": "Language", + "select_help": "Choose your preferred language for the interface", "english": "English", "chinese_simplified": "中文(简体)", "chinese_traditional": "中文(繁体)", - "russian": "俄语", - "german": "德语", - "japanese": "日语", - "korean": "韩语", - "french": "法语", - "spanish": "西班牙语" + "russian": "Русский", + "german": "Deutsch", + "japanese": "日本語", + "korean": "한국어", + "french": "Français", + "spanish": "Español" }, "fileSize": { "zero": "0 字节", @@ -311,7 +311,7 @@ "proxyHostPlaceholder": "proxy.example.com", "proxyHostHelp": "代理服务器的主机名或IP地址", "proxyPort": "代理端口", - "proxyPortPlaceholder": "8080", + "proxyPortPlaceholder": "8080", "proxyPortHelp": "代理服务器的端口号", "proxyUsername": "用户名 (可选)", "proxyUsernamePlaceholder": "用户名", @@ -1166,6 +1166,7 @@ }, "exampleImages": { "pathUpdated": "示例图片路径更新成功", + "pathUpdateFailed": "更新示例图片路径失败:{message}", "downloadInProgress": "下载已在进行中", "enterLocationFirst": "请先输入下载位置", "downloadStarted": "示例图片下载已开始", diff --git a/locales/zh-TW.json b/locales/zh-TW.json index 5e982e5f..4846649c 100644 --- a/locales/zh-TW.json +++ b/locales/zh-TW.json @@ -1166,6 +1166,7 @@ }, "exampleImages": { "pathUpdated": "範例圖片路徑已更新", + "pathUpdateFailed": "更新範例圖片路徑失敗:{message}", "downloadInProgress": "下載已在進行中", "enterLocationFirst": "請先輸入下載位置", "downloadStarted": "範例圖片下載已開始", diff --git a/py/routes/misc_routes.py b/py/routes/misc_routes.py index 494eca9b..32f51f68 100644 --- a/py/routes/misc_routes.py +++ b/py/routes/misc_routes.py @@ -139,7 +139,10 @@ class MiscRoutes: 'proxy_host', 'proxy_port', 'proxy_username', - 'proxy_password' + 'proxy_password', + 'example_images_path', + 'optimizeExampleImages', + 'autoDownloadExampleImages' ] # Build response with only the keys that should be synced diff --git a/static/js/core.js b/static/js/core.js index 779f3094..c2ad6cdb 100644 --- a/static/js/core.js +++ b/static/js/core.js @@ -7,7 +7,7 @@ import { HeaderManager } from './components/Header.js'; import { settingsManager } from './managers/SettingsManager.js'; import { moveManager } from './managers/MoveManager.js'; import { bulkManager } from './managers/BulkManager.js'; -import { exampleImagesManager } from './managers/ExampleImagesManager.js'; +import { ExampleImagesManager } from './managers/ExampleImagesManager.js'; import { helpManager } from './managers/HelpManager.js'; import { bannerService } from './managers/BannerService.js'; import { initTheme, initBackToTop } from './utils/uiHelpers.js'; @@ -50,7 +50,7 @@ export class AppCore { bannerService.initialize(); window.modalManager = modalManager; window.settingsManager = settingsManager; - window.exampleImagesManager = exampleImagesManager; + window.exampleImagesManager = new ExampleImagesManager(); window.helpManager = helpManager; window.moveManager = moveManager; window.bulkManager = bulkManager; diff --git a/static/js/managers/ExampleImagesManager.js b/static/js/managers/ExampleImagesManager.js index 60006400..5ef58702 100644 --- a/static/js/managers/ExampleImagesManager.js +++ b/static/js/managers/ExampleImagesManager.js @@ -1,9 +1,10 @@ import { showToast } from '../utils/uiHelpers.js'; import { state } from '../state/index.js'; import { getStorageItem, setStorageItem } from '../utils/storageHelpers.js'; +import { settingsManager } from './SettingsManager.js'; // ExampleImagesManager.js -class ExampleImagesManager { +export class ExampleImagesManager { constructor() { this.isDownloading = false; this.isPaused = false; @@ -27,7 +28,12 @@ class ExampleImagesManager { } // Initialize the manager - initialize() { + async initialize() { + // Wait for settings to be initialized before proceeding + if (window.settingsManager) { + await window.settingsManager.waitForInitialization(); + } + // Initialize event listeners this.initEventListeners(); @@ -78,86 +84,41 @@ class ExampleImagesManager { // Get custom path input element const pathInput = document.getElementById('exampleImagesPath'); - // Set path from storage if available - const savedPath = getStorageItem('example_images_path', ''); - if (savedPath) { + // Set path from backend settings + const savedPath = state.global.settings.example_images_path || ''; + if (pathInput) { pathInput.value = savedPath; // Enable download button if path is set - this.updateDownloadButtonState(true); - - // Sync the saved path with the backend - try { - const response = await fetch('/api/settings', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - example_images_path: savedPath - }) - }); - - if (!response.ok) { - throw new Error(`HTTP error! Status: ${response.status}`); - } - - const data = await response.json(); - if (!data.success) { - console.error('Failed to sync example images path with backend:', data.error); - } - } catch (error) { - console.error('Failed to sync saved path with backend:', error); - } - } else { - // Disable download button if no path is set - this.updateDownloadButtonState(false); + this.updateDownloadButtonState(!!savedPath); } // Add event listener to validate path input - pathInput.addEventListener('input', async () => { - const hasPath = pathInput.value.trim() !== ''; - this.updateDownloadButtonState(hasPath); - - // Save path to storage when changed - if (hasPath) { - setStorageItem('example_images_path', pathInput.value); + if (pathInput) { + pathInput.addEventListener('input', async () => { + const hasPath = pathInput.value.trim() !== ''; + this.updateDownloadButtonState(hasPath); - // Update path in backend settings + // Update path in backend settings using settingsManager try { - const response = await fetch('/api/settings', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - example_images_path: pathInput.value - }) - }); - - if (!response.ok) { - throw new Error(`HTTP error! Status: ${response.status}`); - } - - const data = await response.json(); - if (!data.success) { - console.error('Failed to update example images path in backend:', data.error); - } else { + await settingsManager.saveSetting('example_images_path', pathInput.value); + if (hasPath) { showToast('toast.exampleImages.pathUpdated', {}, 'success'); } } catch (error) { console.error('Failed to update example images path:', error); + showToast('toast.exampleImages.pathUpdateFailed', { message: error.message }, 'error'); } - } - // Setup or clear auto download based on path availability - if (state.global.settings.autoDownloadExampleImages) { - if (hasPath) { - this.setupAutoDownload(); - } else { - this.clearAutoDownload(); + // Setup or clear auto download based on path availability + if (state.global.settings.autoDownloadExampleImages) { + if (hasPath) { + this.setupAutoDownload(); + } else { + this.clearAutoDownload(); + } } - } - }); + }); + } } catch (error) { console.error('Failed to initialize path options:', error); } @@ -255,7 +216,7 @@ class ExampleImagesManager { return; } - const optimize = document.getElementById('optimizeExampleImages').checked; + const optimize = state.global.settings.optimizeExampleImages; const response = await fetch('/api/download-example-images', { method: 'POST', @@ -746,7 +707,7 @@ class ExampleImagesManager { console.log('Performing auto download check...'); const outputDir = document.getElementById('exampleImagesPath').value; - const optimize = document.getElementById('optimizeExampleImages').checked; + const optimize = state.global.settings.optimizeExampleImages; const response = await fetch('/api/download-example-images', { method: 'POST', @@ -771,6 +732,3 @@ class ExampleImagesManager { } } } - -// Create singleton instance -export const exampleImagesManager = new ExampleImagesManager(); diff --git a/static/js/managers/SettingsManager.js b/static/js/managers/SettingsManager.js index 0767baab..bafb520d 100644 --- a/static/js/managers/SettingsManager.js +++ b/static/js/managers/SettingsManager.js @@ -47,8 +47,6 @@ export class SettingsManager { 'autoplayOnHover', 'displayDensity', 'cardInfoDisplay', - 'optimizeExampleImages', - 'autoDownloadExampleImages', 'includeTriggerWords' ]; @@ -76,14 +74,6 @@ export class SettingsManager { state.global.settings.autoplayOnHover = false; } - if (state.global.settings.optimizeExampleImages === undefined) { - state.global.settings.optimizeExampleImages = true; - } - - if (state.global.settings.autoDownloadExampleImages === undefined) { - state.global.settings.autoDownloadExampleImages = true; - } - if (state.global.settings.cardInfoDisplay === undefined) { state.global.settings.cardInfoDisplay = 'always'; } @@ -147,7 +137,10 @@ export class SettingsManager { proxy_host: '', proxy_port: '', proxy_username: '', - proxy_password: '' + proxy_password: '', + example_images_path: '', + optimizeExampleImages: true, + autoDownloadExampleImages: true }; Object.keys(backendDefaults).forEach(key => { @@ -172,8 +165,6 @@ export class SettingsManager { 'autoplayOnHover', 'displayDensity', 'cardInfoDisplay', - 'optimizeExampleImages', - 'autoDownloadExampleImages', 'includeTriggerWords' ]; @@ -203,7 +194,10 @@ export class SettingsManager { 'proxy_host', 'proxy_port', 'proxy_username', - 'proxy_password' + 'proxy_password', + 'example_images_path', + 'optimizeExampleImages', + 'autoDownloadExampleImages' ]; return backendKeys.includes(settingKey); } @@ -218,7 +212,7 @@ export class SettingsManager { try { const payload = {}; payload[settingKey] = value; - + const response = await fetch('/api/settings', { method: 'POST', headers: { @@ -230,6 +224,12 @@ export class SettingsManager { if (!response.ok) { throw new Error('Failed to save setting to backend'); } + + // Parse response and check for success + const data = await response.json(); + if (data.success === false) { + throw new Error(data.error || 'Failed to save setting to backend'); + } } catch (error) { console.error(`Failed to save backend setting ${settingKey}:`, error); throw error; @@ -985,8 +985,6 @@ export class SettingsManager { await this.saveSetting(settingKey, value); } - showToast('toast.settings.settingsUpdated', { setting: settingKey.replace(/_/g, ' ') }, 'success'); - // Apply frontend settings immediately this.applyFrontendSettings(); @@ -999,8 +997,10 @@ export class SettingsManager { if (value === 'compact') densityName = "Compact"; showToast('toast.settings.displayDensitySet', { density: densityName }, 'success'); + return; } + showToast('toast.settings.settingsUpdated', { setting: settingKey.replace(/_/g, ' ') }, 'success'); } catch (error) { showToast('toast.settings.settingSaveFailed', { message: error.message }, 'error'); }