diff --git a/src/CmdHiddenFileSync.ts b/src/CmdHiddenFileSync.ts index 6e750a5..a075bc3 100644 --- a/src/CmdHiddenFileSync.ts +++ b/src/CmdHiddenFileSync.ts @@ -215,7 +215,7 @@ export class HiddenFileSync extends LiveSyncCommands { } //TODO: Tidy up. Even though it is experimental feature, So dirty... - async syncInternalFilesAndDatabase(direction: "push" | "pull" | "safe", showMessage: boolean, files: InternalFileInfo[] | false = false, targetFiles: string[] | false = false) { + async syncInternalFilesAndDatabase(direction: "push" | "pull" | "safe" | "pullForce" | "pushForce", showMessage: boolean, files: InternalFileInfo[] | false = false, targetFiles: string[] | false = false) { await this.resolveConflictOnInternalFiles(); const logLevel = showMessage ? LOG_LEVEL.NOTICE : LOG_LEVEL.INFO; Logger("Scanning hidden files.", logLevel, "sync_internal"); @@ -289,43 +289,43 @@ export class HiddenFileSync extends LiveSyncCommands { p.push(addProc(async () => { const xFileOnStorage = fileOnStorage; - const xfileOnDatabase = fileOnDatabase; - if (xFileOnStorage && xfileOnDatabase) { + const xFileOnDatabase = fileOnDatabase; + if (xFileOnStorage && xFileOnDatabase) { // Both => Synchronize - if (xfileOnDatabase.mtime == cache.docMtime && xFileOnStorage.mtime == cache.storageMtime) { + if ((direction != "pullForce" && direction != "pushForce") && xFileOnDatabase.mtime == cache.docMtime && xFileOnStorage.mtime == cache.storageMtime) { return; } - const nw = compareMTime(xFileOnStorage.mtime, xfileOnDatabase.mtime); - if (nw > 0) { + const nw = compareMTime(xFileOnStorage.mtime, xFileOnDatabase.mtime); + if (nw > 0 || direction == "pushForce") { await this.storeInternalFileToDatabase(xFileOnStorage); } - if (nw < 0) { + if (nw < 0 || direction == "pullForce") { // skip if not extraction performed. if (!await this.extractInternalFileFromDatabase(filename)) return; } // If process successfully updated or file contents are same, update cache. - cache.docMtime = xfileOnDatabase.mtime; + cache.docMtime = xFileOnDatabase.mtime; cache.storageMtime = xFileOnStorage.mtime; caches[filename] = cache; countUpdatedFolder(filename); - } else if (!xFileOnStorage && xfileOnDatabase) { - if (direction == "push") { - if (xfileOnDatabase.deleted) + } else if (!xFileOnStorage && xFileOnDatabase) { + if (direction == "push" || direction == "pushForce") { + if (xFileOnDatabase.deleted) return; await this.deleteInternalFileOnDatabase(filename, false); - } else if (direction == "pull") { + } else if (direction == "pull" || direction == "pullForce") { if (await this.extractInternalFileFromDatabase(filename)) { countUpdatedFolder(filename); } } else if (direction == "safe") { - if (xfileOnDatabase.deleted) + if (xFileOnDatabase.deleted) return; if (await this.extractInternalFileFromDatabase(filename)) { countUpdatedFolder(filename); } } - } else if (xFileOnStorage && !xfileOnDatabase) { + } else if (xFileOnStorage && !xFileOnDatabase) { await this.storeInternalFileToDatabase(xFileOnStorage); } else { throw new Error("Invalid state on hidden file sync"); @@ -337,7 +337,7 @@ export class HiddenFileSync extends LiveSyncCommands { await this.kvDB.set("diff-caches-internal", caches); // When files has been retrieved from the database. they must be reloaded. - if (direction == "pull" && filesChanged != 0) { + if (direction == "pull" || direction == "pullForce" && filesChanged != 0) { const configDir = normalizePath(this.app.vault.configDir); // Show notification to restart obsidian when something has been changed in configDir. if (configDir in updatedFolders) { @@ -468,7 +468,7 @@ export class HiddenFileSync extends LiveSyncCommands { }; } else { if (isDocContentSame(old.data, content) && !forceWrite) { - // Logger(`internal files STORAGE --> DB:${file.path}: Not changed`); + // Logger(`STORAGE --> DB:${file.path}: (hidden) Not changed`, LOG_LEVEL.VERBOSE); return; } saveData = diff --git a/src/ObsidianLiveSyncSettingTab.ts b/src/ObsidianLiveSyncSettingTab.ts index 5c0c37c..2c37afb 100644 --- a/src/ObsidianLiveSyncSettingTab.ts +++ b/src/ObsidianLiveSyncSettingTab.ts @@ -966,7 +966,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { }); c.addClass("op-warn"); } - + containerSyncSettingEl.createEl("h3", { text: "Synchronization Methods" }); const syncLive: Setting[] = []; const syncNonLive: Setting[] = []; syncLive.push( @@ -1019,6 +1019,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { text.inputEl.setAttribute("type", "number"); }), + new Setting(containerSyncSettingEl) .setName("Sync on Save") .setDesc("When you save file, sync automatically") @@ -1060,7 +1061,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { }) ), ); - + containerSyncSettingEl.createEl("h3", { text: "File deletion" }); new Setting(containerSyncSettingEl) .setName("Use Trash for deleted files") .setDesc("Do not delete files that are deleted in remote, just move to trash.") @@ -1081,6 +1082,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { }) ); + containerSyncSettingEl.createEl("h3", { text: "Conflict resolution" }); new Setting(containerSyncSettingEl) .setName("Use newer file if conflicted (beta)") .setDesc("Resolve conflicts by newer files automatically.") @@ -1119,14 +1121,63 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { }) ); - new Setting(containerSyncSettingEl) - .setName("Sync hidden files") - .addToggle((toggle) => - toggle.setValue(this.plugin.settings.syncInternalFiles).onChange(async (value) => { - this.plugin.settings.syncInternalFiles = value; - await this.plugin.saveSettings(); + containerSyncSettingEl.createEl("h3", { text: "Hidden files" }); + const LABEL_ENABLED = "🔁 : Enabled"; + const LABEL_DISABLED = "âšī¸ : Disabled" + + const hiddenFileSyncSetting = new Setting(containerSyncSettingEl) + .setName("Hidden file synchronization") + const hiddenFileSyncSettingEl = hiddenFileSyncSetting.settingEl + const hiddenFileSyncSettingDiv = hiddenFileSyncSettingEl.createDiv(""); + hiddenFileSyncSettingDiv.innerText = this.plugin.settings.syncInternalFiles ? LABEL_ENABLED : LABEL_DISABLED; + + if (this.plugin.settings.syncInternalFiles) { + new Setting(containerSyncSettingEl) + .setName("Disable Hidden files sync") + .addButton((button) => { + button.setButtonText("Disable") + .onClick(async () => { + this.plugin.settings.syncInternalFiles = false; + await this.plugin.saveSettings(); + this.display(); + }) }) - ); + } else { + + new Setting(containerSyncSettingEl) + .setName("Enable Hidden files sync") + .addButton((button) => { + button.setButtonText("Merge") + .onClick(async () => { + this.plugin.settings.syncInternalFiles = true; + this.display(); + await this.plugin.addOnHiddenFileSync.syncInternalFilesAndDatabase("safe", true); + await this.plugin.saveSettings(); + this.display(); + }) + }) + .addButton((button) => { + button.setButtonText("Fetch") + .onClick(async () => { + this.plugin.settings.syncInternalFiles = true; + this.display(); + await this.plugin.addOnHiddenFileSync.syncInternalFilesAndDatabase("pullForce", true); + await this.plugin.saveSettings(); + Logger(`Restarting the app is strongly recommended!`, LOG_LEVEL.NOTICE); + this.display(); + }) + }) + .addButton((button) => { + button.setButtonText("Overwrite") + .onClick(async () => { + this.plugin.settings.syncInternalFiles = true; + this.display(); + await this.plugin.addOnHiddenFileSync.syncInternalFilesAndDatabase("pushForce", true); + await this.plugin.saveSettings(); + this.display(); + }) + }); + } new Setting(containerSyncSettingEl) .setName("Scan for hidden files before replication") @@ -1191,31 +1242,9 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { }) }) - new Setting(containerSyncSettingEl) - .setName("Touch hidden files") - .setDesc("Update the modified time of all hidden files to the current time.") - .addButton((button) => - button - .setButtonText("Touch") - .setWarning() - .setDisabled(false) - .onClick(async () => { - const filesAll = await this.plugin.addOnHiddenFileSync.scanInternalFiles(); - const targetFiles = await this.plugin.filterTargetFiles(filesAll); - const now = Date.now(); - const newFiles = targetFiles.map(e => ({ ...e, mtime: now })); - let i = 0; - const maxFiles = newFiles.length; - for (const file of newFiles) { - i++; - Logger(`Touched:${file.path} (${i}/${maxFiles})`, LOG_LEVEL.NOTICE, "touch-files"); - await this.plugin.applyMTimeToFile(file); - } - }) - ) containerSyncSettingEl.createEl("h3", { - text: sanitizeHTMLToDom(`Experimental`), + text: sanitizeHTMLToDom(`Synchronization filters`), }); new Setting(containerSyncSettingEl) .setName("Regular expression to ignore files") @@ -1263,7 +1292,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { return text; } ); - + containerSyncSettingEl.createEl("h3", { text: "Efficiency" }); new Setting(containerSyncSettingEl) .setName("Chunk size") .setDesc("Customize chunk size for binary files (0.1MBytes). This cannot be increased when using IBM Cloudant.") @@ -1282,7 +1311,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { }); new Setting(containerSyncSettingEl) - .setName("Read chunks online.") + .setName("Read chunks online") .setDesc("If this option is enabled, LiveSync reads chunks online directly instead of replicating them locally. Increasing Custom chunk size is recommended.") .addToggle((toggle) => { toggle @@ -1292,8 +1321,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { await this.plugin.saveSettings(); }) return toggle; - } - ); + }); containerSyncSettingEl.createEl("h3", { text: sanitizeHTMLToDom(`Advanced settings`), }); @@ -1487,8 +1515,8 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { } } this.plugin.saveSettings(); - await this.plugin.realizeSettingSyncMode(); this.display(); + await this.plugin.realizeSettingSyncMode(); if (inWizard) { // @ts-ignore this.plugin.app.setting.close()