refactor: improve settings tab and main functionality

This commit is contained in:
dralkh 2025-11-07 00:58:41 +03:00
parent 4cbc58b5fa
commit cb9a941597
2 changed files with 95 additions and 13 deletions

47
main.ts
View file

@ -40,6 +40,7 @@ export default class SpaceforgePlugin extends Plugin {
pomodoroService: PomodoroService;
calendarEventService: CalendarEventService;
private readonly stylesheetPath: string = "styles.css";
private readonly stylesheetId: string = "spaceforge-styles";
private lastStylesModTime: number | null = null;
@ -94,6 +95,8 @@ export default class SpaceforgePlugin extends Plugin {
this.calendarEventService.initialize(eventsArray);
this.registerView(
'spaceforge-review-schedule',
(leaf) => new ReviewSidebarView(leaf, this)
@ -165,7 +168,7 @@ export default class SpaceforgePlugin extends Plugin {
this.getSidebarView()?.refresh();
}, 60 * 1000));
this.app.workspace.onLayoutReady(() => this.activateSidebarView());
// this.app.workspace.onLayoutReady(() => this.activateSidebarView()); // Removed automatic activation
this.addStylesheet();
if (this.app.vault.adapter.stat && typeof this.app.vault.adapter.stat === 'function') {
@ -427,11 +430,31 @@ export default class SpaceforgePlugin extends Plugin {
this.reviewScheduleService.lastLinkAnalysisTimestamp = typeof this.pluginState.lastLinkAnalysisTimestamp === 'number' ? this.pluginState.lastLinkAnalysisTimestamp : null;
} catch (error) {
// Fallback to complete defaults if any error during loading sequence
this.settings = { ...DEFAULT_SETTINGS };
this.pluginState = { ...DEFAULT_PLUGIN_STATE_DATA };
console.error("Spaceforge: Error loading data:", error);
// Repopulate services with these fresh defaults
// SIMPLE RECOVERY: Try localStorage backup once, then use defaults
try {
const backupData = await this.app.loadLocalStorage('spaceforge-backup');
if (backupData && typeof backupData === 'string') {
const parsedBackup = JSON.parse(backupData);
if (parsedBackup.reviewData) {
this.settings = { ...DEFAULT_SETTINGS, ...parsedBackup.settings };
this.pluginState = { ...DEFAULT_PLUGIN_STATE_DATA, ...parsedBackup.reviewData };
new Notice("Spaceforge: Recovered data from backup.", 5000);
} else {
throw new Error("Invalid backup format");
}
} else {
throw new Error("No backup found");
}
} catch (backupError) {
console.warn("Spaceforge: Backup recovery failed, using defaults:", backupError);
this.settings = { ...DEFAULT_SETTINGS };
this.pluginState = { ...DEFAULT_PLUGIN_STATE_DATA };
new Notice("Spaceforge: Error loading data. Using defaults to prevent crash.", 5000);
}
// Repopulate services
this.reviewScheduleService.schedules = this.pluginState.schedules || {};
this.reviewHistoryService.history = this.pluginState.history || [];
this.reviewSessionService.reviewSessions = this.pluginState.reviewSessions || { sessions: {}, activeSessionId: null };
@ -440,11 +463,9 @@ export default class SpaceforgePlugin extends Plugin {
this.reviewScheduleService.customNoteOrder = this.pluginState.customNoteOrder || [];
this.reviewScheduleService.lastLinkAnalysisTimestamp = this.pluginState.lastLinkAnalysisTimestamp ?? null;
// Ensure FSRS service is also updated with these default settings
if (this.reviewScheduleService) {
this.reviewScheduleService.updateAlgorithmServicesForSettingsChange();
}
new Notice("Spaceforge: Error loading data, initialized with defaults.", 5000);
}
}
@ -504,6 +525,7 @@ export default class SpaceforgePlugin extends Plugin {
new Notice(`Spaceforge: Created directory for custom data: ${dirPathOnly}`, 3000);
}
// SIMPLE SAVE: Use Obsidian's standard file operations
const file = this.app.vault.getAbstractFileByPath(effectiveSavePath);
if (file instanceof TFile) {
await this.app.vault.modify(file, JSON.stringify(dataToSave, null, 2));
@ -513,14 +535,13 @@ export default class SpaceforgePlugin extends Plugin {
// new Notice(`Spaceforge: Data saved to custom path: ${effectiveSavePath}`, 3000); // Removed notice
// Migration/Cleanup: If we just successfully saved to a custom path,
// and the old default data.json exists, remove it.
// and the old default data.json exists, KEEP IT as backup for safety.
// CRITICAL FIX: Never automatically delete user data to prevent data loss during reloads
const oldFile = this.app.vault.getAbstractFileByPath(defaultPluginDataPath);
if (oldFile instanceof TFile) {
// Check if this is a migration scenario (old data was loaded and new path was empty)
// This check is a bit implicit. A more robust way would be a flag.
// For now, if custom path is active and default exists, assume it's post-migration or user switched.
await this.app.vault.delete(oldFile);
new Notice(`Spaceforge: Removed old data file from default plugin folder as custom path is active.`, 5000);
// Instead of deleting, we'll keep the old file as a backup
// Users can manually clean it up later if needed
new Notice(`Spaceforge: Successfully saved to custom path. Original data file kept as backup for safety.`, 5000);
}
} catch (writeError) {
new Notice(`Error saving data to custom path ${effectiveSavePath}: ${writeError.message}. Falling back to default path for this save.`, 10000);

View file

@ -1287,6 +1287,65 @@ export class SpaceforgeSettingTab extends PluginSettingTab {
});
}
// ========= DATA MANAGEMENT SECTION =========
const dataSection = createCollapsible('Data Management', 'database', false);
const defaultPath = this.plugin.app.vault.configDir + `/plugins/${this.plugin.manifest.id}/data.json`;
let locationDesc = `Default: ${defaultPath}`;
if (this.plugin.settings.useCustomDataPath && this.plugin.settings.customDataPath) {
const customPath = this.plugin.settings.customDataPath.endsWith('/data.json')
? this.plugin.settings.customDataPath
: `${this.plugin.settings.customDataPath}/data.json`;
locationDesc = `Custom: ${customPath}`;
}
new Setting(dataSection)
.setName('Current data location')
.setDesc(locationDesc)
.addExtraButton(button => button
.setIcon('info')
.setTooltip('Current location of your Spaceforge data')
.onClick(() => {
const message = this.plugin.settings.useCustomDataPath && this.plugin.settings.customDataPath
? `Your data is stored at: ${this.plugin.settings.customDataPath.endsWith('/data.json') ? this.plugin.settings.customDataPath : `${this.plugin.settings.customDataPath}/data.json`}`
: `Your data is stored at: ${defaultPath}`;
new Notice(message, 8000);
}));
// Check if old default data file exists when using custom path
if (this.plugin.settings.useCustomDataPath) {
const oldFile = this.plugin.app.vault.getAbstractFileByPath(defaultPath);
if (oldFile) {
new Setting(dataSection)
.setName('Legacy data file found')
.setDesc(`A data file exists at the default location. You can safely delete it or keep it as a backup.`)
.addButton(button => button
.setButtonText('Keep as Backup')
.setIcon('save')
.onClick(() => {
new Notice('Legacy data file kept as backup. You can delete it manually when ready.', 5000);
}))
.addButton(button => button
.setButtonText('Delete Legacy File')
.setIcon('trash')
.setWarning()
.onClick(async () => {
const confirmed = confirm('Are you sure you want to delete the legacy data file? Make sure your custom data path is working correctly before proceeding.');
if (confirmed) {
try {
await this.plugin.app.vault.delete(oldFile);
new Notice('Legacy data file deleted successfully.', 3000);
this.display(); // Refresh settings
} catch (error) {
new Notice('Failed to delete legacy file: ' + error.message, 5000);
}
}
}));
}
}
// Add global action buttons at the bottom
createActionButtons();
}
@ -1378,5 +1437,7 @@ export class SpaceforgeSettingTab extends PluginSettingTab {
row4.createEl('td', { text: 'Recalled easily' });
row4.createEl('td', { text: 'Largest increase in stability' });
}
}
}