Improved debounced saving. Fixed a bug when switching between preference pane and community plugins pane, where the settings were not stored properly.

This commit is contained in:
Andrea Alberti 2024-08-19 01:23:47 +02:00
parent 393d40054a
commit f5e42dad89
5 changed files with 66 additions and 29 deletions

View file

@ -1,7 +1,7 @@
{
"id": "plugins-annotations",
"name": "Plugins Annotations",
"version": "1.6.2",
"version": "1.6.3",
"minAppVersion": "1.5.0",
"description": "Allows adding personal comments to each installed plugin.",
"author": "Andrea Alberti",

View file

@ -92,22 +92,12 @@ export class annotationControl {
});
this.annotation_div.addEventListener('input', (event: Event) => {
// If the user starts typing, it removes the state of placeholder if this was set
if(this.plugin.settings.editable) this.isPlaceholder = false;
});
// Add placeholder class back if no changes are made
this.annotation_div.addEventListener('blur', (event:FocusEvent) => {
if(!this.plugin.settings.editable) { return; }
if(this.clickedLink) return;
const content = this.annotation_div.innerText.trim();
if (this.isPlaceholder || content === '') { // placeholder
if (content === '') { // placeholder
this.isPlaceholder = true;
this.annotationDesc = '';
this.plugin.removeAnnotation(this.pluginId);
this.setPlaceholderClasses();
} else {
this.isPlaceholder = false;
this.annotationDesc = content.trim();
@ -115,10 +105,21 @@ export class annotationControl {
desc: this.annotationDesc,
name: this.pluginName,
});
}
this.plugin.debouncedSaveAnnotations();
});
// Add placeholder class back if no changes are made
this.annotation_div.addEventListener('blur', (event:FocusEvent) => {
if(!this.plugin.settings.editable) { return; }
if(this.clickedLink) return;
if (this.isPlaceholder) { // placeholder
this.setPlaceholderClasses();
} else {
this.removePlaceholderClasses();
}
this.renderAnnotation();
this.plugin.debouncedSaveAnnotations();
});
}

View file

@ -41,10 +41,14 @@ export default class PluginsAnnotations extends Plugin {
</svg>';
private mutationObserver: MutationObserver | null = null;
private saveTimeout: number | null = null;
private observedTab: SettingTab | null = null;
private vaultPath: string | null = null;
/* For debounced saving */
private saveTimeout: number | null = null;
private savePromise: Promise<void> | null = null;
private resolveSavePromise: (() => void) | null = null;
async onload() {
// console.clear();
@ -199,8 +203,8 @@ export default class PluginsAnnotations extends Plugin {
});
// Merge imported backups with backups created while importing the data
importedSettings.backups = [...importedSettings.backups, ...importBackups];
importedSettings.backups = [...importBackups, ...importedSettings.backups];
// Merge loaded settings with default settings
this.settings = Object.assign({}, structuredClone(DEFAULT_SETTINGS), importedSettings);
@ -223,6 +227,7 @@ export default class PluginsAnnotations extends Plugin {
let isRestoreOperation;
if(data === undefined) {
this.waitForSaveToComplete();
data = await this.loadData();
isRestoreOperation = false;
} else {
@ -541,18 +546,38 @@ export default class PluginsAnnotations extends Plugin {
});
}
debouncedSaveAnnotations(callback: ()=>void = ():void => {}) {
debouncedSaveAnnotations(callback: () => void = (): void => {}) {
const timeout_ms = 100;
// Clear the previous timeout and resolve the previous promise if necessary
if (this.saveTimeout) {
clearTimeout(this.saveTimeout);
// By stopping the timer, the promise is unlikely to be resolved. So, we need to resolve it manually.
if (this.resolveSavePromise) {
this.resolveSavePromise(); // Ensure the previous promise is resolved to avoid memory leaks
}
}
// Create a new promise and store the resolve function
this.savePromise = new Promise<void>((resolve) => {
this.resolveSavePromise = resolve; // Store the resolve function
this.saveTimeout = window.setTimeout(async () => {
await this.saveSettings();
callback();
this.saveTimeout = null;
resolve(); // Resolve the promise when the timer completes
}, timeout_ms);
});
}
// Helper method to check if a save is in progress and wait for it
async waitForSaveToComplete() {
if (this.saveTimeout && this.savePromise) {
// Wait for the existing promise to resolve
await this.savePromise;
}
this.saveTimeout = window.setTimeout(async () => {
await this.saveSettings();
callback();
this.saveTimeout = null;
}, timeout_ms);
}
removeCommentsFromTab() {

View file

@ -437,6 +437,7 @@ export class PluginsAnnotationsSettingTab extends PluginSettingTab {
class BackupManager {
private backupTableContainer: HTMLElement;
private UNTITLED_BACKUP = 'Untitled backup';
constructor(private plugin:PluginsAnnotations, private containerEl:HTMLElement) {
@ -468,7 +469,7 @@ class BackupManager {
.setButtonText('Create Backup')
.setCta()
.onClick(async () => {
const backupName = 'Untitled backup';
const backupName = this.UNTITLED_BACKUP;
await backupSettings(backupName,this.plugin.settings,this.plugin.settings.backups);
await this.plugin.saveSettings();
this.updateListBackups();
@ -554,9 +555,18 @@ class BackupManager {
const nameDiv = nameCell.createDiv({ text: backup.name, attr: { contenteditable: 'true' } });
// Handle saving the updated name when editing is complete
nameDiv.addEventListener('blur', async () => {
const newName = nameDiv.textContent?.trim() || 'Unnamed Backup';
backup.name = newName;
nameDiv.addEventListener('input', () => {
const newName = nameDiv.innerText.trim();
backup.name = newName === '' ? this.UNTITLED_BACKUP : newName;
this.plugin.debouncedSaveAnnotations();
});
// Handle saving the updated name when editing is complete
nameDiv.addEventListener('blur', () => {
const newName = nameDiv.innerText.trim();
if(newName === '') {
nameDiv.innerText = backup.name;
}
this.plugin.debouncedSaveAnnotations();
});

View file

@ -221,8 +221,9 @@ export async function backupSettings(backupName: string, toBeBackedUp: unknown,
// Deep copy
const deepCopiedSettings = structuredClone(settingsWithoutBackup);
// Add the backup with the deep-copied settings
destBackups.push({
// Add the deep-copy of the settings to the beginning of the array
// See: https://stackoverflow.com/a/8073687/4216175
destBackups.unshift({
name: backupName,
date: new Date(),
settings: deepCopiedSettings