diff --git a/frontend/obsidian-plugin/src/vault-link-plugin.ts b/frontend/obsidian-plugin/src/vault-link-plugin.ts index 1d31804c..53a17106 100644 --- a/frontend/obsidian-plugin/src/vault-link-plugin.ts +++ b/frontend/obsidian-plugin/src/vault-link-plugin.ts @@ -11,85 +11,43 @@ import { StatusBar } from "./views/status-bar"; import { LogsView } from "./views/logs-view"; import { StatusDescription } from "./views/status-description"; -import { - applyRemoteChangesLocally, - Database, - Logger, - Syncer, - SyncHistory, - SyncService, - initialize, - Settings -} from "sync-client"; +import { Logger, SyncClient } from "sync-client"; export default class VaultLinkPlugin extends Plugin { - private readonly operations = new ObsidianFileOperations(this.app.vault); - private readonly history = new SyncHistory(); - private settingsTab: SyncSettingsTab; - private remoteListenerIntervalId: number | null = null; + private settingsTab: SyncSettingsTab | undefined; + private client!: SyncClient; public async onload(): Promise { Logger.getInstance().info("Starting plugin"); - await initialize(); - - let state = (await this.loadData()) ?? { - settings: undefined, - database: undefined - }; - const database = new Database( - state.database, - async (data: unknown): Promise => { - state = { ...state, database: data }; - return this.saveData(state); + this.client = await SyncClient.create( + new ObsidianFileOperations(this.app.vault), + { + load: this.loadData.bind(this), + save: this.saveData.bind(this) } ); - const settings = new Settings( - state.settings, - async (data: unknown): Promise => { - state = { ...state, settings: data }; - return this.saveData(state); - } - ); - - const syncService = new SyncService(database); - - const syncer = new Syncer( - database, - settings, - syncService, - this.operations, - this.history - ); - - const statusDescription = new StatusDescription( - settings, - database, - syncService, - this.history, - syncer - ); + const statusDescription = new StatusDescription(this.client); this.settingsTab = new SyncSettingsTab({ app: this.app, plugin: this, - settings, - syncService, - statusDescription, - syncer + syncClient: this.client, + statusDescription }); this.addSettingTab(this.settingsTab); - new StatusBar(settings, this, this.history, syncer); + new StatusBar(this, this.client); this.registerView( HistoryView.TYPE, - (leaf) => new HistoryView(leaf, settings, this.history) + (leaf) => + new HistoryView(leaf, this.client.settings, this.client.history) ); this.registerView( LogsView.TYPE, - (leaf) => new LogsView(this, settings, leaf) + (leaf) => new LogsView(this, this.client.settings, leaf) ); this.addRibbonIcon( @@ -103,7 +61,7 @@ export default class VaultLinkPlugin extends Plugin { async (_: MouseEvent) => this.activateView(LogsView.TYPE) ); - const eventHandler = new ObsidianFileEventHandler(syncer); + const eventHandler = new ObsidianFileEventHandler(this.client.syncer); this.app.workspace.onLayoutReady(async () => { Logger.getInstance().info("Initialising sync handlers"); @@ -131,44 +89,12 @@ export default class VaultLinkPlugin extends Plugin { Logger.getInstance().info("Sync handlers initialised"); - void syncer.scheduleSyncForOfflineChanges(); + void this.client.syncer.scheduleSyncForOfflineChanges(); }); - - this.registerRemoteEventListener( - settings, - database, - syncService, - syncer, - settings.getSettings().fetchChangesUpdateIntervalMs - ); - - settings.addOnSettingsChangeHandlers((newSettings, oldSettings) => { - this.registerRemoteEventListener( - settings, - database, - syncService, - syncer, - newSettings.fetchChangesUpdateIntervalMs - ); - - if (!oldSettings.isSyncEnabled && newSettings.isSyncEnabled) { - syncer - .scheduleSyncForOfflineChanges() - .catch((_error: unknown) => { - Logger.getInstance().error( - "Failed to schedule sync for offline changes" - ); - }); - } - }); - - Logger.getInstance().info("Plugin loaded"); } public onunload(): void { - if (this.remoteListenerIntervalId !== null) { - window.clearInterval(this.remoteListenerIntervalId); - } + this.client.onunload(); } public openSettings(): void { @@ -200,28 +126,4 @@ export default class VaultLinkPlugin extends Plugin { await workspace.revealLeaf(leaf); } } - - private registerRemoteEventListener( - settings: Settings, - database: Database, - syncService: SyncService, - syncer: Syncer, - intervalMs: number - ): void { - if (this.remoteListenerIntervalId !== null) { - window.clearInterval(this.remoteListenerIntervalId); - } - - this.remoteListenerIntervalId = window.setInterval( - // eslint-disable-next-line @typescript-eslint/no-misused-promises - async () => - applyRemoteChangesLocally({ - settings, - database, - syncService, - syncer - }), - intervalMs - ); - } } diff --git a/frontend/obsidian-plugin/src/views/settings-tab.ts b/frontend/obsidian-plugin/src/views/settings-tab.ts index 5f911c25..5cd59426 100644 --- a/frontend/obsidian-plugin/src/views/settings-tab.ts +++ b/frontend/obsidian-plugin/src/views/settings-tab.ts @@ -5,43 +5,35 @@ import type VaultLinkPlugin from "src/vault-link-plugin"; import type { StatusDescription } from "./status-description"; import { LogsView } from "./logs-view"; import { HistoryView } from "./history-view"; -import type { SyncService, Syncer, Settings } from "sync-client"; -import { Logger, LogLevel } from "sync-client"; +import type { SyncClient } from "sync-client"; +import { LogLevel } from "sync-client"; export class SyncSettingsTab extends PluginSettingTab { private editedVaultName: string; private readonly plugin: VaultLinkPlugin; - private readonly settings: Settings; - private readonly syncService: SyncService; + private readonly syncClient: SyncClient; private readonly statusDescription: StatusDescription; - private readonly syncer: Syncer; private statusDescriptionSubscription: (() => void) | undefined; public constructor({ app, plugin, - settings, - syncService, - statusDescription, - syncer + syncClient, + statusDescription }: { app: App; plugin: VaultLinkPlugin; - settings: Settings; - syncService: SyncService; + syncClient: SyncClient; statusDescription: StatusDescription; - syncer: Syncer; }) { super(app, plugin); this.plugin = plugin; - this.settings = settings; - this.syncService = syncService; + this.syncClient = syncClient; this.statusDescription = statusDescription; - this.syncer = syncer; - this.editedVaultName = this.settings.getSettings().vaultName; - this.settings.addOnSettingsChangeHandlers( + this.editedVaultName = this.syncClient.settings.getSettings().vaultName; + this.syncClient.settings.addOnSettingsChangeHandlers( (newSettings, oldSettings) => { if (newSettings.vaultName !== oldSettings.vaultName) { this.editedVaultName = newSettings.vaultName; @@ -130,15 +122,15 @@ export class SyncSettingsTab extends PluginSettingTab { .addText((text) => text .setPlaceholder("https://example.com:3030") - .setValue(this.settings.getSettings().remoteUri) + .setValue(this.syncClient.settings.getSettings().remoteUri) .onChange(async (value) => - this.settings.setSetting("remoteUri", value) + this.syncClient.settings.setSetting("remoteUri", value) ) ) .addButton((button) => button.setButtonText("Test connection").onClick(async () => { new Notice( - (await this.syncService.checkConnection()).message + (await this.syncClient.checkConnection()).message ); await this.statusDescription.updateConnectionState(); }) @@ -154,9 +146,9 @@ export class SyncSettingsTab extends PluginSettingTab { .addTextArea((text) => text .setPlaceholder("ey...") - .setValue(this.settings.getSettings().token) + .setValue(this.syncClient.settings.getSettings().token) .onChange(async (value) => - this.settings.setSetting("token", value) + this.syncClient.settings.setSetting("token", value) ) ); @@ -169,23 +161,22 @@ export class SyncSettingsTab extends PluginSettingTab { .addText((text) => text .setPlaceholder("My Obsidian Vault") - .setValue(this.settings.getSettings().vaultName) + .setValue(this.syncClient.settings.getSettings().vaultName) .onChange((value) => (this.editedVaultName = value)) ) .addButton((button) => button.setButtonText("Apply").onClick(async () => { if ( this.editedVaultName === - this.settings.getSettings().vaultName + this.syncClient.settings.getSettings().vaultName ) { return; } - await this.settings.setSetting( + await this.syncClient.settings.setSetting( "vaultName", this.editedVaultName ); - await this.syncer.reset(); - Logger.getInstance().reset(); + await this.syncClient.reset(); new Notice( "Sync state has been reset, you will need to resync" ); @@ -203,8 +194,7 @@ export class SyncSettingsTab extends PluginSettingTab { ) .addButton((button) => button.setButtonText("Reset sync state").onClick(async () => { - await this.syncer.reset(); - Logger.getInstance().reset(); + await this.syncClient.reset(); new Notice( "Sync state has been reset, you will need to resync" ); @@ -223,11 +213,11 @@ export class SyncSettingsTab extends PluginSettingTab { .setDynamicTooltip() .setInstant(false) .setValue( - this.settings.getSettings() + this.syncClient.settings.getSettings() .fetchChangesUpdateIntervalMs / 1000 ) .onChange(async (value) => - this.settings.setSetting( + this.syncClient.settings.setSetting( "fetchChangesUpdateIntervalMs", value * 1000 ) @@ -244,9 +234,14 @@ export class SyncSettingsTab extends PluginSettingTab { .setLimits(1, 16, 1) .setDynamicTooltip() .setInstant(false) - .setValue(this.settings.getSettings().syncConcurrency) + .setValue( + this.syncClient.settings.getSettings().syncConcurrency + ) .onChange(async (value) => - this.settings.setSetting("syncConcurrency", value) + this.syncClient.settings.setSetting( + "syncConcurrency", + value + ) ) ); @@ -260,9 +255,14 @@ export class SyncSettingsTab extends PluginSettingTab { .setLimits(0, 32, 1) .setDynamicTooltip() .setInstant(false) - .setValue(this.settings.getSettings().maxFileSizeMB) + .setValue( + this.syncClient.settings.getSettings().maxFileSizeMB + ) .onChange(async (value) => - this.settings.setSetting("maxFileSizeMB", value) + this.syncClient.settings.setSetting( + "maxFileSizeMB", + value + ) ) ); @@ -276,9 +276,14 @@ export class SyncSettingsTab extends PluginSettingTab { ) .addToggle((toggle) => toggle - .setValue(this.settings.getSettings().isSyncEnabled) + .setValue( + this.syncClient.settings.getSettings().isSyncEnabled + ) .onChange(async (value) => - this.settings.setSetting("isSyncEnabled", value) + this.syncClient.settings.setSetting( + "isSyncEnabled", + value + ) ) ); } @@ -293,9 +298,15 @@ export class SyncSettingsTab extends PluginSettingTab { ) .addToggle((toggle) => toggle - .setValue(this.settings.getSettings().displayNoopSyncEvents) + .setValue( + this.syncClient.settings.getSettings() + .displayNoopSyncEvents + ) .onChange(async (value) => - this.settings.setSetting("displayNoopSyncEvents", value) + this.syncClient.settings.setSetting( + "displayNoopSyncEvents", + value + ) ) ); @@ -312,9 +323,11 @@ export class SyncSettingsTab extends PluginSettingTab { [LogLevel.WARNING]: LogLevel.WARNING, [LogLevel.ERROR]: LogLevel.ERROR }) - .setValue(this.settings.getSettings().minimumLogLevel) + .setValue( + this.syncClient.settings.getSettings().minimumLogLevel + ) .onChange(async (value) => - this.settings.setSetting( + this.syncClient.settings.setSetting( "minimumLogLevel", // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion value as LogLevel diff --git a/frontend/obsidian-plugin/src/views/status-bar.ts b/frontend/obsidian-plugin/src/views/status-bar.ts index b5babae2..9ecd7b87 100644 --- a/frontend/obsidian-plugin/src/views/status-bar.ts +++ b/frontend/obsidian-plugin/src/views/status-bar.ts @@ -1,4 +1,4 @@ -import type { HistoryStats, Settings, SyncHistory, Syncer } from "sync-client"; +import type { HistoryStats, SyncClient } from "sync-client"; import type VaultLinkPlugin from "src/vault-link-plugin"; export class StatusBar { @@ -8,23 +8,23 @@ export class StatusBar { private lastRemaining: number | undefined; public constructor( - private readonly settings: Settings, private readonly plugin: VaultLinkPlugin, - history: SyncHistory, - syncer: Syncer + private readonly syncClient: SyncClient ) { this.statusBarItem = plugin.addStatusBarItem(); - history.addSyncHistoryUpdateListener((status) => { + this.syncClient.history.addSyncHistoryUpdateListener((status) => { this.lastHistoryStats = status; this.updateStatus(); }); - syncer.addRemainingOperationsListener((remainingOperations) => { - this.lastRemaining = remainingOperations; - this.updateStatus(); - }); + this.syncClient.syncer.addRemainingOperationsListener( + (remainingOperations) => { + this.lastRemaining = remainingOperations; + this.updateStatus(); + } + ); - settings.addOnSettingsChangeHandlers(() => { + this.syncClient.settings.addOnSettingsChangeHandlers(() => { this.updateStatus(); }); } @@ -57,7 +57,7 @@ export class StatusBar { } if (!hasShownMessage) { - if (this.settings.getSettings().isSyncEnabled) { + if (this.syncClient.settings.getSettings().isSyncEnabled) { container.createSpan({ text: "VaultLink is idle" }); } else { const button = container.createEl("button", { diff --git a/frontend/obsidian-plugin/src/views/status-description.ts b/frontend/obsidian-plugin/src/views/status-description.ts index b9c87ad8..381547d6 100644 --- a/frontend/obsidian-plugin/src/views/status-description.ts +++ b/frontend/obsidian-plugin/src/views/status-description.ts @@ -1,11 +1,7 @@ import type { HistoryStats, CheckConnectionResult, - SyncService, - SyncHistory, - Syncer, - Database, - Settings + SyncClient } from "sync-client"; export class StatusDescription { @@ -15,32 +11,28 @@ export class StatusDescription { private statusChangeListeners: (() => void)[] = []; - public constructor( - private readonly settings: Settings, - private readonly database: Database, - private readonly syncService: SyncService, - history: SyncHistory, - syncer: Syncer - ) { + public constructor(private readonly syncClient: SyncClient) { void this.updateConnectionState(); - history.addSyncHistoryUpdateListener((status) => { + syncClient.history.addSyncHistoryUpdateListener((status) => { this.lastHistoryStats = status; this.updateDescription(); }); - syncer.addRemainingOperationsListener((remainingOperations) => { - this.lastRemaining = remainingOperations; - this.updateDescription(); - }); + this.syncClient.syncer.addRemainingOperationsListener( + (remainingOperations) => { + this.lastRemaining = remainingOperations; + this.updateDescription(); + } + ); - settings.addOnSettingsChangeHandlers(() => { + this.syncClient.settings.addOnSettingsChangeHandlers(() => { void this.updateConnectionState(); }); } public async updateConnectionState(): Promise { - this.lastConnectionState = await this.syncService.checkConnection(); + this.lastConnectionState = await this.syncClient.checkConnection(); this.updateDescription(); } @@ -75,15 +67,15 @@ export class StatusDescription { container.createSpan({ text: "VaultLink is connected to the server " }); container.createEl("a", { - text: this.settings.getSettings().remoteUri, - href: this.settings.getSettings().remoteUri + text: this.syncClient.settings.getSettings().remoteUri, + href: this.syncClient.settings.getSettings().remoteUri }); container.createSpan({ text: ` and has indexed approximately ` }); container.createSpan({ - text: `${this.database.getDocuments().size}`, + text: `${this.syncClient.documentCount}`, cls: "number" }); container.createSpan({ @@ -95,7 +87,7 @@ export class StatusDescription { (this.lastHistoryStats?.success ?? 0) === 0 && (this.lastHistoryStats?.error ?? 0) === 0 ) { - if (this.settings.getSettings().isSyncEnabled) { + if (this.syncClient.settings.getSettings().isSyncEnabled) { container.createSpan({ text: "Syncing is enabled but VaultLink hasn't found anything to sync yet." });