feat: add manual non-Markdown data export/import to vault

- Implement exportToVault and importFromVault in NonMarkdownStatusStore
- Add getNonMarkdownStore method to StatusStoreManager
- Add manual synchronization buttons to the Synchronization settings UI
- Support data migration between internal storage and vault sync file
This commit is contained in:
Aleix Soler 2026-03-18 12:43:18 +01:00
parent f97bca5fa9
commit 32af8e1c78
3 changed files with 88 additions and 4 deletions

View file

@ -2,6 +2,7 @@ import { PluginSettings, SyncGroup } from "@/types/pluginSettings";
import React from "react";
import { SettingItem } from "./SettingItem";
import settingsService from "@/core/settingsService";
import statusStoreManager from "@/core/statusStoreManager";
export type Props = {
settings: PluginSettings;
@ -22,6 +23,24 @@ export const SynchronizationSettings: React.FC<Props> = ({
alert("Plugin settings imported successfully.");
};
const handleDataExport = async () => {
try {
await statusStoreManager.getNonMarkdownStore().exportToVault();
alert("Non-Markdown status data exported to vault.");
} catch (e) {
alert(`Export failed: ${e.message}`);
}
};
const handleDataImport = async () => {
try {
await statusStoreManager.getNonMarkdownStore().importFromVault();
alert("Non-Markdown status data imported from vault.");
} catch (e) {
alert(`Import failed: ${e.message}`);
}
};
const toggleGroup = (group: SyncGroup) => {
const currentGroups = [...(settings.syncGroups || [])];
const index = currentGroups.indexOf(group);
@ -234,6 +253,18 @@ export const SynchronizationSettings: React.FC<Props> = ({
placeholder="_note-status-data.json"
/>
</SettingItem>
<div
className="note-status-settings__actions"
style={{ marginTop: "1em", display: "flex", gap: "10px" }}
>
<button onClick={handleDataExport}>
📤 Export internal data to vault
</button>
<button onClick={handleDataImport}>
📥 Import data from vault
</button>
</div>
</div>
</div>
);

View file

@ -5,14 +5,22 @@ import { StatusStore } from "./statusStores/types";
class StatusStoreManager {
private stores: StatusStore[] = [];
private nonMarkdownStore: NonMarkdownStatusStore | null = null;
async initialize(plugin: Plugin) {
const frontmatterStore = new FrontmatterStatusStore(plugin.app);
this.stores = [frontmatterStore];
const nonMarkdownStore = new NonMarkdownStatusStore(plugin);
await nonMarkdownStore.initialize();
this.registerStore(nonMarkdownStore);
this.nonMarkdownStore = new NonMarkdownStatusStore(plugin);
await this.nonMarkdownStore.initialize();
this.registerStore(this.nonMarkdownStore);
}
getNonMarkdownStore(): NonMarkdownStatusStore {
if (!this.nonMarkdownStore) {
throw new Error("NonMarkdownStatusStore is not initialized");
}
return this.nonMarkdownStore;
}
registerStore(store: StatusStore) {

View file

@ -58,14 +58,59 @@ export class NonMarkdownStatusStore implements StatusStore {
settings.enableNonMarkdownSync &&
settings.nonMarkdownSyncPath
) {
return normalizePath(settings.nonMarkdownSyncPath);
return this.getVaultPath();
}
return this.getInternalPath();
}
private getInternalPath(): string {
const configDir = this.plugin.app.vault.configDir;
const pluginDir = `${configDir}/plugins/${this.plugin.manifest.id}`;
return normalizePath(`${pluginDir}/non-markdown-statuses.json`);
}
private getVaultPath(): string {
const settings = settingsService.settings;
return normalizePath(settings.nonMarkdownSyncPath);
}
async exportToVault(): Promise<void> {
const internalPath = this.getInternalPath();
const vaultPath = this.getVaultPath();
if (!(await this.plugin.app.vault.adapter.exists(internalPath))) {
throw new Error("No internal data found to export.");
}
const raw = await this.plugin.app.vault.adapter.read(internalPath);
await this.ensureDirectoryExists(vaultPath);
await this.plugin.app.vault.adapter.write(vaultPath, raw);
// Reload if vault sync is active
if (settingsService.settings.enableNonMarkdownSync) {
this.data = await this.loadFromDisk();
}
}
async importFromVault(): Promise<void> {
const internalPath = this.getInternalPath();
const vaultPath = this.getVaultPath();
if (!(await this.plugin.app.vault.adapter.exists(vaultPath))) {
throw new Error("No vault sync file found to import.");
}
const raw = await this.plugin.app.vault.adapter.read(vaultPath);
await this.ensureDirectoryExists(internalPath);
await this.plugin.app.vault.adapter.write(internalPath, raw);
// Reload if vault sync is inactive
if (!settingsService.settings.enableNonMarkdownSync) {
this.data = await this.loadFromDisk();
}
}
canHandle(file: TFile): boolean {
return file.extension !== "md";
}