refactor(settings): Implement component save/load logic and remove getDefaultValue

This commit is contained in:
Grol Grol 2025-04-27 00:26:43 +03:00
parent 7c78611f1c
commit d4e23856c1
4 changed files with 76 additions and 47 deletions

View file

@ -1,10 +1,11 @@
import { App, ButtonComponent, DropdownComponent, Notice, PluginSettingTab, Setting, TextComponent, ToggleComponent } from "obsidian"; import { App, ButtonComponent, DropdownComponent, Notice, PluginSettingTab, Setting, TextComponent, ToggleComponent } from "obsidian";
import CheckboxSyncPlugin from "../main"; import CheckboxSyncPlugin from "../main";
import { CheckboxState, DEFAULT_SETTINGS } from "../types"; import { CheckboxState, CheckboxSyncPluginSettings, DEFAULT_SETTINGS } from "../types";
import { ConfirmModal, InfoModal, SaveConfirmModal } from "./modals"; import { ConfirmModal, InfoModal, SaveConfirmModal } from "./modals";
import { Mutex } from "async-mutex"; import { Mutex } from "async-mutex";
import { SettingGroup } from "./SettingGroup"; import { SettingGroup } from "./SettingGroup";
import { EnableParentSyncSettingComponent } from "./components/EnableParentSyncSettingComponent"; import { EnableParentSyncSettingComponent } from "./components/EnableParentSyncSettingComponent";
import { ValidationError } from "./validation/types";
export class CheckboxSyncPluginSettingTab extends PluginSettingTab { export class CheckboxSyncPluginSettingTab extends PluginSettingTab {
plugin: CheckboxSyncPlugin; plugin: CheckboxSyncPlugin;
@ -12,7 +13,7 @@ export class CheckboxSyncPluginSettingTab extends PluginSettingTab {
private uncheckedSymbolsInput: TextComponent; private uncheckedSymbolsInput: TextComponent;
private ignoreSymbolsInput: TextComponent; private ignoreSymbolsInput: TextComponent;
private unknownPolicyDropdown: DropdownComponent; private unknownPolicyDropdown: DropdownComponent;
private parentToggle: ToggleComponent; // private parentToggle: ToggleComponent;
private childToggle: ToggleComponent; private childToggle: ToggleComponent;
private enableAutomaticFileSyncToggle: ToggleComponent; private enableAutomaticFileSyncToggle: ToggleComponent;
@ -307,12 +308,25 @@ export class CheckboxSyncPluginSettingTab extends PluginSettingTab {
private resetInputsToSavedSettings() { private resetInputsToSavedSettings() {
const settings = this.plugin.settings; // Получаем текущие сохраненные настройки 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.checkedSymbolsInput.setValue(this.arrayToJsonString(settings.checkedSymbols));
this.uncheckedSymbolsInput.setValue(this.arrayToJsonString(settings.uncheckedSymbols)); this.uncheckedSymbolsInput.setValue(this.arrayToJsonString(settings.uncheckedSymbols));
this.ignoreSymbolsInput.setValue(this.arrayToJsonString(settings.ignoreSymbols)); this.ignoreSymbolsInput.setValue(this.arrayToJsonString(settings.ignoreSymbols));
this.unknownPolicyDropdown.setValue(settings.unknownSymbolPolicy); this.unknownPolicyDropdown.setValue(settings.unknownSymbolPolicy);
this.parentToggle.setValue(settings.enableAutomaticParentState); // this.parentToggle.setValue(settings.enableAutomaticParentState);
this.childToggle.setValue(settings.enableAutomaticChildState); this.childToggle.setValue(settings.enableAutomaticChildState);
this.enableAutomaticFileSyncToggle.setValue(settings.enableAutomaticFileSync); this.enableAutomaticFileSyncToggle.setValue(settings.enableAutomaticFileSync);
@ -396,12 +410,36 @@ export class CheckboxSyncPluginSettingTab extends PluginSettingTab {
* Does NOT interact with UI feedback (buttons, notices, error display). * Does NOT interact with UI feedback (buttons, notices, error display).
*/ */
private async validateAndSaveSettings(): Promise<void> { private async validateAndSaveSettings(): Promise<void> {
let errors: ValidationError[] = []; // Массив для сбора всех ошибок валидации
let newSettingsData: Partial<CheckboxSyncPluginSettings> = {}; // Объект для новых данных
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 // 1. Считываем значения из UI
const checkedValue = this.checkedSymbolsInput.getValue(); const checkedValue = this.checkedSymbolsInput.getValue();
const uncheckedValue = this.uncheckedSymbolsInput.getValue(); const uncheckedValue = this.uncheckedSymbolsInput.getValue();
const ignoreValue = this.ignoreSymbolsInput.getValue(); const ignoreValue = this.ignoreSymbolsInput.getValue();
const policyValue = this.unknownPolicyDropdown.getValue() as CheckboxState; const policyValue = this.unknownPolicyDropdown.getValue() as CheckboxState;
const parentValue = this.parentToggle.getValue(); // const parentValue = this.parentToggle.getValue();
const childValue = this.childToggle.getValue(); const childValue = this.childToggle.getValue();
const automaticFileSyncValue = this.enableAutomaticFileSyncToggle.getValue(); const automaticFileSyncValue = this.enableAutomaticFileSyncToggle.getValue();
@ -447,15 +485,23 @@ export class CheckboxSyncPluginSettingTab extends PluginSettingTab {
throw new Error("Unchecked symbols list cannot be empty."); 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 (может кинуть ошибку) // 6. Вызываем сохранение -> await (может кинуть ошибку)
await this.plugin.updateSettings(settings => { await this.plugin.updateSettings(settings => {
settings.checkedSymbols = checkedSymbolsArray; Object.assign(settings, newSettingsData);
settings.uncheckedSymbols = uncheckedSymbolsArray;
settings.ignoreSymbols = ignoreSymbolsArray;
settings.unknownSymbolPolicy = policyValue;
settings.enableAutomaticParentState = parentValue;
settings.enableAutomaticChildState = childValue;
settings.enableAutomaticFileSync = automaticFileSyncValue;
}); });
} }
} }

View file

@ -20,19 +20,7 @@ export abstract class BaseSettingComponent implements ISettingComponent {
abstract validate(): ValidationError | null; 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 { public setChangeListener(listener: () => void): void {
this.onChangeCallback = listener; this.onChangeCallback = listener;

View file

@ -33,15 +33,16 @@ export class EnableParentSyncSettingComponent extends BaseSettingComponent {
if (this.toggleComponent) { if (this.toggleComponent) {
return this.toggleComponent.getValue(); return this.toggleComponent.getValue();
} }
console.warn(`getValueFromUi called before render for ${this.getSettingKey()}`); // Если toggleComponent все еще не инициализирован (render не вызывался?)
// Используем унаследованный getDefaultValue() для консистентности // выбрасываем ошибку, сигнализируя о неправильном использовании
return this.getDefaultValue(); throw new Error(`[${this.getSettingKey()}] Cannot get value from UI before component is rendered.`);
} }
setValueInUi(value: any): void { setValueInUi(value: any): void {
if (this.toggleComponent) { if (this.toggleComponent) {
this.toggleComponent.setValue(value as boolean); this.toggleComponent.setValue(value as boolean);
} }
throw new Error(`[${this.getSettingKey()}] Attempted to set value before component is rendered.`);
} }
validate(): ValidationError | null { validate(): ValidationError | null {

View file

@ -12,12 +12,6 @@ export interface ISettingComponent {
*/ */
getSettingKey(): keyof CheckboxSyncPluginSettings; getSettingKey(): keyof CheckboxSyncPluginSettings;
/**
* Получает значение по умолчанию для этой настройки.
* @returns Значение по умолчанию.
*/
getDefaultValue(): any;
/** /**
* Рендерит UI-элементы для этой настройки в указанный контейнер. * Рендерит UI-элементы для этой настройки в указанный контейнер.
* @param container - HTML-элемент, куда добавлять настройку. * @param container - HTML-элемент, куда добавлять настройку.