This commit is contained in:
Andrea Alberti 2024-08-14 19:53:21 +02:00
parent c31c3e20b8
commit d96ba2a5f1
4 changed files with 146 additions and 63 deletions

View file

@ -61,13 +61,13 @@ export default class PluginsAnnotations extends Plugin {
}
/* Load settings for different versions */
importSettings(data: unknown): {importedSettings: unknown, wasUpdated: boolean} {
async importSettings(data: unknown): Promise<{importedSettings: unknown, wasUpdated: boolean}> {
// Set to true when the settings are updated to the new format
let wasUpdated = false;
// Nested function to handle different versions of settings
const getSettingsFromData = (data: unknown): unknown => {
const getSettingsFromData = async (data: unknown): Promise<unknown> => {
if(data === null) { // if the file is empty
return data;
} else if (isPluginsAnnotationsSettings(data)) {
@ -98,8 +98,9 @@ export default class PluginsAnnotations extends Plugin {
markdown_file_path: DEFAULT_SETTINGS.markdown_file_path
};
wasUpdated = true;
return getSettingsFromData(newSettings);
console.log("VERSION 1.4");
await this.backupSettings('Backup before upgrading from 1.4 to 1.5',newSettings);
return await getSettingsFromData(newSettings);
} else if (isSettingsFormat_1_3_0(data)) { // previous versions where the name of the plugins was not stored
// Upgrade annotations format
const upgradedAnnotations: PluginAnnotationDict_1_4_0 = {};
@ -120,39 +121,75 @@ export default class PluginsAnnotations extends Plugin {
plugins_annotations_uuid: DEFAULT_SETTINGS_1_4_0.plugins_annotations_uuid,
};
wasUpdated = true;
return getSettingsFromData(newSettings);
console.log("VERSION 1.3");
await this.backupSettings('Backup before upgrading from 1.3 to 1.4',newSettings);
return await getSettingsFromData(newSettings);
} else {
// Very first version of the plugin 1.0 -- no options were stored, only the dictionary of annotations
const newSettings: PluginsAnnotationsSettings_1_3_0 = { ...DEFAULT_SETTINGS_1_3_0 };
newSettings.annotations = isPluginAnnotationDictFormat_1_3_0(data) ? data : DEFAULT_SETTINGS_1_3_0.annotations;
wasUpdated = true;
return getSettingsFromData(newSettings);
console.log("VERSION 1.0");
await this.backupSettings('Backup before upgrading from 1.0 to 1.3',newSettings);
return await getSettingsFromData(newSettings);
}
};
const importedSettings = getSettingsFromData(data);
const importedSettings = await getSettingsFromData(data);
return {importedSettings, wasUpdated};
}
async loadSettings(): Promise<void> {
async backupSettings(backupName: string, settings: unknown) {
// Ensure settings is an object
if (typeof settings !== 'object' || settings === null) return;
let settingsWithoutBackup;
// Remove the backups field from the settings to be backed up
if (settings.hasOwnProperty('backups')) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { backups: _, ...rest } = settings as { backups: unknown };
settingsWithoutBackup = rest;
} else {
settingsWithoutBackup = settings;
}
// Use JSON.parse(JSON.stringify()) for a deep copy
const deepCopiedSettings = JSON.parse(JSON.stringify(settingsWithoutBackup));
// Add the backup with the deep-copied settings
this.settings.backups.push({
name: backupName,
date: new Date(),
settings: deepCopiedSettings
});
await this.saveSettings();
}
async loadSettings(data?: unknown): Promise<void> {
// Create a mapping of names to IDs for the installed plugins
this.pluginNameToIdMap = this.constructPluginNameToIdMap();
this.pluginIdToNameMap = this.generateInvertedMap(this.pluginNameToIdMap);
const data = await this.loadData();
if(data.backups) {
data.backups.forEach((backup: PluginBackup) => {
backup.date = new Date(backup.date); // Convert the date string to a Date object
});
if(data === undefined) {
data = await this.loadData();
}
const {importedSettings, wasUpdated} = this.importSettings(data);
const {importedSettings, wasUpdated} = await this.importSettings(data);
// Merge loaded settings with default settings
this.settings = Object.assign({}, DEFAULT_SETTINGS, importedSettings);
if (this.settings.backups) {
this.settings.backups.forEach((backup: PluginBackup) => {
backup.date = new Date(backup.date); // Convert the date string to a Date object
});
}
if(wasUpdated) {
this.debouncedSaveAnnotations();
}
@ -392,7 +429,13 @@ export default class PluginsAnnotations extends Plugin {
configureAnnotation(annotation_container:HTMLDivElement,annotation_div:HTMLDivElement,pluginId:string,pluginName:string) {
annotation_div.contentEditable = this.settings.editable ? 'true' : 'false';
if(this.settings.editable) {
annotation_div.contentEditable = 'true';
annotation_div.classList.add('plugin-comment-annotation-editable');
} else {
annotation_div.contentEditable = 'false';
annotation_div.classList.remove('plugin-comment-annotation-editable');
}
const placeholder = (this.settings.label_placeholder).replace(/\$\{plugin_name\}/g, pluginName);
@ -409,8 +452,12 @@ export default class PluginsAnnotations extends Plugin {
annoType = AnnotationType.html;
annotation_div.classList.add('plugin-comment-placeholder');
if (this.settings.hide_placeholders) {
annotation_container.classList.add(this.settings.editable ? 'plugin-comment-placeholder' : 'plugin-comment-hidden');
if (this.settings.hide_placeholders) { // if it is a placeholder
if(this.settings.editable) { // if fields can be edited, set the placeholder tag
annotation_container.classList.add('plugin-comment-placeholder');
} else { // if fields cannot be edited, just simply hide placeholders
annotation_container.classList.add('plugin-comment-hidden');
}
}
}
@ -568,8 +615,10 @@ export default class PluginsAnnotations extends Plugin {
if (div instanceof HTMLDivElement) {
if(this.settings.editable) {
div.contentEditable = 'true';
div.classList.add('plugin-comment-annotation-editable');
} else {
div.contentEditable = 'false';
div.classList.remove('plugin-comment-annotation-editable');
}
}
});

View file

@ -2,9 +2,9 @@
import PluginsAnnotations from "main";
import { handleMarkdownFilePathChange } from "manageAnnotations";
import { App, Platform, PluginSettingTab, Setting, TextComponent } from "obsidian";
import { App, Notice, Platform, PluginSettingTab, Setting, TextComponent, TFile } from "obsidian";
import { PluginAnnotationDict } from "types";
import { parseFilePath, FileSuggestion, downloadJson } from "utils";
import { parseFilePath, FileSuggestion, downloadJson, showConfirmationDialog } from "utils";
declare const moment: typeof import('moment');
@ -153,15 +153,21 @@ export class PluginsAnnotationsSettingTab extends PluginSettingTab {
text.setPlaceholder('E.g.: 00 Meta/Plugins annotations.md');
text.setValue(this.plugin.settings.markdown_file_path);
const vault_files = this.app.vault.getFiles().filter((f) => f.extension === "md");
const inputEl = text.inputEl;
new FileSuggestion(this.app, inputEl, vault_files);
const fileSuggestion = new FileSuggestion(this.app, inputEl);
const updateVaultFiles = () => {
if(fileSuggestion) {
fileSuggestion.setSuggestions(this.app.vault.getFiles().filter((f) => f.extension === "md"));
}
}
updateVaultFiles();
inputEl.addEventListener('keydown', (event) => {
if (event.key === 'Enter') {
event.preventDefault();
inputEl.dispatchEvent(new Event('change'));
inputEl.blur();
}
});
@ -188,7 +194,8 @@ export class PluginsAnnotationsSettingTab extends PluginSettingTab {
md_filepath_error_div.style.display = 'none';
if(await handleMarkdownFilePathChange(this.plugin, filepath)) {
this.plugin.settings.markdown_file_path = filepath;
this.plugin.debouncedSaveAnnotations();
await this.plugin.saveSettings();
updateVaultFiles();
} else {
text.setValue(this.plugin.settings.markdown_file_path);
}
@ -311,18 +318,9 @@ export class PluginsAnnotationsSettingTab extends PluginSettingTab {
.setButtonText('Create Backup')
.setCta()
.onClick(async () => {
const backupName = 'Backup name (click to edit)';
if (backupName) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { backups: _, ...currentSettings } = this.plugin.settings;
this.plugin.settings.backups.push({
name: backupName,
date: new Date(),
settings: { ...currentSettings }
});
await this.plugin.saveSettings();
this.display();
}
const backupName = 'Untitled backup';
await this.plugin.backupSettings(backupName,this.plugin.settings);
this.display();
})
);
@ -331,7 +329,7 @@ export class PluginsAnnotationsSettingTab extends PluginSettingTab {
.setButtonText('Download settings')
.setCta()
.onClick(async () => {
downloadJson(this.plugin.settings);
downloadJson({...this.plugin.settings, backups: []});
})
);
}
@ -343,18 +341,21 @@ export class PluginsAnnotationsSettingTab extends PluginSettingTab {
this.plugin.settings.backups.sort((a, b) => b.date.getTime() - a.date.getTime());
// Create a wrapper div for the table
const tableDiv = containerEl.createDiv({ cls: 'plugin-comment-backup-table' });
const backupTableContainer = containerEl.createDiv({ cls: 'setting-item' });
const tableDiv = backupTableContainer.createDiv({ cls: 'plugin-comment-backup-table' });
// Create the header row
const headerRow = tableDiv.createDiv({ cls: 'backup-table-row header' });
headerRow.createDiv({ cls: 'backup-table-cell', text: 'Backup name (click to edit)' });
headerRow.createDiv({ cls: 'backup-table-cell', text: 'Created on' });
const headerRow = tableDiv.createDiv({ cls: 'plugin-comment-backup-table-row header' });
headerRow.createDiv({ cls: 'plugin-comment-backup-table-cell', text: 'Backup name (click to edit)' });
headerRow.createDiv({ cls: 'plugin-comment-backup-table-cell', text: 'Created on' });
headerRow.createDiv({ cls: 'plugin-comment-backup-table-cell', text: 'Actions' });
this.plugin.settings.backups.forEach((backup, index) => {
const rowDiv = tableDiv.createDiv({ cls: 'backup-table-row' });
const rowDiv = tableDiv.createDiv({ cls: 'plugin-comment-backup-table-row' });
// Backup name cell
const nameCell = rowDiv.createDiv({ cls: 'backup-table-cell editable-backup-name' });
const nameCell = rowDiv.createDiv({ cls: 'plugin-comment-backup-table-cell plugin-comment-backup-name' });
const nameDiv = nameCell.createDiv({ text: backup.name, attr: { contenteditable: 'true' } });
// Handle saving the updated name when editing is complete
@ -373,24 +374,48 @@ export class PluginsAnnotationsSettingTab extends PluginSettingTab {
});
// Created on cell
const dateCell = rowDiv.createDiv({ cls: 'backup-table-cell' });
const dateCell = rowDiv.createDiv({ cls: 'plugin-comment-backup-table-cell' });
dateCell.setText(moment(backup.date).format('YYYY-MM-DD HH:mm:ss'));
// Add Restore and Delete buttons to the last cell
const actionCell = rowDiv.createDiv({ cls: 'backup-table-cell actions' });
const actionCell = rowDiv.createDiv({ cls: 'plugin-comment-backup-table-cell plugin-comment-backup-buttons' });
actionCell.createEl('button', { text: 'Restore', cls: 'mod-cta' })
.addEventListener('click', async () => {
this.plugin.settings = { backups: this.plugin.settings.backups, ...backup.settings };
await this.plugin.saveSettings();
alert(`Annotations restored from backup: ${backup.name}`);
this.display(); // Refresh the display to reflect the restored annotations
const answer = await showConfirmationDialog(this.plugin.app, 'Delete backup',
createFragment((frag) => {
frag.appendText('You are about to restore the settings from the backup named ');
frag.createEl('strong',{text: this.plugin.settings.backups[index].name});
frag.appendText(' created on ');
frag.createEl('strong',{text: moment(this.plugin.settings.backups[index].date).format('YYYY-MM-DD HH:mm:ss')});
frag.appendText('. If you proceed, the current settings will be overwritten with those from the backup. \
If you want to keep a copy of the current settings, make a backup before proceeding.\
Do you want to proceed restoring the seettings from the backup?');
}));
if(answer) {
const backups = [...this.plugin.settings.backups]; // store a copy of the backups before restoring the old settings
this.plugin.loadSettings(this.plugin.settings.backups[index].settings);
this.plugin.settings.backups = backups; // restore the copy of the backups
await this.plugin.saveSettings(); // save the restored settings with the backups
new Notice(`Annotations restored from backup "${backup.name}"`);
this.display(); // Refresh the display to reflect the restored annotations
}
});
actionCell.createEl('button', { text: 'Delete', cls: 'mod-cta' })
.addEventListener('click', async () => {
this.plugin.settings.backups.splice(index, 1);
await this.plugin.saveSettings();
this.display(); // Refresh the display to remove the deleted backup
const answer = await showConfirmationDialog(this.plugin.app, 'Delete backup',
createFragment((frag) => {
frag.appendText('You are about to delete the backup named ');
frag.createEl('strong',{text: this.plugin.settings.backups[index].name});
frag.appendText(' created on ');
frag.createEl('strong',{text: moment(this.plugin.settings.backups[index].date).format('YYYY-MM-DD HH:mm:ss')});
frag.appendText('. Do you want to continue?');
}));
if(answer) {
this.plugin.settings.backups.splice(index, 1);
await this.plugin.saveSettings();
this.display(); // Refresh the display to remove the deleted backup
}
});
});
}

View file

@ -83,7 +83,9 @@ export async function createFolderIfNotExists(vault: Vault, folderPath: string)
/* File suggestions */
export class FileSuggestion extends AbstractInputSuggest<TFile> {
constructor(app: App, inputEl: HTMLInputElement, private files:TFile[], private onSelectCallback: (file: TFile) => void = (v: TFile) => {}) {
private files:TFile[] = [];
constructor(app: App, inputEl: HTMLInputElement, private onSelectCallback: (file: TFile) => void = (v: TFile) => {}) {
super(app, inputEl);
}
@ -118,6 +120,10 @@ export class FileSuggestion extends AbstractInputSuggest<TFile> {
this.textInputEl.focus()
this.close();
}
setSuggestions(files:TFile[]) {
this.files = files;
}
}

View file

@ -33,6 +33,10 @@
width: 100%;
}
.plugin-comment-annotation-editable {
cursor: text;
}
/* Show comment container when hovering over the plugin container */
.setting-item:hover .plugin-comment.plugin-comment-placeholder {
display: block;
@ -60,7 +64,7 @@ div.plugin-comment-instructions strong {
margin-bottom: 0;
}
.plugin-comment-backup,
.plugin-comment-backup-table,
.plugin-comment-uninstalled {
margin-left:40px !important;
}
@ -71,27 +75,26 @@ div.plugin-comment-instructions strong {
border-collapse: collapse;
}
.backup-table-row {
.plugin-comment-backup-table-row {
display: table-row;
}
.backup-table-cell {
.plugin-comment-backup-table-cell {
display: table-cell;
padding: 8px;
padding: 8px 0 8px 0;
border-bottom: 1px solid var(--background-modifier-border);
vertical-align: middle;
}
.backup-table-row.header .backup-table-cell {
font-weight: bold;
background-color: var(--background-modifier-accent);
.plugin-comment-backup-table-row.header .plugin-comment-backup-table-cell {
font-weight: var(--font-semibold);
}
.editable-backup-name {
.plugin-comment-backup-name {
cursor: text;
}
.actions {
.plugin-comment-backup-buttons {
display: flex;
gap: 10px;
}