feat: added export/import for obsidian settings

This commit is contained in:
Al0cam 2025-04-28 08:58:59 +02:00
parent 1e616ac1a3
commit 2bc63e3872
3 changed files with 229 additions and 5 deletions

194
IO/SettingsIO.ts Normal file
View file

@ -0,0 +1,194 @@
import type { AutoMoverSettings } from "Settings/Settings";
import { Notice, TFile } from "obsidian";
import type { App } from "obsidian";
class SettingsIO {
private static instance: SettingsIO;
private app: App;
private constructor() {}
public static getInstance(): SettingsIO {
if (!SettingsIO.instance) {
SettingsIO.instance = new SettingsIO();
}
return SettingsIO.instance;
}
public init(app: App): void {
this.app = app;
}
/**
* Exports the plugin settings to a JSON file.
*
* @param settings - The settings object to export.
* @returns A promise that resolves to true if the export was successful, false otherwise.
*/
public async exportSettings(settings: AutoMoverSettings) {
try {
if (!this.app) {
throw new Error("App reference not set");
}
const settingsData = JSON.stringify(settings, null, 2);
const electron = (window as any).require
? (window as any).require("electron")
: null;
const remote = electron ? electron.remote : null;
if (!remote || !remote.dialog) {
// Fall back to saving in vault if Electron APIs are not available
return this.exportToVault(settingsData);
}
const { canceled, filePath } = await remote.dialog.showSaveDialog({
title: "Export AutoMover Settings",
defaultPath: "AutoMover_settings.json",
filters: [{ name: "JSON Files", extensions: ["json"] }],
properties: ["createDirectory"],
});
if (canceled || !filePath) {
return false;
}
const fs = require("node:fs");
fs.writeFileSync(filePath, settingsData);
new Notice("Settings exported successfully");
return true;
} catch (error) {
console.error("Failed to export settings:", error);
new Notice("Failed to export settings");
return false;
}
}
/**
* Exports the settings to the vault.
* Exports the settings to a file named AutoMover_settings.json
*
* @param settingsData - The settings data to export.
* @returns A promise that resolves to true if the export was successful, false otherwise.
*/
private async exportToVault(settingsData: string): Promise<boolean> {
try {
const filename = "AutoMover_settings.json";
await this.app.vault.create(filename, settingsData);
new Notice(`Settings exported to vault: ${filename}`);
return true;
} catch (error) {
console.error("Failed to export to vault:", error);
new Notice("Failed to export settings to vault");
return false;
}
}
/**
* Imports settings from a JSON file.
*
* @returns The imported settings or null if the import failed.
*/
public async importSettings(): Promise<AutoMoverSettings | null> {
try {
if (!this.app) {
throw new Error("App reference not set");
}
const electron = (window as any).require
? (window as any).require("electron")
: null;
const remote = electron ? electron.remote : null;
if (!remote || !remote.dialog) {
// Fall back to importing from vault if Electron APIs are not available
return this.importFromVault();
}
const { canceled, filePaths } = await remote.dialog.showOpenDialog({
title: "Import AutoMover Settings",
filters: [{ name: "JSON Files", extensions: ["json"] }],
properties: ["openFile"],
});
if (canceled || !filePaths || filePaths.length === 0) {
return null;
}
const fs = require("node:fs");
const fileContent = fs.readFileSync(filePaths[0], "utf8");
const importedSettings = JSON.parse(fileContent);
if (!this.validateSettings(importedSettings)) {
new Notice("Invalid settings file format");
return null;
}
new Notice("Settings imported successfully");
return importedSettings;
} catch (error) {
console.error("Failed to import settings:", error);
new Notice("Failed to import settings");
return null;
}
}
/**
* Fallback method to import settings from the vault.
* from the file AutoMover_settings.json
*
* @returns The imported settings or null if the import failed.
*/
private async importFromVault(): Promise<AutoMoverSettings | null> {
try {
const filename = "AutoMover_settings.json";
const file = this.app.vault.getAbstractFileByPath(filename);
if (!file || !(file instanceof TFile)) {
new Notice(`Could not find ${filename} in vault`);
return null;
}
const fileContent = await this.app.vault.read(file);
const importedSettings = JSON.parse(fileContent);
if (!this.validateSettings(importedSettings)) {
new Notice("Invalid settings file format");
return null;
}
new Notice("Settings imported from vault successfully");
return importedSettings;
} catch (error) {
console.error("Failed to import from vault:", error);
new Notice("Failed to import settings from vault");
return null;
}
}
/**
* Validates the settings object to ensure it has the correct structure.
*
* @param settings - The settings object to validate.
* @return True if the settings object is valid, false otherwise.
*/
private validateSettings(settings: any): settings is AutoMoverSettings {
if (!settings) return false;
if (typeof settings.moveOnOpen !== "boolean") return false;
if (!Array.isArray(settings.movingRules)) return false;
if (!Array.isArray(settings.exclusionRules)) return false;
if (typeof settings.automaticMoving !== "boolean") return false;
if (settings.timer !== null && typeof settings.timer !== "number")
return false;
return true;
}
}
const settingsIO = SettingsIO.getInstance();
export default settingsIO;

View file

@ -1,8 +1,9 @@
import type AutoMoverPlugin from "main";
import settingsIO from "IO/SettingsIO";
import { ExclusionRule } from "Models/ExclusionRule";
import { MovingRule } from "Models/MovingRule";
import * as obsidian from "obsidian";
import timerUtil from "Utils/TimerUtil";
import type AutoMoverPlugin from "main";
import * as obsidian from "obsidian";
export class SettingsTab extends obsidian.PluginSettingTab {
plugin: AutoMoverPlugin;
@ -17,6 +18,34 @@ export class SettingsTab extends obsidian.PluginSettingTab {
containerEl.empty();
new obsidian.Setting(containerEl).setName("Automatic moving").setHeading();
new obsidian.Setting(containerEl)
.setName("Export/Import of settings")
.setDesc(
`Import will replace the current settings.
If you aren't prompted to choose a location, then the file will be exported to/imported from the vault root as AutoMover_settings.json.`,
)
.addButton((button) => {
button.setButtonText("Export settings");
button.onClick(async () => {
settingsIO.exportSettings(this.plugin.settings);
});
})
.addButton((button) => {
button.setButtonText("Import settings");
button.onClick(async () => {
const importedSettings = await settingsIO.importSettings();
if (importedSettings) {
this.plugin.settings = importedSettings;
await this.plugin.saveData(this.plugin.settings);
this.plugin.app.workspace.trigger(
"AutoMover:automatic-moving-update",
this.plugin.settings,
);
this.display();
}
});
});
new obsidian.Setting(containerEl)
.setName("Move on open")
.setDesc("Should the file be moved when it is opened?")

View file

@ -1,11 +1,11 @@
import settingsIO from "IO/SettingsIO";
import * as Settings from "Settings/Settings";
import { SettingsTab } from "Settings/SettingsTab";
import exclusionMatcherUtil from "Utils/ExclusionMatcherUtil";
import movingUtil from "Utils/MovingUtil";
import ruleMatcherUtil from "Utils/RuleMatcherUtil";
import * as obsidian from "obsidian";
import exclusionMatcherUtil from "Utils/ExclusionMatcherUtil";
import timerUtil from "Utils/TimerUtil";
import { addIcon } from "obsidian";
import * as obsidian from "obsidian";
export default class AutoMoverPlugin extends obsidian.Plugin {
settings: Settings.AutoMoverSettings;
@ -13,6 +13,7 @@ export default class AutoMoverPlugin extends obsidian.Plugin {
async onload() {
// console.log("loading plugin");
movingUtil.init(this.app);
settingsIO.init(this.app);
/**
* Loading settings and creating the settings tab
*/