diff --git a/src/ui/CheckboxSyncPluginSettingTab.ts b/src/ui/CheckboxSyncPluginSettingTab.ts index 6b5f3fe..0a83935 100644 --- a/src/ui/CheckboxSyncPluginSettingTab.ts +++ b/src/ui/CheckboxSyncPluginSettingTab.ts @@ -1,10 +1,11 @@ import { App, ButtonComponent, DropdownComponent, Notice, PluginSettingTab, Setting, TextComponent, ToggleComponent } from "obsidian"; import CheckboxSyncPlugin from "../main"; -import { CheckboxState, DEFAULT_SETTINGS } from "../types"; +import { CheckboxState, CheckboxSyncPluginSettings, DEFAULT_SETTINGS } from "../types"; import { ConfirmModal, InfoModal, SaveConfirmModal } from "./modals"; import { Mutex } from "async-mutex"; import { SettingGroup } from "./SettingGroup"; import { EnableParentSyncSettingComponent } from "./components/EnableParentSyncSettingComponent"; +import { ValidationError } from "./validation/types"; export class CheckboxSyncPluginSettingTab extends PluginSettingTab { plugin: CheckboxSyncPlugin; @@ -12,7 +13,7 @@ export class CheckboxSyncPluginSettingTab extends PluginSettingTab { private uncheckedSymbolsInput: TextComponent; private ignoreSymbolsInput: TextComponent; private unknownPolicyDropdown: DropdownComponent; - private parentToggle: ToggleComponent; + // private parentToggle: ToggleComponent; private childToggle: ToggleComponent; private enableAutomaticFileSyncToggle: ToggleComponent; @@ -307,12 +308,25 @@ export class CheckboxSyncPluginSettingTab extends PluginSettingTab { private resetInputsToSavedSettings() { const settings = this.plugin.settings; // Получаем текущие сохраненные настройки + for (const group of this.settingGroups) { + for (const component of group.components) { + const key = component.getSettingKey(); + if (key in settings) { + try { + component.setValueInUi(settings[key]); + } catch (error) { + console.error(`Error resetting UI for component ${key}:`, error); + } + } + } + } + // Обновляем значения всех полей ввода this.checkedSymbolsInput.setValue(this.arrayToJsonString(settings.checkedSymbols)); this.uncheckedSymbolsInput.setValue(this.arrayToJsonString(settings.uncheckedSymbols)); this.ignoreSymbolsInput.setValue(this.arrayToJsonString(settings.ignoreSymbols)); this.unknownPolicyDropdown.setValue(settings.unknownSymbolPolicy); - this.parentToggle.setValue(settings.enableAutomaticParentState); + // this.parentToggle.setValue(settings.enableAutomaticParentState); this.childToggle.setValue(settings.enableAutomaticChildState); this.enableAutomaticFileSyncToggle.setValue(settings.enableAutomaticFileSync); @@ -396,12 +410,36 @@ export class CheckboxSyncPluginSettingTab extends PluginSettingTab { * Does NOT interact with UI feedback (buttons, notices, error display). */ private async validateAndSaveSettings(): Promise { + + let errors: ValidationError[] = []; // Массив для сбора всех ошибок валидации + let newSettingsData: Partial = {}; // Объект для новых данных + + + for (const group of this.settingGroups) { + for (const component of group.components) { + const key = component.getSettingKey(); + try { + const value = component.getValueFromUi(); + const validationError = component.validate(); + if (validationError) { + errors.push(validationError); // Добавляем ошибку + } else { + // Если индивидуальная валидация прошла, сохраняем значение + newSettingsData[key] = value; + } + } catch (err: any) { + console.error(`Error processing component ${key}:`, err); + errors.push({ field: key, message: `Failed to read value: ${err.message}` }); + } + } + } + // 1. Считываем значения из UI const checkedValue = this.checkedSymbolsInput.getValue(); const uncheckedValue = this.uncheckedSymbolsInput.getValue(); const ignoreValue = this.ignoreSymbolsInput.getValue(); const policyValue = this.unknownPolicyDropdown.getValue() as CheckboxState; - const parentValue = this.parentToggle.getValue(); + // const parentValue = this.parentToggle.getValue(); const childValue = this.childToggle.getValue(); const automaticFileSyncValue = this.enableAutomaticFileSyncToggle.getValue(); @@ -447,15 +485,23 @@ export class CheckboxSyncPluginSettingTab extends PluginSettingTab { throw new Error("Unchecked symbols list cannot be empty."); } + newSettingsData.checkedSymbols = checkedSymbolsArray; + newSettingsData.uncheckedSymbols = uncheckedSymbolsArray; + newSettingsData.ignoreSymbols = ignoreSymbolsArray; + newSettingsData.unknownSymbolPolicy = policyValue; + newSettingsData.enableAutomaticChildState = childValue; + newSettingsData.enableAutomaticFileSync = automaticFileSyncValue; + + if (errors.length > 0) { + // Формируем сообщение об ошибке + const errorMessage = errors.map(e => `❌ ${e.field ? `[${e.field}]: ` : ''}${e.message}`).join('\n'); + throw new Error(errorMessage); + } + + // 6. Вызываем сохранение -> await (может кинуть ошибку) await this.plugin.updateSettings(settings => { - settings.checkedSymbols = checkedSymbolsArray; - settings.uncheckedSymbols = uncheckedSymbolsArray; - settings.ignoreSymbols = ignoreSymbolsArray; - settings.unknownSymbolPolicy = policyValue; - settings.enableAutomaticParentState = parentValue; - settings.enableAutomaticChildState = childValue; - settings.enableAutomaticFileSync = automaticFileSyncValue; + Object.assign(settings, newSettingsData); }); } } \ No newline at end of file diff --git a/src/ui/components/BaseSettingComponent.ts b/src/ui/components/BaseSettingComponent.ts index ec138d3..85de0bf 100644 --- a/src/ui/components/BaseSettingComponent.ts +++ b/src/ui/components/BaseSettingComponent.ts @@ -5,36 +5,24 @@ import { ValidationError } from '../validation/types'; // Убрали App и CheckboxSyncPlugin из импортов, если они не нужны ВСЕМ наследникам export abstract class BaseSettingComponent implements ISettingComponent { - protected setting: Setting; // Инициализируется в render конкретного компонента - protected onChangeCallback: () => void = () => {}; + protected setting: Setting; // Инициализируется в render конкретного компонента + protected onChangeCallback: () => void = () => { }; - // Конструктор теперь пустой или с минимальными общими зависимостями - constructor() {} + // Конструктор теперь пустой или с минимальными общими зависимостями + constructor() { } - // --- Методы для реализации наследниками --- - abstract getSettingKey(): keyof CheckboxSyncPluginSettings; - // render теперь должен сам создавать Setting и сохранять ссылку в this.setting - abstract render(container: HTMLElement, currentValue: any): void; - abstract getValueFromUi(): any; - abstract setValueInUi(value: any): void; - abstract validate(): ValidationError | null; + // --- Методы для реализации наследниками --- + abstract getSettingKey(): keyof CheckboxSyncPluginSettings; + // render теперь должен сам создавать Setting и сохранять ссылку в this.setting + abstract render(container: HTMLElement, currentValue: any): void; + abstract getValueFromUi(): any; + abstract setValueInUi(value: any): void; + abstract validate(): ValidationError | null; - public getDefaultValue(): any { - const key = this.getSettingKey(); // Получаем ключ от наследника - if (key in DEFAULT_SETTINGS) { - // Возвращаем значение из импортированного объекта - return DEFAULT_SETTINGS[key]; - } else { - // Логируем предупреждение, если ключ не найден (ошибка конфигурации) - console.warn( - `[${this.constructor.name}] Default value for key "${key}" not found in DEFAULT_SETTINGS. Returning undefined.` - ); - return undefined; // Или null, или выбросить ошибку, но undefined безопаснее - } + + // --- Общая реализация --- + public setChangeListener(listener: () => void): void { + this.onChangeCallback = listener; } - // --- Общая реализация --- - public setChangeListener(listener: () => void): void { - this.onChangeCallback = listener; - } } \ No newline at end of file diff --git a/src/ui/components/EnableParentSyncSettingComponent.ts b/src/ui/components/EnableParentSyncSettingComponent.ts index 02b8e8a..96e0139 100644 --- a/src/ui/components/EnableParentSyncSettingComponent.ts +++ b/src/ui/components/EnableParentSyncSettingComponent.ts @@ -33,15 +33,16 @@ export class EnableParentSyncSettingComponent extends BaseSettingComponent { if (this.toggleComponent) { return this.toggleComponent.getValue(); } - console.warn(`getValueFromUi called before render for ${this.getSettingKey()}`); - // Используем унаследованный getDefaultValue() для консистентности - return this.getDefaultValue(); + // Если toggleComponent все еще не инициализирован (render не вызывался?) + // выбрасываем ошибку, сигнализируя о неправильном использовании + throw new Error(`[${this.getSettingKey()}] Cannot get value from UI before component is rendered.`); } setValueInUi(value: any): void { if (this.toggleComponent) { this.toggleComponent.setValue(value as boolean); } + throw new Error(`[${this.getSettingKey()}] Attempted to set value before component is rendered.`); } validate(): ValidationError | null { diff --git a/src/ui/interfaces/ISettingComponent.ts b/src/ui/interfaces/ISettingComponent.ts index 9d26ce5..33a63cd 100644 --- a/src/ui/interfaces/ISettingComponent.ts +++ b/src/ui/interfaces/ISettingComponent.ts @@ -12,12 +12,6 @@ export interface ISettingComponent { */ getSettingKey(): keyof CheckboxSyncPluginSettings; - /** - * Получает значение по умолчанию для этой настройки. - * @returns Значение по умолчанию. - */ - getDefaultValue(): any; - /** * Рендерит UI-элементы для этой настройки в указанный контейнер. * @param container - HTML-элемент, куда добавлять настройку.