mirror of
https://github.com/alberti42/obsidian-plugins-annotations.git
synced 2026-07-22 10:10:24 +00:00
Backups
This commit is contained in:
parent
c31c3e20b8
commit
d96ba2a5f1
4 changed files with 146 additions and 63 deletions
85
src/main.ts
85
src/main.ts
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
Loading…
Reference in a new issue