diff --git a/src/settings/settings.ts b/src/settings/settings.ts index 1a607f3..2c917e2 100644 --- a/src/settings/settings.ts +++ b/src/settings/settings.ts @@ -9,6 +9,7 @@ export interface GitHubSyncSettings { syncStrategy: "manual" | "interval"; syncInterval: number; syncOnStartup: boolean; + syncConfigDir: boolean; conflictHandling: "ignore" | "ask" | "overwrite"; showStatusBarItem: boolean; showSyncRibbonButton: boolean; @@ -25,6 +26,7 @@ export const DEFAULT_SETTINGS: GitHubSyncSettings = { syncStrategy: "manual", syncInterval: 1, syncOnStartup: false, + syncConfigDir: false, conflictHandling: "ask", showStatusBarItem: true, showSyncRibbonButton: true, diff --git a/src/settings/tab.ts b/src/settings/tab.ts index 5f628e4..3dd36ce 100644 --- a/src/settings/tab.ts +++ b/src/settings/tab.ts @@ -181,6 +181,18 @@ export default class GitHubSyncSettingsTab extends PluginSettingTab { }); }); + new Setting(containerEl) + .setName("Sync configs") + .setDesc("Sync Vault config folder with remote repository") + .addToggle((toggle) => { + toggle + .setValue(this.plugin.settings.syncConfigDir) + .onChange(async (value) => { + this.plugin.settings.syncConfigDir = value; + await this.plugin.saveSettings(); + }); + }); + const conflictHandlingOptions = { ignore: "Ignore remote file", ask: "Ask", diff --git a/src/sync-manager.ts b/src/sync-manager.ts index fd77f23..1052ede 100644 --- a/src/sync-manager.ts +++ b/src/sync-manager.ts @@ -52,9 +52,9 @@ export default class SyncManager { [key: string]: GetTreeResponseItem; }): boolean { return ( - Object.keys(files).filter((filePath: string) => { - filePath.startsWith(this.settings.repoContentDir); - }).length === 0 + Object.keys(files).filter((filePath: string) => + filePath.startsWith(this.settings.repoContentDir), + ).length === 0 ); } @@ -72,10 +72,10 @@ export default class SyncManager { ); // There are files or folders in the local content dir return ( - files.length > 0 || + files.length === 0 || // We filter out the config dir in case the user wants to sync the whole // vault. The config dir is always present so it's fine if we find it. - folders.filter((f) => f !== this.vault.configDir).length > 0 + folders.filter((f) => f !== this.vault.configDir).length === 0 ); } return true; @@ -120,15 +120,21 @@ export default class SyncManager { files: { [key: string]: GetTreeResponseItem }, treeSha: string, ) { - // There's no remote manifest and there are files in the repo, - // though there are no files locally either, so we can just download everything - // and sync the remote manifest. await Promise.all( Object.keys(files) .filter((filePath: string) => { if (filePath.startsWith(this.settings.repoContentDir)) { return true; - } else if (filePath.startsWith(this.vault.configDir)) { + } else if ( + filePath === `${this.vault.configDir}/github-sync-metadata.json` + ) { + // The metadata file must always be synced + return true; + } else if ( + this.settings.syncConfigDir && + filePath.startsWith(this.vault.configDir) + ) { + // Include files in the config dir only if the user has enabled it return true; } return false; @@ -492,6 +498,18 @@ export default class SyncManager { } }); + if (!this.settings.syncConfigDir) { + // Remove all actions that involve the config directory if the user doesn't want to sync it. + // The manifest file is always synced. + return actions.filter((action: SyncAction) => { + return ( + !action.filePath.startsWith(this.vault.configDir) || + action.filePath === + `${this.vault.configDir}/github-sync-metadata.json` + ); + }); + } + return actions; } @@ -580,13 +598,15 @@ export default class SyncManager { async loadMetadata() { await this.metadataStore.load(); if (Object.keys(this.metadataStore.data.files).length === 0) { - // Must be the first time we run, initialize the metadata store - // with the files from the config directory let files = []; - let folders = [this.vault.configDir]; + let folders = [this.settings.localContentDir]; while (folders.length > 0) { const folder = folders.pop(); - if (!folder) { + if (folder === undefined) { + continue; + } + if (!this.settings.syncConfigDir && folder === this.vault.configDir) { + // Skip the config dir if the user doesn't want to sync it continue; } const res = await this.vault.adapter.list(folder); @@ -598,23 +618,34 @@ export default class SyncManager { // Obsidian recommends not syncing the workspace file return; } - if (filePath.startsWith(`${this.vault.configDir}/plugins`)) { - // Let's not sync plugins for the time being - return; + + // We change the remote path only if the file is not in the config dir + // as that directory is always at the root of the repo. + // If it's synced obviously. + let remotePath = filePath; + if (!filePath.startsWith(this.vault.configDir)) { + if (this.settings.localContentDir === "") { + remotePath = `${this.settings.repoContentDir}/${filePath}`; + } else { + remotePath = filePath.replace( + this.settings.localContentDir, + this.settings.repoContentDir, + ); + } } + this.metadataStore.data.files[filePath] = { localPath: filePath, - // The config dir is always stored in the repo root so we use - // the same path for remote - remotePath: filePath, + remotePath: remotePath, sha: null, dirty: false, justDownloaded: false, lastModified: Date.now(), }; }); - // Add itself, if we're here the manifest file doesn't exist - // so it can't add itself to the list of files + + // Must be the first time we run, initialize the metadata store + // with itself and all files in the local content dir. this.metadataStore.data.files[ `${this.vault.configDir}/github-sync-metadata.json` ] = { diff --git a/src/views/onboarding/component.tsx b/src/views/onboarding/component.tsx index a35ee50..bc55107 100644 --- a/src/views/onboarding/component.tsx +++ b/src/views/onboarding/component.tsx @@ -353,6 +353,7 @@ const FirstSyncStepComponent = ({ return []; }, ); + await syncManager.loadMetadata(); try { await syncManager.firstSync(); } catch (e) {